]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - fs/nfsd/nfs4xdr.c
Merge branch 'for-3.11' of git://linux-nfs.org/~bfields/linux
[linux-imx.git] / fs / nfsd / nfs4xdr.c
index 6cd86e0fe450a2b4ed36e447b946970cceaca95b..c2a4701d7286478cca66cda986565a7b3105504c 100644 (file)
 #include "cache.h"
 #include "netns.h"
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+#endif
+
+
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
 /*
@@ -134,6 +139,19 @@ xdr_error:                                 \
        }                                       \
 } while (0)
 
+static void next_decode_page(struct nfsd4_compoundargs *argp)
+{
+       argp->pagelist++;
+       argp->p = page_address(argp->pagelist[0]);
+       if (argp->pagelen < PAGE_SIZE) {
+               argp->end = argp->p + (argp->pagelen>>2);
+               argp->pagelen = 0;
+       } else {
+               argp->end = argp->p + (PAGE_SIZE>>2);
+               argp->pagelen -= PAGE_SIZE;
+       }
+}
+
 static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 {
        /* We want more bytes than seem to be available.
@@ -161,16 +179,7 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
         * guarantee p points to at least nbytes bytes.
         */
        memcpy(p, argp->p, avail);
-       /* step to next page */
-       argp->p = page_address(argp->pagelist[0]);
-       argp->pagelist++;
-       if (argp->pagelen < PAGE_SIZE) {
-               argp->end = argp->p + (argp->pagelen>>2);
-               argp->pagelen = 0;
-       } else {
-               argp->end = argp->p + (PAGE_SIZE>>2);
-               argp->pagelen -= PAGE_SIZE;
-       }
+       next_decode_page(argp);
        memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
        argp->p += XDR_QUADLEN(nbytes - avail);
        return p;
@@ -242,7 +251,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;
@@ -380,6 +390,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)
@@ -428,7 +464,11 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
        /* callback_sec_params4 */
        READ_BUF(4);
        READ32(nr_secflavs);
-       cbs->flavor = (u32)(-1);
+       if (nr_secflavs)
+               cbs->flavor = (u32)(-1);
+       else
+               /* Is this legal? Be generous, take it to mean AUTH_NONE: */
+               cbs->flavor = 0;
        for (i = 0; i < nr_secflavs; ++i) {
                READ_BUF(4);
                READ32(dummy);
@@ -576,7 +616,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;
 
@@ -827,7 +867,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;
@@ -841,7 +881,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;
@@ -1063,7 +1103,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
@@ -1567,6 +1607,7 @@ struct nfsd4_minorversion_ops {
 static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
        [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
        [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
+       [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
 };
 
 static __be32
@@ -1953,6 +1994,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, void *context, int len, __be32 **pp, int *buflen)
+{ return 0; }
+#endif
+
 static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
 {
        /* As per referral draft:  */
@@ -2012,6 +2083,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 = {
@@ -2065,6 +2139,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;
@@ -2093,6 +2182,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;
@@ -2400,6 +2491,12 @@ out_acl:
                        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);
@@ -2412,6 +2509,10 @@ out_acl:
        status = nfs_ok;
 
 out:
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+       if (context)
+               security_release_secctx(context, contextlen);
+#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
        kfree(acl);
        if (fhp == &tempfh)
                fh_put(&tempfh);
@@ -3176,16 +3277,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;
@@ -3226,6 +3329,14 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
        return nfserr;
 }
 
+static const u32 nfs4_minimal_spo_must_enforce[2] = {
+       [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
+             1 << (OP_EXCHANGE_ID - 32) |
+             1 << (OP_CREATE_SESSION - 32) |
+             1 << (OP_DESTROY_SESSION - 32) |
+             1 << (OP_DESTROY_CLIENTID - 32)
+};
+
 static __be32
 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                         struct nfsd4_exchange_id *exid)
@@ -3249,7 +3360,8 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                8 /* eir_clientid */ +
                4 /* eir_sequenceid */ +
                4 /* eir_flags */ +
-               4 /* spr_how (SP4_NONE) */ +
+               4 /* spr_how */ +
+               8 /* spo_must_enforce, spo_must_allow */ +
                8 /* so_minor_id */ +
                4 /* so_major_id.len */ +
                (XDR_QUADLEN(major_id_sz) * 4) +
@@ -3261,9 +3373,21 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
        WRITE32(exid->seqid);
        WRITE32(exid->flags);
 
-       /* state_protect4_r. Currently only support SP4_NONE */
-       BUG_ON(exid->spa_how != SP4_NONE);
        WRITE32(exid->spa_how);
+       switch (exid->spa_how) {
+       case SP4_NONE:
+               break;
+       case SP4_MACH_CRED:
+               /* spo_must_enforce bitmap: */
+               WRITE32(2);
+               WRITE32(nfs4_minimal_spo_must_enforce[0]);
+               WRITE32(nfs4_minimal_spo_must_enforce[1]);
+               /* empty spo_must_allow bitmap: */
+               WRITE32(0);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
 
        /* The server_owner struct */
        WRITE64(minor_id);      /* Minor id */
@@ -3635,13 +3759,17 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
        iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
        BUG_ON(iov->iov_len > PAGE_SIZE);
        if (nfsd4_has_session(cs)) {
+               struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+               struct nfs4_client *clp = cs->session->se_client;
                if (cs->status != nfserr_replay_cache) {
                        nfsd4_store_cache_entry(resp);
                        cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
                }
                /* Renew the clientid on success and on replay */
-               put_client_renew(cs->session->se_client);
+               spin_lock(&nn->client_lock);
                nfsd4_put_session(cs->session);
+               spin_unlock(&nn->client_lock);
+               put_client_renew(clp);
        }
        return 1;
 }