@@ -979,6 +979,11 @@ static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh)
&setattr->sa_label);
if (status)
goto out;
+ if (setattr->sa_ima.len)
+ status = nfsd4_set_ima_metadata(rqstp, &cstate->current_fh,
+ &setattr->sa_ima);
+ if (status)
+ goto out;
status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
0, (time_t)0);
out:
@@ -2135,6 +2140,10 @@ static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
ret += NFS4_MAXLABELLEN + 12;
bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
}
+ if (bmap2 & FATTR4_WORD2_LINUX_IMA) {
+ ret += NFS4_MAXIMALEN + 4;
+ bmap2 &= ~FATTR4_WORD2_LINUX_IMA;
+ }
/*
* Largest of remaining attributes are 16 bytes (e.g.,
* supported_attributes)
@@ -39,6 +39,7 @@
#include <linux/statfs.h>
#include <linux/utsname.h>
#include <linux/pagemap.h>
+#include <linux/xattr.h>
#include <linux/sunrpc/svcauth_gss.h>
#include "idmap.h"
@@ -318,7 +319,8 @@ static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
static __be32
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
struct iattr *iattr, struct nfs4_acl **acl,
- struct xdr_netobj *label, int *umask)
+ struct xdr_netobj *label, int *umask,
+ struct xdr_netobj *ima)
{
struct timespec ts;
int expected_len, len = 0;
@@ -455,7 +457,6 @@ static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
goto xdr_error;
}
}
-
label->len = 0;
if (IS_ENABLED(CONFIG_NFSD_V4_SECURITY_LABEL) &&
bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
@@ -489,6 +490,21 @@ static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
*umask = dummy32 & S_IRWXUGO;
iattr->ia_valid |= ATTR_MODE;
}
+ ima->len = 0;
+ if (bmval[2] & FATTR4_WORD2_LINUX_IMA) {
+ READ_BUF(4);
+ len += 4;
+ dummy32 = be32_to_cpup(p++);
+ READ_BUF(dummy32);
+ if (dummy32 > NFS4_MAXIMALEN)
+ return nfserr_badlabel;
+ len += (XDR_QUADLEN(dummy32) << 2);
+ READMEM(buf, dummy32);
+ ima->len = dummy32;
+ ima->data = svcxdr_dupstr(argp, buf, dummy32);
+ if (!ima->data)
+ return nfserr_jukebox;
+ }
if (len != expected_len)
goto xdr_error;
@@ -684,7 +700,7 @@ static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
&create->cr_acl, &create->cr_label,
- &create->cr_umask);
+ &create->cr_umask, &create->cr_ima);
if (status)
goto out;
@@ -936,7 +952,7 @@ static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_ne
case NFS4_CREATE_GUARDED:
status = nfsd4_decode_fattr(argp, open->op_bmval,
&open->op_iattr, &open->op_acl, &open->op_label,
- &open->op_umask);
+ &open->op_umask, &open->op_ima);
if (status)
goto out;
break;
@@ -951,7 +967,7 @@ static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_ne
COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
status = nfsd4_decode_fattr(argp, open->op_bmval,
&open->op_iattr, &open->op_acl, &open->op_label,
- &open->op_umask);
+ &open->op_umask, &open->op_ima);
if (status)
goto out;
break;
@@ -1188,7 +1204,8 @@ static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_ne
if (status)
return status;
return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
- &setattr->sa_acl, &setattr->sa_label, NULL);
+ &setattr->sa_acl, &setattr->sa_label, NULL,
+ &setattr->sa_ima);
}
static __be32
@@ -2430,6 +2447,7 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
.dentry = dentry,
};
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct xdr_netobj ima = { 0, NULL };
BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
BUG_ON(!nfsd_attrs_supported(minorversion, bmval));
@@ -2491,6 +2509,16 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
}
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
+ if (bmval2 & FATTR4_WORD2_LINUX_IMA) {
+ err = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA,
+ (char **)&ima.data, 0, GFP_KERNEL);
+ if (err == -ENODATA)
+ bmval2 &= ~FATTR4_WORD2_LINUX_IMA;
+ else if (err < 0)
+ goto out_nfserr;
+ ima.len = err;
+ }
+
status = nfsd4_encode_bitmap(xdr, bmval0, bmval1, bmval2);
if (status)
goto out;
@@ -2913,6 +2941,14 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
goto out;
}
+ if (bmval2 & FATTR4_WORD2_LINUX_IMA) {
+ p = xdr_reserve_space(xdr,
+ sizeof(__be32) + xdr_align_size(ima.len));
+ if (!p)
+ goto out_resource;
+ xdr_encode_netobj(p, &ima);
+ }
+
attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
status = nfs_ok;
@@ -2922,6 +2958,7 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
if (context)
security_release_secctx(context, contextlen);
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
+ kfree(ima.data);
kfree(acl);
if (tempfh) {
fh_put(tempfh);
@@ -362,6 +362,7 @@ static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
FATTR4_WORD2_CHANGE_ATTR_TYPE | \
FATTR4_WORD2_MODE_UMASK | \
+ FATTR4_WORD2_LINUX_IMA | \
NFSD4_2_SECURITY_ATTRS)
extern const u32 nfsd_suppattrs[3][3];
@@ -399,7 +400,7 @@ static inline bool nfsd_attrs_supported(u32 minorversion, const u32 *bmval)
#define MAYBE_FATTR4_WORD2_SECURITY_LABEL 0
#endif
#define NFSD_WRITEABLE_ATTRS_WORD2 \
- (FATTR4_WORD2_MODE_UMASK \
+ (FATTR4_WORD2_MODE_UMASK | FATTR4_WORD2_LINUX_IMA \
| MAYBE_FATTR4_WORD2_SECURITY_LABEL)
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
@@ -551,6 +551,25 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
#endif
+__be32 nfsd4_set_ima_metadata(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct xdr_netobj *ima)
+{
+ struct dentry *dentry;
+ int host_error;
+ __be32 error;
+
+ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
+ if (error)
+ return error;
+ dentry = fhp->fh_dentry;
+
+ inode_lock(d_inode(dentry));
+ host_error = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, ima->data,
+ ima->len, 0);
+ inode_unlock(d_inode(dentry));
+ return nfserrno(host_error);
+}
+
__be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
u64 dst_pos, u64 count)
{
@@ -55,6 +55,9 @@ __be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
#ifdef CONFIG_NFSD_V4
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
struct xdr_netobj *);
+__be32 nfsd4_set_ima_metadata(struct svc_rqst *rqstp,
+ struct svc_fh *fhp,
+ struct xdr_netobj *ima);
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
struct file *, loff_t, loff_t, int);
__be32 nfsd4_clone_file_range(struct file *, u64, struct file *,
@@ -123,6 +123,7 @@ struct nfsd4_create {
struct nfsd4_change_info cr_cinfo; /* response */
struct nfs4_acl *cr_acl;
struct xdr_netobj cr_label;
+ struct xdr_netobj cr_ima;
};
#define cr_datalen u.link.datalen
#define cr_data u.link.data
@@ -255,6 +256,7 @@ struct nfsd4_open {
struct nfs4_clnt_odstate *op_odstate; /* used during processing */
struct nfs4_acl *op_acl;
struct xdr_netobj op_label;
+ struct xdr_netobj op_ima;
};
struct nfsd4_open_confirm {
@@ -339,6 +341,7 @@ struct nfsd4_setattr {
struct iattr sa_iattr; /* request */
struct nfs4_acl *sa_acl;
struct xdr_netobj sa_label;
+ struct xdr_netobj sa_ima;
};
struct nfsd4_setclientid {
@@ -151,20 +151,20 @@
EXPORT_SYMBOL(__vfs_setxattr);
/**
- * __vfs_setxattr_noperm - perform setxattr operation without performing
- * permission checks.
+ * __vfs_setxattr_noperm - perform setxattr operation without performing
+ * permission checks.
*
- * @dentry - object to perform setxattr on
- * @name - xattr name to set
- * @value - value to set @name to
- * @size - size of @value
- * @flags - flags to pass into filesystem operations
+ * @dentry: object to perform setxattr on
+ * @name: xattr name to set
+ * @value: value to set @name to
+ * @size: size of @value
+ * @flags: flags to pass into filesystem operations
*
- * returns the result of the internal setxattr or setsecurity operations.
+ * Returns the result of the internal setxattr or setsecurity operations.
*
- * This function requires the caller to lock the inode's i_mutex before it
- * is executed. It also assumes that the caller will make the appropriate
- * permission checks.
+ * This function requires the caller to lock the inode's i_mutex before it
+ * is executed. It also assumes that the caller will make the appropriate
+ * permission checks.
*/
int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
@@ -202,7 +202,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
return error;
}
-
+EXPORT_SYMBOL_GPL(__vfs_setxattr_noperm);
int
vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
@@ -295,6 +295,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
*xattr_value = value;
return error;
}
+EXPORT_SYMBOL_GPL(vfs_getxattr_alloc);
ssize_t
__vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
Provide code to handle the "security.ima" xattr. The NFS server converts incoming GETATTR and SETATTR calls to acesses and updates of the xattr. Even if CONFIG_IMA is disabled, the NFS server can access and update that extended attribute. The new FATTR4 bit is made up; meaning we still have to go through a standards process to allocate a bit that all NFS vendors agree on. Thus there is no guarantee this prototype will interoperate with others or with a future standards-based implementation. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/nfsd/nfs4proc.c | 9 +++++++++ fs/nfsd/nfs4xdr.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ fs/nfsd/nfsd.h | 3 ++- fs/nfsd/vfs.c | 19 +++++++++++++++++++ fs/nfsd/vfs.h | 3 +++ fs/nfsd/xdr4.h | 3 +++ fs/xattr.c | 25 +++++++++++++------------ 7 files changed, 92 insertions(+), 19 deletions(-)