]> rtime.felk.cvut.cz Git - linux-imx.git/commitdiff
Merge branch 'labeled-nfs' into linux-next
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 28 Jun 2013 20:29:51 +0000 (16:29 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 28 Jun 2013 20:29:51 +0000 (16:29 -0400)
* labeled-nfs:
  NFS: Apply v4.1 capabilities to v4.2
  NFS: Add in v4.2 callback operation
  NFS: Make callbacks minor version generic
  Kconfig: Add Kconfig entry for Labeled NFS V4 client
  NFS: Extend NFS xattr handlers to accept the security namespace
  NFS: Client implementation of Labeled-NFS
  NFS: Add label lifecycle management
  NFS:Add labels to client function prototypes
  NFSv4: Extend fattr bitmaps to support all 3 words
  NFSv4: Introduce new label structure
  NFSv4: Add label recommended attribute and NFSv4 flags
  NFSv4.2: Added NFS v4.2 support to the NFS client
  SELinux: Add new labeling type native labels
  LSM: Add flags field to security_sb_set_mnt_opts for in kernel mount data.
  Security: Add Hook to test if the particular xattr is part of a MAC model.
  Security: Add hook to calculate context based on a negative dentry.
  NFS: Add NFSv4.2 protocol constants

Conflicts:
fs/nfs/nfs4proc.c

30 files changed:
fs/nfs/Kconfig
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/dir.c
fs/nfs/getroot.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/namespace.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
fs/nfs/proc.c
fs/nfs/super.c
fs/nfsd/nfsd.h
include/linux/nfs4.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/security.h
security/capability.c
security/security.c
security/selinux/hooks.c
security/selinux/include/security.h
security/selinux/ss/policydb.c
security/smack/smack_lsm.c

index 13ca196385f5faff6c88fd32e00d317cf68dcbd7..b5e80b0af315b065954fe42c2a832e85ff57b23f 100644 (file)
@@ -104,6 +104,15 @@ config NFS_V4_1
 
          If unsure, say N.
 
+config NFS_V4_2
+       bool "NFS client support for NFSv4.2"
+       depends on NFS_V4_1
+       help
+         This option enables support for minor version 2 of the NFSv4 protocol
+         in the kernel's NFS client.
+
+         If unsure, say N.
+
 config PNFS_FILE_LAYOUT
        tristate
        depends on NFS_V4_1
@@ -131,6 +140,11 @@ config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
          If the NFS client is unchanged from the upstream kernel, this
          option should be set to the default "kernel.org".
 
+config NFS_V4_SECURITY_LABEL
+       bool
+       depends on NFS_V4_2 && SECURITY
+       default y
+
 config ROOT_NFS
        bool "Root file system on NFS"
        depends on NFS_FS=y && IP_PNP
index cff089a412c7f4bde3bd2f1d5138a4679535b772..78e368d8186d179aaf4e2e191ff93af796f90eb8 100644 (file)
@@ -282,6 +282,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct n
                        ret = nfs4_callback_up_net(serv, net);
                        break;
                case 1:
+               case 2:
                        ret = nfs41_callback_up_net(serv, net);
                        break;
                default:
index efd54f0a4c468f9c7e7726b751df36c6f94d1d16..84326e9fb47aa476e3f2ad7a072055172b3f6ab0 100644 (file)
@@ -32,6 +32,8 @@ enum nfs4_callback_opnum {
        OP_CB_WANTS_CANCELLED = 12,
        OP_CB_NOTIFY_LOCK   = 13,
        OP_CB_NOTIFY_DEVICEID = 14,
+/* Callback operations new to NFSv4.2 */
+       OP_CB_OFFLOAD = 15,
        OP_CB_ILLEGAL = 10044,
 };
 
@@ -39,6 +41,7 @@ struct cb_process_state {
        __be32                  drc_status;
        struct nfs_client       *clp;
        u32                     slotid;
+       u32                     minorversion;
        struct net              *net;
 };
 
index 0bc27684ebfa338d0b77c295427c31472ec66049..e6ebc4c38c812c01c79c587862ac9192d721765a 100644 (file)
@@ -406,7 +406,8 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
        int i;
        __be32 status = htonl(NFS4ERR_BADSESSION);
 
-       clp = nfs4_find_client_sessionid(cps->net, args->csa_addr, &args->csa_sessionid);
+       clp = nfs4_find_client_sessionid(cps->net, args->csa_addr,
+                                        &args->csa_sessionid, cps->minorversion);
        if (clp == NULL)
                goto out;
 
index a35582c9d4440f8fe907192427b5c4f9a9a3b061..f4ccfe6521ec80f80fd4b9096bcc7ed49ab7d795 100644 (file)
@@ -166,9 +166,9 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
        if (unlikely(p == NULL))
                return htonl(NFS4ERR_RESOURCE);
        hdr->minorversion = ntohl(*p++);
-       /* Check minor version is zero or one. */
-       if (hdr->minorversion <= 1) {
-               hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
+       /* Check for minor version support */
+       if (hdr->minorversion <= NFS4_MAX_MINOR_VERSION) {
+               hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 and v4.2 */
        } else {
                pr_warn_ratelimited("NFS: %s: NFSv4 server callback with "
                        "illegal minor version %u!\n",
@@ -786,6 +786,26 @@ static void nfs4_cb_free_slot(struct cb_process_state *cps)
 }
 #endif /* CONFIG_NFS_V4_1 */
 
+#ifdef CONFIG_NFS_V4_2
+static __be32
+preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op)
+{
+       __be32 status = preprocess_nfs41_op(nop, op_nr, op);
+       if (status != htonl(NFS4ERR_OP_ILLEGAL))
+               return status;
+
+       if (op_nr == OP_CB_OFFLOAD)
+               return htonl(NFS4ERR_NOTSUPP);
+       return htonl(NFS4ERR_OP_ILLEGAL);
+}
+#else /* CONFIG_NFS_V4_2 */
+static __be32
+preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op)
+{
+       return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
+}
+#endif /* CONFIG_NFS_V4_2 */
+
 static __be32
 preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
 {
@@ -801,8 +821,7 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
        return htonl(NFS_OK);
 }
 
-static __be32 process_op(uint32_t minorversion, int nop,
-               struct svc_rqst *rqstp,
+static __be32 process_op(int nop, struct svc_rqst *rqstp,
                struct xdr_stream *xdr_in, void *argp,
                struct xdr_stream *xdr_out, void *resp,
                struct cb_process_state *cps)
@@ -819,10 +838,22 @@ static __be32 process_op(uint32_t minorversion, int nop,
                return status;
 
        dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
-               __func__, minorversion, nop, op_nr);
+               __func__, cps->minorversion, nop, op_nr);
+
+       switch (cps->minorversion) {
+       case 0:
+               status = preprocess_nfs4_op(op_nr, &op);
+               break;
+       case 1:
+               status = preprocess_nfs41_op(nop, op_nr, &op);
+               break;
+       case 2:
+               status = preprocess_nfs42_op(nop, op_nr, &op);
+               break;
+       default:
+               status = htonl(NFS4ERR_MINOR_VERS_MISMATCH);
+       }
 
-       status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) :
-                               preprocess_nfs4_op(op_nr, &op);
        if (status == htonl(NFS4ERR_OP_ILLEGAL))
                op_nr = OP_CB_ILLEGAL;
        if (status)
@@ -885,14 +916,15 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
                        return rpc_drop_reply;
        }
 
+       cps.minorversion = hdr_arg.minorversion;
        hdr_res.taglen = hdr_arg.taglen;
        hdr_res.tag = hdr_arg.tag;
        if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
                return rpc_system_err;
 
        while (status == 0 && nops != hdr_arg.nops) {
-               status = process_op(hdr_arg.minorversion, nops, rqstp,
-                                   &xdr_in, argp, &xdr_out, resp, &cps);
+               status = process_op(nops, rqstp, &xdr_in,
+                                   argp, &xdr_out, resp, &cps);
                nops++;
        }
 
index dbb65fb9e82dfe7b3fbb1f68c404ce9bd429e9ed..340b1eff02679ad3485f51f6c526ba3363b2ef53 100644 (file)
@@ -1074,7 +1074,7 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
        }
 
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
-               error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr);
+               error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL);
                if (error < 0) {
                        dprintk("nfs_create_server: getattr error = %d\n", -error);
                        goto error;
index c662ff6befd649f87409e0c8f32a931d973225bd..c3f21888654ff595cb0449055c2d2694bfc2d429 100644 (file)
@@ -435,6 +435,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        struct dentry *alias;
        struct inode *dir = parent->d_inode;
        struct inode *inode;
+       int status;
 
        if (filename.name[0] == '.') {
                if (filename.len == 1)
@@ -447,7 +448,9 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        dentry = d_lookup(parent, &filename);
        if (dentry != NULL) {
                if (nfs_same_file(dentry, entry)) {
-                       nfs_refresh_inode(dentry->d_inode, entry->fattr);
+                       status = nfs_refresh_inode(dentry->d_inode, entry->fattr);
+                       if (!status)
+                               nfs_setsecurity(dentry->d_inode, entry->fattr, entry->label);
                        goto out;
                } else {
                        if (d_invalidate(dentry) != 0)
@@ -460,7 +463,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
        if (dentry == NULL)
                return;
 
-       inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
+       inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
        if (IS_ERR(inode))
                goto out;
 
@@ -585,10 +588,16 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        if (entry.fh == NULL || entry.fattr == NULL)
                goto out;
 
+       entry.label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
+       if (IS_ERR(entry.label)) {
+               status = PTR_ERR(entry.label);
+               goto out;
+       }
+
        array = nfs_readdir_get_array(page);
        if (IS_ERR(array)) {
                status = PTR_ERR(array);
-               goto out;
+               goto out_label_free;
        }
        memset(array, 0, sizeof(struct nfs_cache_array));
        array->eof_index = -1;
@@ -614,6 +623,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        nfs_readdir_free_large_page(pages_ptr, pages, array_size);
 out_release_array:
        nfs_readdir_release_array(page);
+out_label_free:
+       nfs4_label_free(entry.label);
 out:
        nfs_free_fattr(entry.fattr);
        nfs_free_fhandle(entry.fh);
@@ -1040,6 +1051,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        struct dentry *parent;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
+       struct nfs4_label *label = NULL;
        int error;
 
        if (flags & LOOKUP_RCU)
@@ -1082,7 +1094,11 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        if (fhandle == NULL || fattr == NULL)
                goto out_error;
 
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
+       if (IS_ERR(label))
+               goto out_error;
+
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
        if (error)
                goto out_bad;
        if (nfs_compare_fh(NFS_FH(inode), fhandle))
@@ -1090,8 +1106,12 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        if ((error = nfs_refresh_inode(inode, fattr)) != 0)
                goto out_bad;
 
+       nfs_setsecurity(inode, fattr, label);
+
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
+
 out_set_verifier:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_valid:
@@ -1108,6 +1128,7 @@ out_zap_parent:
  out_bad:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
        nfs_mark_for_revalidate(dir);
        if (inode && S_ISDIR(inode->i_mode)) {
                /* Purge readdir caches. */
@@ -1128,6 +1149,7 @@ out_zap_parent:
 out_error:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n",
                        __func__, dentry->d_parent->d_name.name,
@@ -1256,6 +1278,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
        struct inode *inode = NULL;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
+       struct nfs4_label *label = NULL;
        int error;
 
        dfprintk(VFS, "NFS: lookup(%s/%s)\n",
@@ -1282,17 +1305,21 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
        if (fhandle == NULL || fattr == NULL)
                goto out;
 
+       label = nfs4_label_alloc(NFS_SERVER(dir), GFP_NOWAIT);
+       if (IS_ERR(label))
+               goto out;
+
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
                res = ERR_PTR(error);
                goto out_unblock_sillyrename;
        }
-       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label);
        res = ERR_CAST(inode);
        if (IS_ERR(res))
                goto out_unblock_sillyrename;
@@ -1310,6 +1337,7 @@ no_entry:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
 out_unblock_sillyrename:
        nfs_unblock_sillyrename(parent);
+       nfs4_label_free(label);
 out:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
@@ -1508,7 +1536,8 @@ no_open:
  * Code common to create, mkdir, and mknod.
  */
 int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
-                               struct nfs_fattr *fattr)
+                               struct nfs_fattr *fattr,
+                               struct nfs4_label *label)
 {
        struct dentry *parent = dget_parent(dentry);
        struct inode *dir = parent->d_inode;
@@ -1521,18 +1550,18 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
        if (dentry->d_inode)
                goto out;
        if (fhandle->size == 0) {
-               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL);
                if (error)
                        goto out_error;
        }
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                struct nfs_server *server = NFS_SB(dentry->d_sb);
-               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
+               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr, NULL);
                if (error < 0)
                        goto out_error;
        }
