Skip to content

Commit

Permalink
NFSD: Server implementation of MAC Labeling
Browse files Browse the repository at this point in the history
Implement labeled NFS on the server: encoding and decoding, and writing
and reading, of file labels.

Enabled with CONFIG_NFSD_V4_SECURITY_LABEL.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: Miguel Rodel Felipe <[email protected]>
Signed-off-by: Phua Eu Gene <[email protected]>
Signed-off-by: Khin Mi Mi Aung <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
  • Loading branch information
dpquigl authored and J. Bruce Fields committed May 15, 2013
1 parent 4bdc33e commit 18032ca
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 10 deletions.
16 changes: 16 additions & 0 deletions fs/nfsd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ config NFSD_V4

If unsure, say N.

config NFSD_V4_SECURITY_LABEL
bool "Provide Security Label support for NFSv4 server"
depends on NFSD_V4 && SECURITY
help

Say Y here if you want enable fine-grained security label attribute
support for NFS version 4. Security labels allow security modules like
SELinux and Smack to label files to facilitate enforcement of their policies.
Without this an NFSv4 mount will have the same label on each file.

If you do not wish to enable fine-grained security labels SELinux or
Smack policies on NFSv4 files, say N.

WARNING: there is still a chance of backwards-incompatible protocol changes.
For now we recommend "Y" only for developers and testers."

config NFSD_FAULT_INJECTION
bool "NFS server manual fault injection"
depends on NFSD_V4 && DEBUG_KERNEL
Expand Down
41 changes: 41 additions & 0 deletions fs/nfsd/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,36 @@
#include "current_stateid.h"
#include "netns.h"

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#include <linux/security.h>

static inline void
nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
{
struct inode *inode = resfh->fh_dentry->d_inode;
int status;

mutex_lock(&inode->i_mutex);
status = security_inode_setsecctx(resfh->fh_dentry,
label->data, label->len);
mutex_unlock(&inode->i_mutex);

if (status)
/*
* XXX: We should really fail the whole open, but we may
* already have created a new file, so it may be too
* late. For now this seems the least of evils:
*/
bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;

return;
}
#else
static inline void
nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
{ }
#endif

#define NFSDDBG_FACILITY NFSDDBG_PROC

static u32 nfsd_attrmask[] = {
Expand Down Expand Up @@ -239,6 +269,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
(u32 *)open->op_verf.data,
&open->op_truncate, &open->op_created);

if (!status && open->op_label.len)
nfsd4_security_inode_setsecctx(resfh, &open->op_label, open->op_bmval);

/*
* Following rfc 3530 14.2.16, use the returned bitmask
* to indicate which attributes we used to store the
Expand Down Expand Up @@ -637,6 +670,9 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status)
goto out;

if (create->cr_label.len)
nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval);

if (create->cr_acl != NULL)
do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
create->cr_bmval);
Expand Down Expand Up @@ -916,6 +952,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
setattr->sa_acl);
if (status)
goto out;
if (setattr->sa_label.len)
status = nfsd4_set_nfs4_label(rqstp, &cstate->current_fh,
&setattr->sa_label);
if (status)
goto out;
status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
0, (time_t)0);
out:
Expand Down
108 changes: 100 additions & 8 deletions fs/nfsd/nfs4xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
#include "cache.h"
#include "netns.h"

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#include <linux/security.h>
#endif


#define NFSDDBG_FACILITY NFSDDBG_XDR

/*
Expand Down Expand Up @@ -242,7 +247,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)

static __be32
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
struct iattr *iattr, struct nfs4_acl **acl)
struct iattr *iattr, struct nfs4_acl **acl,
struct xdr_netobj *label)
{
int expected_len, len = 0;
u32 dummy32;
Expand Down Expand Up @@ -380,6 +386,32 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
goto xdr_error;
}
}

label->len = 0;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
READ_BUF(4);
len += 4;
READ32(dummy32); /* lfs: we don't use it */
READ_BUF(4);
len += 4;
READ32(dummy32); /* pi: we don't use it either */
READ_BUF(4);
len += 4;
READ32(dummy32);
READ_BUF(dummy32);
if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN)
return nfserr_badlabel;
len += (XDR_QUADLEN(dummy32) << 2);
READMEM(buf, dummy32);
label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
if (!label->data)
return nfserr_jukebox;
defer_free(argp, kfree, label->data);
memcpy(label->data, buf, dummy32);
}
#endif

if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
|| bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
|| bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
Expand Down Expand Up @@ -576,7 +608,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
return status;

status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
&create->cr_acl);
&create->cr_acl, &create->cr_label);
if (status)
goto out;

Expand Down Expand Up @@ -827,7 +859,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
case NFS4_CREATE_UNCHECKED:
case NFS4_CREATE_GUARDED:
status = nfsd4_decode_fattr(argp, open->op_bmval,
&open->op_iattr, &open->op_acl);
&open->op_iattr, &open->op_acl, &open->op_label);
if (status)
goto out;
break;
Expand All @@ -841,7 +873,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
READ_BUF(NFS4_VERIFIER_SIZE);
COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
status = nfsd4_decode_fattr(argp, open->op_bmval,
&open->op_iattr, &open->op_acl);
&open->op_iattr, &open->op_acl, &open->op_label);
if (status)
goto out;
break;
Expand Down Expand Up @@ -1063,7 +1095,7 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
if (status)
return status;
return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
&setattr->sa_acl);
&setattr->sa_acl, &setattr->sa_label);
}

