nfsd: restrict filehandles accepted in V4ROOT case
authorSteve Dickson <SteveD@redhat.com>
Wed, 9 Sep 2009 19:06:05 +0000 (15:06 -0400)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Tue, 15 Dec 2009 19:07:24 +0000 (14:07 -0500)
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>
fs/nfsd/nfsd.h
fs/nfsd/nfsfh.c
fs/nfsd/vfs.c

index 74f67c2aca3470564bdbd79bfc5ed558f22c97aa..ac121ad1654036c79b23ed79f7f9163a84bd8bab 100644 (file)
@@ -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
  */
index 951938d6c495c4536b6a988fd628460c4b18494a..44812c32e51e404c50df0c2740fda3411f506fe1 100644 (file)
@@ -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;
index a0015a958aeff545688b10bf8ee85ef88c7f069c..f6ca32b07e11e2bb9a1d1e2b192a863b95b66259 100644 (file)
@@ -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.