-       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label);
        error = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_error;
index 44efaa8c5f78f38bda40753542740874b50c7155..66984a9aafaad9ad74d81415d5737cbafad81618 100644 (file)
@@ -95,7 +95,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
                goto out;
        }
 
-       inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
+       inode = nfs_fhget(sb, mntfh, fsinfo.fattr, NULL);
        if (IS_ERR(inode)) {
                dprintk("nfs_get_root: get root inode failed\n");
                ret = ERR_CAST(inode);
index e09920cacd8bffadb764b9264a3f3e02133a2997..8ea62be3fefe5e51cb244ef69106dbb706d7e259 100644 (file)
@@ -161,11 +161,19 @@ static void nfs_zap_caches_locked(struct inode *inode)
 
        memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
        if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
                nfs_fscache_invalidate(inode);
-       } else {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
-       }
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                                       | NFS_INO_INVALID_LABEL
+                                       | NFS_INO_INVALID_DATA
+                                       | NFS_INO_INVALID_ACCESS
+                                       | NFS_INO_INVALID_ACL
+                                       | NFS_INO_REVAL_PAGECACHE;
+       } else
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                                       | NFS_INO_INVALID_LABEL
+                                       | NFS_INO_INVALID_ACCESS
+                                       | NFS_INO_INVALID_ACL
+                                       | NFS_INO_REVAL_PAGECACHE;
 }
 
 void nfs_zap_caches(struct inode *inode)
@@ -256,12 +264,72 @@ nfs_init_locked(struct inode *inode, void *opaque)
        return 0;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                                       struct nfs4_label *label)
+{
+       int error;
+
+       if (label == NULL)
+               return;
+
+       if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL) == 0)
+               return;
+
+       if (NFS_SERVER(inode)->nfs_client->cl_minorversion < 2)
+               return;
+
+       if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
+               error = security_inode_notifysecctx(inode, label->label,
+                               label->len);
+               if (error)
+                       printk(KERN_ERR "%s() %s %d "
+                                       "security_inode_notifysecctx() %d\n",
+                                       __func__,
+                                       (char *)label->label,
+                                       label->len, error);
+       }
+}
+
+struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)
+{
+       struct nfs4_label *label = NULL;
+       int minor_version = server->nfs_client->cl_minorversion;
+
+       if (minor_version < 2)
+               return label;
+
+       if (!(server->caps & NFS_CAP_SECURITY_LABEL))
+               return label;
+
+       label = kzalloc(sizeof(struct nfs4_label), flags);
+       if (label == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       label->label = kzalloc(NFS4_MAXLABELLEN, flags);
+       if (label->label == NULL) {
+               kfree(label);
+               return ERR_PTR(-ENOMEM);
+       }
+       label->len = NFS4_MAXLABELLEN;
+
+       return label;
+}
+EXPORT_SYMBOL_GPL(nfs4_label_alloc);
+#else
+void inline nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                                       struct nfs4_label *label)
+{
+}
+#endif
+EXPORT_SYMBOL_GPL(nfs_setsecurity);
+
 /*
  * This is our front-end to iget that looks up inodes by file handle
  * instead of inode number.
  */
 struct inode *
-nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
+nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs_find_desc desc = {
                .fh     = fh,
@@ -383,6 +451,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                         */
                        inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
                }
+
+               nfs_setsecurity(inode, fattr, label);
+
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
                nfsi->access_cache = RB_ROOT;
@@ -392,6 +463,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                unlock_new_inode(inode);
        } else
                nfs_refresh_inode(inode, fattr);
+               nfs_setsecurity(inode, fattr, label);
        dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n",
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode),
@@ -448,7 +520,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
                NFS_PROTO(inode)->return_delegation(inode);
        error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
        if (error == 0)
-               nfs_refresh_inode(inode, fattr);
+               error = nfs_refresh_inode(inode, fattr);
        nfs_free_fattr(fattr);
 out:
        return error;
@@ -797,6 +869,7 @@ int
 __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
        int              status = -ESTALE;
+       struct nfs4_label *label = NULL;
        struct nfs_fattr *fattr = NULL;
        struct nfs_inode *nfsi = NFS_I(inode);
 
@@ -814,7 +887,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                goto out;
 
        nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
-       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr);
+
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(label)) {
+               status = PTR_ERR(label);
+               goto out;
+       }
+
+       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label);
        if (status != 0) {
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
                         inode->i_sb->s_id,
@@ -824,7 +904,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                        if (!S_ISDIR(inode->i_mode))
                                set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
                }
-               goto out;
+               goto err_out;
        }
 
        status = nfs_refresh_inode(inode, fattr);
@@ -832,7 +912,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
                         inode->i_sb->s_id,
                         (long long)NFS_FILEID(inode), status);
-               goto out;
+               goto err_out;
        }
 
        if (nfsi->cache_validity & NFS_INO_INVALID_ACL)
@@ -842,7 +922,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode));
 
- out:
+err_out:
+       nfs4_label_free(label);
+out:
        nfs_free_fattr(fattr);
        return status;
 }
@@ -870,7 +952,8 @@ static int nfs_attribute_cache_expired(struct inode *inode)
  */
 int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
-       if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR)
+       if (!(NFS_I(inode)->cache_validity &
+                       (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
                        && !nfs_attribute_cache_expired(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
@@ -1250,6 +1333,7 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        spin_lock(&inode->i_lock);
        status = nfs_post_op_update_inode_locked(inode, fattr);
        spin_unlock(&inode->i_lock);
+
        return status;
 }
 EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
@@ -1490,7 +1574,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                inode->i_blocks = fattr->du.nfs2.blocks;
 
        /* Update attrtimeo value if we're out of the unstable period */
-       if (invalid & NFS_INO_INVALID_ATTR) {
+       if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) {
                nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
@@ -1503,6 +1587,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                }
        }
        invalid &= ~NFS_INO_INVALID_ATTR;
+       invalid &= ~NFS_INO_INVALID_LABEL;
        /* Don't invalidate the data if we were to blame */
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
                                || S_ISLNK(inode->i_mode)))
index 4bd53f4e0fd2a758c6c2613030b663c7efd8eab1..3c8373f90ab3150f2530a795b977c1489a344771 100644 (file)
@@ -165,7 +165,7 @@ extern void nfs_free_client(struct nfs_client *);
 extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
 extern struct nfs_client *
 nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
-                               struct nfs4_sessionid *);
+                               struct nfs4_sessionid *, u32);
 extern struct nfs_server *nfs_create_server(struct nfs_mount_info *,
                                        struct nfs_subversion *);
 extern struct nfs_server *nfs4_create_server(
index fc8dc20fdeb9c90274d5acb8ef2bb831af297425..348b535cd7866d9e18cfa6f9412b650266244f0a 100644 (file)
@@ -280,7 +280,7 @@ struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry,
        struct dentry *parent = dget_parent(dentry);
 
        /* Look it up again to get its attributes */
-       err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr);
+       err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr, NULL);
        dput(parent);
        if (err != 0)
                return ERR_PTR(err);
index 43ea96ced28cbc763a60048ab95cefcf737ecf40..39c185b03cc04243c870f7e1c56d105e0b9b0d7d 100644 (file)
@@ -98,7 +98,7 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
  */
 static int
 nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs3_procedures[NFS3PROC_GETATTR],
@@ -143,7 +143,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 
 static int
 nfs3_proc_lookup(struct inode *dir, struct qstr *name,
-                struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+                struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+                struct nfs4_label *label)
 {
        struct nfs3_diropargs   arg = {
                .fh             = NFS_FH(dir),
@@ -300,7 +301,7 @@ static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_
        status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
        nfs_post_op_update_inode(dir, data->res.dir_attr);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        return status;
 }
 
index e64a00c0337148805a59ff6c774cf0e680efd07b..ee81e354bce7a9d7fbc36023c993fe312ef3e255 100644 (file)
@@ -303,10 +303,10 @@ is_ds_client(struct nfs_client *clp)
 extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[];
 
 extern const u32 nfs4_fattr_bitmap[3];
-extern const u32 nfs4_statfs_bitmap[2];
-extern const u32 nfs4_pathconf_bitmap[2];
+extern const u32 nfs4_statfs_bitmap[3];
+extern const u32 nfs4_pathconf_bitmap[3];
 extern const u32 nfs4_fsinfo_bitmap[3];
-extern const u32 nfs4_fs_locations_bitmap[2];
+extern const u32 nfs4_fs_locations_bitmap[3];
 
 void nfs4_free_client(struct nfs_client *);
 
index 0054e4bd6b555c3ff514c689bc933f70b5f1f573..90dce91dd5b5c7aa61a7a17c34242619bbfc5b8d 100644 (file)
@@ -66,6 +66,11 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
        if (err)
                goto error;
 
+       if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) {
+               err = -EINVAL;
+               goto error;
+       }
+
        spin_lock_init(&clp->cl_lock);
        INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
@@ -562,14 +567,14 @@ static bool nfs4_cb_match_client(const struct sockaddr *addr,
  */
 struct nfs_client *
 nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
-                          struct nfs4_sessionid *sid)
+                          struct nfs4_sessionid *sid, u32 minorversion)
 {
        struct nfs_client *clp;
        struct nfs_net *nn = net_generic(net, nfs_net_id);
 
        spin_lock(&nn->nfs_client_lock);
        list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
-               if (nfs4_cb_match_client(addr, clp, 1) == false)
+               if (nfs4_cb_match_client(addr, clp, minorversion) == false)
                        continue;
 
                if (!nfs4_has_session(clp))
@@ -592,7 +597,7 @@ nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
 
 struct nfs_client *
 nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
-                          struct nfs4_sessionid *sid)
+                          struct nfs4_sessionid *sid, u32 minorversion)
 {
        return NULL;
 }
index 6d46f966d169643b57531565abfe59396d952168..d95616f140ad1be205d2980520a235b20b8ca9dd 100644 (file)
@@ -77,17 +77,68 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
 static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
 static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
-static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);
-static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state);
+                           struct nfs4_state *state, struct nfs4_label *ilabel,
+                           struct nfs4_label *olabel);
 #ifdef CONFIG_NFS_V4_1
 static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
                struct rpc_cred *);
 static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
                struct rpc_cred *);
 #endif
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static inline struct nfs4_label *
+nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
+       struct iattr *sattr, struct nfs4_label *label)
+{
+       int err;
+
+       if (label == NULL)
+               return NULL;
+
+       if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0)
+               return NULL;
+
+       if (NFS_SERVER(dir)->nfs_client->cl_minorversion < 2)
+               return NULL;
+
+       err = security_dentry_init_security(dentry, sattr->ia_mode,
+                               &dentry->d_name, (void **)&label->label, &label->len);
+       if (err == 0)
+               return label;
+
+       return NULL;
+}
+static inline void
+nfs4_label_release_security(struct nfs4_label *label)
+{
+       if (label)
+               security_release_secctx(label->label, label->len);
+}
+static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
+{
+       if (label)
+               return server->attr_bitmask;
+
+       return server->attr_bitmask_nl;
+}
+#else
+static inline struct nfs4_label *
+nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
+       struct iattr *sattr, struct nfs4_label *l)
+{ return NULL; }
+static inline void
+nfs4_label_release_security(struct nfs4_label *label)
+{ return; }
+static inline u32 *
+nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
+{ return server->attr_bitmask; }
+#endif
+
 /* Prevent leaks of NFSv4 errors into userland */
 static int nfs4_map_errors(int err)
 {
@@ -136,7 +187,10 @@ const u32 nfs4_fattr_bitmap[3] = {
        | FATTR4_WORD1_SPACE_USED
        | FATTR4_WORD1_TIME_ACCESS
        | FATTR4_WORD1_TIME_METADATA
-       | FATTR4_WORD1_TIME_MODIFY
+       | FATTR4_WORD1_TIME_MODIFY,
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+       FATTR4_WORD2_SECURITY_LABEL
+#endif
 };
 
 static const u32 nfs4_pnfs_open_bitmap[3] = {
@@ -163,7 +217,7 @@ static const u32 nfs4_open_noattr_bitmap[3] = {
        | FATTR4_WORD0_FILEID,
 };
 
-const u32 nfs4_statfs_bitmap[2] = {
+const u32 nfs4_statfs_bitmap[3] = {
        FATTR4_WORD0_FILES_AVAIL
        | FATTR4_WORD0_FILES_FREE
        | FATTR4_WORD0_FILES_TOTAL,
@@ -172,7 +226,7 @@ const u32 nfs4_statfs_bitmap[2] = {
        | FATTR4_WORD1_SPACE_TOTAL
 };
 
-const u32 nfs4_pathconf_bitmap[2] = {
+const u32 nfs4_pathconf_bitmap[3] = {
        FATTR4_WORD0_MAXLINK
        | FATTR4_WORD0_MAXNAME,
        0
@@ -187,7 +241,7 @@ const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
                        FATTR4_WORD2_LAYOUT_BLKSIZE
 };
 
-const u32 nfs4_fs_locations_bitmap[2] = {
+const u32 nfs4_fs_locations_bitmap[3] = {
        FATTR4_WORD0_TYPE
        | FATTR4_WORD0_CHANGE
        | FATTR4_WORD0_SIZE
@@ -203,7 +257,7 @@ const u32 nfs4_fs_locations_bitmap[2] = {
        | FATTR4_WORD1_TIME_ACCESS
        | FATTR4_WORD1_TIME_METADATA
        | FATTR4_WORD1_TIME_MODIFY
-       | FATTR4_WORD1_MOUNTED_ON_FILEID
+       | FATTR4_WORD1_MOUNTED_ON_FILEID,
 };
 
 static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
@@ -764,6 +818,7 @@ struct nfs4_opendata {
        struct nfs4_string owner_name;
        struct nfs4_string group_name;
        struct nfs_fattr f_attr;
+       struct nfs4_label *f_label;
        struct dentry *dir;
        struct dentry *dentry;
        struct nfs4_state_owner *owner;
@@ -809,6 +864,7 @@ nfs4_map_atomic_open_claim(struct nfs_server *server,
 static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 {
        p->o_res.f_attr = &p->f_attr;
+       p->o_res.f_label = p->f_label;
        p->o_res.seqid = p->o_arg.seqid;
        p->c_res.seqid = p->c_arg.seqid;
        p->o_res.server = p->o_arg.server;
@@ -820,6 +876,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
                struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs,
+               struct nfs4_label *label,
                enum open_claim_type4 claim,
                gfp_t gfp_mask)
 {
@@ -831,9 +888,14 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p = kzalloc(sizeof(*p), gfp_mask);
        if (p == NULL)
                goto err;
+
+       p->f_label = nfs4_label_alloc(server, gfp_mask);
+       if (IS_ERR(p->f_label))
+               goto err_free_p;
+
        p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
        if (p->o_arg.seqid == NULL)
-               goto err_free;
+               goto err_free_label;
        nfs_sb_active(dentry->d_sb);
        p->dentry = dget(dentry);
        p->dir = parent;
@@ -854,8 +916,9 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p->o_arg.id.uniquifier = sp->so_seqid.owner_id;
        p->o_arg.name = &dentry->d_name;
        p->o_arg.server = server;
-       p->o_arg.bitmask = server->attr_bitmask;
+       p->o_arg.bitmask = nfs4_bitmask(server, label);
        p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
+       p->o_arg.label = label;
        p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
        switch (p->o_arg.claim) {
        case NFS4_OPEN_CLAIM_NULL:
@@ -886,7 +949,10 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        nfs4_init_opendata_res(p);
        kref_init(&p->kref);
        return p;
-err_free:
+
+err_free_label:
+       nfs4_label_free(p->f_label);
+err_free_p:
        kfree(p);
 err:
        dput(parent);
@@ -903,6 +969,9 @@ static void nfs4_opendata_free(struct kref *kref)
        if (p->state != NULL)
                nfs4_put_open_state(p->state);
        nfs4_put_state_owner(p->owner);
+
+       nfs4_label_free(p->f_label);
+
        dput(p->dir);
        dput(p->dentry);
        nfs_sb_deactive(sb);
@@ -1181,6 +1250,8 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
        if (ret)
                goto err;
 
+       nfs_setsecurity(inode, &data->f_attr, data->f_label);
+
        if (data->o_res.delegation_type != 0)
                nfs4_opendata_check_deleg(data, state);
        update_open_stateid(state, &data->o_res.stateid, NULL,
@@ -1207,7 +1278,7 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
        ret = -EAGAIN;
        if (!(data->f_attr.valid & NFS_ATTR_FATTR))
                goto err;
-       inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
+       inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr, data->f_label);
        ret = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto err;
@@ -1260,7 +1331,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
        struct nfs4_opendata *opendata;
 
        opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
-                       NULL, claim, GFP_NOFS);
+                       NULL, NULL, claim, GFP_NOFS);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@ -1786,7 +1857,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
                        return status;
        }
        if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
-               _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr);
+               _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label);
        return 0;
 }
 
@@ -2017,7 +2088,8 @@ out:
 static int _nfs4_do_open(struct inode *dir,
                        struct nfs_open_context *ctx,
                        int flags,
-                       struct iattr *sattr)
+                       struct iattr *sattr,
+                       struct nfs4_label *label)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
@@ -2028,6 +2100,7 @@ static int _nfs4_do_open(struct inode *dir,
        struct nfs4_threshold **ctx_th = &ctx->mdsthreshold;
        fmode_t fmode = ctx->mode & (FMODE_READ|FMODE_WRITE|FMODE_EXEC);
        enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
+       struct nfs4_label *olabel = NULL;
        int status;
 
        /* Protect against reboot recovery conflicts */
@@ -2046,14 +2119,22 @@ static int _nfs4_do_open(struct inode *dir,
        if (dentry->d_inode)
                claim = NFS4_OPEN_CLAIM_FH;
        opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
-                       claim, GFP_KERNEL);
+                       label, claim, GFP_KERNEL);
        if (opendata == NULL)
                goto err_put_state_owner;
 
+       if (label) {
+               olabel = nfs4_label_alloc(server, GFP_KERNEL);
+               if (IS_ERR(olabel)) {
+                       status = PTR_ERR(olabel);
+                       goto err_opendata_put;
+               }
+       }
+
        if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
                opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
                if (!opendata->f_attr.mdsthreshold)
-                       goto err_opendata_put;
+                       goto err_free_label;
                opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];
        }
        if (dentry->d_inode != NULL)
@@ -2061,7 +2142,7 @@ static int _nfs4_do_open(struct inode *dir,
 
        status = _nfs4_open_and_get_state(opendata, fmode, flags, ctx);
        if (status != 0)
-               goto err_opendata_put;
+               goto err_free_label;
        state = ctx->state;
 
        if ((opendata->o_arg.open_flags & O_EXCL) &&
@@ -2071,10 +2152,12 @@ static int _nfs4_do_open(struct inode *dir,
                nfs_fattr_init(opendata->o_res.f_attr);
                status = nfs4_do_setattr(state->inode, cred,
                                opendata->o_res.f_attr, sattr,
-                               state);
-               if (status == 0)
+                               state, label, olabel);
+               if (status == 0) {
                        nfs_setattr_update_inode(state->inode, sattr);
-               nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+                       nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+                       nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
+               }
        }
 
        if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
@@ -2083,9 +2166,13 @@ static int _nfs4_do_open(struct inode *dir,
                kfree(opendata->f_attr.mdsthreshold);
        opendata->f_attr.mdsthreshold = NULL;
 
+       nfs4_label_free(olabel);
+
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
        return 0;
+err_free_label:
+       nfs4_label_free(olabel);
 err_opendata_put:
        kfree(opendata->f_attr.mdsthreshold);
        nfs4_opendata_put(opendata);
@@ -2099,7 +2186,8 @@ out_err:
 static struct nfs4_state *nfs4_do_open(struct inode *dir,
                                        struct nfs_open_context *ctx,
                                        int flags,
-                                       struct iattr *sattr)
+                                       struct iattr *sattr,
+                                       struct nfs4_label *label)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs4_exception exception = { };
@@ -2107,7 +2195,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
        int status;
 
        do {
-               status = _nfs4_do_open(dir, ctx, flags, sattr);
+               status = _nfs4_do_open(dir, ctx, flags, sattr, label);
                res = ctx->state;
                if (status == 0)
                        break;
@@ -2154,7 +2242,8 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
 
 static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state)
+                           struct nfs4_state *state, struct nfs4_label *ilabel,
+                           struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
         struct nfs_setattrargs  arg = {
@@ -2162,9 +2251,11 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                 .iap            = sattr,
                .server         = server,
                .bitmask = server->attr_bitmask,
+               .label          = ilabel,
         };
         struct nfs_setattrres  res = {
                .fattr          = fattr,
+               .label          = olabel,
                .server         = server,
         };
         struct rpc_message msg = {
@@ -2178,6 +2269,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        bool truncate;
        int status;
 
+       arg.bitmask = nfs4_bitmask(server, ilabel);
+       if (ilabel)
+               arg.bitmask = nfs4_bitmask(server, olabel);
+
        nfs_fattr_init(fattr);
 
        /* Servers should only apply open mode checks for file size changes */
@@ -2204,7 +2299,8 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
 
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                           struct nfs_fattr *fattr, struct iattr *sattr,
-                          struct nfs4_state *state)
+                          struct nfs4_state *state, struct nfs4_label *ilabel,
+                          struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct nfs4_exception exception = {
@@ -2213,7 +2309,7 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        };
        int err;
        do {
-               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state);
+               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel);
                switch (err) {
                case -NFS4ERR_OPENMODE:
                        if (!(sattr->ia_valid & ATTR_SIZE)) {
@@ -2458,9 +2554,15 @@ static struct inode *
 nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
 {
        struct nfs4_state *state;
+       struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL;
+
+       label = nfs4_label_init_security(dir, ctx->dentry, attr, &l);
 
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, ctx, open_flags, attr);
+       state = nfs4_do_open(dir, ctx, open_flags, attr, label);
+
+       nfs4_label_release_security(label);
+
        if (IS_ERR(state))
                return ERR_CAST(state);
        return state->inode;
@@ -2519,7 +2621,17 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
                        server->caps |= NFS_CAP_CTIME;
                if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
                        server->caps |= NFS_CAP_MTIME;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+               if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL)
+                       server->caps |= NFS_CAP_SECURITY_LABEL;
+#endif
+               memcpy(server->attr_bitmask_nl, res.attr_bitmask,
+                               sizeof(server->attr_bitmask));
 
+               if (server->caps & NFS_CAP_SECURITY_LABEL) {
+                       server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+                       res.attr_bitmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+               }
                memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
                server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
                server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
@@ -2545,8 +2657,9 @@ int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
 static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                struct nfs_fsinfo *info)
 {
+       u32 bitmask[3];
        struct nfs4_lookup_root_arg args = {
-               .bitmask = nfs4_fattr_bitmap,
+               .bitmask = bitmask,
        };
        struct nfs4_lookup_res res = {
                .server = server,
@@ -2559,6 +2672,13 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_resp = &res,
        };
 
+       bitmask[0] = nfs4_fattr_bitmap[0];
+       bitmask[1] = nfs4_fattr_bitmap[1];
+       /*
+        * Process the label in the upcoming getfattr
+        */
+       bitmask[2] = nfs4_fattr_bitmap[2] & ~FATTR4_WORD2_SECURITY_LABEL;
+
        nfs_fattr_init(info->fattr);
        return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 }
@@ -2678,6 +2798,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
 {
        int error;
        struct nfs_fattr *fattr = info->fattr;
+       struct nfs4_label *label = NULL;
 
        error = nfs4_server_capabilities(server, mntfh);
        if (error < 0) {
@@ -2685,16 +2806,23 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
                return error;
        }
 
-       error = nfs4_proc_getattr(server, mntfh, fattr);
+       label = nfs4_label_alloc(server, GFP_KERNEL);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       error = nfs4_proc_getattr(server, mntfh, fattr, label);
        if (error < 0) {
                dprintk("nfs4_get_root: getattr error = %d\n", -error);
-               return error;
+               goto err_free_label;
        }
 
        if (fattr->valid & NFS_ATTR_FATTR_FSID &&
            !nfs_fsid_equal(&server->fsid, &fattr->fsid))
                memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
 
+err_free_label:
+       nfs4_label_free(label);
+
        return error;
 }
 
@@ -2741,7 +2869,8 @@ out:
        return status;
 }
 
-static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+                               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_getattr_arg args = {
                .fh = fhandle,
@@ -2749,6 +2878,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        };
        struct nfs4_getattr_res res = {
                .fattr = fattr,
+               .label = label,
                .server = server,
        };
        struct rpc_message msg = {
@@ -2756,18 +2886,21 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       
+
+       args.bitmask = nfs4_bitmask(server, label);
+
        nfs_fattr_init(fattr);
        return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 }
 
-static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+                               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(server,
-                               _nfs4_proc_getattr(server, fhandle, fattr),
+                               _nfs4_proc_getattr(server, fhandle, fattr, label),
                                &exception);
        } while (exception.retry);
        return err;
