lkml.org 
[lkml]   [1998]   [Nov]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH] kernel nfsd security and access.
Date
From
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/

\
 
 \ /
  Last update: 2005-03-22 13:45    [W:0.029 / U:0.564 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site