summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Dickson <SteveD@redhat.com>2009-09-09 23:06:05 +0400
committerJ. Bruce Fields <bfields@citi.umich.edu>2009-12-15 22:07:24 +0300
commit03a816b46d7eba78da11e4025f0af195b32fa464 (patch)
tree5069e9183dd5535c61bc7eff8a8a03202a19568d
parentf2ca7153ca49a407ea1c7232c9fa7e9849f03f9c (diff)
downloadlinux-03a816b46d7eba78da11e4025f0af195b32fa464.tar.xz
nfsd: restrict filehandles accepted in V4ROOT case
On V4ROOT exports, only accept filehandles that are the *root* of some export. This allows mountd to allow or deny access to individual directories and symlinks on the pseudofilesystem. Note that the checks in readdir and lookup are not enough, since a malicious host with access to the network could guess filehandles that they weren't able to obtain through lookup or readdir. Signed-off-by: Steve Dickson <steved@redhat.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
-rw-r--r--fs/nfsd/nfsd.h5
-rw-r--r--fs/nfsd/nfsfh.c34
-rw-r--r--fs/nfsd/vfs.c6
3 files changed, 39 insertions, 6 deletions
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 74f67c2aca34..ac121ad16540 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -70,6 +70,11 @@ int nfsd_create_serv(void);
extern int nfsd_max_blksize;
+static inline int nfsd_v4client(struct svc_rqst *rq)
+{
+ return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
+}
+
/*
* NFSv4 State
*/
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 951938d6c495..44812c32e51e 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -103,6 +103,36 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
return nfserrno(nfsd_setuser(rqstp, exp));
}
+static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
+ struct dentry *dentry, struct svc_export *exp)
+{
+ if (!(exp->ex_flags & NFSEXP_V4ROOT))
+ return nfs_ok;
+ /*
+ * v2/v3 clients have no need for the V4ROOT export--they use
+ * the mount protocl instead; also, further V4ROOT checks may be
+ * in v4-specific code, in which case v2/v3 clients could bypass
+ * them.
+ */
+ if (!nfsd_v4client(rqstp))
+ return nfserr_stale;
+ /*
+ * We're exposing only the directories and symlinks that have to be
+ * traversed on the way to real exports:
+ */
+ if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) &&
+ !S_ISLNK(dentry->d_inode->i_mode)))
+ return nfserr_stale;
+ /*
+ * A pseudoroot export gives permission to access only one
+ * single directory; the kernel has to make another upcall
+ * before granting access to anything else under it:
+ */
+ if (unlikely(dentry != exp->ex_path.dentry))
+ return nfserr_stale;
+ return nfs_ok;
+}
+
/*
* Use the given filehandle to look up the corresponding export and
* dentry. On success, the results are used to set fh_export and
@@ -299,6 +329,10 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
* (for example, if different id-squashing options are in
* effect on the new filesystem).
*/
+ error = check_pseudo_root(rqstp, dentry, exp);
+ if (error)
+ goto out;
+
error = nfsd_setuser_and_check_port(rqstp, exp);
if (error)
goto out;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index a0015a958aef..f6ca32b07e11 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -72,12 +72,6 @@ struct raparm_hbucket {
#define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1)
static struct raparm_hbucket raparm_hash[RAPARM_HASH_SIZE];
-static inline int
-nfsd_v4client(struct svc_rqst *rq)
-{
- return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
-}
-
/*
* Called from nfsd_lookup and encode_dirent. Check if we have crossed
* a mount point.