@@ -2797,6 +2930,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
        struct inode *inode = dentry->d_inode;
        struct rpc_cred *cred = NULL;
        struct nfs4_state *state = NULL;
+       struct nfs4_label *label = NULL;
        int status;
 
        if (pnfs_ld_layoutret_on_setattr(inode))
@@ -2823,15 +2957,22 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
                }
        }
 
-       status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
-       if (status == 0)
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label);
+       if (status == 0) {
                nfs_setattr_update_inode(inode, sattr);
+               nfs_setsecurity(inode, fattr, label);
+       }
+       nfs4_label_free(label);
        return status;
 }
 
 static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                const struct qstr *name, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        int                    status;
@@ -2843,6 +2984,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
        struct nfs4_lookup_res res = {
                .server = server,
                .fattr = fattr,
+               .label = label,
                .fh = fhandle,
        };
        struct rpc_message msg = {
@@ -2851,6 +2993,8 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                .rpc_resp = &res,
        };
 
+       args.bitmask = nfs4_bitmask(server, label);
+
        nfs_fattr_init(fattr);
 
        dprintk("NFS call  lookup %s\n", name->name);
@@ -2869,13 +3013,13 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
 
 static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
                                   struct qstr *name, struct nfs_fh *fhandle,
-                                  struct nfs_fattr *fattr)
+                                  struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = { };
        struct rpc_clnt *client = *clnt;
        int err;
        do {
-               err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr);
+               err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label);
                switch (err) {
                case -NFS4ERR_BADNAME:
                        err = -ENOENT;
@@ -2909,12 +3053,13 @@ out:
 }
 
 static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
-                           struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+                           struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+                           struct nfs4_label *label)
 {
        int status;
        struct rpc_clnt *client = NFS_CLIENT(dir);
 
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, label);
        if (client != NFS_CLIENT(dir)) {
                rpc_shutdown_client(client);
                nfs_fixup_secinfo_attributes(fattr);
@@ -2929,7 +3074,7 @@ nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name,
        int status;
        struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
 
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL);
        if (status < 0) {
                rpc_shutdown_client(client);
                return ERR_PTR(status);
@@ -2954,7 +3099,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
                .rpc_cred = entry->cred,
        };
        int mode = entry->mask;
-       int status;
+       int status = 0;
 
        /*
         * Determine which access bits we want to ask for...
@@ -3059,6 +3204,7 @@ static int
 nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                 int flags)
 {
+       struct nfs4_label l, *ilabel = NULL;
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
        int status = 0;
@@ -3067,13 +3213,16 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        if (IS_ERR(ctx))
                return PTR_ERR(ctx);
 
+       ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, ctx, flags, sattr);
+       state = nfs4_do_open(dir, ctx, flags, sattr, ilabel);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
                goto out;
        }
 out:
+       nfs4_label_release_security(ilabel);
        put_nfs_open_context(ctx);
        return status;
 }
@@ -3122,6 +3271,8 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
        res->server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
        nfs41_init_sequence(&args->seq_args, &res->seq_res, 1);
+
+       nfs_fattr_init(res->dir_attr);
 }
 
 static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data)
@@ -3197,7 +3348,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
                .rpc_resp = &res,
        };
        int status = -ENOMEM;
-       
+
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
@@ -3231,6 +3382,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        };
        struct nfs4_link_res res = {
                .server = server,
+               .label = NULL,
        };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
@@ -3243,11 +3395,24 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        if (res.fattr == NULL)
                goto out;
 
+       res.label = nfs4_label_alloc(server, GFP_KERNEL);
+       if (IS_ERR(res.label)) {
+               status = PTR_ERR(res.label);
+               goto out;
+       }
+       arg.bitmask = nfs4_bitmask(server, res.label);
+
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(inode, res.fattr);
+               status = nfs_post_op_update_inode(inode, res.fattr);
+               if (!status)
+                       nfs_setsecurity(inode, res.fattr, res.label);
        }
+
+
+       nfs4_label_free(res.label);
+
 out:
        nfs_free_fattr(res.fattr);
        return status;
@@ -3271,6 +3436,7 @@ struct nfs4_createdata {
        struct nfs4_create_res res;
        struct nfs_fh fh;
        struct nfs_fattr fattr;
+       struct nfs4_label *label;
 };
 
 static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
@@ -3282,6 +3448,10 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
        if (data != NULL) {
                struct nfs_server *server = NFS_SERVER(dir);
 
+               data->label = nfs4_label_alloc(server, GFP_KERNEL);
+               if (IS_ERR(data->label))
+                       goto out_free;
+
                data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE];
                data->msg.rpc_argp = &data->arg;
                data->msg.rpc_resp = &data->res;
@@ -3290,13 +3460,17 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
                data->arg.name = name;
                data->arg.attrs = sattr;
                data->arg.ftype = ftype;
-               data->arg.bitmask = server->attr_bitmask;
+               data->arg.bitmask = nfs4_bitmask(server, data->label);
                data->res.server = server;
                data->res.fh = &data->fh;
                data->res.fattr = &data->fattr;
+               data->res.label = data->label;
                nfs_fattr_init(data->res.fattr);
        }
        return data;
+out_free:
+       kfree(data);
+       return NULL;
 }
 
 static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
@@ -3305,18 +3479,20 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
                                    &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label);
        }
        return status;
 }
 
 static void nfs4_free_createdata(struct nfs4_createdata *data)
 {
+       nfs4_label_free(data->label);
        kfree(data);
 }
 
 static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
-               struct page *page, unsigned int len, struct iattr *sattr)
+               struct page *page, unsigned int len, struct iattr *sattr,
+               struct nfs4_label *label)
 {
        struct nfs4_createdata *data;
        int status = -ENAMETOOLONG;
@@ -3332,6 +3508,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
        data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK];
        data->arg.u.symlink.pages = &page;
        data->arg.u.symlink.len = len;
+       data->arg.label = label;
        
        status = nfs4_do_create(dir, dentry, data);
 
@@ -3344,18 +3521,24 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
                struct page *page, unsigned int len, struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
+
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
                                _nfs4_proc_symlink(dir, dentry, page,
-                                                       len, sattr),
+                                                       len, sattr, label),
                                &exception);
        } while (exception.retry);
+
+       nfs4_label_release_security(label);
        return err;
 }
 
 static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
-               struct iattr *sattr)
+               struct iattr *sattr, struct nfs4_label *label)
 {
        struct nfs4_createdata *data;
        int status = -ENOMEM;
@@ -3364,6 +3547,7 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
        if (data == NULL)
                goto out;
 
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
 
        nfs4_free_createdata(data);
@@ -3375,14 +3559,19 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
                struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
 
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_mkdir(dir, dentry, sattr),
+                               _nfs4_proc_mkdir(dir, dentry, sattr, label),
                                &exception);
        } while (exception.retry);
+       nfs4_label_release_security(label);
+
        return err;
 }
 
@@ -3440,7 +3629,7 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
 }
 
 static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
-               struct iattr *sattr, dev_t rdev)
+               struct iattr *sattr, struct nfs4_label *label, dev_t rdev)
 {
        struct nfs4_createdata *data;
        int mode = sattr->ia_mode;
@@ -3465,7 +3654,8 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                status = -EINVAL;
                goto out_free;
        }
-       
+
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
 out_free:
        nfs4_free_createdata(data);
@@ -3477,14 +3667,20 @@ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                struct iattr *sattr, dev_t rdev)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
 
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_mknod(dir, dentry, sattr, rdev),
+                               _nfs4_proc_mknod(dir, dentry, sattr, label, rdev),
                                &exception);
        } while (exception.retry);
+
+       nfs4_label_release_security(label);
+
        return err;
 }
 
@@ -4211,6 +4407,155 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
        return err;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static int _nfs4_get_security_label(struct inode *inode, void *buf,
+                                       size_t buflen)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_fattr fattr;
+       struct nfs4_label label = {0, 0, buflen, buf};
+
+       u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
+       struct nfs4_getattr_arg args = {
+               .fh             = NFS_FH(inode),
+               .bitmask        = bitmask,
+       };
+       struct nfs4_getattr_res res = {
+               .fattr          = &fattr,
+               .label          = &label,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
+               .rpc_argp       = &args,
+               .rpc_resp       = &res,
+       };
+       int ret;
+
+       nfs_fattr_init(&fattr);
+
+       ret = rpc_call_sync(server->client, &msg, 0);
+       if (ret)
+               return ret;
+       if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
+               return -ENOENT;
+       if (buflen < label.len)
+               return -ERANGE;
+       return 0;
+}
+
+static int nfs4_get_security_label(struct inode *inode, void *buf,
+                                       size_t buflen)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+               return -EOPNOTSUPP;
+
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               _nfs4_get_security_label(inode, buf, buflen),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
+static int _nfs4_do_set_security_label(struct inode *inode,
+               struct nfs4_label *ilabel,
+               struct nfs_fattr *fattr,
+               struct nfs4_label *olabel)
+{
+
+       struct iattr sattr = {0};
+       struct nfs_server *server = NFS_SERVER(inode);
+       const u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
+       struct nfs_setattrargs args = {
+               .fh             = NFS_FH(inode),
+               .iap            = &sattr,
+               .server         = server,
+               .bitmask        = bitmask,
+               .label          = ilabel,
+       };
+       struct nfs_setattrres res = {
+               .fattr          = fattr,
+               .label          = olabel,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
+               .rpc_argp       = &args,
+               .rpc_resp       = &res,
+       };
+       int status;
+
+       nfs4_stateid_copy(&args.stateid, &zero_stateid);
+
+       status = rpc_call_sync(server->client, &msg, 0);
+       if (status)
+               dprintk("%s failed: %d\n", __func__, status);
+
+       return status;
+}
+
+static int nfs4_do_set_security_label(struct inode *inode,
+               struct nfs4_label *ilabel,
+               struct nfs_fattr *fattr,
+               struct nfs4_label *olabel)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               _nfs4_do_set_security_label(inode, ilabel,
+                               fattr, olabel),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
+static int
+nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen)
+{
+       struct nfs4_label ilabel, *olabel = NULL;
+       struct nfs_fattr fattr;
+       struct rpc_cred *cred;
+       struct inode *inode = dentry->d_inode;
+       int status;
+
+       if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+               return -EOPNOTSUPP;
+
+       nfs_fattr_init(&fattr);
+
+       ilabel.pi = 0;
+       ilabel.lfs = 0;
+       ilabel.label = (char *)buf;
+       ilabel.len = buflen;
+
+       cred = rpc_lookup_cred();
+       if (IS_ERR(cred))
+               return PTR_ERR(cred);
+
+       olabel = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(olabel)) {
+               status = -PTR_ERR(olabel);
+               goto out;
+       }
+
+       status = nfs4_do_set_security_label(inode, &ilabel, &fattr, olabel);
+       if (status == 0)
+               nfs_setsecurity(inode, &fattr, olabel);
+
+       nfs4_label_free(olabel);
+out:
+       put_rpccred(cred);
+       return status;
+}
+#endif /* CONFIG_NFS_V4_SECURITY_LABEL */
+
+
 static int
 nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
 {
@@ -5324,6 +5669,53 @@ static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
        return len;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static inline int nfs4_server_supports_labels(struct nfs_server *server)
+{
+       return server->caps & NFS_CAP_SECURITY_LABEL;
+}
+
+static int nfs4_xattr_set_nfs4_label(struct dentry *dentry, const char *key,
+                                  const void *buf, size_t buflen,
+                                  int flags, int type)
+{
+       if (security_ismaclabel(key))
+               return nfs4_set_security_label(dentry, buf, buflen);
+
+       return -EOPNOTSUPP;
+}
+
+static int nfs4_xattr_get_nfs4_label(struct dentry *dentry, const char *key,
+                                  void *buf, size_t buflen, int type)
+{
+       if (security_ismaclabel(key))
+               return nfs4_get_security_label(dentry->d_inode, buf, buflen);
+       return -EOPNOTSUPP;
+}
+
+static size_t nfs4_xattr_list_nfs4_label(struct dentry *dentry, char *list,
+                                      size_t list_len, const char *name,
+                                      size_t name_len, int type)
+{
+       size_t len = 0;
+
+       if (nfs_server_capable(dentry->d_inode, NFS_CAP_SECURITY_LABEL)) {
+               len = security_inode_listsecurity(dentry->d_inode, NULL, 0);
+               if (list && len <= list_len)
+                       security_inode_listsecurity(dentry->d_inode, list, len);
+       }
+       return len;
+}
+
+static const struct xattr_handler nfs4_xattr_nfs4_label_handler = {
+       .prefix = XATTR_SECURITY_PREFIX,
+       .list   = nfs4_xattr_list_nfs4_label,
+       .get    = nfs4_xattr_get_nfs4_label,
+       .set    = nfs4_xattr_set_nfs4_label,
+};
+#endif
+
+
 /*
  * nfs_fhget will use either the mounted_on_fileid or the fileid
  */
@@ -5347,7 +5739,7 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
                                   struct page *page)
 {
        struct nfs_server *server = NFS_SERVER(dir);
-       u32 bitmask[2] = {
+       u32 bitmask[3] = {
                [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
        };
        struct nfs4_fs_locations_arg args = {
@@ -7052,11 +7444,33 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
 };
 #endif
 
+#if defined(CONFIG_NFS_V4_2)
+static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
+       .minor_version = 2,
+       .init_caps = NFS_CAP_READDIRPLUS
+               | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_CHANGE_ATTR
+               | NFS_CAP_POSIX_LOCK
+               | NFS_CAP_STATEID_NFSV41
+               | NFS_CAP_ATOMIC_OPEN_V1,
+       .call_sync = nfs4_call_sync_sequence,
+       .match_stateid = nfs41_match_stateid,
+       .find_root_sec = nfs41_find_root_sec,
+       .free_lock_state = nfs41_free_lock_state,
+       .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
+       .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
+       .state_renewal_ops = &nfs41_state_renewal_ops,
+};
+#endif
+
 const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
        [0] = &nfs_v4_0_minor_ops,
 #if defined(CONFIG_NFS_V4_1)
        [1] = &nfs_v4_1_minor_ops,
 #endif
+#if defined(CONFIG_NFS_V4_2)
+       [2] = &nfs_v4_2_minor_ops,
+#endif
 };
 
 const struct inode_operations nfs4_dir_inode_operations = {
@@ -7156,6 +7570,9 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
 
 const struct xattr_handler *nfs4_xattr_handlers[] = {
        &nfs4_xattr_nfs4_acl_handler,
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+       &nfs4_xattr_nfs4_label_handler,
+#endif
        NULL
 };
 
index 27cc76d88f9a430ebf403822aad98370c105528a..0abfb8466e796cb8c0894ae6fbc38180865904f4 100644 (file)
@@ -102,12 +102,23 @@ static int nfs4_stat_to_errno(int);
 #define nfs4_path_maxsz                (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
 #define nfs4_owner_maxsz       (1 + XDR_QUADLEN(IDMAP_NAMESZ))
 #define nfs4_group_maxsz       (1 + XDR_QUADLEN(IDMAP_NAMESZ))
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+/* PI(4 bytes) + LFS(4 bytes) + 1(for null terminator?) + MAXLABELLEN */
+#define        nfs4_label_maxsz        (4 + 4 + 1 + XDR_QUADLEN(NFS4_MAXLABELLEN))
+#define encode_readdir_space 24
+#define encode_readdir_bitmask_sz 3
+#else
+#define        nfs4_label_maxsz        0
+#define encode_readdir_space 20
+#define encode_readdir_bitmask_sz 2
+#endif
 /* We support only one layout type per file system */
 #define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8)
 /* This is based on getfattr, which uses the most attributes: */
 #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
                                3 + 3 + 3 + nfs4_owner_maxsz + \
-                               nfs4_group_maxsz + decode_mdsthreshold_maxsz))
+                               nfs4_group_maxsz + nfs4_label_maxsz + \
+                                decode_mdsthreshold_maxsz))
 #define nfs4_fattr_maxsz       (nfs4_fattr_bitmap_maxsz + \
                                nfs4_fattr_value_maxsz)
 #define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