static __be32
Expand Down Expand Up @@ -1954,6 +1986,36 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
FATTR4_WORD0_RDATTR_ERROR)
#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
static inline __be32
nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
{
__be32 *p = *pp;

if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4))
return nfserr_resource;

/*
* For now we use a 0 here to indicate the null translation; in
* the future we may place a call to translation code here.
*/
if ((*buflen -= 8) < 0)
return nfserr_resource;

WRITE32(0); /* lfs */
WRITE32(0); /* pi */
p = xdr_encode_opaque(p, context, len);
*buflen -= (XDR_QUADLEN(len) << 2) + 4;

*pp = p;
return 0;
}
#else
static inline __be32
nfsd4_encode_security_label(struct svc_rqst *rqstp, struct dentry *dentry, __be32 **pp, int *buflen)
{ return 0; }
#endif

static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
{
/* As per referral draft: */
Expand Down Expand Up @@ -2013,6 +2075,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
int err;
int aclsupport = 0;
struct nfs4_acl *acl = NULL;
void *context = NULL;
int contextlen;
bool contextsupport = false;
struct nfsd4_compoundres *resp = rqstp->rq_resp;
u32 minorversion = resp->cstate.minorversion;
struct path path = {
Expand Down Expand Up @@ -2066,6 +2131,21 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
}
}

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) ||
bmval[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
err = security_inode_getsecctx(dentry->d_inode,
&context, &contextlen);
contextsupport = (err == 0);
if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
if (err == -EOPNOTSUPP)
bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
else if (err)
goto out_nfserr;
}
}
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */

if (bmval2) {
if ((buflen -= 16) < 0)
goto out_resource;
Expand Down Expand Up @@ -2094,6 +2174,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,

if (!aclsupport)
word0 &= ~FATTR4_WORD0_ACL;
if (!contextsupport)
word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
if (!word2) {
if ((buflen -= 12) < 0)
goto out_resource;
Expand Down Expand Up @@ -2401,6 +2483,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
get_parent_attributes(exp, &stat);
WRITE64(stat.ino);
}
if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
status = nfsd4_encode_security_label(rqstp, context,
contextlen, &p, &buflen);
if (status)
goto out;
}
if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
WRITE32(3);
WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
Expand All @@ -2413,6 +2501,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
status = nfs_ok;

out:
if (context)
security_release_secctx(context, contextlen);
kfree(acl);
if (fhp == &tempfh)
fh_put(&tempfh);
Expand Down Expand Up @@ -3177,16 +3267,18 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
{
__be32 *p;

RESERVE_SPACE(12);
RESERVE_SPACE(16);
if (nfserr) {
WRITE32(2);
WRITE32(3);
WRITE32(0);
WRITE32(0);
WRITE32(0);
}
else {
WRITE32(2);
WRITE32(3);
WRITE32(setattr->sa_bmval[0]);
WRITE32(setattr->sa_bmval[1]);
WRITE32(setattr->sa_bmval[2]);
}
ADJUST_ARGS();
return nfserr;
Expand Down
18 changes: 16 additions & 2 deletions fs/nfsd/nfsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,13 @@ void nfsd_lockd_shutdown(void);
#define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
(NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)

#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
(NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL)
#else
#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0
#endif

static inline u32 nfsd_suppattrs0(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
Expand All @@ -342,8 +349,11 @@ static inline u32 nfsd_suppattrs1(u32 minorversion)

static inline u32 nfsd_suppattrs2(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2
: NFSD4_SUPPORTED_ATTRS_WORD2;
switch (minorversion) {
default: return NFSD4_2_SUPPORTED_ATTRS_WORD2;
case 1: return NFSD4_1_SUPPORTED_ATTRS_WORD2;
case 0: return NFSD4_SUPPORTED_ATTRS_WORD2;
}
}

/* These will return ERR_INVAL if specified in GETATTR or READDIR. */
Expand All @@ -356,7 +366,11 @@ static inline u32 nfsd_suppattrs2(u32 minorversion)
#define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
#define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL
#else
#define NFSD_WRITEABLE_ATTRS_WORD2 0
#endif

#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
NFSD_WRITEABLE_ATTRS_WORD0
Expand Down
28 changes: 28 additions & 0 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <asm/uaccess.h>
#include <linux/exportfs.h>
#include <linux/writeback.h>
#include <linux/security.h>

#ifdef CONFIG_NFSD_V3
#include "xdr3.h"
Expand Down Expand Up @@ -621,6 +622,33 @@ int nfsd4_is_junction(struct dentry *dentry)
return 0;
return 1;
}
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct xdr_netobj *label)
{
__be32 error;
int host_error;
struct dentry *dentry;

error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
if (error)
return error;

dentry = fhp->fh_dentry;

mutex_lock(&dentry->d_inode->i_mutex);
host_error = security_inode_setsecctx(dentry, label->data, label->len);
mutex_unlock(&dentry->d_inode->i_mutex);
return nfserrno(host_error);
}
#else
__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct xdr_netobj *label)
{
return nfserr_notsupp;
}
#endif

#endif /* defined(CONFIG_NFSD_V4) */

#ifdef CONFIG_NFSD_V3
Expand Down
2 changes: 2 additions & 0 deletions fs/nfsd/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ int nfsd_mountpoint(struct dentry *, struct svc_export *);
__be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
struct nfs4_acl *);
int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **);
__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
struct xdr_netobj *);
#endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
Expand Down
Loading

0 comments on commit 18032ca

Please sign in to comment.