Commit 738a35195941ecf604d3070e2a053e1df3de350b

Authored by David Howells
Committed by Trond Myklebust
1 parent 27ba851244

NFS: Secure the roots of the NFS subtrees in a shared superblock

Invoke security_d_instantiate() on root dentries after allocating them with
dentry_alloc_anon().  Normally dentry_alloc_root() would do that, but we don't
call that as we don't want to assign a name to the root dentry at this point
(we may discover the real name later).

Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

Showing 1 changed file with 5 additions and 0 deletions Inline Diff

1 /* getroot.c: get the root dentry for an NFS mount 1 /* getroot.c: get the root dentry for an NFS mount
2 * 2 *
3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com) 4 * Written by David Howells (dhowells@redhat.com)
5 * 5 *
6 * This program is free software; you can redistribute it and/or 6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License 7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version. 9 * 2 of the License, or (at your option) any later version.
10 */ 10 */
11 11
12 #include <linux/config.h> 12 #include <linux/config.h>
13 #include <linux/module.h> 13 #include <linux/module.h>
14 #include <linux/init.h> 14 #include <linux/init.h>
15 15
16 #include <linux/time.h> 16 #include <linux/time.h>
17 #include <linux/kernel.h> 17 #include <linux/kernel.h>
18 #include <linux/mm.h> 18 #include <linux/mm.h>
19 #include <linux/string.h> 19 #include <linux/string.h>
20 #include <linux/stat.h> 20 #include <linux/stat.h>
21 #include <linux/errno.h> 21 #include <linux/errno.h>
22 #include <linux/unistd.h> 22 #include <linux/unistd.h>
23 #include <linux/sunrpc/clnt.h> 23 #include <linux/sunrpc/clnt.h>
24 #include <linux/sunrpc/stats.h> 24 #include <linux/sunrpc/stats.h>
25 #include <linux/nfs_fs.h> 25 #include <linux/nfs_fs.h>
26 #include <linux/nfs_mount.h> 26 #include <linux/nfs_mount.h>
27 #include <linux/nfs4_mount.h> 27 #include <linux/nfs4_mount.h>
28 #include <linux/lockd/bind.h> 28 #include <linux/lockd/bind.h>
29 #include <linux/smp_lock.h> 29 #include <linux/smp_lock.h>
30 #include <linux/seq_file.h> 30 #include <linux/seq_file.h>
31 #include <linux/mount.h> 31 #include <linux/mount.h>
32 #include <linux/nfs_idmap.h> 32 #include <linux/nfs_idmap.h>
33 #include <linux/vfs.h> 33 #include <linux/vfs.h>
34 #include <linux/namei.h> 34 #include <linux/namei.h>
35 #include <linux/namespace.h> 35 #include <linux/namespace.h>
36 #include <linux/security.h>
36 37
37 #include <asm/system.h> 38 #include <asm/system.h>
38 #include <asm/uaccess.h> 39 #include <asm/uaccess.h>
39 40
40 #include "nfs4_fs.h" 41 #include "nfs4_fs.h"
41 #include "delegation.h" 42 #include "delegation.h"
42 #include "internal.h" 43 #include "internal.h"
43 44
44 #define NFSDBG_FACILITY NFSDBG_CLIENT 45 #define NFSDBG_FACILITY NFSDBG_CLIENT
45 #define NFS_PARANOIA 1 46 #define NFS_PARANOIA 1
46 47
47 /* 48 /*
48 * get an NFS2/NFS3 root dentry from the root filehandle 49 * get an NFS2/NFS3 root dentry from the root filehandle
49 */ 50 */
50 struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) 51 struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
51 { 52 {
52 struct nfs_server *server = NFS_SB(sb); 53 struct nfs_server *server = NFS_SB(sb);
53 struct nfs_fsinfo fsinfo; 54 struct nfs_fsinfo fsinfo;
54 struct nfs_fattr fattr; 55 struct nfs_fattr fattr;
55 struct dentry *mntroot; 56 struct dentry *mntroot;
56 struct inode *inode; 57 struct inode *inode;
57 int error; 58 int error;
58 59
59 /* create a dummy root dentry with dummy inode for this superblock */ 60 /* create a dummy root dentry with dummy inode for this superblock */
60 if (!sb->s_root) { 61 if (!sb->s_root) {
61 struct nfs_fh dummyfh; 62 struct nfs_fh dummyfh;
62 struct dentry *root; 63 struct dentry *root;
63 struct inode *iroot; 64 struct inode *iroot;
64 65
65 memset(&dummyfh, 0, sizeof(dummyfh)); 66 memset(&dummyfh, 0, sizeof(dummyfh));
66 memset(&fattr, 0, sizeof(fattr)); 67 memset(&fattr, 0, sizeof(fattr));
67 nfs_fattr_init(&fattr); 68 nfs_fattr_init(&fattr);
68 fattr.valid = NFS_ATTR_FATTR; 69 fattr.valid = NFS_ATTR_FATTR;
69 fattr.type = NFDIR; 70 fattr.type = NFDIR;
70 fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; 71 fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
71 fattr.nlink = 2; 72 fattr.nlink = 2;
72 73
73 iroot = nfs_fhget(sb, &dummyfh, &fattr); 74 iroot = nfs_fhget(sb, &dummyfh, &fattr);
74 if (IS_ERR(iroot)) 75 if (IS_ERR(iroot))
75 return ERR_PTR(PTR_ERR(iroot)); 76 return ERR_PTR(PTR_ERR(iroot));
76 77
77 root = d_alloc_root(iroot); 78 root = d_alloc_root(iroot);
78 if (!root) { 79 if (!root) {
79 iput(iroot); 80 iput(iroot);
80 return ERR_PTR(-ENOMEM); 81 return ERR_PTR(-ENOMEM);
81 } 82 }
82 83
83 sb->s_root = root; 84 sb->s_root = root;
84 } 85 }
85 86
86 /* get the actual root for this mount */ 87 /* get the actual root for this mount */
87 fsinfo.fattr = &fattr; 88 fsinfo.fattr = &fattr;
88 89
89 error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 90 error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
90 if (error < 0) { 91 if (error < 0) {
91 dprintk("nfs_get_root: getattr error = %d\n", -error); 92 dprintk("nfs_get_root: getattr error = %d\n", -error);
92 return ERR_PTR(error); 93 return ERR_PTR(error);
93 } 94 }
94 95
95 inode = nfs_fhget(sb, mntfh, fsinfo.fattr); 96 inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
96 if (IS_ERR(inode)) { 97 if (IS_ERR(inode)) {
97 dprintk("nfs_get_root: get root inode failed\n"); 98 dprintk("nfs_get_root: get root inode failed\n");
98 return ERR_PTR(PTR_ERR(inode)); 99 return ERR_PTR(PTR_ERR(inode));
99 } 100 }
100 101
101 /* root dentries normally start off anonymous and get spliced in later 102 /* root dentries normally start off anonymous and get spliced in later
102 * if the dentry tree reaches them; however if the dentry already 103 * if the dentry tree reaches them; however if the dentry already
103 * exists, we'll pick it up at this point and use it as the root 104 * exists, we'll pick it up at this point and use it as the root
104 */ 105 */
105 mntroot = d_alloc_anon(inode); 106 mntroot = d_alloc_anon(inode);
106 if (!mntroot) { 107 if (!mntroot) {
107 iput(inode); 108 iput(inode);
108 dprintk("nfs_get_root: get root dentry failed\n"); 109 dprintk("nfs_get_root: get root dentry failed\n");
109 return ERR_PTR(-ENOMEM); 110 return ERR_PTR(-ENOMEM);
110 } 111 }
111 112
113 security_d_instantiate(mntroot, inode);
114
112 if (!mntroot->d_op) 115 if (!mntroot->d_op)
113 mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; 116 mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
114 117
115 return mntroot; 118 return mntroot;
116 } 119 }
117 120
118 #ifdef CONFIG_NFS_V4 121 #ifdef CONFIG_NFS_V4
119 122
120 /* 123 /*
121 * Do a simple pathwalk from the root FH of the server to the nominated target 124 * Do a simple pathwalk from the root FH of the server to the nominated target
122 * of the mountpoint 125 * of the mountpoint
123 * - give error on symlinks 126 * - give error on symlinks
124 * - give error on ".." occurring in the path 127 * - give error on ".." occurring in the path
125 * - follow traversals 128 * - follow traversals
126 */ 129 */
127 int nfs4_path_walk(struct nfs_server *server, 130 int nfs4_path_walk(struct nfs_server *server,
128 struct nfs_fh *mntfh, 131 struct nfs_fh *mntfh,
129 const char *path) 132 const char *path)
130 { 133 {
131 struct nfs_fsinfo fsinfo; 134 struct nfs_fsinfo fsinfo;
132 struct nfs_fattr fattr; 135 struct nfs_fattr fattr;
133 struct nfs_fh lastfh; 136 struct nfs_fh lastfh;
134 struct qstr name; 137 struct qstr name;
135 int ret; 138 int ret;
136 //int referral_count = 0; 139 //int referral_count = 0;
137 140
138 dprintk("--> nfs4_path_walk(,,%s)\n", path); 141 dprintk("--> nfs4_path_walk(,,%s)\n", path);
139 142
140 fsinfo.fattr = &fattr; 143 fsinfo.fattr = &fattr;
141 nfs_fattr_init(&fattr); 144 nfs_fattr_init(&fattr);
142 145
143 if (*path++ != '/') { 146 if (*path++ != '/') {
144 dprintk("nfs4_get_root: Path does not begin with a slash\n"); 147 dprintk("nfs4_get_root: Path does not begin with a slash\n");
145 return -EINVAL; 148 return -EINVAL;
146 } 149 }
147 150
148 /* Start by getting the root filehandle from the server */ 151 /* Start by getting the root filehandle from the server */
149 ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 152 ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
150 if (ret < 0) { 153 if (ret < 0) {
151 dprintk("nfs4_get_root: getroot error = %d\n", -ret); 154 dprintk("nfs4_get_root: getroot error = %d\n", -ret);
152 return ret; 155 return ret;
153 } 156 }
154 157
155 if (fattr.type != NFDIR) { 158 if (fattr.type != NFDIR) {
156 printk(KERN_ERR "nfs4_get_root:" 159 printk(KERN_ERR "nfs4_get_root:"
157 " getroot encountered non-directory\n"); 160 " getroot encountered non-directory\n");
158 return -ENOTDIR; 161 return -ENOTDIR;
159 } 162 }
160 163
161 if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { 164 if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
162 printk(KERN_ERR "nfs4_get_root:" 165 printk(KERN_ERR "nfs4_get_root:"
163 " getroot obtained referral\n"); 166 " getroot obtained referral\n");
164 return -EREMOTE; 167 return -EREMOTE;
165 } 168 }
166 169
167 next_component: 170 next_component:
168 dprintk("Next: %s\n", path); 171 dprintk("Next: %s\n", path);
169 172
170 /* extract the next bit of the path */ 173 /* extract the next bit of the path */
171 if (!*path) 174 if (!*path)
172 goto path_walk_complete; 175 goto path_walk_complete;
173 176
174 name.name = path; 177 name.name = path;
175 while (*path && *path != '/') 178 while (*path && *path != '/')
176 path++; 179 path++;
177 name.len = path - (const char *) name.name; 180 name.len = path - (const char *) name.name;
178 181
179 eat_dot_dir: 182 eat_dot_dir:
180 while (*path == '/') 183 while (*path == '/')
181 path++; 184 path++;
182 185
183 if (path[0] == '.' && (path[1] == '/' || !path[1])) { 186 if (path[0] == '.' && (path[1] == '/' || !path[1])) {
184 path += 2; 187 path += 2;
185 goto eat_dot_dir; 188 goto eat_dot_dir;
186 } 189 }
187 190
188 if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) 191 if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2])
189 ) { 192 ) {
190 printk(KERN_ERR "nfs4_get_root:" 193 printk(KERN_ERR "nfs4_get_root:"
191 " Mount path contains reference to \"..\"\n"); 194 " Mount path contains reference to \"..\"\n");
192 return -EINVAL; 195 return -EINVAL;
193 } 196 }
194 197
195 /* lookup the next FH in the sequence */ 198 /* lookup the next FH in the sequence */
196 memcpy(&lastfh, mntfh, sizeof(lastfh)); 199 memcpy(&lastfh, mntfh, sizeof(lastfh));
197 200
198 dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path); 201 dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path);
199 202
200 ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name, 203 ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name,
201 mntfh, &fattr); 204 mntfh, &fattr);
202 if (ret < 0) { 205 if (ret < 0) {
203 dprintk("nfs4_get_root: getroot error = %d\n", -ret); 206 dprintk("nfs4_get_root: getroot error = %d\n", -ret);
204 return ret; 207 return ret;
205 } 208 }
206 209
207 if (fattr.type != NFDIR) { 210 if (fattr.type != NFDIR) {
208 printk(KERN_ERR "nfs4_get_root:" 211 printk(KERN_ERR "nfs4_get_root:"
209 " lookupfh encountered non-directory\n"); 212 " lookupfh encountered non-directory\n");
210 return -ENOTDIR; 213 return -ENOTDIR;
211 } 214 }
212 215
213 if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { 216 if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
214 printk(KERN_ERR "nfs4_get_root:" 217 printk(KERN_ERR "nfs4_get_root:"
215 " lookupfh obtained referral\n"); 218 " lookupfh obtained referral\n");
216 return -EREMOTE; 219 return -EREMOTE;
217 } 220 }
218 221
219 goto next_component; 222 goto next_component;
220 223
221 path_walk_complete: 224 path_walk_complete:
222 memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); 225 memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
223 dprintk("<-- nfs4_path_walk() = 0\n"); 226 dprintk("<-- nfs4_path_walk() = 0\n");
224 return 0; 227 return 0;
225 } 228 }
226 229
227 /* 230 /*
228 * get an NFS4 root dentry from the root filehandle 231 * get an NFS4 root dentry from the root filehandle
229 */ 232 */
230 struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) 233 struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
231 { 234 {
232 struct nfs_server *server = NFS_SB(sb); 235 struct nfs_server *server = NFS_SB(sb);
233 struct nfs_fattr fattr; 236 struct nfs_fattr fattr;
234 struct dentry *mntroot; 237 struct dentry *mntroot;
235 struct inode *inode; 238 struct inode *inode;
236 int error; 239 int error;
237 240
238 dprintk("--> nfs4_get_root()\n"); 241 dprintk("--> nfs4_get_root()\n");
239 242
240 /* create a dummy root dentry with dummy inode for this superblock */ 243 /* create a dummy root dentry with dummy inode for this superblock */
241 if (!sb->s_root) { 244 if (!sb->s_root) {
242 struct nfs_fh dummyfh; 245 struct nfs_fh dummyfh;
243 struct dentry *root; 246 struct dentry *root;
244 struct inode *iroot; 247 struct inode *iroot;
245 248
246 memset(&dummyfh, 0, sizeof(dummyfh)); 249 memset(&dummyfh, 0, sizeof(dummyfh));
247 memset(&fattr, 0, sizeof(fattr)); 250 memset(&fattr, 0, sizeof(fattr));
248 nfs_fattr_init(&fattr); 251 nfs_fattr_init(&fattr);
249 fattr.valid = NFS_ATTR_FATTR; 252 fattr.valid = NFS_ATTR_FATTR;
250 fattr.type = NFDIR; 253 fattr.type = NFDIR;
251 fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; 254 fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
252 fattr.nlink = 2; 255 fattr.nlink = 2;
253 256
254 iroot = nfs_fhget(sb, &dummyfh, &fattr); 257 iroot = nfs_fhget(sb, &dummyfh, &fattr);
255 if (IS_ERR(iroot)) 258 if (IS_ERR(iroot))
256 return ERR_PTR(PTR_ERR(iroot)); 259 return ERR_PTR(PTR_ERR(iroot));
257 260
258 root = d_alloc_root(iroot); 261 root = d_alloc_root(iroot);
259 if (!root) { 262 if (!root) {
260 iput(iroot); 263 iput(iroot);
261 return ERR_PTR(-ENOMEM); 264 return ERR_PTR(-ENOMEM);
262 } 265 }
263 266
264 sb->s_root = root; 267 sb->s_root = root;
265 } 268 }
266 269
267 /* get the info about the server and filesystem */ 270 /* get the info about the server and filesystem */
268 error = nfs4_server_capabilities(server, mntfh); 271 error = nfs4_server_capabilities(server, mntfh);
269 if (error < 0) { 272 if (error < 0) {
270 dprintk("nfs_get_root: getcaps error = %d\n", 273 dprintk("nfs_get_root: getcaps error = %d\n",
271 -error); 274 -error);
272 return ERR_PTR(error); 275 return ERR_PTR(error);
273 } 276 }
274 277
275 /* get the actual root for this mount */ 278 /* get the actual root for this mount */
276 error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); 279 error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
277 if (error < 0) { 280 if (error < 0) {
278 dprintk("nfs_get_root: getattr error = %d\n", -error); 281 dprintk("nfs_get_root: getattr error = %d\n", -error);
279 return ERR_PTR(error); 282 return ERR_PTR(error);
280 } 283 }
281 284
282 inode = nfs_fhget(sb, mntfh, &fattr); 285 inode = nfs_fhget(sb, mntfh, &fattr);
283 if (IS_ERR(inode)) { 286 if (IS_ERR(inode)) {
284 dprintk("nfs_get_root: get root inode failed\n"); 287 dprintk("nfs_get_root: get root inode failed\n");
285 return ERR_PTR(PTR_ERR(inode)); 288 return ERR_PTR(PTR_ERR(inode));
286 } 289 }
287 290
288 /* root dentries normally start off anonymous and get spliced in later 291 /* root dentries normally start off anonymous and get spliced in later
289 * if the dentry tree reaches them; however if the dentry already 292 * if the dentry tree reaches them; however if the dentry already
290 * exists, we'll pick it up at this point and use it as the root 293 * exists, we'll pick it up at this point and use it as the root
291 */ 294 */
292 mntroot = d_alloc_anon(inode); 295 mntroot = d_alloc_anon(inode);
293 if (!mntroot) { 296 if (!mntroot) {
294 iput(inode); 297 iput(inode);
295 dprintk("nfs_get_root: get root dentry failed\n"); 298 dprintk("nfs_get_root: get root dentry failed\n");
296 return ERR_PTR(-ENOMEM); 299 return ERR_PTR(-ENOMEM);
297 } 300 }
301
302 security_d_instantiate(mntroot, inode);
298 303
299 if (!mntroot->d_op) 304 if (!mntroot->d_op)
300 mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; 305 mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
301 306
302 dprintk("<-- nfs4_get_root()\n"); 307 dprintk("<-- nfs4_get_root()\n");
303 return mntroot; 308 return mntroot;
304 } 309 }
305 310
306 #endif /* CONFIG_NFS_V4 */ 311 #endif /* CONFIG_NFS_V4 */
307 312