Messages in this thread Patch in this message | | | Subject | [PATCH] kernel nfsd security and access. | Date | Tue, 03 Nov 1998 21:30:38 -0800 | From | "G. Allen Morris III" <> |
| |
Here is a new patch for fs/nfsd.
This patch fixes four problems:
1) Groups where not being handles correctly.
2) Capabilities where not set correctly. (This would let any user traverse directories w/o x permission.)
3) mountd could segfault exp_rootfh().
4) There was a security problem with sub-mounts that would allow access to portions of a filesystem that would not be accessible if sub-mounts where not available. This patch will only allow you to make a sub_mount for a root_squashed export is at least one of x mode bits is set in each directory between that export point and the sub-mount. Interestingly both IRIX 5.3 and SunOS 4 nfs servers allow these sub-mounts.
BUGS: 0111 is tested directly against the i_mode.
------------------------- cut here -------------------------------
Index: linux/fs/nfsd/auth.c diff -u linux/fs/nfsd/auth.c:1.1 linux/fs/nfsd/auth.c:1.1.2.2 --- linux/fs/nfsd/auth.c:1.1 Fri Oct 16 12:08:05 1998 +++ linux/fs/nfsd/auth.c Fri Oct 30 16:41:00 1998 @@ -41,7 +41,20 @@ current->fsgid = cred->cr_gid; else current->fsgid = exp->ex_anon_gid; - for (i = 0; i < NGROUPS; i++) + for (i = 0; i < NGROUPS; i++) { current->groups[i] = cred->cr_groups[i]; + if (current->groups[i] == NOGROUP) + break; + } + current->ngroups = i; + + if ((cred->cr_uid)) { + cap_lower(current->cap_effective, CAP_DAC_OVERRIDE); + cap_lower(current->cap_effective, CAP_DAC_READ_SEARCH); + } else { + cap_raise(current->cap_effective, CAP_DAC_OVERRIDE); + cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH); + } + rqstp->rq_userset = 1; } Index: linux/fs/nfsd/export.c diff -u linux/fs/nfsd/export.c:1.5 linux/fs/nfsd/export.c:1.1.2.30 --- linux/fs/nfsd/export.c:1.5 Sat Oct 24 11:36:37 1998 +++ linux/fs/nfsd/export.c Tue Nov 3 14:38:31 1998 @@ -457,14 +457,17 @@ char *path, struct knfs_fh *f) { struct svc_export *exp; - struct dentry *dentry; + struct dentry *dentry = NULL; struct inode *inode; struct svc_fh fh; int err; + err = -EPERM; if (path) { - dentry = lookup_dentry(path, NULL, 0); - + if (!(dentry = lookup_dentry(path, NULL, 0))) { + printk("nfsd: exp_rootfh path not found %s", path); + return -EPERM; + } dev = dentry->d_inode->i_dev; ino = dentry->d_inode->i_ino; @@ -474,17 +477,21 @@ } else { dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino); - exp = exp_get(clp, dev, ino); - dentry = dget(exp->ex_dentry); + if ((exp = exp_get(clp, dev, ino))) + if (!(dentry = dget(exp->ex_dentry))) { + printk("exp_rootfh: Aieee, NULL dentry\n"); + return -EPERM; + } } - err = -EPERM; - if (!exp) + if (!exp) { + dprintk("nfsd: exp_rootfh export not found.\n"); goto out; + } inode = dentry->d_inode; if (!inode) { printk("exp_rootfh: Aieee, NULL d_inode\n"); - return -EPERM; + goto out; } if (inode->i_dev != dev || inode->i_ino != ino) { printk("exp_rootfh: Aieee, ino/dev mismatch\n"); Index: linux/fs/nfsd/nfsfh.c diff -u linux/fs/nfsd/nfsfh.c:1.2 linux/fs/nfsd/nfsfh.c:1.1.2.8 --- linux/fs/nfsd/nfsfh.c:1.2 Sat Oct 24 11:36:37 1998 +++ linux/fs/nfsd/nfsfh.c Mon Nov 2 19:50:24 1998 @@ -1061,31 +1061,6 @@ goto out; /* - * Security: Check that the export is valid for dentry <gam3@acm.org> - */ - if (fh->fh_dev != fh->fh_xdev) { - printk("fh_verify: Security: export on other device" - " (%d, %d).\n", fh->fh_dev, fh->fh_xdev); - goto out; - } else { - struct dentry *tdentry = dentry; - - do { - if (exp->ex_dentry == tdentry) { - error = 0; - break; - } - if (tdentry->d_parent == tdentry) - break; - } while ((tdentry = tdentry->d_parent)); - if (error) { - printk("fh_verify: Security: %s/%s bad export.\n", - dentry->d_parent->d_name.name, - dentry->d_name.name); - goto out; - } - } - /* * Note: it's possible the returned dentry won't be the one in the * file handle. We can correct the file handle for our use, but * unfortunately the client will keep sending the broken one. Let's @@ -1105,6 +1080,7 @@ check_type: dentry = fhp->fh_dentry; inode = dentry->d_inode; + exp = fhp->fh_export; if (type > 0 && (inode->i_mode & S_IFMT) != type) { error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir; goto out; @@ -1114,9 +1090,45 @@ goto out; } + /* + * Security: Check that the export is valid for dentry <gam3@acm.org> + */ + if (fh->fh_dev != fh->fh_xdev) { + printk("fh_verify: Security: export on other device" + " (%d, %d).\n", fh->fh_dev, fh->fh_xdev); + goto out; + } else if (exp->ex_dentry != dentry) { + struct dentry *tdentry = dentry; + error = nfserr_stale; + + do { + tdentry = tdentry->d_parent; + if (exp->ex_dentry == tdentry) { + error = 0; + break; + } + /* must have an execute bit set unless root */ + if (current->fsuid && !(tdentry->d_inode->i_mode & 0111)) { + error = nfserr_stale; +#ifdef NFSD_PARANOIA + goto out1; +#else + goto out; +#endif + } + } while ((tdentry != tdentry->d_parent)); + if (error) { + printk("fh_verify: Security: %s/%s bad export.\n", + dentry->d_parent->d_name.name, + dentry->d_name.name); + goto out; + } + } + /* Finally, check access permissions. */ - error = nfsd_permission(fhp->fh_export, dentry, access); + error = nfsd_permission(exp, dentry, access); #ifdef NFSD_PARANOIA +out1: if (error) printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, access, error); - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |