mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-21 11:44:01 +08:00
e65ce2a50c
The posix acl permission checking helpers determine whether a caller is privileged over an inode according to the acls associated with the inode. Add helpers that make it possible to handle acls on idmapped mounts. The vfs and the filesystems targeted by this first iteration make use of posix_acl_fix_xattr_from_user() and posix_acl_fix_xattr_to_user() to translate basic posix access and default permissions such as the ACL_USER and ACL_GROUP type according to the initial user namespace (or the superblock's user namespace) to and from the caller's current user namespace. Adapt these two helpers to handle idmapped mounts whereby we either map from or into the mount's user namespace depending on in which direction we're translating. Similarly, cap_convert_nscap() is used by the vfs to translate user namespace and non-user namespace aware filesystem capabilities from the superblock's user namespace to the caller's user namespace. Enable it to handle idmapped mounts by accounting for the mount's user namespace. In addition the fileystems targeted in the first iteration of this patch series make use of the posix_acl_chmod() and, posix_acl_update_mode() helpers. Both helpers perform permission checks on the target inode. Let them handle idmapped mounts. These two helpers are called when posix acls are set by the respective filesystems to handle this case we extend the ->set() method to take an additional user namespace argument to pass the mount's user namespace down. Link: https://lore.kernel.org/r/20210121131959.646623-9-christian.brauner@ubuntu.com Cc: Christoph Hellwig <hch@lst.de> Cc: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
428 lines
10 KiB
C
428 lines
10 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Process version 2 NFSACL requests.
|
|
*
|
|
* Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
|
|
*/
|
|
|
|
#include "nfsd.h"
|
|
/* FIXME: nfsacl.h is a broken header */
|
|
#include <linux/nfsacl.h>
|
|
#include <linux/gfp.h>
|
|
#include "cache.h"
|
|
#include "xdr3.h"
|
|
#include "vfs.h"
|
|
|
|
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
|
|
|
/*
|
|
* NULL call.
|
|
*/
|
|
static __be32
|
|
nfsacld_proc_null(struct svc_rqst *rqstp)
|
|
{
|
|
return rpc_success;
|
|
}
|
|
|
|
/*
|
|
* Get the Access and/or Default ACL of a file.
|
|
*/
|
|
static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd3_getaclargs *argp = rqstp->rq_argp;
|
|
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
|
struct posix_acl *acl;
|
|
struct inode *inode;
|
|
svc_fh *fh;
|
|
|
|
dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
|
|
|
|
fh = fh_copy(&resp->fh, &argp->fh);
|
|
resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
|
|
inode = d_inode(fh->fh_dentry);
|
|
|
|
if (argp->mask & ~NFS_ACL_MASK) {
|
|
resp->status = nfserr_inval;
|
|
goto out;
|
|
}
|
|
resp->mask = argp->mask;
|
|
|
|
resp->status = fh_getattr(fh, &resp->stat);
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
|
|
if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
|
|
acl = get_acl(inode, ACL_TYPE_ACCESS);
|
|
if (acl == NULL) {
|
|
/* Solaris returns the inode's minimum ACL. */
|
|
acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
|
|
}
|
|
if (IS_ERR(acl)) {
|
|
resp->status = nfserrno(PTR_ERR(acl));
|
|
goto fail;
|
|
}
|
|
resp->acl_access = acl;
|
|
}
|
|
if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
|
|
/* Check how Solaris handles requests for the Default ACL
|
|
of a non-directory! */
|
|
acl = get_acl(inode, ACL_TYPE_DEFAULT);
|
|
if (IS_ERR(acl)) {
|
|
resp->status = nfserrno(PTR_ERR(acl));
|
|
goto fail;
|
|
}
|
|
resp->acl_default = acl;
|
|
}
|
|
|
|
/* resp->acl_{access,default} are released in nfssvc_release_getacl. */
|
|
out:
|
|
return rpc_success;
|
|
|
|
fail:
|
|
posix_acl_release(resp->acl_access);
|
|
posix_acl_release(resp->acl_default);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Set the Access and/or Default ACL of a file.
|
|
*/
|
|
static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd3_setaclargs *argp = rqstp->rq_argp;
|
|
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
|
struct inode *inode;
|
|
svc_fh *fh;
|
|
int error;
|
|
|
|
dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
|
|
|
|
fh = fh_copy(&resp->fh, &argp->fh);
|
|
resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
|
|
inode = d_inode(fh->fh_dentry);
|
|
|
|
error = fh_want_write(fh);
|
|
if (error)
|
|
goto out_errno;
|
|
|
|
fh_lock(fh);
|
|
|
|
error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS,
|
|
argp->acl_access);
|
|
if (error)
|
|
goto out_drop_lock;
|
|
error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT,
|
|
argp->acl_default);
|
|
if (error)
|
|
goto out_drop_lock;
|
|
|
|
fh_unlock(fh);
|
|
|
|
fh_drop_write(fh);
|
|
|
|
resp->status = fh_getattr(fh, &resp->stat);
|
|
|
|
out:
|
|
/* argp->acl_{access,default} may have been allocated in
|
|
nfssvc_decode_setaclargs. */
|
|
posix_acl_release(argp->acl_access);
|
|
posix_acl_release(argp->acl_default);
|
|
return rpc_success;
|
|
|
|
out_drop_lock:
|
|
fh_unlock(fh);
|
|
fh_drop_write(fh);
|
|
out_errno:
|
|
resp->status = nfserrno(error);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check file attributes
|
|
*/
|
|
static __be32 nfsacld_proc_getattr(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd_fhandle *argp = rqstp->rq_argp;
|
|
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
|
|
|
dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh));
|
|
|
|
fh_copy(&resp->fh, &argp->fh);
|
|
resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
|
out:
|
|
return rpc_success;
|
|
}
|
|
|
|
/*
|
|
* Check file access
|
|
*/
|
|
static __be32 nfsacld_proc_access(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd3_accessargs *argp = rqstp->rq_argp;
|
|
struct nfsd3_accessres *resp = rqstp->rq_resp;
|
|
|
|
dprintk("nfsd: ACCESS(2acl) %s 0x%x\n",
|
|
SVCFH_fmt(&argp->fh),
|
|
argp->access);
|
|
|
|
fh_copy(&resp->fh, &argp->fh);
|
|
resp->access = argp->access;
|
|
resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
|
out:
|
|
return rpc_success;
|
|
}
|
|
|
|
/*
|
|
* XDR decode functions
|
|
*/
|
|
|
|
static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd3_getaclargs *argp = rqstp->rq_argp;
|
|
|
|
p = nfs2svc_decode_fh(p, &argp->fh);
|
|
if (!p)
|
|
return 0;
|
|
argp->mask = ntohl(*p); p++;
|
|
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
|
|
static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd3_setaclargs *argp = rqstp->rq_argp;
|
|
struct kvec *head = rqstp->rq_arg.head;
|
|
unsigned int base;
|
|
int n;
|
|
|
|
p = nfs2svc_decode_fh(p, &argp->fh);
|
|
if (!p)
|
|
return 0;
|
|
argp->mask = ntohl(*p++);
|
|
if (argp->mask & ~NFS_ACL_MASK ||
|
|
!xdr_argsize_check(rqstp, p))
|
|
return 0;
|
|
|
|
base = (char *)p - (char *)head->iov_base;
|
|
n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
|
|
(argp->mask & NFS_ACL) ?
|
|
&argp->acl_access : NULL);
|
|
if (n > 0)
|
|
n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
|
|
(argp->mask & NFS_DFACL) ?
|
|
&argp->acl_default : NULL);
|
|
return (n > 0);
|
|
}
|
|
|
|
static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd_fhandle *argp = rqstp->rq_argp;
|
|
|
|
p = nfs2svc_decode_fh(p, &argp->fh);
|
|
if (!p)
|
|
return 0;
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd3_accessargs *argp = rqstp->rq_argp;
|
|
|
|
p = nfs2svc_decode_fh(p, &argp->fh);
|
|
if (!p)
|
|
return 0;
|
|
argp->access = ntohl(*p++);
|
|
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
/*
|
|
* XDR encode functions
|
|
*/
|
|
|
|
/* GETACL */
|
|
static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
|
struct dentry *dentry = resp->fh.fh_dentry;
|
|
struct inode *inode;
|
|
struct kvec *head = rqstp->rq_res.head;
|
|
unsigned int base;
|
|
int n;
|
|
int w;
|
|
|
|
*p++ = resp->status;
|
|
if (resp->status != nfs_ok)
|
|
return xdr_ressize_check(rqstp, p);
|
|
|
|
/*
|
|
* Since this is version 2, the check for nfserr in
|
|
* nfsd_dispatch actually ensures the following cannot happen.
|
|
* However, it seems fragile to depend on that.
|
|
*/
|
|
if (dentry == NULL || d_really_is_negative(dentry))
|
|
return 0;
|
|
inode = d_inode(dentry);
|
|
|
|
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
|
|
*p++ = htonl(resp->mask);
|
|
if (!xdr_ressize_check(rqstp, p))
|
|
return 0;
|
|
base = (char *)p - (char *)head->iov_base;
|
|
|
|
rqstp->rq_res.page_len = w = nfsacl_size(
|
|
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
|
|
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
|
|
while (w > 0) {
|
|
if (!*(rqstp->rq_next_page++))
|
|
return 0;
|
|
w -= PAGE_SIZE;
|
|
}
|
|
|
|
n = nfsacl_encode(&rqstp->rq_res, base, inode,
|
|
resp->acl_access,
|
|
resp->mask & NFS_ACL, 0);
|
|
if (n > 0)
|
|
n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
|
|
resp->acl_default,
|
|
resp->mask & NFS_DFACL,
|
|
NFS_ACL_DEFAULT);
|
|
return (n > 0);
|
|
}
|
|
|
|
static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
|
|
|
*p++ = resp->status;
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
|
|
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
|
|
out:
|
|
return xdr_ressize_check(rqstp, p);
|
|
}
|
|
|
|
/* ACCESS */
|
|
static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p)
|
|
{
|
|
struct nfsd3_accessres *resp = rqstp->rq_resp;
|
|
|
|
*p++ = resp->status;
|
|
if (resp->status != nfs_ok)
|
|
goto out;
|
|
|
|
p = nfs2svc_encode_fattr(rqstp, p, &resp->fh, &resp->stat);
|
|
*p++ = htonl(resp->access);
|
|
out:
|
|
return xdr_ressize_check(rqstp, p);
|
|
}
|
|
|
|
/*
|
|
* XDR release functions
|
|
*/
|
|
static void nfsaclsvc_release_getacl(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
|
|
|
fh_put(&resp->fh);
|
|
posix_acl_release(resp->acl_access);
|
|
posix_acl_release(resp->acl_default);
|
|
}
|
|
|
|
static void nfsaclsvc_release_attrstat(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd_attrstat *resp = rqstp->rq_resp;
|
|
|
|
fh_put(&resp->fh);
|
|
}
|
|
|
|
static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
|
|
{
|
|
struct nfsd3_accessres *resp = rqstp->rq_resp;
|
|
|
|
fh_put(&resp->fh);
|
|
}
|
|
|
|
struct nfsd3_voidargs { int dummy; };
|
|
|
|
#define ST 1 /* status*/
|
|
#define AT 21 /* attributes */
|
|
#define pAT (1+AT) /* post attributes - conditional */
|
|
#define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */
|
|
|
|
static const struct svc_procedure nfsd_acl_procedures2[5] = {
|
|
[ACLPROC2_NULL] = {
|
|
.pc_func = nfsacld_proc_null,
|
|
.pc_decode = nfssvc_decode_voidarg,
|
|
.pc_encode = nfssvc_encode_voidres,
|
|
.pc_argsize = sizeof(struct nfsd_voidargs),
|
|
.pc_ressize = sizeof(struct nfsd_voidres),
|
|
.pc_cachetype = RC_NOCACHE,
|
|
.pc_xdrressize = ST,
|
|
},
|
|
[ACLPROC2_GETACL] = {
|
|
.pc_func = nfsacld_proc_getacl,
|
|
.pc_decode = nfsaclsvc_decode_getaclargs,
|
|
.pc_encode = nfsaclsvc_encode_getaclres,
|
|
.pc_release = nfsaclsvc_release_getacl,
|
|
.pc_argsize = sizeof(struct nfsd3_getaclargs),
|
|
.pc_ressize = sizeof(struct nfsd3_getaclres),
|
|
.pc_cachetype = RC_NOCACHE,
|
|
.pc_xdrressize = ST+1+2*(1+ACL),
|
|
},
|
|
[ACLPROC2_SETACL] = {
|
|
.pc_func = nfsacld_proc_setacl,
|
|
.pc_decode = nfsaclsvc_decode_setaclargs,
|
|
.pc_encode = nfsaclsvc_encode_attrstatres,
|
|
.pc_release = nfsaclsvc_release_attrstat,
|
|
.pc_argsize = sizeof(struct nfsd3_setaclargs),
|
|
.pc_ressize = sizeof(struct nfsd_attrstat),
|
|
.pc_cachetype = RC_NOCACHE,
|
|
.pc_xdrressize = ST+AT,
|
|
},
|
|
[ACLPROC2_GETATTR] = {
|
|
.pc_func = nfsacld_proc_getattr,
|
|
.pc_decode = nfsaclsvc_decode_fhandleargs,
|
|
.pc_encode = nfsaclsvc_encode_attrstatres,
|
|
.pc_release = nfsaclsvc_release_attrstat,
|
|
.pc_argsize = sizeof(struct nfsd_fhandle),
|
|
.pc_ressize = sizeof(struct nfsd_attrstat),
|
|
.pc_cachetype = RC_NOCACHE,
|
|
.pc_xdrressize = ST+AT,
|
|
},
|
|
[ACLPROC2_ACCESS] = {
|
|
.pc_func = nfsacld_proc_access,
|
|
.pc_decode = nfsaclsvc_decode_accessargs,
|
|
.pc_encode = nfsaclsvc_encode_accessres,
|
|
.pc_release = nfsaclsvc_release_access,
|
|
.pc_argsize = sizeof(struct nfsd3_accessargs),
|
|
.pc_ressize = sizeof(struct nfsd3_accessres),
|
|
.pc_cachetype = RC_NOCACHE,
|
|
.pc_xdrressize = ST+AT+1,
|
|
},
|
|
};
|
|
|
|
static unsigned int nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)];
|
|
const struct svc_version nfsd_acl_version2 = {
|
|
.vs_vers = 2,
|
|
.vs_nproc = 5,
|
|
.vs_proc = nfsd_acl_procedures2,
|
|
.vs_count = nfsd_acl_count2,
|
|
.vs_dispatch = nfsd_dispatch,
|
|
.vs_xdrsize = NFS3_SVC_XDRSIZE,
|
|
};
|