@@ -115,6 +126,7 @@ static int nfs4_stat_to_errno(int);
                                 1 + 2 + 1 + \
                                nfs4_owner_maxsz + \
                                nfs4_group_maxsz + \
+                               nfs4_label_maxsz + \
                                4 + 4)
 #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
 #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
@@ -192,9 +204,11 @@ static int nfs4_stat_to_errno(int);
                                 encode_stateid_maxsz + 3)
 #define decode_read_maxsz      (op_decode_hdr_maxsz + 2)
 #define encode_readdir_maxsz   (op_encode_hdr_maxsz + \
-                                2 + encode_verifier_maxsz + 5)
+                                2 + encode_verifier_maxsz + 5 + \
+                               nfs4_label_maxsz)
 #define decode_readdir_maxsz   (op_decode_hdr_maxsz + \
-                                decode_verifier_maxsz)
+                                decode_verifier_maxsz + \
+                               nfs4_label_maxsz + nfs4_fattr_maxsz)
 #define encode_readlink_maxsz  (op_encode_hdr_maxsz)
 #define decode_readlink_maxsz  (op_decode_hdr_maxsz + 1)
 #define encode_write_maxsz     (op_encode_hdr_maxsz + \
@@ -974,7 +988,9 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
        encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE);
 }
 
-static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
+                               const struct nfs4_label *label,
+                               const struct nfs_server *server)
 {
        char owner_name[IDMAP_NAMESZ];
        char owner_group[IDMAP_NAMESZ];
@@ -985,15 +1001,16 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
        int len;
        uint32_t bmval0 = 0;
        uint32_t bmval1 = 0;
+       uint32_t bmval2 = 0;
 
        /*
         * We reserve enough space to write the entire attribute buffer at once.
         * In the worst-case, this would be
-        *   12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
-        *          = 36 bytes, plus any contribution from variable-length fields
+        * 16(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
+        * = 40 bytes, plus any contribution from variable-length fields
         *            such as owner/group.
         */
-       len = 16;
+       len = 20;
 
        /* Sigh */
        if (iap->ia_valid & ATTR_SIZE)
@@ -1023,6 +1040,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                }
                len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
        }
+       if (label)
+               len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
        if (iap->ia_valid & ATTR_ATIME_SET)
                len += 16;
        else if (iap->ia_valid & ATTR_ATIME)
@@ -1037,9 +1056,9 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
         * We write the bitmap length now, but leave the bitmap and the attribute
         * buffer length to be backfilled at the end of this routine.
         */
-       *p++ = cpu_to_be32(2);
+       *p++ = cpu_to_be32(3);
        q = p;
-       p += 3;
+       p += 4;
 
        if (iap->ia_valid & ATTR_SIZE) {
                bmval0 |= FATTR4_WORD0_SIZE;
@@ -1077,6 +1096,13 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
                *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
        }
+       if (label) {
+               bmval2 |= FATTR4_WORD2_SECURITY_LABEL;
+               *p++ = cpu_to_be32(label->lfs);
+               *p++ = cpu_to_be32(label->pi);
+               *p++ = cpu_to_be32(label->len);
+               p = xdr_encode_opaque_fixed(p, label->label, label->len);
+       }
 
        /*
         * Now we backfill the bitmap and the attribute buffer length.
@@ -1086,9 +1112,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
                                len, ((char *)p - (char *)q) + 4);
                BUG();
        }
-       len = (char *)p - (char *)q - 12;
+       len = (char *)p - (char *)q - 16;
        *q++ = htonl(bmval0);
        *q++ = htonl(bmval1);
+       *q++ = htonl(bmval2);
        *q = htonl(len);
 
 /* out: */
@@ -1142,7 +1169,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
        }
 
        encode_string(xdr, create->name->len, create->name->name);
-       encode_attrs(xdr, create->attrs, create->server);
+       encode_attrs(xdr, create->attrs, create->label, create->server);
 }
 
 static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
@@ -1194,8 +1221,10 @@ encode_getattr_three(struct xdr_stream *xdr,
 
 static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
 {
-       encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
-                          bitmask[1] & nfs4_fattr_bitmap[1], hdr);
+       encode_getattr_three(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
+                          bitmask[1] & nfs4_fattr_bitmap[1],
+                          bitmask[2] & nfs4_fattr_bitmap[2],
+                          hdr);
 }
 
 static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask,
@@ -1373,11 +1402,11 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
        switch(arg->createmode) {
        case NFS4_CREATE_UNCHECKED:
                *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
-               encode_attrs(xdr, arg->u.attrs, arg->server);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server);
                break;
        case NFS4_CREATE_GUARDED:
                *p = cpu_to_be32(NFS4_CREATE_GUARDED);
-               encode_attrs(xdr, arg->u.attrs, arg->server);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server);
                break;
        case NFS4_CREATE_EXCLUSIVE:
                *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
@@ -1387,7 +1416,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
                *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
                encode_nfs4_verifier(xdr, &arg->u.verifier);
                dummy.ia_valid = 0;
-               encode_attrs(xdr, &dummy, arg->server);
+               encode_attrs(xdr, &dummy, arg->label, arg->server);
        }
 }
 
@@ -1538,7 +1567,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args,
 
 static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
 {
-       uint32_t attrs[2] = {
+       uint32_t attrs[3] = {
                FATTR4_WORD0_RDATTR_ERROR,
                FATTR4_WORD1_MOUNTED_ON_FILEID,
        };
@@ -1561,20 +1590,26 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
        encode_op_hdr(xdr, OP_READDIR, decode_readdir_maxsz, hdr);
        encode_uint64(xdr, readdir->cookie);
        encode_nfs4_verifier(xdr, &readdir->verifier);
-       p = reserve_space(xdr, 20);
+       p = reserve_space(xdr, encode_readdir_space);
        *p++ = cpu_to_be32(dircount);
        *p++ = cpu_to_be32(readdir->count);
-       *p++ = cpu_to_be32(2);
-
+       *p++ = cpu_to_be32(encode_readdir_bitmask_sz);
        *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]);
-       *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
+       *p   = cpu_to_be32(attrs[1] & readdir->bitmask[1]);
+       if (encode_readdir_bitmask_sz > 2) {
+               if (hdr->minorversion > 1)
+                       attrs[2] |= FATTR4_WORD2_SECURITY_LABEL;
+               p++, *p++ = cpu_to_be32(attrs[2] & readdir->bitmask[2]);
+       }
        memcpy(verf, readdir->verifier.data, sizeof(verf));
-       dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
+
+       dprintk("%s: cookie = %llu, verifier = %08x:%08x, bitmap = %08x:%08x:%08x\n",
                        __func__,
                        (unsigned long long)readdir->cookie,
                        verf[0], verf[1],
                        attrs[0] & readdir->bitmask[0],
-                       attrs[1] & readdir->bitmask[1]);
+                       attrs[1] & readdir->bitmask[1],
+                       attrs[2] & readdir->bitmask[2]);
 }
 
 static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr)
@@ -1633,7 +1668,7 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
 {
        encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
        encode_nfs4_stateid(xdr, &arg->stateid);
-       encode_attrs(xdr, arg->iap, server);
+       encode_attrs(xdr, arg->iap, arg->label, server);
 }
 
 static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
@@ -4044,6 +4079,56 @@ static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap,
        return status;
 }
 
+static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap,
+                                       struct nfs4_label *label)
+{
+       uint32_t pi = 0;
+       uint32_t lfs = 0;
+       __u32 len;
+       __be32 *p;
+       int status = 0;
+
+       if (unlikely(bitmap[2] & (FATTR4_WORD2_SECURITY_LABEL - 1U)))
+               return -EIO;
+       if (likely(bitmap[2] & FATTR4_WORD2_SECURITY_LABEL)) {
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               lfs = be32_to_cpup(p++);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               pi = be32_to_cpup(p++);
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(!p))
+                       goto out_overflow;
+               len = be32_to_cpup(p++);
+               p = xdr_inline_decode(xdr, len);
+               if (unlikely(!p))
+                       goto out_overflow;
+               if (len < NFS4_MAXLABELLEN) {
+                       if (label) {
+                               memcpy(label->label, p, len);
+                               label->len = len;
+                               label->pi = pi;
+                               label->lfs = lfs;
+                               status = NFS_ATTR_FATTR_V4_SECURITY_LABEL;
+                       }
+                       bitmap[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+               } else
+                       printk(KERN_WARNING "%s: label too long (%u)!\n",
+                                       __func__, len);
+       }
+       if (label && label->label)
+               dprintk("%s: label=%s, len=%d, PI=%d, LFS=%d\n", __func__,
+                       (char *)label->label, label->len, label->pi, label->lfs);
+       return status;
+
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
 static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
 {
        int status = 0;
@@ -4386,7 +4471,7 @@ out_overflow:
 
 static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
                struct nfs_fattr *fattr, struct nfs_fh *fh,
-               struct nfs4_fs_locations *fs_loc,
+               struct nfs4_fs_locations *fs_loc, struct nfs4_label *label,
                const struct nfs_server *server)
 {
        int status;
@@ -4494,6 +4579,13 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
        if (status < 0)
                goto xdr_error;
 
+       if (label) {
+               status = decode_attr_security_label(xdr, bitmap, label);
+               if (status < 0)
+                       goto xdr_error;
+               fattr->valid |= status;
+       }
+
 xdr_error:
        dprintk("%s: xdr returned %d\n", __func__, -status);
        return status;
@@ -4501,7 +4593,7 @@ xdr_error:
 
 static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr,
                struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc,
-               const struct nfs_server *server)
+               struct nfs4_label *label, const struct nfs_server *server)
 {
        unsigned int savep;
        uint32_t attrlen,
@@ -4520,7 +4612,8 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat
        if (status < 0)
                goto xdr_error;
 
-       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, server);
+       status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc,
+                                       label, server);
        if (status < 0)
                goto xdr_error;
 
@@ -4530,10 +4623,16 @@ xdr_error:
        return status;
 }
 
+static int decode_getfattr_label(struct xdr_stream *xdr, struct nfs_fattr *fattr,
+               struct nfs4_label *label, const struct nfs_server *server)
+{
+       return decode_getfattr_generic(xdr, fattr, NULL, NULL, label, server);
+}
+
 static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
                const struct nfs_server *server)
 {
-       return decode_getfattr_generic(xdr, fattr, NULL, NULL, server);
+       return decode_getfattr_generic(xdr, fattr, NULL, NULL, NULL, server);
 }
 
 /*
@@ -5925,7 +6024,7 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server);
+       status = decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -5951,7 +6050,8 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp,
                goto out;
        status = decode_getfh(xdr, res->fh);
        if (status == 0)
-               status = decode_getfattr(xdr, res->fattr, res->server);
+               status = decode_getfattr_label(xdr, res->fattr,
+                                               res->label, res->server);
 out:
        return status;
 }
@@ -6042,7 +6142,7 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_restorefh(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6071,7 +6171,7 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_getfh(xdr, res->fh);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6103,7 +6203,7 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server);
+       status = decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6236,7 +6336,7 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
                goto out;
        if (res->access_request)
                decode_access(xdr, &res->access_supported, &res->access_result);
-       decode_getfattr(xdr, res->f_attr, res->server);
+       decode_getfattr_label(xdr, res->f_attr, res->f_label, res->server);
 out:
        return status;
 }
@@ -6313,7 +6413,7 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp,
        status = decode_setattr(xdr);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
+       decode_getfattr_label(xdr, res->fattr, res->label, res->server);
 out:
        return status;
 }
@@ -6702,7 +6802,7 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
        xdr_enter_page(xdr, PAGE_SIZE);
        status = decode_getfattr_generic(xdr, &res->fs_locations->fattr,
                                         NULL, res->fs_locations,
-                                        res->fs_locations->server);
+                                        NULL, res->fs_locations->server);
 out:
        return status;
 }
@@ -7115,7 +7215,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                goto out_overflow;
 
        if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
-                                 NULL, entry->server) < 0)
+                       NULL, entry->label, entry->server) < 0)
                goto out_overflow;
        if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID)
                entry->ino = entry->fattr->mounted_on_fileid;
index fc8de9016acfb2a6c3185e3008aa5f70e23f4545..c041c41f7a52bcc849400bd55eae238c47b1ad0d 100644 (file)
@@ -98,7 +98,7 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
  */
 static int
 nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs_procedures[NFSPROC_GETATTR],
@@ -146,7 +146,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
 
 static int
 nfs_proc_lookup(struct inode *dir, struct qstr *name,
-               struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+               struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+               struct nfs4_label *label)
 {
        struct nfs_diropargs    arg = {
                .fh             = NFS_FH(dir),
@@ -243,7 +244,7 @@ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        nfs_mark_for_revalidate(dir);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        nfs_free_createdata(data);
 out:
        dprintk("NFS reply create: %d\n", status);
@@ -290,7 +291,7 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        }
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        nfs_free_createdata(data);
 out:
        dprintk("NFS reply mknod: %d\n", status);
@@ -442,7 +443,7 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
         * should fill in the data with a LOOKUP call on the wire.
         */
        if (status == 0)
-               status = nfs_instantiate(dentry, fh, fattr);
+               status = nfs_instantiate(dentry, fh, fattr, NULL);
 
 out_free:
        nfs_free_fattr(fattr);
@@ -471,7 +472,7 @@ nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        nfs_mark_for_revalidate(dir);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        nfs_free_createdata(data);
 out:
        dprintk("NFS reply mkdir: %d\n", status);
index 8d51101771fc69fdbd030b03c20f791eee248bbc..71fdc0dfa0d28d9e293cbb58729d63395af7cc99 100644 (file)
@@ -269,7 +269,7 @@ static match_table_t nfs_local_lock_tokens = {
 
 enum {
        Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0,
-       Opt_vers_4_1,
+       Opt_vers_4_1, Opt_vers_4_2,
 
        Opt_vers_err
 };
@@ -280,6 +280,7 @@ static match_table_t nfs_vers_tokens = {
        { Opt_vers_4, "4" },
        { Opt_vers_4_0, "4.0" },
        { Opt_vers_4_1, "4.1" },
+       { Opt_vers_4_2, "4.2" },
 
        { Opt_vers_err, NULL }
 };
@@ -832,6 +833,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
                seq_printf(m, "\n\tnfsv4:\t");
                seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
                seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
+               seq_printf(m, ",bm2=0x%x", nfss->attr_bitmask[2]);
                seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
                show_sessions(m, nfss);
                show_pnfs(m, nfss);
@@ -1097,6 +1099,10 @@ static int nfs_parse_version_string(char *string,
                mnt->version = 4;
                mnt->minorversion = 1;
                break;
+       case Opt_vers_4_2:
+               mnt->version = 4;
+               mnt->minorversion = 2;
+               break;
        default:
                return 0;
        }
@@ -2423,7 +2429,21 @@ static int nfs_bdi_register(struct nfs_server *server)
 int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
                        struct nfs_mount_info *mount_info)
 {
-       return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts);
+       int error;
+       unsigned long kflags = 0, kflags_out = 0;
+       if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
+               kflags |= SECURITY_LSM_NATIVE_LABELS;
+
+       error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts,
+                                               kflags, &kflags_out);
+       if (error)
+               goto err;
+
+       if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL &&
+               !(kflags_out & SECURITY_LSM_NATIVE_LABELS))
+               NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL;
+err:
+       return error;
 }
 EXPORT_SYMBOL_GPL(nfs_set_sb_security);
 
index 07a473fd49bc1a577bfbc1c44c37efaafb9d01a0..c0d93170585d203e9543b898c5e3691e6234e920 100644 (file)
@@ -243,6 +243,12 @@ void               nfsd_lockd_shutdown(void);
 #define nfserr_reject_deleg            cpu_to_be32(NFS4ERR_REJECT_DELEG)
 #define nfserr_returnconflict          cpu_to_be32(NFS4ERR_RETURNCONFLICT)
 #define nfserr_deleg_revoked           cpu_to_be32(NFS4ERR_DELEG_REVOKED)
+#define nfserr_partner_notsupp         cpu_to_be32(NFS4ERR_PARTNER_NOTSUPP)
+#define nfserr_partner_no_auth         cpu_to_be32(NFS4ERR_PARTNER_NO_AUTH)
+#define nfserr_metadata_notsupp                cpu_to_be32(NFS4ERR_METADATA_NOTSUPP)
+#define nfserr_offload_denied          cpu_to_be32(NFS4ERR_OFFLOAD_DENIED)
+#define nfserr_wrong_lfs               cpu_to_be32(NFS4ERR_WRONG_LFS)
+#define nfserr_badlabel                cpu_to_be32(NFS4ERR_BADLABEL)
 
 /* error codes for internal use */
 /* if a request fails due to kmalloc failure, it gets dropped.
index 7b8fc73810ad83baca0e61e8c843ed4cf75d9d98..e36dee52f224f146805b58a48008012dc68602a8 100644 (file)
@@ -32,6 +32,15 @@ struct nfs4_acl {
        struct nfs4_ace aces[0];
 };
 
+#define NFS4_MAXLABELLEN       2048
+
+struct nfs4_label {
+       uint32_t        lfs;
+       uint32_t        pi;
+       u32             len;
+       char    *label;
+};
+
 typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;
 
 struct nfs_stateid4 {
@@ -219,6 +228,14 @@ enum nfsstat4 {
        NFS4ERR_REJECT_DELEG    = 10085,        /* on callback */
        NFS4ERR_RETURNCONFLICT  = 10086,        /* outstanding layoutreturn */
        NFS4ERR_DELEG_REVOKED   = 10087,        /* deleg./layout revoked */
+
+       /* nfs42 */
+       NFS4ERR_PARTNER_NOTSUPP = 10088,
+       NFS4ERR_PARTNER_NO_AUTH = 10089,
+       NFS4ERR_METADATA_NOTSUPP = 10090,
+       NFS4ERR_OFFLOAD_DENIED = 10091,
+       NFS4ERR_WRONG_LFS = 10092,
+       NFS4ERR_BADLABEL = 10093,
 };
 
 static inline bool seqid_mutating_err(u32 err)
@@ -378,6 +395,7 @@ enum lock_type4 {
 #define FATTR4_WORD1_FS_LAYOUT_TYPES    (1UL << 30)
 #define FATTR4_WORD2_LAYOUT_BLKSIZE     (1UL << 1)
 #define FATTR4_WORD2_MDSTHRESHOLD       (1UL << 4)
+#define FATTR4_WORD2_SECURITY_LABEL     (1UL << 17)
 
 /* MDS threshold bitmap bits */
 #define THRESHOLD_RD                    (1UL << 0)
@@ -390,11 +408,15 @@ enum lock_type4 {
 #define NFS4_VERSION 4
 #define NFS4_MINOR_VERSION 0
 
+#if defined(CONFIG_NFS_V4_2)
+#define NFS4_MAX_MINOR_VERSION 2
+#else
 #if defined(CONFIG_NFS_V4_1)
 #define NFS4_MAX_MINOR_VERSION 1
 #else
 #define NFS4_MAX_MINOR_VERSION 0
 #endif /* CONFIG_NFS_V4_1 */
+#endif /* CONFIG_NFS_V4_2 */
 
 #define NFS4_DEBUG 1
 
index 1384ed92cad65801e50f1b2f4e4cc247ee1e1f84..0b176297aaf6337e231785558b99ac93c5b6bac3 100644 (file)
@@ -207,6 +207,7 @@ struct nfs_inode {
 #define NFS_INO_INVALID_ACL    0x0010          /* cached acls are invalid */
 #define NFS_INO_REVAL_PAGECACHE        0x0020          /* must revalidate pagecache */
 #define NFS_INO_REVAL_FORCED   0x0040          /* force revalidation ignoring a delegation */
+#define NFS_INO_INVALID_LABEL  0x0080          /* cached label is invalid */
 
 /*
  * Bit offsets in flags field
@@ -336,7 +337,7 @@ extern void nfs_zap_mapping(struct inode *inode, struct address_space *mapping);
 extern void nfs_zap_caches(struct inode *);
 extern void nfs_invalidate_atime(struct inode *);
 extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
-                               struct nfs_fattr *);
+                               struct nfs_fattr *, struct nfs4_label *);
 extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
@@ -352,6 +353,8 @@ extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
 extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
 extern int nfs_setattr(struct dentry *, struct iattr *);
 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
+extern void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                               struct nfs4_label *label);
 extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
 extern void put_nfs_open_context(struct nfs_open_context *ctx);
 extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
@@ -469,7 +472,8 @@ extern const struct file_operations nfs_dir_operations;
 extern const struct dentry_operations nfs_dentry_operations;
 
 extern void nfs_force_lookup_revalidate(struct inode *dir);
-extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr);
+extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh,
+                       struct nfs_fattr *fattr, struct nfs4_label *label);
 extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags);
 extern void nfs_access_zap_cache(struct inode *inode);
 
@@ -497,6 +501,24 @@ extern const struct inode_operations nfs_referral_inode_operations;
 extern int nfs_mountpoint_expiry_timeout;
 extern void nfs_release_automount_timer(void);
 
+/*
+ * linux/fs/nfs/nfs4proc.c
+ */
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags);
+static inline void nfs4_label_free(struct nfs4_label *label)
+{
+       if (label) {
+               kfree(label->label);
+               kfree(label);
+       }
+       return;
+}
+#else
+static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; }
+static inline void nfs4_label_free(void *label) {}
+#endif
+
 /*
  * linux/fs/nfs/unlink.c
  */
index 3b7fa2abecca690e0d007a4aa97fded3e1a1bad3..d2212432c456a3fb2b1cbe33311d6e789d518173 100644 (file)
@@ -146,7 +146,12 @@ struct nfs_server {
        u32                     attr_bitmask[3];/* V4 bitmask representing the set
                                                   of attributes supported on this
                                                   filesystem */
-       u32                     cache_consistency_bitmask[2];
+       u32                     attr_bitmask_nl[3];
+                                               /* V4 bitmask representing the
+                                                  set of attributes supported
+                                                  on this filesystem excluding
+                                                  the label support bit. */
+       u32                     cache_consistency_bitmask[3];
                                                /* V4 bitmask representing the subset
                                                   of change attribute, size, ctime
                                                   and mtime attributes supported by
@@ -200,5 +205,6 @@ struct nfs_server {
 #define NFS_CAP_UIDGID_NOMAP   (1U << 15)
 #define NFS_CAP_STATEID_NFSV41 (1U << 16)
 #define NFS_CAP_ATOMIC_OPEN_V1 (1U << 17)
+#define NFS_CAP_SECURITY_LABEL (1U << 18)
 
 #endif
index 32c95d64e3aa865a64e467b26fe194fd58c547a8..8651574a305bb44815e0c5e19826a3fbcbf7a37e 100644 (file)
@@ -101,6 +101,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 22)
 #define NFS_ATTR_FATTR_OWNER_NAME      (1U << 23)
 #define NFS_ATTR_FATTR_GROUP_NAME      (1U << 24)
+#define NFS_ATTR_FATTR_V4_SECURITY_LABEL (1U << 25)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
                | NFS_ATTR_FATTR_MODE \
@@ -120,7 +121,8 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_V3 (NFS_ATTR_FATTR \
                | NFS_ATTR_FATTR_SPACE_USED)
 #define NFS_ATTR_FATTR_V4 (NFS_ATTR_FATTR \
-               | NFS_ATTR_FATTR_SPACE_USED)
+               | NFS_ATTR_FATTR_SPACE_USED \
+               | NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
 /*
  * Info on the file system
@@ -348,6 +350,7 @@ struct nfs_openargs {
        const u32 *             open_bitmap;
        __u32                   claim;
        enum createmode4        createmode;
+       const struct nfs4_label *label;
 };
 
 struct nfs_openres {
@@ -357,6 +360,7 @@ struct nfs_openres {
        struct nfs4_change_info cinfo;
        __u32                   rflags;
        struct nfs_fattr *      f_attr;
+       struct nfs4_label       *f_label;
        struct nfs_seqid *      seqid;
        const struct nfs_server *server;
        fmode_t                 delegation_type;
@@ -599,6 +603,7 @@ struct nfs_entry {
        int                     eof;
        struct nfs_fh *         fh;
        struct nfs_fattr *      fattr;
+       struct nfs4_label  *label;
        unsigned char           d_type;
        struct nfs_server *     server;
 };
@@ -631,6 +636,7 @@ struct nfs_setattrargs {
        struct iattr *                  iap;
        const struct nfs_server *       server; /* Needed for name mapping */
        const u32 *                     bitmask;
+       const struct nfs4_label         *label;
 };
 
 struct nfs_setaclargs {
@@ -666,6 +672,7 @@ struct nfs_getaclres {
 struct nfs_setattrres {
        struct nfs4_sequence_res        seq_res;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
        const struct nfs_server *       server;
 };
 
@@ -863,6 +870,7 @@ struct nfs4_create_arg {
        const struct iattr *            attrs;
        const struct nfs_fh *           dir_fh;
        const u32 *                     bitmask;
+       const struct nfs4_label         *label;
 };
 
 struct nfs4_create_res {
@@ -870,6 +878,7 @@ struct nfs4_create_res {
        const struct nfs_server *       server;
        struct nfs_fh *                 fh;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
        struct nfs4_change_info         dir_cinfo;
 };
 
@@ -894,6 +903,7 @@ struct nfs4_getattr_res {
        struct nfs4_sequence_res        seq_res;
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
 };
 
 struct nfs4_link_arg {
@@ -908,6 +918,7 @@ struct nfs4_link_res {
        struct nfs4_sequence_res        seq_res;
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
+       struct nfs4_label               *label;
        struct nfs4_change_info         cinfo;
        struct nfs_fattr *              dir_attr;
 };
@@ -925,6 +936,7 @@ struct nfs4_lookup_res {
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
        struct nfs_fh *                 fh;
+       struct nfs4_label               *label;
 };
 
 struct nfs4_lookup_root_arg {
@@ -1367,11 +1379,12 @@ struct nfs_rpc_ops {
        struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *,
                                     struct nfs_subversion *);
        int     (*getattr) (struct nfs_server *, struct nfs_fh *,
-                           struct nfs_fattr *);
+                           struct nfs_fattr *, struct nfs4_label *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
                            struct iattr *);
        int     (*lookup)  (struct inode *, struct qstr *,
-                           struct nfs_fh *, struct nfs_fattr *);
+                           struct nfs_fh *, struct nfs_fattr *,
+                           struct nfs4_label *);
        int     (*access)  (struct inode *, struct nfs_access_entry *);
        int     (*readlink)(struct inode *, struct page *, unsigned int,
                            unsigned int);
index 4686491852a70b0665633ee1bce5e44d344b3aa3..a585a9085e46cf6959a153bb8991bbfdfa84cc33 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/capability.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/string.h>
 
 struct linux_binprm;
 struct cred;
@@ -60,6 +61,9 @@ struct mm_struct;
 #define SECURITY_CAP_NOAUDIT 0
 #define SECURITY_CAP_AUDIT 1
 
+/* LSM Agnostic defines for sb_set_mnt_opts */
+#define SECURITY_LSM_NATIVE_LABELS     1
+
 struct ctl_table;
 struct audit_krule;
 struct user_namespace;
@@ -306,6 +310,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     Parse a string of security data filling in the opts structure
  *     @options string containing all mount options known by the LSM
  *     @opts binary data structure usable by the LSM
+ * @dentry_init_security:
+ *     Compute a context for a dentry as the inode is not yet available
+ *     since NFSv4 has no label backed by an EA anyway.
+ *     @dentry dentry to use in calculating the context.
+ *     @mode mode used to determine resource type.
+ *     @name name of the last path component used to create file
+ *     @ctx pointer to place the pointer to the resulting context in.
+ *     @ctxlen point to place the length of the resulting context.
+ *
  *
  * Security hooks for inode operations.
  *
@@ -1313,6 +1326,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @pages contains the number of pages.
  *     Return 0 if permission is granted.
  *
+ * @ismaclabel:
+ *     Check if the extended attribute specified by @name
+ *     represents a MAC label. Returns 1 if name is a MAC
+ *     attribute otherwise returns 0.
+ *     @name full extended attribute name to check against
+ *     LSM as a MAC label.
+ *
  * @secid_to_secctx:
  *     Convert secid to security context.  If secdata is NULL the length of
  *     the result will be returned in seclen, but no secdata will be returned.
@@ -1439,10 +1459,16 @@ struct security_operations {
        int (*sb_pivotroot) (struct path *old_path,
                             struct path *new_path);
        int (*sb_set_mnt_opts) (struct super_block *sb,
-                               struct security_mnt_opts *opts);
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags);
        int (*sb_clone_mnt_opts) (const struct super_block *oldsb,
                                   struct super_block *newsb);
        int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts);
+       int (*dentry_init_security) (struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen);
+
 
 #ifdef CONFIG_SECURITY_PATH
        int (*path_unlink) (struct path *dir, struct dentry *dentry);
@@ -1590,6 +1616,7 @@ struct security_operations {
 
        int (*getprocattr) (struct task_struct *p, char *name, char **value);
        int (*setprocattr) (struct task_struct *p, char *name, void *value, size_t size);
+       int (*ismaclabel) (const char *name);
        int (*secid_to_secctx) (u32 secid, char **secdata, u32 *seclen);
        int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
        void (*release_secctx) (char *secdata, u32 seclen);
@@ -1725,10 +1752,16 @@ int security_sb_mount(const char *dev_name, struct path *path,
                      const char *type, unsigned long flags, void *data);
 int security_sb_umount(struct vfsmount *mnt, int flags);
 int security_sb_pivotroot(struct path *old_path, struct path *new_path);
-int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts);
+int security_sb_set_mnt_opts(struct super_block *sb,
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags);
 int security_sb_clone_mnt_opts(const struct super_block *oldsb,
                                struct super_block *newsb);
 int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
+int security_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen);
 
 int security_inode_alloc(struct inode *inode);
 void security_inode_free(struct inode *inode);
@@ -1840,6 +1873,7 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode);
 int security_getprocattr(struct task_struct *p, char *name, char **value);
 int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
 int security_netlink_send(struct sock *sk, struct sk_buff *skb);
+int security_ismaclabel(const char *name);
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
 int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
 void security_release_secctx(char *secdata, u32 seclen);
@@ -2011,7 +2045,9 @@ static inline int security_sb_pivotroot(struct path *old_path,
 }
 
 static inline int security_sb_set_mnt_opts(struct super_block *sb,
-                                          struct security_mnt_opts *opts)
+                                          struct security_mnt_opts *opts,
+                                          unsigned long kern_flags,
+                                          unsigned long *set_kern_flags)
 {
        return 0;
 }
@@ -2035,6 +2071,16 @@ static inline int security_inode_alloc(struct inode *inode)
 static inline void security_inode_free(struct inode *inode)
 { }
 
+static inline int security_dentry_init_security(struct dentry *dentry,
+                                                int mode,
+                                                struct qstr *name,
+                                                void **ctx,
+                                                u32 *ctxlen)
+{
+       return -EOPNOTSUPP;
+}
+
+
 static inline int security_inode_init_security(struct inode *inode,
                                                struct inode *dir,
                                                const struct qstr *qstr,
@@ -2520,6 +2566,11 @@ static inline int security_netlink_send(struct sock *sk, struct sk_buff *skb)
        return cap_netlink_send(sk, skb);
 }
 
+static inline int security_ismaclabel(const char *name)
+{
+       return 0;
+}
+
 static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return -EOPNOTSUPP;
index 1728d4e375db509c4e192e0e69fb1a0ee020bf49..d32e16e3c6ae661163359f70f1fcf6c66c49dae3 100644 (file)
@@ -91,7 +91,10 @@ static int cap_sb_pivotroot(struct path *old_path, struct path *new_path)
 }
 
 static int cap_sb_set_mnt_opts(struct super_block *sb,
-                              struct security_mnt_opts *opts)
+                              struct security_mnt_opts *opts,
+                              unsigned long kern_flags,
+                              unsigned long *set_kern_flags)
+
 {
        if (unlikely(opts->num_mnt_opts))
                return -EOPNOTSUPP;
@@ -109,6 +112,13 @@ static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
        return 0;
 }
 
+static int cap_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+{
+       return 0;
+}
+
 static int cap_inode_alloc_security(struct inode *inode)
 {
        return 0;
@@ -816,6 +826,11 @@ static int cap_setprocattr(struct task_struct *p, char *name, void *value,
        return -EINVAL;
 }
 
+static int cap_ismaclabel(const char *name)
+{
+       return 0;
+}
+
 static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return -EOPNOTSUPP;
@@ -931,6 +946,7 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, sb_set_mnt_opts);
        set_to_cap_if_null(ops, sb_clone_mnt_opts);
        set_to_cap_if_null(ops, sb_parse_opts_str);
+       set_to_cap_if_null(ops, dentry_init_security);
        set_to_cap_if_null(ops, inode_alloc_security);
        set_to_cap_if_null(ops, inode_free_security);
        set_to_cap_if_null(ops, inode_init_security);
@@ -1034,6 +1050,7 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, d_instantiate);
        set_to_cap_if_null(ops, getprocattr);
        set_to_cap_if_null(ops, setprocattr);
+       set_to_cap_if_null(ops, ismaclabel);
        set_to_cap_if_null(ops, secid_to_secctx);
        set_to_cap_if_null(ops, secctx_to_secid);
        set_to_cap_if_null(ops, release_secctx);
index a3dce87d1aeffccb92181631ef2687baec41d36c..94b35aef6871a9978cf21799cfec3385502bc555 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <linux/capability.h>
+#include <linux/dcache.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -293,9 +294,12 @@ int security_sb_pivotroot(struct path *old_path, struct path *new_path)
 }
 
 int security_sb_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts)
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags)
 {
-       return security_ops->sb_set_mnt_opts(sb, opts);
+       return security_ops->sb_set_mnt_opts(sb, opts, kern_flags,
+                                               set_kern_flags);
 }
 EXPORT_SYMBOL(security_sb_set_mnt_opts);
 
@@ -324,6 +328,15 @@ void security_inode_free(struct inode *inode)
        security_ops->inode_free_security(inode);
 }
 
+int security_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+{
+       return security_ops->dentry_init_security(dentry, mode, name,
+                                                       ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_dentry_init_security);
+
 int security_inode_init_security(struct inode *inode, struct inode *dir,
                                 const struct qstr *qstr,
                                 const initxattrs initxattrs, void *fs_data)
@@ -647,6 +660,7 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
                return 0;
        return security_ops->inode_listsecurity(inode, buffer, buffer_size);
 }
+EXPORT_SYMBOL(security_inode_listsecurity);
 
 void security_inode_getsecid(const struct inode *inode, u32 *secid)
 {
@@ -1047,6 +1061,12 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb)
        return security_ops->netlink_send(sk, skb);
 }
 
+int security_ismaclabel(const char *name)
+{
+       return security_ops->ismaclabel(name);
+}
+EXPORT_SYMBOL(security_ismaclabel);
+
 int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return security_ops->secid_to_secctx(secid, secdata, seclen);
index 5c6f2cd2d095ee8b2a4e828123c00f7966639b6a..9f8e9b2e717a2740b7cf750023d08d30dbdde919 100644 (file)
@@ -81,6 +81,7 @@
 #include <linux/syslog.h>
 #include <linux/user_namespace.h>
 #include <linux/export.h>
+#include <linux/security.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
 
@@ -284,13 +285,14 @@ static void superblock_free_security(struct super_block *sb)
 
 /* The file system's label must be initialized prior to use. */
 
-static const char *labeling_behaviors[6] = {
+static const char *labeling_behaviors[7] = {
        "uses xattr",
        "uses transition SIDs",
        "uses task SIDs",
        "uses genfs_contexts",
        "not configured for labeling",
        "uses mountpoint labeling",
+       "uses native labeling",
 };
 
 static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
@@ -552,7 +554,9 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
  * labeling information.
  */
 static int selinux_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts)
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags)
 {
        const struct cred *cred = current_cred();
        int rc = 0, i;
@@ -580,6 +584,12 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                        "before the security server is initialized\n");
                goto out;
        }
+       if (kern_flags && !set_kern_flags) {
+               /* Specifying internal flags without providing a place to
+                * place the results is not allowed */
+               rc = -EINVAL;
+               goto out;
+       }
 
        /*
         * Binary mount data FS will come through this function twice.  Once
@@ -670,14 +680,21 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        if (strcmp(sb->s_type->name, "proc") == 0)
                sbsec->flags |= SE_SBPROC;
 
-       /* Determine the labeling behavior to use for this filesystem type. */
-       rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
-       if (rc) {
-               printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
-                      __func__, sb->s_type->name, rc);
-               goto out;
+       if (!sbsec->behavior) {
+               /*
+                * Determine the labeling behavior to use for this
+                * filesystem type.
+                */
+               rc = security_fs_use((sbsec->flags & SE_SBPROC) ?
+                                       "proc" : sb->s_type->name,
+                                       &sbsec->behavior, &sbsec->sid);
+               if (rc) {
+                       printk(KERN_WARNING
+                               "%s: security_fs_use(%s) returned %d\n",
+                                       __func__, sb->s_type->name, rc);
+                       goto out;
+               }
        }
-
        /* sets the context of the superblock for the fs being mounted. */
        if (fscontext_sid) {
                rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
@@ -692,6 +709,11 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         * sets the label used on all file below the mountpoint, and will set
         * the superblock context if not already set.
         */
+       if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) {
+               sbsec->behavior = SECURITY_FS_USE_NATIVE;
+               *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
+       }
+
        if (context_sid) {
                if (!fscontext_sid) {
                        rc = may_context_mount_sb_relabel(context_sid, sbsec,
@@ -723,7 +745,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        }
 
        if (defcontext_sid) {
-               if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+               if (sbsec->behavior != SECURITY_FS_USE_XATTR &&
+                       sbsec->behavior != SECURITY_FS_USE_NATIVE) {
                        rc = -EINVAL;
                        printk(KERN_WARNING "SELinux: defcontext option is "
                               "invalid for this filesystem type\n");
@@ -980,7 +1003,7 @@ static int superblock_doinit(struct super_block *sb, void *data)
                goto out_err;
 
 out:
-       rc = selinux_set_mnt_opts(sb, &opts);
+       rc = selinux_set_mnt_opts(sb, &opts, 0, NULL);
 
 out_err:
        security_free_mnt_opts(&opts);
@@ -1222,6 +1245,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
        }
 
        switch (sbsec->behavior) {
+       case SECURITY_FS_USE_NATIVE:
+               break;
        case SECURITY_FS_USE_XATTR:
                if (!inode->i_op->getxattr) {
                        isec->sid = sbsec->def_sid;
@@ -2515,6 +2540,40 @@ static void selinux_inode_free_security(struct inode *inode)
        inode_free_security(inode);
 }
 
+static int selinux_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+{
+       const struct cred *cred = current_cred();
+       struct task_security_struct *tsec;
+       struct inode_security_struct *dsec;
+       struct superblock_security_struct *sbsec;
+       struct inode *dir = dentry->d_parent->d_inode;
+       u32 newsid;
+       int rc;
+
+       tsec = cred->security;
+       dsec = dir->i_security;
+       sbsec = dir->i_sb->s_security;
+
+       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+               newsid = tsec->create_sid;
+       } else {
+               rc = security_transition_sid(tsec->sid, dsec->sid,
+                                            inode_mode_to_security_class(mode),
+                                            name,
+                                            &newsid);
+               if (rc) {
+                       printk(KERN_WARNING
+                               "%s: security_transition_sid failed, rc=%d\n",
+                              __func__, -rc);
+                       return rc;
+               }
+       }
+
+       return security_sid_to_context(newsid, (char **)ctx, ctxlen);
+}
+
 static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       const struct qstr *qstr, char **name,
                                       void **value, size_t *len)
@@ -2849,7 +2908,10 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                return;
        }
 
+       isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
+       isec->initialized = 1;
+
        return;
 }
 
@@ -2937,6 +2999,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
        if (rc)
                return rc;
 
+       isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
        isec->initialized = 1;
        return 0;
@@ -5420,6 +5483,11 @@ abort_change:
        return error;
 }
 
+static int selinux_ismaclabel(const char *name)
+{
+       return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0);
+}
+
 static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
 {
        return security_sid_to_context(secid, secdata, seclen);
@@ -5562,6 +5630,7 @@ static struct security_operations selinux_ops = {
        .sb_clone_mnt_opts =            selinux_sb_clone_mnt_opts,
        .sb_parse_opts_str =            selinux_parse_opts_str,
 
+       .dentry_init_security =         selinux_dentry_init_security,
 
        .inode_alloc_security =         selinux_inode_alloc_security,
        .inode_free_security =          selinux_inode_free_security,
@@ -5657,6 +5726,7 @@ static struct security_operations selinux_ops = {
        .getprocattr =                  selinux_getprocattr,
        .setprocattr =                  selinux_setprocattr,
 
+       .ismaclabel =                   selinux_ismaclabel,
        .secid_to_secctx =              selinux_secid_to_secctx,
        .secctx_to_secid =              selinux_secctx_to_secid,
        .release_secctx =               selinux_release_secctx,
index 6d3885165d143a27fb218e785aa4a13ac7937045..8fd8e18ea34019c863d91ba88268b8c4018f3410 100644 (file)
@@ -169,6 +169,8 @@ int security_get_allow_unknown(void);
 #define SECURITY_FS_USE_GENFS          4 /* use the genfs support */
 #define SECURITY_FS_USE_NONE           5 /* no labeling support */
 #define SECURITY_FS_USE_MNTPOINT       6 /* use mountpoint labeling */
+#define SECURITY_FS_USE_NATIVE         7 /* use native label support */
+#define SECURITY_FS_USE_MAX            7 /* Highest SECURITY_FS_USE_XXX */
 
 int security_fs_use(const char *fstype, unsigned int *behavior,
        u32 *sid);
index 9cd9b7c661ec16bd821f45a9e1a40ad69320f20c..c8adde3aff8fdbe93fb2f867e55f71b9879685a5 100644 (file)
@@ -2168,7 +2168,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info,
 
                                rc = -EINVAL;
                                c->v.behavior = le32_to_cpu(buf[0]);
-                               if (c->v.behavior > SECURITY_FS_USE_NONE)
+                               /* Determined at runtime, not in policy DB. */
+                               if (c->v.behavior == SECURITY_FS_USE_MNTPOINT)
+                                       goto out;
+                               if (c->v.behavior > SECURITY_FS_USE_MAX)
                                        goto out;
 
                                rc = -ENOMEM;
index d52c780bdb788add9fa45684f1ae72a63d91663f..a7f485bb4e2ea58b8473a85c2d758601fd0aa49c 100644 (file)
@@ -3328,6 +3328,16 @@ static void smack_audit_rule_free(void *vrule)
 
 #endif /* CONFIG_AUDIT */
 
+/**
+ * smack_ismaclabel - check if xattr @name references a smack MAC label
+ * @name: Full xattr name to check.
+ */
+static int smack_ismaclabel(const char *name)
+{
+       return (strcmp(name, XATTR_SMACK_SUFFIX) == 0);
+}
+
+
 /**
  * smack_secid_to_secctx - return the smack label for a secid
  * @secid: incoming integer
@@ -3524,6 +3534,7 @@ struct security_operations smack_ops = {
        .audit_rule_free =              smack_audit_rule_free,
 #endif /* CONFIG_AUDIT */
 
+       .ismaclabel =                   smack_ismaclabel,
        .secid_to_secctx =              smack_secid_to_secctx,
        .secctx_to_secid =              smack_secctx_to_secid,
        .release_secctx =               smack_release_secctx,