Commit bea92fd5 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] add proper NFSv3 permissions checking.

Add full support for the NFSv3 permissions checking. Ensures that we
work properly with NFSv3 servers that do uid/gid mapping and/or have
support for ACLs.

Permissions are cached in the struct nfs_inode in order to reduce the
number of RPC calls. The cache timeout period is given by the ordinary
attribute timeout.
parent 3322b8ec
...@@ -1080,38 +1080,70 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1080,38 +1080,70 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
int int
nfs_permission(struct inode *inode, int mask) nfs_permission(struct inode *inode, int mask)
{ {
int error = vfs_permission(inode, mask); struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
struct rpc_cred *cred;
int mode = inode->i_mode;
int res;
if (!NFS_PROTO(inode)->access) if (mask & MAY_WRITE) {
goto out; /*
*
if (error == -EROFS) * Nobody gets write access to a read-only fs.
goto out; *
*/
if (IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/* /*
* Trust UNIX mode bits except: *
* * Nobody gets write access to an immutable file.
* 1) When override capabilities may have been invoked *
* 2) When root squashing may be involved */
* 3) When ACLs may overturn a negative answer */ if (IS_IMMUTABLE(inode))
if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH) return -EACCES;
&& (current->fsuid != 0) && (current->fsgid != 0) }
&& error != -EACCES)
goto out;
lock_kernel(); lock_kernel();
error = NFS_PROTO(inode)->access(inode, mask, 0); if (!NFS_PROTO(inode)->access)
goto out_notsup;
if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv &&
current->uid != 0 && current->gid != 0 && cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
(current->fsuid != current->uid || current->fsgid != current->gid)) if (cache->cred == cred
error = NFS_PROTO(inode)->access(inode, mask, 1); && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) {
if (!(res = cache->err)) {
/* Is the mask a subset of an accepted mask? */
if ((cache->mask & mask) == mask)
goto out;
} else {
/* ...or is it a superset of a rejected mask? */
if ((cache->mask & mask) == cache->mask)
goto out;
}
}
res = NFS_PROTO(inode)->access(inode, cred, mask);
if (!res || res == -EACCES)
goto add_cache;
out:
put_rpccred(cred);
unlock_kernel(); unlock_kernel();
return res;
out: out_notsup:
return error; nfs_revalidate_inode(NFS_SERVER(inode), inode);
res = vfs_permission(inode, mask);
unlock_kernel();
return res;
add_cache:
cache->jiffies = jiffies;
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = cred;
cache->mask = mask;
cache->err = res;
unlock_kernel();
return res;
} }
/* /*
......
...@@ -125,8 +125,12 @@ nfs_delete_inode(struct inode * inode) ...@@ -125,8 +125,12 @@ nfs_delete_inode(struct inode * inode)
static void static void
nfs_clear_inode(struct inode *inode) nfs_clear_inode(struct inode *inode)
{ {
struct rpc_cred *cred = NFS_I(inode)->mm_cred; struct nfs_inode *nfsi = NFS_I(inode);
struct rpc_cred *cred = nfsi->mm_cred;
if (cred)
put_rpccred(cred);
cred = nfsi->cache_access.cred;
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
} }
...@@ -722,6 +726,7 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -722,6 +726,7 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies; NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
NFS_I(inode)->cache_access.cred = NULL;
unlock_new_inode(inode); unlock_new_inode(inode);
} else } else
...@@ -1085,6 +1090,16 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1085,6 +1090,16 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
NFS_CACHE_ISIZE(inode) = new_size; NFS_CACHE_ISIZE(inode) = new_size;
inode->i_size = new_isize; inode->i_size = new_isize;
if (inode->i_mode != fattr->mode ||
inode->i_uid != fattr->uid ||
inode->i_gid != fattr->gid) {
struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
if (*cred) {
put_rpccred(*cred);
*cred = NULL;
}
}
inode->i_mode = fattr->mode; inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink; inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid; inode->i_uid = fattr->uid;
......
...@@ -133,16 +133,22 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name, ...@@ -133,16 +133,22 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
} }
static int static int
nfs3_proc_access(struct inode *inode, int mode, int ruid) nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
{ {
struct nfs_fattr fattr; struct nfs_fattr fattr;
struct nfs3_accessargs arg = { struct nfs3_accessargs arg = {
fh: NFS_FH(inode), .fh = NFS_FH(inode),
}; };
struct nfs3_accessres res = { struct nfs3_accessres res = {
fattr: &fattr, .fattr = &fattr,
};
struct rpc_message msg = {
.rpc_proc = NFS3PROC_ACCESS,
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_cred = cred
}; };
int status, flags; int status;
dprintk("NFS call access\n"); dprintk("NFS call access\n");
fattr.valid = 0; fattr.valid = 0;
...@@ -160,8 +166,7 @@ nfs3_proc_access(struct inode *inode, int mode, int ruid) ...@@ -160,8 +166,7 @@ nfs3_proc_access(struct inode *inode, int mode, int ruid)
if (mode & MAY_EXEC) if (mode & MAY_EXEC)
arg.access |= NFS3_ACCESS_EXECUTE; arg.access |= NFS3_ACCESS_EXECUTE;
} }
flags = (ruid) ? RPC_CALL_REALUID : 0; status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_ACCESS, &arg, &res, flags);
nfs_refresh_inode(inode, &fattr); nfs_refresh_inode(inode, &fattr);
dprintk("NFS reply access\n"); dprintk("NFS reply access\n");
......
...@@ -89,6 +89,16 @@ ...@@ -89,6 +89,16 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
/*
* NFSv3 Access mode cache
*/
struct nfs_access_cache {
unsigned long jiffies;
struct rpc_cred * cred;
int mask;
int err;
};
/* /*
* nfs fs inode data in memory * nfs fs inode data in memory
*/ */
...@@ -138,6 +148,8 @@ struct nfs_inode { ...@@ -138,6 +148,8 @@ struct nfs_inode {
*/ */
unsigned long cache_mtime_jiffies; unsigned long cache_mtime_jiffies;
struct nfs_access_cache cache_access;
/* /*
* This is the cookie verifier used for NFSv3 readdir * This is the cookie verifier used for NFSv3 readdir
* operations * operations
......
...@@ -300,7 +300,7 @@ struct nfs_rpc_ops { ...@@ -300,7 +300,7 @@ struct nfs_rpc_ops {
struct iattr *); struct iattr *);
int (*lookup) (struct inode *, struct qstr *, int (*lookup) (struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *); struct nfs_fh *, struct nfs_fattr *);
int (*access) (struct inode *, int , int); int (*access) (struct inode *, struct rpc_cred *, int);
int (*readlink)(struct inode *, struct page *); int (*readlink)(struct inode *, struct page *);
int (*read) (struct inode *, struct rpc_cred *, int (*read) (struct inode *, struct rpc_cred *,
struct nfs_fattr *, struct nfs_fattr *,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment