2008-12-01 16:42:36

by David P. Quigley

[permalink] [raw]
Subject: [Labeled-nfs] [RFC v4] Security Label Support for NFSv4


Hello,

This is the latest version of the NFS label support patch set. The set
contains one patch which will be removed when it makes it's way upstream from
the NFS maintainers' trees. This is the patch to fix a use before init bug in
the nfs4recovery code. Changes since the last patchset are listed below.

If you want a tree with the patches already applied we have posted a public
git tree that is ready for cloning and use. This tree can be found at
http://git.selinuxproject.org/git. You can find information on how to build
and setup a labeled nfs at http://www.selinuxproject.org/page/Labeled_NFS.

Features:

* Client
* Obtains labels from server for NFS files while still allowing for
SELinux context mounts to override untrusted labeled servers.
* Allows setting labels on files over NFS via xattr interface.
* Server
* Exports labels to clients. As of the moment there is no ability to
restrict this based on label components such as MLS levels.
* Persistent storage of labels assuming exported file system supports
it.

Changes since last patchset:

The life cycle management patch has been fixed to return the error from kmalloc
up the call stack. The patch use to have a panic in the case of memory
allocation failure which was a temporary measure until this was ready.

Inode locking was added around the functions in the NFS server code which
assign the label to the inode when received from the wire.

Memory allocations were changed from GFP_ATOMIC to GFP_KERNEL

An bug that resulted in memory corruption when MLS support was enabled has
also been fixed.

The process label transport mechanism has been removed from the patchset since
a new version of it is in the works. This new method provides the security
guarantees needed for our purposes while providing compatibility with
existing rpcsec flavors and fixing a potential MITM attack against kerberos. A
more detailed explanation of the mechanism will be given when the design has
been solidified and we have an initial implementation.

fs/Kconfig | 30 +++
fs/nfs/client.c | 16 ++
fs/nfs/dir.c | 32 +++-
fs/nfs/getroot.c | 44 +++-
fs/nfs/inode.c | 69 +++++-
fs/nfs/namespace.c | 3 +
fs/nfs/nfs3proc.c | 7 +
fs/nfs/nfs4proc.c | 489 +++++++++++++++++++++++++++++++---
fs/nfs/nfs4xdr.c | 55 ++++-
fs/nfs/proc.c | 12 +-
fs/nfs/super.c | 46 ++++-
fs/nfs/unlink.c | 12 +-
fs/nfsd/export.c | 3 +
fs/nfsd/nfs4proc.c | 35 +++-
fs/nfsd/nfs4recover.c | 6 +-
fs/nfsd/nfs4xdr.c | 106 +++++++-
fs/nfsd/vfs.c | 28 ++
fs/xattr.c | 55 +++-
include/linux/nfs4.h | 8 +
include/linux/nfs4_mount.h | 6 +-
include/linux/nfs_fs.h | 26 ++
include/linux/nfs_fs_sb.h | 2 +-
include/linux/nfs_xdr.h | 7 +
include/linux/nfsd/export.h | 5 +-
include/linux/nfsd/nfsd.h | 9 +-
include/linux/nfsd/xdr4.h | 3 +
include/linux/security.h | 88 +++++++
include/linux/xattr.h | 1 +
security/capability.c | 29 ++
security/security.c | 32 +++
security/selinux/hooks.c | 141 +++++++++--
security/selinux/include/security.h | 4 +
security/selinux/ss/policydb.c | 5 +-
security/smack/smack_lsm.c | 10 +
34 files changed, 1315 insertions(+), 109 deletions(-)


2008-12-01 16:40:17

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 07/14] KConfig: Add KConfig entries for Labeled NFS

This patch adds two entries into the fs/KConfig file. The first entry
NFS_V4_SECURITY_LABEL enables security label support for the NFSv4 client while
the second entry NFSD_V4_SECURITY_LABEL enables security labeling support on
the server side.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/Kconfig | 30 ++++++++++++++++++++++++++++++
1 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 522469a..db76fcb 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1216,6 +1216,22 @@ config NFS_V4

If unsure, say N.

+config NFS_V4_SECURITY_LABEL
+ bool "Provide Security Label support for NFSv4 client"
+ depends on NFS_V4 && SECURITY
+ help
+
+ Say Y here if you want enable fine-grained security label attribute
+ support for NFS version 4. Security labels allow security modules like
+ SELinux and Smack to label files to facilitate enforcement of their policies.
+ Without this an NFSv4 mount will have the same label on each file.
+
+ If you do not wish to enable fine-grained security labels SELinux or
+ Smack policies on NFSv4 files, say N.
+
+
+ If unsure, say N.
+
config ROOT_NFS
bool "Root file system on NFS"
depends on NFS_FS=y && IP_PNP
@@ -1308,6 +1324,20 @@ config NFSD_V4

If unsure, say N.

+config NFSD_V4_SECURITY_LABEL
+ bool "Provide Security Label support for NFSv4 server"
+ depends on NFSD_V4 && SECURITY
+ help
+
+ Say Y here if you want enable fine-grained security label attribute
+ support for NFS version 4. Security labels allow security modules like
+ SELinux and Smack to label files to facilitate enforcement of their policies.
+ Without this an NFSv4 mount will have the same label on each file.
+
+ If you do not wish to enable fine-grained security labels SELinux or
+ Smack policies on NFSv4 files, say N.
+
+
config LOCKD
tristate

--
1.5.5.1

2008-12-01 16:41:00

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 09/14] NFS: Add security_label text mount option and handling code to NFS

This patch adds two new text options to to the NFS mount options to specify
security labeling. It also sends certain LSM related mount options into the
module for handling.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/nfs/super.c | 9 +++++++++
include/linux/nfs4_mount.h | 6 +++++-
2 files changed, 14 insertions(+), 1 deletions(-)

diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index f48db67..9b89a4b 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -75,6 +75,7 @@ enum {
Opt_acl, Opt_noacl,
Opt_rdirplus, Opt_nordirplus,
Opt_sharecache, Opt_nosharecache,
+ Opt_security_label, Opt_nosecurity_label,

/* Mount options that take integer arguments */
Opt_port,
@@ -129,6 +130,8 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_nordirplus, "nordirplus" },
{ Opt_sharecache, "sharecache" },
{ Opt_nosharecache, "nosharecache" },
+ { Opt_security_label, "security_label" },
+ { Opt_nosecurity_label, "nosecurity_label" },

{ Opt_port, "port=%u" },
{ Opt_rsize, "rsize=%u" },
@@ -1035,6 +1038,12 @@ static int nfs_parse_mount_options(char *raw,
case Opt_nosharecache:
mnt->flags |= NFS_MOUNT_UNSHARED;
break;
+ case Opt_nosecurity_label:
+ mnt->flags &= ~NFS4_MOUNT_SECURITY_LABEL;
+ break;
+ case Opt_security_label:
+ mnt->flags |= NFS4_MOUNT_SECURITY_LABEL;
+ break;

/*
* options that take numeric values
diff --git a/include/linux/nfs4_mount.h b/include/linux/nfs4_mount.h
index a0dcf66..e65067b 100644
--- a/include/linux/nfs4_mount.h
+++ b/include/linux/nfs4_mount.h
@@ -17,6 +17,7 @@
* but here they are anyway.
*/
#define NFS4_MOUNT_VERSION 1
+#define NFS4_MAX_CONTEXT_LEN 4096

struct nfs_string {
unsigned int len;
@@ -53,6 +54,8 @@ struct nfs4_mount_data {
/* Pseudo-flavours to use for authentication. See RFC2623 */
int auth_flavourlen; /* 1 */
int __user *auth_flavours; /* 1 */
+
+ char context[NFS4_MAX_CONTEXT_LEN + 1]; /* 2 */
};

/* bits in the flags field */
@@ -66,6 +69,7 @@ struct nfs4_mount_data {
#define NFS4_MOUNT_NOAC 0x0020 /* 1 */
#define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */
#define NFS4_MOUNT_UNSHARED 0x8000 /* 1 */
-#define NFS4_MOUNT_FLAGMASK 0x9033
+#define NFS4_MOUNT_SECURITY_LABEL 0x10000 /* 2 */
+#define NFS4_MOUNT_FLAGMASK 0x19033

#endif
--
1.5.5.1

2008-12-01 16:41:35

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

This patch adds a new recommended attribute named label into the NFSv4 file
attribute structure. It also adds several new flags to allow the NFS client and
server to determine if this attribute is supported and if it is being sent over
the wire.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
include/linux/nfs4.h | 2 ++
include/linux/nfs_fs_sb.h | 2 +-
include/linux/nfs_xdr.h | 4 ++++
include/linux/nfsd/export.h | 5 +++--
include/linux/nfsd/nfsd.h | 7 ++++---
5 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index ea03667..144eacf 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -21,6 +21,7 @@
#define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX
+#define NFS4_MAXLABELLEN 4096

#define NFS4_ACCESS_READ 0x0001
#define NFS4_ACCESS_LOOKUP 0x0002
@@ -345,6 +346,7 @@ enum lock_type4 {
#define FATTR4_WORD1_TIME_MODIFY (1UL << 21)
#define FATTR4_WORD1_TIME_MODIFY_SET (1UL << 22)
#define FATTR4_WORD1_MOUNTED_ON_FILEID (1UL << 23)
+#define FATTR4_WORD1_SECURITY_LABEL (1UL << 31)

#define NFSPROC4_NULL 0
#define NFSPROC4_COMPOUND 1
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 4e477ae..ed42b07 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -134,5 +134,5 @@ struct nfs_server {
#define NFS_CAP_SYMLINKS (1U << 2)
#define NFS_CAP_ACLS (1U << 3)
#define NFS_CAP_ATOMIC_OPEN (1U << 4)
-
+#define NFS_CAP_SECURITY_LABEL (1U << 5)
#endif
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index c1c31ac..58532cb 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -57,6 +57,10 @@ struct nfs_fattr {
__u64 pre_change_attr;/* pre-op NFSv4 change attribute */
unsigned long time_start;
unsigned long gencount;
+#ifdef CONFIG_SECURITY
+ void *label;
+ __u32 label_len;
+#endif
};

#define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */
diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h
index 5431512..bb831fc 100644
--- a/include/linux/nfsd/export.h
+++ b/include/linux/nfsd/export.h
@@ -32,7 +32,8 @@
#define NFSEXP_ALLSQUASH 0x0008
#define NFSEXP_ASYNC 0x0010
#define NFSEXP_GATHERED_WRITES 0x0020
-/* 40 80 100 currently unused */
+#define NFSEXP_SECURITY_LABEL 0x0040 /* Support security label fattr4 */
+/* 80 100 currently unused */
#define NFSEXP_NOHIDE 0x0200
#define NFSEXP_NOSUBTREECHECK 0x0400
#define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */
@@ -40,7 +41,7 @@
#define NFSEXP_FSID 0x2000
#define NFSEXP_CROSSMOUNT 0x4000
#define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */
-#define NFSEXP_ALLFLAGS 0xFE3F
+#define NFSEXP_ALLFLAGS 0xFE7F

/* The flags that may vary depending on security flavor: */
#define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 2126940..8219925 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -318,8 +318,8 @@ extern struct timeval nfssvc_boot;
| FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \
| FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \
| FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \
- | FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \
- | FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
+ | FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY \
+ | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID | FATTR4_WORD1_SECURITY_LABEL)

/* These will return ERR_INVAL if specified in GETATTR or READDIR. */
#define NFSD_WRITEONLY_ATTRS_WORD1 \
@@ -330,7 +330,8 @@ extern struct timeval nfssvc_boot;
(FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL )
#define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
- | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
+ | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET \
+ | FATTR4_WORD1_SECURITY_LABEL)

#endif /* CONFIG_NFSD_V4 */

--
1.5.5.1

2008-12-01 16:41:19

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 02/14] VFS: Factor out part of vfs_setxattr so it can be called from the SELinux hook for inode_setsecctx.

This factors out the part of the vfs_setxattr function that performs the
setting of the xattr and its notification. This is needed so the SELinux
implementation of inode_setsecctx can handle the setting of it's xattr while
maintaining the proper separation of layers.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/xattr.c | 55 +++++++++++++++++++++++++++++++++++++-----------
include/linux/xattr.h | 1 +
2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 468377e..2f93006 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -66,22 +66,28 @@ xattr_permission(struct inode *inode, const char *name, int mask)
return inode_permission(inode, mask);
}

-int
-vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
- size_t size, int flags)
+/**
+ * __vfs_setxattr_noperm - perform setxattr operation without performing
+ * permission checks.
+ *
+ * @dentry - object to perform setxattr on
+ * @name - xattr name to set
+ * @value - value to set @name to
+ * @size - size of @value
+ * @flags - flags to pass into filesystem operations
+ *
+ * returns the result of the internal setxattr or setsecurity operations.
+ *
+ * This function requires the caller to lock the inode's i_mutex before it
+ * is executed. It also assumes that the caller will make the appropriate
+ * permission checks.
+ */
+int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
- int error;
-
- error = xattr_permission(inode, name, MAY_WRITE);
- if (error)
- return error;
+ int error = -EOPNOTSUPP;

- mutex_lock(&inode->i_mutex);
- error = security_inode_setxattr(dentry, name, value, size, flags);
- if (error)
- goto out;
- error = -EOPNOTSUPP;
if (inode->i_op->setxattr) {
error = inode->i_op->setxattr(dentry, name, value, size, flags);
if (!error) {
@@ -97,6 +103,29 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
if (!error)
fsnotify_xattr(dentry);
}
+
+ return error;
+}
+
+
+int
+vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+ size_t size, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ error = xattr_permission(inode, name, MAY_WRITE);
+ if (error)
+ return error;
+
+ mutex_lock(&inode->i_mutex);
+ error = security_inode_setxattr(dentry, name, value, size, flags);
+ if (error)
+ goto out;
+
+ error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
+
out:
mutex_unlock(&inode->i_mutex);
return error;
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index d131e35..5c84af8 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -49,6 +49,7 @@ struct xattr_handler {
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
+int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int);
int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
int vfs_removexattr(struct dentry *, const char *);

--
1.5.5.1

2008-12-01 16:41:50

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 13/14] NFS: Extend NFS xattr handlers to accept the security namespace

The existing NFSv4 xattr handlers do not accept xattr calls to the security
namespace. This patch extends these handlers to accept xattrs from the security
namespace in addition to the default NFSv4 ACL namespace.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/nfs/nfs4proc.c | 48 +++++++++++++++++++++++++++++++++++++-----------
security/security.c | 1 +
2 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9db51ea..eeb41fa 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3974,10 +3974,13 @@ int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf,
{
struct inode *inode = dentry->d_inode;

- if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
- return -EOPNOTSUPP;
-
- return nfs4_proc_set_acl(inode, buf, buflen);
+ if (strcmp(key, XATTR_NAME_NFSV4_ACL) == 0)
+ return nfs4_proc_set_acl(inode, buf, buflen);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (security_ismaclabel(key))
+ return nfs4_set_security_label(dentry, buf, buflen);
+#endif
+ return -EOPNOTSUPP;
}

/* The getxattr man page suggests returning -ENODATA for unknown attributes,
@@ -3989,22 +3992,45 @@ ssize_t nfs4_getxattr(struct dentry *dentry, const char *key, void *buf,
{
struct inode *inode = dentry->d_inode;

- if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
- return -EOPNOTSUPP;
+ if (strcmp(key, XATTR_NAME_NFSV4_ACL) == 0)
+ return nfs4_proc_get_acl(inode, buf, buflen);

- return nfs4_proc_get_acl(inode, buf, buflen);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (security_ismaclabel(key))
+ return nfs4_get_security_label(inode, buf, buflen);
+#endif
+ return -EOPNOTSUPP;
}

ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen)
{
- size_t len = strlen(XATTR_NAME_NFSV4_ACL) + 1;
+ size_t len = 0, l;
+ char *p;

- if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
+ if (nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
+ len += strlen(XATTR_NAME_NFSV4_ACL) + 1;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dentry->d_inode, NFS_CAP_SECURITY_LABEL))
+ len += security_inode_listsecurity(dentry->d_inode, NULL, 0);
+#endif
+ if (!len)
return 0;
if (buf && buflen < len)
return -ERANGE;
- if (buf)
- memcpy(buf, XATTR_NAME_NFSV4_ACL, len);
+ if (!buf)
+ return len;
+
+ p = buf;
+ if (nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode))) {
+ l = strlen(XATTR_NAME_NFSV4_ACL) + 1;
+ memcpy(p, XATTR_NAME_NFSV4_ACL, l);
+ p += l;
+ }
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dentry->d_inode, NFS_CAP_SECURITY_LABEL))
+ p += security_inode_listsecurity(dentry->d_inode, p,
+ buflen - (p - buf));
+#endif
return len;
}

diff --git a/security/security.c b/security/security.c
index d3194e1..b5e59fb 100644
--- a/security/security.c
+++ b/security/security.c
@@ -545,6 +545,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)
{
--
1.5.5.1

2008-12-01 16:42:14

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 01/14] patch fix_use_before_init_in_nfsd4_list_rec_dir


Signed-off-by: David P. Quigley <[email protected]>
---
fs/nfsd/nfs4recover.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index bb93946..7084252 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -228,8 +228,10 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)

filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY);
status = PTR_ERR(filp);
- if (IS_ERR(filp))
- goto out;
+ if (IS_ERR(filp)) {
+ nfs4_reset_user(uid, gid);
+ return PTR_ERR(filp);
+ }
INIT_LIST_HEAD(dentries);
status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
fput(filp);
--
1.5.5.1

2008-12-01 16:42:57

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 11/14] NFSv4: Introduce new label structure

In order to mimic the way that NFSv4 ACLs are implemented we have created a
structure to be used to pass label data up and down the call chain. This patch
adds the new structure and new members to the required NFSv4 call structures.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
include/linux/nfs4.h | 6 ++++++
include/linux/nfs_xdr.h | 3 +++
include/linux/nfsd/xdr4.h | 3 +++
3 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 144eacf..dd99b27 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -112,6 +112,12 @@ struct nfs4_acl {
struct nfs4_ace aces[0];
};

+struct nfs4_label {
+ void *label;
+ u32 len;
+};
+
+
typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;
typedef struct { char data[NFS4_STATEID_SIZE]; } nfs4_stateid;

diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 58532cb..177a62c 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -136,6 +136,7 @@ struct nfs_openargs {
const struct nfs_server *server; /* Needed for ID mapping */
const u32 * bitmask;
__u32 claim;
+ const struct nfs4_label *label;
};

struct nfs_openres {
@@ -354,6 +355,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 {
@@ -578,6 +580,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 {
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h
index 27bd3e3..a0f3d79 100644
--- a/include/linux/nfsd/xdr4.h
+++ b/include/linux/nfsd/xdr4.h
@@ -94,6 +94,7 @@ struct nfsd4_create {
struct iattr cr_iattr; /* request */
struct nfsd4_change_info cr_cinfo; /* response */
struct nfs4_acl *cr_acl;
+ struct nfs4_label *cr_label;
};
#define cr_linklen u.link.namelen
#define cr_linkname u.link.name
@@ -223,6 +224,7 @@ struct nfsd4_open {
int op_truncate; /* used during processing */
struct nfs4_stateowner *op_stateowner; /* used during processing */
struct nfs4_acl *op_acl;
+ struct nfs4_label *op_label;
};
#define op_iattr u.iattr
#define op_verf u.verf
@@ -304,6 +306,7 @@ struct nfsd4_setattr {
u32 sa_bmval[2]; /* request */
struct iattr sa_iattr; /* request */
struct nfs4_acl *sa_acl;
+ struct nfs4_label *sa_label;
};

struct nfsd4_setclientid {
--
1.5.5.1

2008-12-01 16:43:26

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 12/14] NFS: Client implementation of Labeled-NFS

This patch implements the client transport and handling support for labeled
NFS. The patch adds two functions to encode and decode the security label
recommended attribute which makes use of the LSM hooks added earlier. It also
adds code to grab the label from the file attribute structures and encode the
label to be sent back to the server.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/nfs/inode.c | 49 +++++++-
fs/nfs/nfs4proc.c | 303 ++++++++++++++++++++++++++++++++++++++++++----
fs/nfs/nfs4xdr.c | 55 ++++++++-
fs/nfs/super.c | 33 +++++-
include/linux/nfs_fs.h | 2 +
security/selinux/hooks.c | 5 +
6 files changed, 412 insertions(+), 35 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 33ae87b..c025624 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -143,10 +143,13 @@ static void nfs_zap_caches_locked(struct inode *inode)
nfsi->attrtimeo_timestamp = jiffies;

memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR| \
+ NFS_INO_INVALID_LABEL| \
+ NFS_INO_INVALID_ACCESS| \
+ NFS_INO_INVALID_ACL| \
+ NFS_INO_REVAL_PAGECACHE;
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;
- 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_DATA;
}

void nfs_zap_caches(struct inode *inode)
@@ -235,6 +238,33 @@ nfs_init_locked(struct inode *inode, void *opaque)
/* Don't use READDIRPLUS on directories that we believe are too large */
#define NFS_LIMIT_READDIRPLUS (8*PAGE_SIZE)

+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr)
+{
+ int error;
+
+/* BUG_ON(!mutex_is_locked(&inode->i_mutex)); */
+
+ if ((fattr->valid & NFS_ATTR_FATTR_V4) &&
+ (fattr->bitmap[1] & FATTR4_WORD1_SECURITY_LABEL) &&
+ (fattr->label != NULL) &&
+ (inode->i_security != NULL)) {
+ error = security_inode_notifysecctx(inode, fattr->label,
+ fattr->label_len);
+ /* XXX: debug output */
+ if (error)
+ printk(KERN_ERR "%s() %s %d "
+ "security_inode_notifysecctx() %d\n",
+ __func__,
+ (char *)fattr->label, fattr->label_len, error);
+ }
+}
+#else
+void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr)
+{
+}
+#endif
+
/*
* This is our front-end to iget that looks up inodes by file handle
* instead of inode number.
@@ -315,6 +345,11 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
inode->i_gid = fattr->gid;
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ nfs_setsecurity(inode, fattr);
+#endif
+
if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) {
/*
* report the blocks in 512byte units
@@ -749,7 +784,7 @@ int nfs_attribute_timeout(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_timeout(inode))
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
@@ -1183,6 +1218,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
inode->i_gid = fattr->gid;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ nfs_setsecurity(inode, fattr);
+#endif

if (fattr->valid & (NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4)) {
/*
@@ -1194,7 +1232,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
}

/* 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;
@@ -1207,6 +1245,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)))
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 3a0d25f..9db51ea 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -48,6 +48,7 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
#include <linux/mount.h>
+#include <linux/nfs4_mount.h>

#include "nfs4_fs.h"
#include "delegation.h"
@@ -97,6 +98,9 @@ const u32 nfs4_fattr_bitmap[2] = {
| FATTR4_WORD1_TIME_ACCESS
| FATTR4_WORD1_TIME_METADATA
| FATTR4_WORD1_TIME_MODIFY
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ | FATTR4_WORD1_SECURITY_LABEL
+#endif
};

const u32 nfs4_statfs_bitmap[2] = {
@@ -251,7 +255,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)

static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
struct nfs4_state_owner *sp, int flags,
- const struct iattr *attrs)
+ const struct iattr *attrs, struct nfs4_label *label)
{
struct dentry *parent = dget_parent(path->dentry);
struct inode *dir = parent->d_inode;
@@ -277,6 +281,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
p->o_arg.server = server;
p->o_arg.bitmask = server->attr_bitmask;
p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
+ p->o_arg.label = label;
if (flags & O_EXCL) {
u32 *s = (u32 *) p->o_arg.u.verifier.data;
s[0] = jiffies;
@@ -567,7 +572,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
{
struct nfs4_opendata *opendata;

- opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
+ opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL, NULL);
if (opendata == NULL)
return ERR_PTR(-ENOMEM);
opendata->state = state;
@@ -1046,7 +1051,7 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
/*
* Returns a referenced nfs4_state
*/
-static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct nfs4_label *label, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
@@ -1068,7 +1073,7 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE));
down_read(&clp->cl_sem);
status = -ENOMEM;
- opendata = nfs4_opendata_alloc(path, sp, flags, sattr);
+ opendata = nfs4_opendata_alloc(path, sp, flags, sattr, label);
if (opendata == NULL)
goto err_release_rwsem;

@@ -1103,14 +1108,14 @@ out_err:
}


-static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred)
+static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct nfs4_label *label, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int status;

do {
- status = _nfs4_do_open(dir, path, flags, sattr, cred, &res);
+ status = _nfs4_do_open(dir, path, flags, sattr, label, cred, &res);
if (status == 0)
break;
/* NOTE: BAD_SEQID means the server and client disagree about the
@@ -1154,14 +1159,15 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int

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_label *label, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_setattrargs arg = {
.fh = NFS_FH(inode),
.iap = sattr,
.server = server,
- .bitmask = server->attr_bitmask,
+ .bitmask = server->attr_bitmask,
+ .label = label,
};
struct nfs_setattrres res = {
.fattr = fattr,
@@ -1193,14 +1199,14 @@ 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_label *label, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
- _nfs4_do_setattr(inode, cred, fattr, sattr, state),
+ _nfs4_do_setattr(inode, cred, fattr, sattr, label, state),
&exception);
} while (exception.retry);
return err;
@@ -1406,6 +1412,7 @@ out_close:
struct dentry *
nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
+ struct nfs4_label l, *label = NULL;
struct path path = {
.mnt = nd->path.mnt,
.dentry = dentry,
@@ -1416,11 +1423,21 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
struct nfs4_state *state;
struct dentry *res;

+ memset(&attr, 0, sizeof(struct iattr));
if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
attr.ia_valid = ATTR_MODE;
if (!IS_POSIXACL(dir))
attr.ia_mode &= ~current->fs->umask;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL)) {
+ int error;
+ error = security_dentry_init_security(dentry,
+ attr.ia_mode, &l.label, &l.len);
+ if (error == 0)
+ label = &l;
+ }
+#endif
} else {
attr.ia_valid = 0;
BUG_ON(nd->intent.open.flags & O_CREAT);
@@ -1432,8 +1449,12 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */
nfs_block_sillyrename(parent);
- state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred);
+ state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, label, cred);
put_rpccred(cred);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL)
+ security_release_secctx(l.label, l.len);
+#endif
if (IS_ERR(state)) {
if (PTR_ERR(state) == -ENOENT) {
d_add(dentry, NULL);
@@ -1464,7 +1485,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
cred = rpc_lookup_cred();
if (IS_ERR(cred))
return PTR_ERR(cred);
- state = nfs4_do_open(dir, &path, openflags, NULL, cred);
+ state = nfs4_do_open(dir, &path, openflags, NULL, NULL, cred);
put_rpccred(cred);
if (IS_ERR(state)) {
switch (PTR_ERR(state)) {
@@ -1506,6 +1527,13 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
server->caps |= NFS_CAP_ACLS;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->flags & NFS4_MOUNT_SECURITY_LABEL &&
+ res.attr_bitmask[1] & FATTR4_WORD1_SECURITY_LABEL) {
+ server->caps |= NFS_CAP_SECURITY_LABEL;
+ } else
+#endif
+ server->attr_bitmask[1] &= ~FATTR4_WORD1_SECURITY_LABEL;
if (res.has_links != 0)
server->caps |= NFS_CAP_HARDLINKS;
if (res.has_symlinks != 0)
@@ -1688,9 +1716,11 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
}
}

- status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
- if (status == 0)
+ status = nfs4_do_setattr(inode, cred, fattr, sattr, NULL, state);
+ if (status == 0) {
nfs_setattr_update_inode(inode, sattr);
+ nfs_setsecurity(inode, fattr);
+ }
return status;
}

@@ -1913,6 +1943,7 @@ static int
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags, struct nameidata *nd)
{
+ struct nfs4_label l, *label = NULL;
struct path path = {
.mnt = nd->path.mnt,
.dentry = dentry,
@@ -1926,7 +1957,18 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
status = PTR_ERR(cred);
goto out;
}
- state = nfs4_do_open(dir, &path, flags, sattr, cred);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (((nd->flags & LOOKUP_CREATE) != 0) &&
+ nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL)) {
+ status = security_dentry_init_security(dentry,
+ sattr->ia_mode, &l.label, &l.len);
+ /* XXX: should this be more fatal? */
+ if (status == 0)
+ label = &l;
+ }
+#endif
+
+ state = nfs4_do_open(dir, &path, flags, sattr, label, cred);
d_drop(dentry);
if (IS_ERR(state)) {
status = PTR_ERR(state);
@@ -1945,10 +1987,12 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
if (status < 0)
goto out;
#endif
- status = nfs4_do_setattr(state->inode, cred, &fattr, sattr, state);
- if (status == 0)
+ status = nfs4_do_setattr(state->inode, cred, &fattr, sattr, label, state);
+ if (status == 0) {
nfs_setattr_update_inode(state->inode, sattr);
- nfs_post_op_update_inode(state->inode, &fattr);
+ nfs_post_op_update_inode(state->inode, &fattr);
+ nfs_setsecurity(state->inode, &fattr);
+ }
nfs_fattr_fini(&fattr);
}
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
@@ -1958,6 +2002,10 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
out_putcred:
put_rpccred(cred);
out:
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL)
+ security_release_secctx(label->label, label->len);
+#endif
return status;
}

@@ -2241,7 +2289,8 @@ static void nfs4_free_createdata(struct nfs4_createdata *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;
@@ -2257,6 +2306,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);

@@ -2269,18 +2319,33 @@ 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;
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL)) {
+ err = security_dentry_init_security(dentry,
+ sattr->ia_mode, &l.label, &l.len);
+ if (err == 0)
+ label = &l;
+ }
+#endif
+
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_symlink(dir, dentry, page,
- len, sattr),
+ len, sattr, label),
&exception);
} while (exception.retry);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL)
+ security_release_secctx(l.label, l.len);
+#endif
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;
@@ -2289,6 +2354,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);
@@ -2300,12 +2366,27 @@ 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;
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL)) {
+ err = security_dentry_init_security(dentry,
+ sattr->ia_mode, &l.label, &l.len);
+ if (err == 0)
+ label = &l;
+ }
+#endif
+
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);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL)
+ security_release_secctx(l.label, l.len);
+#endif
return err;
}

@@ -2360,7 +2441,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;
@@ -2385,7 +2466,7 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
data->arg.u.device.specdata1 = MAJOR(rdev);
data->arg.u.device.specdata2 = MINOR(rdev);
}
-
+ data->arg.label = label;
status = nfs4_do_create(dir, dentry, data);

nfs4_free_createdata(data);
@@ -2397,12 +2478,27 @@ 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;
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL)) {
+ err = security_dentry_init_security(dentry,
+ sattr->ia_mode, &l.label, &l.len);
+ if (err == 0)
+ label = &l;
+ }
+#endif
+
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);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL)
+ security_release_secctx(l.label, l.len);
+#endif
return err;
}

@@ -2850,6 +2946,163 @@ 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;
+ u32 bitmask[2] = { 0, FATTR4_WORD1_SECURITY_LABEL };
+ struct nfs4_getattr_arg args = {
+ .fh = NFS_FH(inode),
+ .bitmask = bitmask,
+ };
+ struct nfs4_getattr_res res = {
+ .fattr = &fattr,
+ .server = server,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+ int ret;
+
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+ nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ nfs_fattr_init(&fattr);
+
+ ret = rpc_call_sync(server->client, &msg, 0);
+ if (ret)
+ goto out;
+ if (!(fattr.bitmap[1] & FATTR4_WORD1_SECURITY_LABEL))
+ return -ENOENT;
+ if (buflen < fattr.label_len) {
+ ret = -ERANGE;
+ goto out;
+ }
+ memcpy(buf, fattr.label, fattr.label_len);
+out:
+ nfs_fattr_fini(&fattr);
+ return ret;
+}
+
+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 *label,
+ struct nfs_fattr *fattr,
+ struct nfs4_state *state)
+{
+
+ struct iattr sattr;
+ struct nfs_server *server = NFS_SERVER(inode);
+ const u32 bitmask[2] = { 0, FATTR4_WORD1_SECURITY_LABEL };
+ struct nfs_setattrargs args = {
+ .fh = NFS_FH(inode),
+ .iap = &sattr,
+ .server = server,
+ .bitmask = bitmask,
+ .label = label,
+ };
+ struct nfs_setattrres res = {
+ .fattr = fattr,
+ .server = server,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+ unsigned long timestamp = jiffies;
+ int status;
+
+ memset(&sattr, 0, sizeof(struct iattr));
+
+ if (nfs4_copy_delegation_stateid(&args.stateid, inode)) {
+ /* Use that stateid */
+ } else if (state != NULL) {
+ msg.rpc_cred = state->owner->so_cred;
+ nfs4_copy_stateid(&args.stateid, state, current->files);
+ } else
+ memcpy(&args.stateid, &zero_stateid, sizeof(args.stateid));
+
+ status = rpc_call_sync(server->client, &msg, 0);
+ if (status == 0 && state != NULL)
+ renew_lease(server, timestamp);
+ return status;
+}
+
+static int nfs4_do_set_security_label(struct inode *inode,
+ struct nfs4_label *label,
+ struct nfs_fattr *fattr,
+ struct nfs4_state *state)
+{
+ struct nfs4_exception exception = { };
+ int err;
+
+ do {
+ err = nfs4_handle_exception(NFS_SERVER(inode),
+ _nfs4_do_set_security_label(inode, label, fattr, state),
+ &exception);
+ } while (exception.retry);
+ return err;
+}
+
+static int
+nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen)
+{
+ struct nfs4_label label;
+ struct nfs_fattr fattr;
+ struct rpc_cred *cred;
+ struct nfs_open_context *ctx;
+ struct nfs4_state *state = NULL;
+ struct inode *inode = dentry->d_inode;
+ int status;
+
+ if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+ return -EOPNOTSUPP;
+
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+ nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ nfs_fattr_init(&fattr);
+
+ label.label = (char *)buf;
+ label.len = buflen;
+
+ cred = rpc_lookup_cred();
+ if (IS_ERR(cred))
+ return PTR_ERR(cred);
+
+ /* Search for an existing open(O_WRITE) file */
+ ctx = nfs_find_open_context(inode, cred, FMODE_WRITE);
+ if (ctx != NULL)
+ state = ctx->state;
+
+ status = nfs4_do_set_security_label(inode, &label, &fattr, state);
+ if (status == 0)
+ nfs_setsecurity(inode, &fattr);
+ if (ctx != NULL)
+ put_nfs_open_context(ctx);
+ put_rpccred(cred);
+ nfs_fattr_fini(&fattr);
+ return status;
+}
+#endif /* CONFIG_NFS_V4_SECURITY_LABEL */
+
static int
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
{
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index b916297..104fd29 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -601,7 +601,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
}

-static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+static int 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];
@@ -651,6 +651,10 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
}
len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
}
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL)
+ len += 4 + (XDR_QUADLEN(label->len) << 2);
+#endif
if (iap->ia_valid & ATTR_ATIME_SET)
len += 16;
else if (iap->ia_valid & ATTR_ATIME)
@@ -709,6 +713,13 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
WRITE32(NFS4_SET_TO_SERVER_TIME);
}
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (label != NULL) {
+ bmval1 |= FATTR4_WORD1_SECURITY_LABEL;
+ WRITE32(label->len);
+ WRITEMEM(label->label, label->len);
+ }
+#endif

/*
* Now we backfill the bitmap and the attribute buffer length.
@@ -792,7 +803,7 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
WRITE32(create->name->len);
WRITEMEM(create->name->name, create->name->len);

- return encode_attrs(xdr, create->attrs, create->server);
+ return encode_attrs(xdr, create->attrs, create->label, create->server);
}

static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap)
@@ -1000,7 +1011,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
switch(arg->open_flags & O_EXCL) {
case 0:
WRITE32(NFS4_CREATE_UNCHECKED);
- encode_attrs(xdr, arg->u.attrs, arg->server);
+ encode_attrs(xdr, arg->u.attrs, arg->label, arg->server);
break;
default:
WRITE32(NFS4_CREATE_EXCLUSIVE);
@@ -1301,7 +1312,7 @@ static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *
WRITE32(OP_SETATTR);
WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);

- if ((status = encode_attrs(xdr, arg->iap, server)))
+ if ((status = encode_attrs(xdr, arg->iap, arg->label, server)))
return status;

return 0;
@@ -2954,6 +2965,39 @@ static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, str
return status;
}

+static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap, void **ctx, __u32 *ctxlen)
+{
+ __u32 len;
+ __be32 *p;
+ int rc = 0;
+
+ if (unlikely(bitmap[1] & (FATTR4_WORD1_SECURITY_LABEL - 1U)))
+ return -EIO;
+ if (likely(bitmap[1] & FATTR4_WORD1_SECURITY_LABEL)) {
+ READ_BUF(4);
+ READ32(len);
+ READ_BUF(len);
+ if (len < XDR_MAX_NETOBJ) {
+ if (*ctx != NULL) {
+ if (*ctxlen < len) {
+ printk(KERN_ERR
+ "%s(): ctxlen %d < len %d\n",
+ __func__, *ctxlen, len);
+ /* rc = -ENOMEM; */
+ *ctx = NULL; /* leak */
+ } else {
+ memcpy(*ctx, p, len);
+ }
+ }
+ *ctxlen = len;
+ } else
+ printk(KERN_WARNING "%s: label too long (%u)!\n",
+ __FUNCTION__, len);
+ bitmap[1] &= ~FATTR4_WORD1_SECURITY_LABEL;
+ }
+ return rc;
+}
+
static int verify_attr_len(struct xdr_stream *xdr, __be32 *savep, uint32_t attrlen)
{
unsigned int attrwords = XDR_QUADLEN(attrlen);
@@ -3188,6 +3232,9 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, cons
goto xdr_error;
if ((status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid)) != 0)
goto xdr_error;
+ if ((status = decode_attr_security_label(xdr, bitmap,
+ &fattr->label, &fattr->label_len)) != 0)
+ goto xdr_error;
if (fattr->fileid == 0 && fileid != 0)
fattr->fileid = fileid;
if ((status = verify_attr_len(xdr, savep, attrlen)) == 0)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index ab071e1..41c979a 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -565,8 +565,15 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
nfs_show_mountd_options(m, nfss, showdefaults);

#ifdef CONFIG_NFS_V4
- if (clp->rpc_ops->version == 4)
+ if (clp->rpc_ops->version == 4) {
seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfss->caps & NFS_CAP_SECURITY_LABEL)
+ seq_printf(m, ",security_label");
+ else
+ seq_printf(m, ",nosecurity_label");
+#endif
+ }
#endif
}

@@ -623,6 +630,12 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfss->caps & NFS_CAP_SECURITY_LABEL)
+ seq_printf(m, ",security_label");
+ else
+ seq_printf(m, ",nosecurity_label");
+#endif
}
#endif

@@ -2212,6 +2225,9 @@ static int nfs4_validate_mount_data(void *options,
if (data == NULL)
goto out_no_data;

+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ args->flags |= NFS4_MOUNT_SECURITY_LABEL; /* Default */
+#endif
args->rsize = NFS_MAX_FILE_IO_SIZE;
args->wsize = NFS_MAX_FILE_IO_SIZE;
args->acregmin = NFS_DEF_ACREGMIN;
@@ -2399,6 +2415,21 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
goto error_splat_super;
}

+#ifdef CONFIG_SECURITY_SELINUX
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ security_sb_parse_opts_str("native_labels", &data->lsm_opts);
+ else
+ data->flags &= ~NFS4_MOUNT_SECURITY_LABEL;
+ /* XXX: we need a way to separate the default use of
+ security labels if supported from the userland
+ passing in the requirment that they be present.
+
+ default - will
+ -o nosecurity_label - won't
+ -o security_label - must
+ */
+#endif
+
error = security_sb_set_mnt_opts(s, &data->lsm_opts);
if (error)
goto error_splat_root;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 6120a28..4dc2fc1 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -197,6 +197,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
@@ -341,6 +342,7 @@ extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *map
extern int nfs_revalidate_mapping_nolock(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);
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, int mode);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9e73750..4560f8f 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2844,7 +2844,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;
}

@@ -2934,7 +2937,9 @@ 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;
}

--
1.5.5.1

2008-12-01 16:44:04

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 05/14] Security: Add Hook to test if the particular xattr is part of a MAC model.

There are areas in the Labeled NFS code where where we need to test if the
attribute being requested exhibits the semantics of a MAC model. This allows us
to make sure that we get the desired semantics from the attribute instead of
something else such as capabilities or a time based LSM.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
include/linux/security.h | 11 +++++++++++
security/capability.c | 6 ++++++
security/security.c | 6 ++++++
security/selinux/hooks.c | 6 ++++++
security/smack/smack_lsm.c | 10 ++++++++++
5 files changed, 39 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index ccbfb06..5eac603 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1252,6 +1252,10 @@ 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.
+ * @name full extended attribute name to check against LSM as a MAC label.
+ *
* @secid_to_secctx:
* Convert secid to security context.
* @secid contains the security ID.
@@ -1521,6 +1525,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);
@@ -1771,6 +1776,7 @@ 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_netlink_recv(struct sk_buff *skb, int cap);
+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);
@@ -2489,6 +2495,11 @@ static inline int security_netlink_recv(struct sk_buff *skb, int cap)
return cap_netlink_recv(skb, cap);
}

+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;
diff --git a/security/capability.c b/security/capability.c
index 6d38303..ed24c60 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -740,6 +740,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;
@@ -957,6 +962,7 @@ void 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);
diff --git a/security/security.c b/security/security.c
index ab978bf..d3194e1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -867,6 +867,12 @@ int security_netlink_recv(struct sk_buff *skb, int cap)
}
EXPORT_SYMBOL(security_netlink_recv);

+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);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index dcd6d50..60d6bcc 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5414,6 +5414,11 @@ boundary_ok:
return size;
}

+static int selinux_ismaclabel(const char *name)
+{
+ return (strcmp(name,XATTR_NAME_SELINUX) == 0);
+}
+
static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
return security_sid_to_context(secid, secdata, seclen);
@@ -5655,6 +5660,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,
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6e2dc0b..49a2ca7 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2529,6 +2529,15 @@ 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_NAME_SMACK) == 0);
+}
+
+/*
* smack_secid_to_secctx - return the smack label for a secid
* @secid: incoming integer
* @secdata: destination
@@ -2706,6 +2715,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,
--
1.5.5.1

2008-12-01 16:43:42

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 14/14] NFSD: Server implementation of MAC Labeling

This patch adds the ability to encode and decode file labels on the server for
the purpose of sending them to the client and also to process label change
requests from the client.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/nfsd/export.c | 3 +
fs/nfsd/nfs4proc.c | 35 ++++++++++++++-
fs/nfsd/nfs4xdr.c | 106 ++++++++++++++++++++++++++++++++++++++++++---
fs/nfsd/vfs.c | 28 ++++++++++++
include/linux/nfsd/nfsd.h | 2 +
5 files changed, 166 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 5839b22..2630759 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -1428,6 +1428,9 @@ static struct flags {
{ NFSEXP_ALLSQUASH, {"all_squash", ""}},
{ NFSEXP_ASYNC, {"async", "sync"}},
{ NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}},
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ { NFSEXP_SECURITY_LABEL, {"security_label", ""}},
+#endif
{ NFSEXP_NOHIDE, {"nohide", ""}},
{ NFSEXP_CROSSMOUNT, {"crossmnt", ""}},
{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 669461e..5b910cf 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -49,6 +49,10 @@
#include <linux/nfs4_acl.h>
#include <linux/sunrpc/gss_api.h>

+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+#endif
+
#define NFSDDBG_FACILITY NFSDDBG_PROC

static inline void
@@ -103,6 +107,18 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
(u32 *)open->op_verf.data,
&open->op_truncate, &created);

+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (!status && open->op_label != NULL) {
+ struct inode *inode = resfh.fh_dentry->d_inode;
+
+ mutex_lock(&inode->i_mutex);
+ /* Is it appropriate to just kick back an error? */
+ status = security_inode_setsecctx(resfh.fh_dentry,
+ open->op_label->label, open->op_label->len);
+ mutex_unlock(&inode->i_mutex);
+ }
+#endif
+
/* If we ever decide to use different attrs to store the
* verifier in nfsd_create_v3, then we'll need to change this
*/
@@ -432,6 +448,18 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_badtype;
}

+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (!status && create->cr_label != NULL) {
+ struct inode *inode = resfh.fh_dentry->d_inode;
+
+ mutex_lock(&inode->i_mutex);
+ /* Is it appropriate to just kick back an error? */
+ status = security_inode_setsecctx(resfh.fh_dentry,
+ create->cr_label->label, create->cr_label->len);
+ mutex_unlock(&inode->i_mutex);
+ }
+#endif
+
if (!status) {
fh_unlock(&cstate->current_fh);
set_change_info(&create->cr_cinfo, &cstate->current_fh);
@@ -670,6 +698,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
setattr->sa_acl);
if (status)
goto out;
+ if (setattr->sa_label != NULL)
+ status = nfsd4_set_nfs4_label(rqstp, &cstate->current_fh,
+ setattr->sa_label);
+ if (status)
+ goto out;
status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
0, (time_t)0);
out:
@@ -964,7 +997,7 @@ out:
return status;
}

-static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = {
+static struct nfsd4_operation nfsd4_ops[LAST_NFS4_OP+1] = {
[OP_ACCESS] = {
.op_func = (nfsd4op_func)nfsd4_access,
.op_name = "OP_ACCESS",
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index afcdf4b..69af020 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -59,6 +59,10 @@
#include <linux/sunrpc/gss_api.h>
#include <linux/sunrpc/svcauth_gss.h>

+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+#endif
+
#define NFSDDBG_FACILITY NFSDDBG_XDR

/*
@@ -249,7 +253,7 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)

static __be32
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,
- struct nfs4_acl **acl)
+ struct nfs4_acl **acl, struct nfs4_label **label)
{
int expected_len, len = 0;
u32 dummy32;
@@ -402,6 +406,39 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
goto xdr_error;
}
}
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (bmval[1] & FATTR4_WORD1_SECURITY_LABEL) {
+ READ_BUF(4);
+ len += 4;
+ READ32(dummy32);
+ READ_BUF(dummy32);
+ len += (XDR_QUADLEN(dummy32) << 2);
+ READMEM(buf, dummy32);
+
+ if (dummy32 > NFS4_MAXLABELLEN)
+ return nfserr_resource;
+
+ *label = kzalloc(sizeof(struct nfs4_label), GFP_KERNEL);
+ if (*label == NULL) {
+ host_err = -ENOMEM;
+ goto out_nfserr;
+ }
+
+ (*label)->label = kmalloc(dummy32 + 1, GFP_KERNEL);
+ if ((*label)->label == NULL) {
+ host_err = -ENOMEM;
+ kfree(*label);
+ goto out_nfserr;
+ }
+
+ (*label)->len = dummy32;
+ memcpy((*label)->label, buf, dummy32);
+ ((char *)(*label)->label)[dummy32] = '\0';
+
+ defer_free(argp, kfree, (*label)->label);
+ defer_free(argp, kfree, *label);
+ }
+#endif
if (len != expected_len)
goto xdr_error;

@@ -495,7 +532,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
return status;

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

DECODE_TAIL;
@@ -654,7 +691,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED:
case NFS4_CREATE_GUARDED:
- if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl)))
+ if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl, &open->op_label)))
goto out;
break;
case NFS4_CREATE_EXCLUSIVE:
@@ -853,8 +890,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
if (status)
return status;
- return nfsd4_decode_fattr(argp, setattr->sa_bmval,
- &setattr->sa_iattr, &setattr->sa_acl);
+ return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
+ &setattr->sa_acl, &setattr->sa_label);
}

static __be32
@@ -917,7 +954,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
* nfsd4_proc_verify; however we still decode here just to return
* correct error in case of bad xdr. */
#if 0
- status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl);
+ status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl, &ve_label);
if (status == nfserr_inval) {
status = nfserrno(status);
goto out;
@@ -1380,6 +1417,34 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
}

+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+static inline __be32
+nfsd4_encode_security_label(struct svc_rqst *rqstp, struct dentry *dentry, __be32 **p, int *buflen)
+{
+ void *context;
+ int err;
+ int len;
+
+ err = 0;
+ (void)security_inode_getsecctx(dentry->d_inode, &context, &len);
+ if (len < 0)
+ return nfserrno(len);
+
+ if (*buflen < ((XDR_QUADLEN(len) << 2) + 4)) {
+ err = nfserr_resource;
+ goto out;
+ }
+
+ *p = xdr_encode_opaque(*p, context, len);
+ *buflen -= (XDR_QUADLEN(len) << 2) + 4;
+ BUG_ON(*buflen < 0);
+
+out:
+ security_release_secctx(context, len);
+ return err;
+}
+#endif
+
#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
FATTR4_WORD0_RDATTR_ERROR)
#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
@@ -1475,6 +1540,14 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS;
}
}
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (bmval1 & FATTR4_WORD1_SECURITY_LABEL) {
+ if (/* XXX !selinux_enabled */ 0)
+ bmval1 &= ~FATTR4_WORD1_SECURITY_LABEL;
+ }
+#else
+ bmval1 &= ~FATTR4_WORD1_SECURITY_LABEL;
+#endif
if ((buflen -= 16) < 0)
goto out_resource;

@@ -1485,15 +1558,24 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,

if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
u32 word0 = NFSD_SUPPORTED_ATTRS_WORD0;
+ u32 word1 = NFSD_SUPPORTED_ATTRS_WORD1;
if ((buflen -= 12) < 0)
goto out_resource;
if (!aclsupport)
word0 &= ~FATTR4_WORD0_ACL;
if (!exp->ex_fslocs.locations)
word0 &= ~FATTR4_WORD0_FS_LOCATIONS;
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
+ word1 |= FATTR4_WORD1_SECURITY_LABEL;
+ else
+ word1 &= ~FATTR4_WORD1_SECURITY_LABEL;
+#else
+ word1 &= ~FATTR4_WORD1_SECURITY_LABEL;
+#endif
WRITE32(2);
WRITE32(word0);
- WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
+ WRITE32(word1);
}
if (bmval0 & FATTR4_WORD0_TYPE) {
if ((buflen -= 4) < 0)
@@ -1803,6 +1885,16 @@ out_acl:
}
WRITE64(stat.ino);
}
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (bmval1 & FATTR4_WORD1_SECURITY_LABEL) {
+ status = nfsd4_encode_security_label(rqstp, dentry,
+ &p, &buflen);
+ if (status == nfserr_resource)
+ goto out_resource;
+ if (status)
+ goto out;
+ }
+#endif
*attrlenp = htonl((char *)p - (char *)attrlenp - 4);
*countp = p - buffer;
status = nfs_ok;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 4433c8f..d4c4365 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -556,6 +556,34 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac
return error;
}

+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct nfs4_label *label)
+{
+ __be32 error;
+ int host_error;
+ struct dentry *dentry;
+
+ /* Get inode */
+ /* XXX: should we have a MAY_SSECCTX? */
+ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
+ if (error)
+ return error;
+
+ dentry = fhp->fh_dentry;
+
+ mutex_lock(&dentry->d_inode->i_mutex);
+ host_error = security_inode_setsecctx(dentry, label->label, label->len);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ return nfserrno(host_error);
+}
+#else
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct nfs4_label *label)
+{
+}
+#endif
+
#endif /* defined(CONFIG_NFS_V4) */

#ifdef CONFIG_NFSD_V3
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 8219925..e7393b9 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -87,6 +87,8 @@ __be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
__be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
struct nfs4_acl *);
int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **);
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
+ struct nfs4_label *);
#endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
--
1.5.5.1

2008-12-01 16:44:26

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 06/14] SELinux: Add new labeling type native labels

There currently doesn't exist a labeling type that is adequate for use with
labeled NFS. Since NFS doesn't really support xattrs we can't use the use xattr
labeling behavior. For this we developed a new labeling type. The native
labeling type is used solely by NFS to ensure NFS inodes are labeled at runtime
by the NFS code instead of relying on the SELinux security server on the client
end.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
security/selinux/hooks.c | 74 +++++++++++++++++++++++++++-------
security/selinux/include/security.h | 4 ++
security/selinux/ss/policydb.c | 5 ++-
3 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 60d6bcc..9e73750 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -89,7 +89,7 @@
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX

-#define NUM_SEL_MNT_OPTS 4
+#define NUM_SEL_MNT_OPTS 5

extern unsigned int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
@@ -302,13 +302,14 @@ extern int ss_initialized;

/* The file system's label must be initialized prior to use. */

-static char *labeling_behaviors[6] = {
+static char *labeling_behaviors[7] = {
"uses xattr",
"uses transition SIDs",
"uses task SIDs",
"uses genfs_contexts",
"not configured for labeling",
"uses mountpoint labeling",
+ "uses native labels",
};

static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
@@ -324,6 +325,7 @@ enum {
Opt_fscontext = 2,
Opt_defcontext = 3,
Opt_rootcontext = 4,
+ Opt_native_labels = 5,
};

static const match_table_t tokens = {
@@ -331,6 +333,7 @@ static const match_table_t tokens = {
{Opt_fscontext, FSCONTEXT_STR "%s"},
{Opt_defcontext, DEFCONTEXT_STR "%s"},
{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
+ {Opt_native_labels, NATIVELABELS_STR},
{Opt_error, NULL},
};

@@ -518,6 +521,10 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
opts->mnt_opts[i] = context;
opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
}
+ if (sbsec->flags == NATIVE_LABELS_MNT) {
+ opts->mnt_opts[i] = NULL;
+ opts->mnt_opts_flags[i++] = NATIVE_LABELS_MNT;
+ }

BUG_ON(i != opts->num_mnt_opts);

@@ -606,12 +613,16 @@ static int selinux_set_mnt_opts(struct super_block *sb,
*/
for (i = 0; i < num_opts; i++) {
u32 sid;
+ if (flags[i] == NATIVE_LABELS_MNT) {
+ sbsec->flags |= NATIVE_LABELS_MNT;
+ continue;
+ }
rc = security_context_to_sid(mount_options[i],
- strlen(mount_options[i]), &sid);
+ strlen(mount_options[i]), &sid);
if (rc) {
printk(KERN_WARNING "SELinux: security_context_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- mount_options[i], sb->s_id, name, rc);
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ mount_options[i], sb->s_id, name, rc);
goto out;
}
switch (flags[i]) {
@@ -670,14 +681,15 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (strcmp(sb->s_type->name, "proc") == 0)
sbsec->proc = 1;

- /* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(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(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) {

@@ -693,6 +705,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.
*/
+ /* NATIVE_LABELS can be overridden by 'context=' mounts, below. */
+ if (sbsec->flags & NATIVE_LABELS_MNT) {
+ sbsec->behavior = SECURITY_FS_USE_NATIVE;
+ }
+
if (context_sid) {
if (!fscontext_sid) {
rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
@@ -709,6 +726,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,

sbsec->mntpoint_sid = context_sid;
sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+ sbsec->flags &= ~NATIVE_LABELS_MNT; /* Exclusive */
}

if (rootcontext_sid) {
@@ -721,7 +739,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");
@@ -818,6 +837,7 @@ static int selinux_parse_opts_str(char *options,
char *p;
char *context = NULL, *defcontext = NULL;
char *fscontext = NULL, *rootcontext = NULL;
+ int native_labels = 0;
int rc, num_mnt_opts = 0;

opts->num_mnt_opts = 0;
@@ -885,9 +905,15 @@ static int selinux_parse_opts_str(char *options,
}
break;

+ case Opt_native_labels:
+ printk("%s() got Opt_native_labels\n", __func__);
+ native_labels = 1;
+ break;
+
+
default:
rc = -EINVAL;
- printk(KERN_WARNING "SELinux: unknown mount option\n");
+ printk(KERN_WARNING "SELinux: unknown mount option \"%s\"\n", p);
goto out_err;

}
@@ -920,6 +946,10 @@ static int selinux_parse_opts_str(char *options,
opts->mnt_opts[num_mnt_opts] = defcontext;
opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
}
+ if (native_labels) {
+ opts->mnt_opts[num_mnt_opts] = NULL;
+ opts->mnt_opts_flags[num_mnt_opts++] = NATIVE_LABELS_MNT;
+ }

opts->num_mnt_opts = num_mnt_opts;
return 0;
@@ -966,7 +996,12 @@ static void selinux_write_opts(struct seq_file *m,
char *prefix;

for (i = 0; i < opts->num_mnt_opts; i++) {
- char *has_comma = strchr(opts->mnt_opts[i], ',');
+ char *has_comma;
+
+ if (opts->mnt_opts[i])
+ has_comma = strchr(opts->mnt_opts[i], ',');
+ else
+ has_comma = NULL;

switch (opts->mnt_opts_flags[i]) {
case CONTEXT_MNT:
@@ -981,6 +1016,10 @@ static void selinux_write_opts(struct seq_file *m,
case DEFCONTEXT_MNT:
prefix = DEFCONTEXT_STR;
break;
+ case NATIVE_LABELS_MNT:
+ seq_putc(m, ',');
+ seq_puts(m, NATIVELABELS_STR);
+ continue;
default:
BUG();
};
@@ -1188,6 +1227,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;
@@ -2358,7 +2399,8 @@ static inline int selinux_option(char *option, int len)
return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
- match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len));
+ match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
+ match_prefix(NATIVELABELS_STR, sizeof(NATIVELABELS_STR)-1, option, len));
}

static inline void take_option(char **to, char *from, int *first, int len)
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 7244737..b38fd98 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -41,11 +41,13 @@
#define FSCONTEXT_MNT 0x02
#define ROOTCONTEXT_MNT 0x04
#define DEFCONTEXT_MNT 0x08
+#define NATIVE_LABELS_MNT 0x10

#define CONTEXT_STR "context="
#define FSCONTEXT_STR "fscontext="
#define ROOTCONTEXT_STR "rootcontext="
#define DEFCONTEXT_STR "defcontext="
+#define NATIVELABELS_STR "native_labels"

struct netlbl_lsm_secattr;

@@ -147,6 +149,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);
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 72e4a54..6dfe138 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1937,7 +1937,10 @@ int policydb_read(struct policydb *p, void *fp)
if (rc < 0)
goto bad;
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 bad;
+ if (c->v.behavior > SECURITY_FS_USE_MAX)
goto bad;
len = le32_to_cpu(buf[1]);
c->u.name = kmalloc(len + 1, GFP_KERNEL);
--
1.5.5.1

2008-12-01 16:45:02

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 03/14] LSM/SELinux: inode_{get,set,notify}secctx hooks to access LSM security context information.

This patch introduces three new hooks. The inode_getsecctx hook is used to get
all relevant information from an LSM about an inode. The inode_setsecctx is
used to set both the in-core and on-disk state for the inode based on a context
derived from inode_getsecctx.The final hook inode_notifysecctx will notify the
LSM of a change for the in-core state of the inode in question. These hooks are
for use in the labeled NFS code and addresses concerns of how to set security
on an inode in a multi-xattr LSM. For historical reasons Stephen Smalley's
explanation of the reason for these hooks is pasted below.

Quote Stephen Smalley

inode_setsecctx: Change the security context of an inode. Updates the
in core security context managed by the security module and invokes the
fs code as needed (via __vfs_setxattr_noperm) to update any backing
xattrs that represent the context. Example usage: NFS server invokes
this hook to change the security context in its incore inode and on the
backing file system to a value provided by the client on a SETATTR
operation.

inode_notifysecctx: Notify the security module of what the security
context of an inode should be. Initializes the incore security context
managed by the security module for this inode. Example usage: NFS
client invokes this hook to initialize the security context in its
incore inode to the value provided by the server for the file when the
server returned the file's attributes to the client.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
include/linux/security.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++
security/capability.c | 17 ++++++++++++++
security/security.c | 18 +++++++++++++++
security/selinux/hooks.c | 25 +++++++++++++++++++++
4 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index c13f1ce..7229ef1 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1289,6 +1289,41 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* audit_rule_init.
* @rule contains the allocated rule
*
+ * @inode_notifysecctx:
+ * Notify the security module of what the security context of an inode
+ * should be. Initializes the incore security context managed by the
+ * security module for this inode. Example usage: NFS client invokes
+ * this hook to initialize the security context in its incore inode to the
+ * value provided by the server for the file when the server returned the
+ * file's attributes to the client.
+ *
+ * Must be called with inode->i_mutex locked.
+ *
+ * @inode we wish to set the security context of.
+ * @ctx contains the string which we wish to set in the inode.
+ * @ctxlen contains the length of @ctx.
+ *
+ * @inode_setsecctx:
+ * Change the security context of an inode. Updates the
+ * incore security context managed by the security module and invokes the
+ * fs code as needed (via __vfs_setxattr_noperm) to update any backing
+ * xattrs that represent the context. Example usage: NFS server invokes
+ * this hook to change the security context in its incore inode and on the
+ * backing filesystem to a value provided by the client on a SETATTR
+ * operation.
+ *
+ * Must be called with inode->i_mutex locked.
+ *
+ * @dentry contains the inode we wish to set the security context of.
+ * @ctx contains the string which we wish to set in the inode.
+ * @ctxlen contains the length of @ctx.
+ *
+ * @inode_getsecctx:
+ * Returns a string containing all relavent security context information
+ *
+ * @inode we wish to set the security context of.
+ * @ctx is a pointer in which to place the allocated security context.
+ * @ctxlen points to the place to put the length of @ctx.
* This is the main security structure.
*/
struct security_operations {
@@ -1479,6 +1514,10 @@ struct security_operations {
int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
void (*release_secctx) (char *secdata, u32 seclen);

+ int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
+ int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
+ int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
+
#ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct socket *sock,
struct socket *other, struct sock *newsk);
@@ -1723,6 +1762,9 @@ 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);

+int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
+int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
+int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
#else /* CONFIG_SECURITY */
struct security_mnt_opts {
};
@@ -2440,6 +2482,19 @@ static inline int security_secctx_to_secid(const char *secdata,
static inline void security_release_secctx(char *secdata, u32 seclen)
{
}
+
+static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return -EOPNOTSUPP;
+}
+static inline int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return -EOPNOTSUPP;
+}
+static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_SECURITY */

#ifdef CONFIG_SECURITY_NETWORK
diff --git a/security/capability.c b/security/capability.c
index 2458748..27985c5 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -749,6 +749,20 @@ static void cap_release_secctx(char *secdata, u32 seclen)
{
}

+static int cap_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return 0;
+}
+
+static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return 0;
+}
+
+static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ return 0;
+}
#ifdef CONFIG_KEYS
static int cap_key_alloc(struct key *key, struct task_struct *ctx,
unsigned long flags)
@@ -940,6 +954,9 @@ void security_fixup_ops(struct security_operations *ops)
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);
+ set_to_cap_if_null(ops, inode_notifysecctx);
+ set_to_cap_if_null(ops, inode_setsecctx);
+ set_to_cap_if_null(ops, inode_getsecctx);
#ifdef CONFIG_SECURITY_NETWORK
set_to_cap_if_null(ops, unix_stream_connect);
set_to_cap_if_null(ops, unix_may_send);
diff --git a/security/security.c b/security/security.c
index c0acfa7..1d8d3b0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -878,6 +878,24 @@ void security_release_secctx(char *secdata, u32 seclen)
}
EXPORT_SYMBOL(security_release_secctx);

+int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return security_ops->inode_notifysecctx(inode, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_inode_notifysecctx);
+
+int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return security_ops->inode_setsecctx(dentry, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_inode_setsecctx);
+
+int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ return security_ops->inode_getsecctx(inode, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_inode_getsecctx);
+
#ifdef CONFIG_SECURITY_NETWORK

int security_unix_stream_connect(struct socket *sock, struct socket *other,
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f85597a..7ed038c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5399,6 +5399,28 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
kfree(secdata);
}

+/*
+ * called with inode->i_mutex locked
+ */
+static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+{
+ return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0);
+}
+
+/*
+ * called with inode->i_mutex locked
+ */
+static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+{
+ return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0);
+}
+
+static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+{
+ *ctxlen = selinux_inode_getsecurity(inode, XATTR_SELINUX_SUFFIX,
+ ctx, true);
+ return *ctxlen;
+}
#ifdef CONFIG_KEYS

static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
@@ -5605,6 +5627,9 @@ static struct security_operations selinux_ops = {
.secid_to_secctx = selinux_secid_to_secctx,
.secctx_to_secid = selinux_secctx_to_secid,
.release_secctx = selinux_release_secctx,
+ .inode_notifysecctx = selinux_inode_notifysecctx,
+ .inode_setsecctx = selinux_inode_setsecctx,
+ .inode_getsecctx = selinux_inode_getsecctx,

.unix_stream_connect = selinux_socket_unix_stream_connect,
.unix_may_send = selinux_socket_unix_may_send,
--
1.5.5.1

2008-12-01 16:44:45

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 04/14] Security: Add hook to calculate context based on a negative dentry.

There is a time where we need to calculate a context without the
inode having been created yet. To do this we take the negative dentry and
calculate a context based on the process and the parent directory contexts.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
include/linux/security.h | 22 ++++++++++++++++++++++
security/capability.c | 6 ++++++
security/security.c | 7 +++++++
security/selinux/hooks.c | 31 +++++++++++++++++++++++++++++++
4 files changed, 66 insertions(+), 0 deletions(-)

diff --git a/include/linux/security.h b/include/linux/security.h
index 7229ef1..ccbfb06 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -301,6 +301,14 @@ 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.
+ * @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.
*
@@ -1384,6 +1392,9 @@ struct security_operations {
void (*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,
+ void **ctx, u32 *ctxlen);
+

int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
@@ -1652,6 +1663,8 @@ int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *o
void 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,
+ void **ctx, u32 *ctxlen);

int security_inode_alloc(struct inode *inode);
void security_inode_free(struct inode *inode);
@@ -2000,6 +2013,15 @@ 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,
+ void **ctx,
+ u32 *ctxlen)
+{
+ return -EOPNOTSUPP;
+}
+
+
static inline int security_inode_init_security(struct inode *inode,
struct inode *dir,
char **name,
diff --git a/security/capability.c b/security/capability.c
index 27985c5..6d38303 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -139,6 +139,11 @@ 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, void **ctx, u32 *ctxlen)
+{
+ return 0;
+}
+
static int cap_inode_alloc_security(struct inode *inode)
{
return 0;
@@ -863,6 +868,7 @@ void 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);
diff --git a/security/security.c b/security/security.c
index 1d8d3b0..ab978bf 100644
--- a/security/security.c
+++ b/security/security.c
@@ -358,6 +358,13 @@ void security_inode_free(struct inode *inode)
security_ops->inode_free_security(inode);
}

+int security_dentry_init_security (struct dentry *dentry, int mode,
+ void **ctx, u32 *ctxlen)
+{
+ return security_ops->dentry_init_security (dentry, mode, ctx, ctxlen);
+}
+EXPORT_SYMBOL(security_dentry_init_security);
+
int security_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len)
{
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 7ed038c..dcd6d50 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2502,6 +2502,36 @@ static void selinux_inode_free_security(struct inode *inode)
inode_free_security(inode);
}

+static int selinux_dentry_init_security(struct dentry *dentry, int mode, void **ctx, u32 *ctxlen)
+{
+ 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 = current->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),
+ &newsid);
+ if (rc) {
+ printk(KERN_WARNING "%s: "
+ "security_transition_sid failed, rc=%d\n",
+ __FUNCTION__, -rc);
+ return rc;
+ }
+ }
+
+ return security_sid_to_context(newsid, (char **)ctx, ctxlen);
+}
+
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value,
size_t *len)
@@ -5527,6 +5557,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,
--
1.5.5.1

2008-12-01 16:45:42

by David P. Quigley

[permalink] [raw]
Subject: [PATCH 10/14] NFS: Introduce lifecycle management for label attribute.

Two fields have been added to the nfs_fattr structure to carry the security
label and its length. This has raised the need to provide lifecycle management
for these values. This patch introduces two macros nfs_fattr_alloc and
nfs_fattr_fini which are used to allocate and destroy these fields inside the
nfs_fattr structure. These macros do not modify any other components of the
structure so nfs_fattr_init still has to be used on these structures. In the
event that CONFIG_SECURITY is not set these calls should compile away.

Signed-off-by: Matthew N. Dodd <[email protected]>
Signed-off-by: David P. Quigley <[email protected]>
---
fs/nfs/client.c | 16 ++++++
fs/nfs/dir.c | 32 ++++++++++-
fs/nfs/getroot.c | 44 +++++++++++++++-
fs/nfs/inode.c | 20 +++++++-
fs/nfs/namespace.c | 3 +
fs/nfs/nfs3proc.c | 7 +++
fs/nfs/nfs4proc.c | 138 +++++++++++++++++++++++++++++++++++++++++++++---
fs/nfs/proc.c | 12 ++++-
fs/nfs/super.c | 4 ++
fs/nfs/unlink.c | 12 +++-
include/linux/nfs_fs.h | 24 ++++++++
11 files changed, 296 insertions(+), 16 deletions(-)

diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 7547600..3c4a4cc 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -901,6 +901,8 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
struct nfs_fattr fattr;
int error;

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
@@ -951,10 +953,12 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
spin_unlock(&nfs_client_lock);

server->mount_time = jiffies;
+ nfs_fattr_fini(&fattr);
return server;

error:
nfs_free_server(server);
+ nfs_fattr_fini(&fattr);
return ERR_PTR(error);
}

@@ -1108,6 +1112,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,

dprintk("--> nfs4_create_server()\n");

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
@@ -1148,11 +1154,13 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
spin_unlock(&nfs_client_lock);

server->mount_time = jiffies;
+ nfs_fattr_fini(&fattr);
dprintk("<-- nfs4_create_server() = %p\n", server);
return server;

error:
nfs_free_server(server);
+ nfs_fattr_fini(&fattr);
dprintk("<-- nfs4_create_server() = error %d\n", error);
return ERR_PTR(error);
}
@@ -1170,6 +1178,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,

dprintk("--> nfs4_create_referral_server()\n");

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
@@ -1226,10 +1236,12 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
server->mount_time = jiffies;

dprintk("<-- nfs_create_referral_server() = %p\n", server);
+ nfs_fattr_fini(&fattr);
return server;

error:
nfs_free_server(server);
+ nfs_fattr_fini(&fattr);
dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
return ERR_PTR(error);
}
@@ -1251,6 +1263,8 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
(unsigned long long) fattr->fsid.major,
(unsigned long long) fattr->fsid.minor);

+ memset(&fattr_fsinfo, 0, sizeof(struct nfs_fattr));
+
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
@@ -1293,11 +1307,13 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,

server->mount_time = jiffies;

+ nfs_fattr_fini(&fattr_fsinfo);
dprintk("<-- nfs_clone_server() = %p\n", server);
return server;

out_free_server:
nfs_free_server(server);
+ nfs_fattr_fini(&fattr_fsinfo);
dprintk("<-- nfs_clone_server() = error %d\n", error);
return ERR_PTR(error);
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 3e64b98..8855b01 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -557,6 +557,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
my_entry.eof = 0;
my_entry.fh = &fh;
my_entry.fattr = &fattr;
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
nfs_fattr_init(&fattr);
desc->entry = &my_entry;

@@ -594,6 +595,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
res = 0;
break;
}
+ nfs_fattr_fini(&fattr);
}
out:
nfs_unblock_sillyrename(dentry);
@@ -777,10 +779,12 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
struct inode *dir;
struct inode *inode;
struct dentry *parent;
- int error;
+ int error = 0;
struct nfs_fh fhandle;
struct nfs_fattr fattr;

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
parent = dget_parent(dentry);
dir = parent->d_inode;
nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
@@ -809,6 +813,13 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
if (NFS_STALE(inode))
goto out_bad;

+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL))
+ error = nfs_fattr_alloc(&fattr, GFP_NOWAIT);
+ if (error < 0)
+ goto out_bad;
+#endif
+
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error)
goto out_bad;
@@ -820,6 +831,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_valid:
dput(parent);
+ nfs_fattr_fini(&fattr);
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
__func__, dentry->d_parent->d_name.name,
dentry->d_name.name);
@@ -838,6 +850,7 @@ out_zap_parent:
}
d_drop(dentry);
dput(parent);
+ nfs_fattr_fini(&fattr);
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n",
__func__, dentry->d_parent->d_name.name,
dentry->d_name.name);
@@ -906,7 +919,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
struct dentry *res;
struct dentry *parent;
struct inode *inode = NULL;
- int error;
+ int error = 0;
struct nfs_fh fhandle;
struct nfs_fattr fattr;

@@ -914,6 +927,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
dentry->d_parent->d_name.name, dentry->d_name.name);
nfs_inc_stats(dir, NFSIOS_VFSLOOKUP);

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
res = ERR_PTR(-ENAMETOOLONG);
if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
goto out;
@@ -931,6 +946,13 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
goto out;
}

+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL))
+ error = nfs_fattr_alloc(&fattr, GFP_NOWAIT);
+ if (error < 0)
+ goto out;
+#endif
+
parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */
nfs_block_sillyrename(parent);
@@ -957,6 +979,8 @@ no_entry:
out_unblock_sillyrename:
nfs_unblock_sillyrename(parent);
out:
+ /* Label will give 'unused' warning on 'no_entry' case. */
+ nfs_fattr_fini(&fattr);
return res;
}

@@ -1222,6 +1246,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
dfprintk(VFS, "NFS: create(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);

+ memset(&attr, 0, sizeof(struct iattr));
attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE;

@@ -1252,6 +1277,7 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
if (!new_valid_dev(rdev))
return -EINVAL;

+ memset(&attr, 0, sizeof(struct iattr));
attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE;

@@ -1275,6 +1301,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
dfprintk(VFS, "NFS: mkdir(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);

+ memset(&attr, 0, sizeof(struct iattr));
attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode | S_IFDIR;

@@ -1484,6 +1511,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
if (pathlen > PAGE_SIZE)
return -ENAMETOOLONG;

+ memset(&attr, 0, sizeof(struct iattr));
attr.ia_mode = S_IFLNK | S_IRWXUGO;
attr.ia_valid = ATTR_MODE;

diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
index b7c9b2d..a8a922d 100644
--- a/fs/nfs/getroot.c
+++ b/fs/nfs/getroot.c
@@ -31,7 +31,6 @@
#include <linux/vfs.h>
#include <linux/namei.h>
#include <linux/mnt_namespace.h>
-#include <linux/security.h>

#include <asm/system.h>
#include <asm/uaccess.h>
@@ -84,6 +83,8 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
struct inode *inode;
int error;

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
/* get the actual root for this mount */
fsinfo.fattr = &fattr;

@@ -118,6 +119,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
if (!mntroot->d_op)
mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;

+ nfs_fattr_fini(&fattr);
return mntroot;
}

@@ -142,6 +144,14 @@ int nfs4_path_walk(struct nfs_server *server,

dprintk("--> nfs4_path_walk(,,%s)\n", path);

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ /* Unconditional, no server caps yet. */
+ ret = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+#endif
+
fsinfo.fattr = &fattr;
nfs_fattr_init(&fattr);

@@ -153,12 +163,14 @@ int nfs4_path_walk(struct nfs_server *server,
ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
if (ret < 0) {
dprintk("nfs4_get_root: getroot error = %d\n", -ret);
+ nfs_fattr_fini(&fattr);
return ret;
}

if (fattr.type != NFDIR) {
printk(KERN_ERR "nfs4_get_root:"
" getroot encountered non-directory\n");
+ nfs_fattr_fini(&fattr);
return -ENOTDIR;
}

@@ -166,6 +178,7 @@ int nfs4_path_walk(struct nfs_server *server,
if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
printk(KERN_ERR "nfs4_get_root:"
" getroot obtained referral\n");
+ nfs_fattr_fini(&fattr);
return -EREMOTE;
}

@@ -198,6 +211,7 @@ eat_dot_dir:
) {
printk(KERN_ERR "nfs4_get_root:"
" Mount path contains reference to \"..\"\n");
+ nfs_fattr_fini(&fattr);
return -EINVAL;
}

@@ -206,16 +220,27 @@ eat_dot_dir:

dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path);

+ nfs_fattr_fini(&fattr);
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ ret = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+#endif
+
ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name,
mntfh, &fattr);
if (ret < 0) {
dprintk("nfs4_get_root: getroot error = %d\n", -ret);
+ nfs_fattr_fini(&fattr);
return ret;
}

if (fattr.type != NFDIR) {
printk(KERN_ERR "nfs4_get_root:"
" lookupfh encountered non-directory\n");
+ nfs_fattr_fini(&fattr);
return -ENOTDIR;
}

@@ -223,6 +248,7 @@ eat_dot_dir:
if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
printk(KERN_ERR "nfs4_get_root:"
" lookupfh obtained referral\n");
+ nfs_fattr_fini(&fattr);
return -EREMOTE;
}

@@ -230,6 +256,7 @@ eat_dot_dir:

path_walk_complete:
memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
+ nfs_fattr_fini(&fattr);
dprintk("<-- nfs4_path_walk() = 0\n");
return 0;
}
@@ -255,19 +282,34 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
return ERR_PTR(error);
}

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ error = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (error < 0) {
+ dprintk("nfs_get_root: nfs_fattr_alloc error = %d\n",
+ error);
+ return ERR_PTR(error);
+ }
+#endif
+
/* get the actual root for this mount */
error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
if (error < 0) {
+ nfs_fattr_fini(&fattr);
dprintk("nfs_get_root: getattr error = %d\n", -error);
return ERR_PTR(error);
}

inode = nfs_fhget(sb, mntfh, &fattr);
if (IS_ERR(inode)) {
+ nfs_fattr_fini(&fattr);
dprintk("nfs_get_root: get root inode failed\n");
return ERR_CAST(inode);
}

+ nfs_fattr_fini(&fattr);
+
error = nfs_superblock_set_dummy_root(sb, inode);
if (error != 0)
return ERR_PTR(error);
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index d22eb38..33ae87b 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -351,7 +351,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct nfs_fattr fattr;
- int error;
+ int error = 0;

nfs_inc_stats(inode, NFSIOS_VFSSETATTR);

@@ -359,6 +359,14 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
attr->ia_valid &= ~ATTR_MODE;

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+ error = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (error < 0)
+ return error;
+#endif
+
if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
attr->ia_valid &= ~ATTR_SIZE;
@@ -382,6 +390,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
if (error == 0)
nfs_refresh_inode(inode, &fattr);
+ nfs_fattr_fini(&fattr);
return error;
}

@@ -674,6 +683,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out;

nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
+
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+ status = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+#endif
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
if (status != 0) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
@@ -703,6 +720,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
(long long)NFS_FILEID(inode));

out:
+ nfs_fattr_fini(&fattr);
return status;
}

diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 64a288e..6ca294a 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -109,6 +109,8 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
if (IS_ROOT(dentry))
goto out_err;

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
dprintk("%s: enter\n", __func__);
dput(nd->path.dentry);
nd->path.dentry = dget(dentry);
@@ -145,6 +147,7 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
nd->path.dentry = dget(mnt->mnt_root);
schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout);
out:
+ nfs_fattr_fini(&fattr);
dprintk("%s: done, returned %d\n", __func__, err);

dprintk("<-- nfs_follow_mountpoint() = %d\n", err);
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index c55be7a..fd86215 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -294,6 +294,9 @@ static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_

static void nfs3_free_createdata(struct nfs3_createdata *data)
{
+
+ nfs_fattr_fini(data->res.fattr);
+ nfs_fattr_fini(data->res.dir_attr);
kfree(data);
}

@@ -420,6 +423,7 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
return 0;
res = task->tk_msg.rpc_resp;
nfs_post_op_update_inode(dir, &res->dir_attr);
+ nfs_fattr_fini(&res->dir_attr);
return 1;
}

@@ -618,6 +622,9 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
dprintk("NFS call readdir%s %d\n",
plus? "plus" : "", (unsigned int) cookie);

+
+ memset(&dir_attr, 0, sizeof(struct nfs_fattr));
+
nfs_fattr_init(&dir_attr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 83e700a..3a0d25f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -243,6 +243,8 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
p->o_res.seqid = p->o_arg.seqid;
p->c_res.seqid = p->c_arg.seqid;
p->o_res.server = p->o_arg.server;
+ memset(&p->f_attr, 0, sizeof(struct nfs_fattr));
+ memset(&p->dir_attr, 0, sizeof(struct nfs_fattr));
nfs_fattr_init(&p->f_attr);
nfs_fattr_init(&p->dir_attr);
}
@@ -288,6 +290,17 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
p->c_arg.seqid = p->o_arg.seqid;
nfs4_init_opendata_res(p);
kref_init(&p->kref);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL) {
+ if (nfs_fattr_alloc(&p->f_attr, GFP_KERNEL) < 0)
+ goto err_free;
+ if (nfs_fattr_alloc(&p->dir_attr, GFP_KERNEL) < 0) {
+ nfs_fattr_fini(&p->f_attr);
+ goto err_free;
+ }
+ }
+#endif
+
return p;
err_free:
kfree(p);
@@ -304,6 +317,8 @@ static void nfs4_opendata_free(struct kref *kref)
nfs_free_seqid(p->o_arg.seqid);
if (p->state != NULL)
nfs4_put_open_state(p->state);
+ nfs_fattr_fini(&p->f_attr);
+ nfs_fattr_fini(&p->dir_attr);
nfs4_put_state_owner(p->owner);
dput(p->dir);
path_put(&p->path);
@@ -1210,6 +1225,7 @@ static void nfs4_free_closedata(void *data)
nfs_free_seqid(calldata->arg.seqid);
nfs4_put_state_owner(sp);
path_put(&calldata->path);
+ nfs_fattr_fini(&calldata->fattr);
kfree(calldata);
}

@@ -1317,9 +1333,15 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
};
int status = -ENOMEM;

- calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
+ calldata = kzalloc(sizeof(*calldata), GFP_KERNEL);
if (calldata == NULL)
goto out;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ status = nfs_fattr_alloc(&calldata->fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+#endif
calldata->inode = state->inode;
calldata->state = state;
calldata->arg.fh = NFS_FH(state->inode);
@@ -1347,6 +1369,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
rpc_put_task(task);
return status;
out_free_calldata:
+ nfs_fattr_fini(&calldata->fattr);
kfree(calldata);
out:
nfs4_put_open_state(state);
@@ -1762,7 +1785,9 @@ 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;
+
+ memset(&fattr, 0, sizeof(struct nfs_fattr));

/*
* Determine which access bits we want to ask for...
@@ -1780,6 +1805,12 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
if (mode & MAY_EXEC)
args.access |= NFS4_ACCESS_EXECUTE;
}
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ status = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (status < 0)
+ return status;
+#endif
nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (!status) {
@@ -1792,6 +1823,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
entry->mask |= MAY_EXEC;
nfs_refresh_inode(inode, &fattr);
}
+ nfs_fattr_fini(&fattr);
return status;
}

@@ -1904,10 +1936,20 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
if (flags & O_EXCL) {
struct nfs_fattr fattr;
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ //XXX: Should we d_drop the dentry?
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+ if (nfs_server_capable(state->inode, NFS_CAP_SECURITY_LABEL))
+ status = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+#endif
status = nfs4_do_setattr(state->inode, cred, &fattr, sattr, state);
if (status == 0)
nfs_setattr_update_inode(state->inode, sattr);
nfs_post_op_update_inode(state->inode, &fattr);
+ nfs_fattr_fini(&fattr);
}
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
status = nfs4_intent_set_file(nd, &path, state);
@@ -1936,14 +1978,22 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
.rpc_argp = &args,
.rpc_resp = &res,
};
- int status;
+ int status = 0;

+ memset(&res.dir_attr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ status = nfs_fattr_alloc(&res.dir_attr, GFP_KERNEL);
+ if (status < 0)
+ return status;
+#endif
nfs_fattr_init(&res.dir_attr);
status = rpc_call_sync(server->client, &msg, 0);
if (status == 0) {
update_changeattr(dir, &res.cinfo);
nfs_post_op_update_inode(dir, &res.dir_attr);
}
+ nfs_fattr_fini(&res.dir_attr);
return status;
}

@@ -1968,6 +2018,13 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
args->bitmask = server->attr_bitmask;
res->server = server;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
+
+ memset(&res->dir_attr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ nfs_fattr_alloc(&res->dir_attr, GFP_KERNEL);
+#endif
+ nfs_fattr_init(&res->dir_attr);
}

static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
@@ -1978,6 +2035,7 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
return 0;
update_changeattr(dir, &res->cinfo);
nfs_post_op_update_inode(dir, &res->dir_attr);
+ nfs_fattr_fini(&res->dir_attr);
return 1;
}

@@ -2003,8 +2061,21 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
.rpc_argp = &arg,
.rpc_resp = &res,
};
- int status;
-
+ int status = 0;
+
+ memset(&old_fattr, 0, sizeof(struct nfs_fattr));
+ memset(&new_fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL) {
+ status = nfs_fattr_alloc(&old_fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+ status = nfs_fattr_alloc(&new_fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+ }
+#endif
+
nfs_fattr_init(res.old_fattr);
nfs_fattr_init(res.new_fattr);
status = rpc_call_sync(server->client, &msg, 0);
@@ -2015,6 +2086,9 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
update_changeattr(new_dir, &res.new_cinfo);
nfs_post_op_update_inode(new_dir, res.new_fattr);
}
+out:
+ nfs_fattr_fini(&old_fattr);
+ nfs_fattr_fini(&new_fattr);
return status;
}

@@ -2052,7 +2126,20 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
.rpc_argp = &arg,
.rpc_resp = &res,
};
- int status;
+ int status = 0;
+
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+ memset(&dir_attr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL) {
+ status = nfs_fattr_alloc(&fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+ status = nfs_fattr_alloc(&dir_attr, GFP_KERNEL);
+ if (status < 0)
+ goto out;
+ }
+#endif

nfs_fattr_init(res.fattr);
nfs_fattr_init(res.dir_attr);
@@ -2062,7 +2149,9 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
nfs_post_op_update_inode(dir, res.dir_attr);
nfs_post_op_update_inode(inode, res.fattr);
}
-
+out:
+ nfs_fattr_fini(&fattr);
+ nfs_fattr_fini(&dir_attr);
return status;
}

@@ -2091,6 +2180,7 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
struct qstr *name, struct iattr *sattr, u32 ftype)
{
struct nfs4_createdata *data;
+ int status;

data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data != NULL) {
@@ -2109,10 +2199,27 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
data->res.fh = &data->fh;
data->res.fattr = &data->fattr;
data->res.dir_fattr = &data->dir_fattr;
+ memset(&data->fattr, 0, sizeof(struct nfs_fattr));
+ memset(&data->dir_fattr, 0, sizeof(struct nfs_fattr));
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL) {
+ status = nfs_fattr_alloc(&data->fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out_free;
+ status = nfs_fattr_alloc(&data->dir_fattr, GFP_KERNEL);
+ if (status < 0) {
+ nfs_fattr_fini(&data->fattr);
+ goto out_free;
+ }
+ }
+#endif
nfs_fattr_init(data->res.fattr);
nfs_fattr_init(data->res.dir_fattr);
}
return data;
+out_free:
+ kfree(data);
+ return NULL;
}

static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
@@ -2128,6 +2235,8 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_

static void nfs4_free_createdata(struct nfs4_createdata *data)
{
+ nfs_fattr_fini(&data->fattr);
+ nfs_fattr_fini(&data->dir_fattr);
kfree(data);
}

@@ -2960,6 +3069,9 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)

static void nfs4_delegreturn_release(void *calldata)
{
+ struct nfs4_delegreturndata *data = calldata;
+
+ nfs_fattr_fini(data->res.fattr);
kfree(calldata);
}

@@ -2985,7 +3097,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
};
int status = 0;

- data = kmalloc(sizeof(*data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
data->args.fhandle = &data->fh;
@@ -2999,6 +3111,13 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
data->timestamp = jiffies;
data->rpc_status = 0;

+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (server->caps & NFS_CAP_SECURITY_LABEL)
+ status = nfs_fattr_alloc(&data->fattr, GFP_KERNEL);
+ if (status < 0)
+ goto out_free;
+#endif
+
task_setup_data.callback_data = data;
msg.rpc_argp = &data->args,
msg.rpc_resp = &data->res,
@@ -3017,6 +3136,9 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
out:
rpc_put_task(task);
return status;
+out_free:
+ kfree(data);
+ return status;
}

int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 1934652..9b0e36e 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -216,12 +216,14 @@ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
};
int status;

- nfs_fattr_init(&fattr);
dprintk("NFS call create %s\n", dentry->d_name.name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+ nfs_fattr_init(&fattr);
nfs_mark_for_revalidate(dir);
if (status == 0)
status = nfs_instantiate(dentry, &fhandle, &fattr);
+ nfs_fattr_fini(&fattr);
dprintk("NFS reply create: %d\n", status);
return status;
}
@@ -263,6 +265,7 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
sattr->ia_size = new_encode_dev(rdev);/* get out your barf bag */
}

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir);
@@ -274,6 +277,7 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
}
if (status == 0)
status = nfs_instantiate(dentry, &fhandle, &fattr);
+ nfs_fattr_fini(&fattr);
dprintk("NFS reply mknod: %d\n", status);
return status;
}
@@ -386,6 +390,8 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,

dprintk("NFS call symlink %s\n", dentry->d_name.name);

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir);

@@ -400,6 +406,7 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
status = nfs_instantiate(dentry, &fhandle, &fattr);
}

+ nfs_fattr_fini(&fattr);
dprintk("NFS reply symlink: %d\n", status);
return status;
}
@@ -427,11 +434,14 @@ nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
int status;

dprintk("NFS call mkdir %s\n", dentry->d_name.name);
+
+ memset(&fattr, 0, sizeof(struct nfs_fattr));
nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir);
if (status == 0)
status = nfs_instantiate(dentry, &fhandle, &fattr);
+ nfs_fattr_fini(&fattr);
dprintk("NFS reply mkdir: %d\n", status);
return status;
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 9b89a4b..ab071e1 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -388,6 +388,8 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
};
int error;

+ memset(&fattr, 0, sizeof(struct nfs_fattr));
+
error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
if (error < 0)
goto out_err;
@@ -419,10 +421,12 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)

buf->f_namelen = server->namelen;

+ nfs_fattr_fini(&fattr);
return 0;

out_err:
dprintk("%s: statfs error = %d\n", __func__, -error);
+ nfs_fattr_fini(&fattr);
return error;
}

diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index ecc2953..2bdcc76 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -123,11 +123,10 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n
};
struct rpc_task *task;
struct dentry *alias;
+ int ret = 0;

alias = d_lookup(parent, &data->args.name);
if (alias != NULL) {
- int ret = 0;
-
/*
* Hey, we raced with lookup... See if we need to transfer
* the sillyrename information to the aliased dentry.
@@ -150,9 +149,16 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n
nfs_dec_sillycount(dir);
return 0;
}
+ memset(&data->res.dir_attr, 0, sizeof(struct nfs_fattr));
+ nfs_fattr_init(&data->res.dir_attr);
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ if (NFS_SERVER(dir)->caps & NFS_CAP_SECURITY_LABEL)
+ ret = nfs_fattr_alloc(&data->res.dir_attr, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+#endif
nfs_sb_active(dir->i_sb);
data->args.fh = NFS_FH(dir);
- nfs_fattr_init(&data->res.dir_attr);

NFS_PROTO(dir)->unlink_setup(&msg, dir);

diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 4eaa834..6120a28 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -59,6 +59,7 @@
#include <linux/nfs_fs_sb.h>

#include <linux/mempool.h>
+#include <linux/security.h>

/*
* These are the default flags for swap requests
@@ -350,6 +351,29 @@ extern void nfs_fattr_init(struct nfs_fattr *fattr);
extern __be32 root_nfs_parse_addr(char *name); /*__init*/
extern unsigned long nfs_inc_attr_generation_counter(void);

+#ifdef CONFIG_SECURITY
+
+static inline int nfs_fattr_alloc(struct nfs_fattr *fattr, gfp_t flags)
+{
+ fattr->label = kzalloc(NFS4_MAXLABELLEN, flags);
+ if (fattr->label == NULL)
+ return -ENOMEM;
+ fattr->label_len = NFS4_MAXLABELLEN;
+ return 0;
+}
+
+static inline void nfs_fattr_fini(struct nfs_fattr *fattr)
+{
+ security_release_secctx(fattr->label, fattr->label_len);
+ fattr->label = NULL;
+ fattr->label_len = 0;
+}
+
+#else
+static inline int nfs_fattr_alloc(struct nfs_fattr *fattr, gfp_t flags) {}
+static inline void nfs_fattr_fini(struct nfs_fattr *fattr) {}
+#endif
+
/*
* linux/fs/nfs/file.c
*/
--
1.5.5.1

2008-12-05 02:39:23

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Wed, 26 Nov 2008, David P. Quigley wrote:

> --- a/include/linux/nfs_xdr.h
> +++ b/include/linux/nfs_xdr.h
> @@ -57,6 +57,10 @@ struct nfs_fattr {
> __u64 pre_change_attr;/* pre-op NFSv4 change attribute */
> unsigned long time_start;
> unsigned long gencount;
> +#ifdef CONFIG_SECURITY
> + void *label;
> + __u32 label_len;
> +#endif

Should this be CONFIG_NFS_V4_SECURITY_LABEL ?


--
James Morris
<[email protected]>

2008-12-05 04:48:21

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 09/14] NFS: Add security_label text mount option and handling code to NFS

On Wed, 26 Nov 2008, David P. Quigley wrote:

> + { Opt_security_label, "security_label" },
> + { Opt_nosecurity_label, "nosecurity_label" },

"seclabel" might be easier for admins to type.


--
James Morris
<[email protected]>

2008-12-05 05:45:46

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 10/14] NFS: Introduce lifecycle management for label attribute.

On Wed, 26 Nov 2008, David P. Quigley wrote:

> +#ifdef CONFIG_NFS_V4_SECURITY_LABEL
> + if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL))
> + error = nfs_fattr_alloc(&fattr, GFP_NOWAIT);
> + if (error < 0)
> + goto out_bad;
> +#endif

Why GFP_NOWAIT ?

--
James Morris
<[email protected]>

2008-12-05 09:39:55

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 12/14] NFS: Client implementation of Labeled-NFS

On Wed, 26 Nov 2008, David P. Quigley wrote:

> +#ifdef CONFIG_NFS_V4_SECURITY_LABEL
> + if (((nd->flags & LOOKUP_CREATE) != 0) &&
> + nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL)) {
> + status = security_dentry_init_security(dentry,
> + sattr->ia_mode, &l.label, &l.len);
> + /* XXX: should this be more fatal? */
> + if (status == 0)
> + label = &l;
> + }
> +#endif

Per the comment, it seems this function should fail and propagate an error
if status != 0.

> + memset(&fattr, 0, sizeof(struct nfs_fattr));
> + nfs_fattr_alloc(&fattr, GFP_KERNEL);

Need to check the return value.

> + nfs_fattr_alloc(&fattr, GFP_KERNEL);

Ditto.

> +static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap, void **ctx, __u32 *ctxlen)
> +{
> + __u32 len;
> + __be32 *p;
> + int rc = 0;
> +
> + if (unlikely(bitmap[1] & (FATTR4_WORD1_SECURITY_LABEL - 1U)))
> + return -EIO;
> + if (likely(bitmap[1] & FATTR4_WORD1_SECURITY_LABEL)) {
> + READ_BUF(4);
> + READ32(len);
> + READ_BUF(len);
> + if (len < XDR_MAX_NETOBJ) {
> + if (*ctx != NULL) {
> + if (*ctxlen < len) {
> + printk(KERN_ERR
> + "%s(): ctxlen %d < len %d\n",
> + __func__, *ctxlen, len);
> + /* rc = -ENOMEM; */
> + *ctx = NULL; /* leak */

Shouldn't the function stop processing and return an error?

> + } else {
> + memcpy(*ctx, p, len);
> + }
> + }
> + *ctxlen = len;
> + } else
> + printk(KERN_WARNING "%s: label too long (%u)!\n",
> + __FUNCTION__, len);
> + bitmap[1] &= ~FATTR4_WORD1_SECURITY_LABEL;
> + }
> + return rc;
> +}
> +



--
James Morris
<[email protected]>

2008-12-05 09:59:18

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 03/14] LSM/SELinux: inode_{get,set,notify}secctx hooks to access LSM security context information.

On Wed, 26 Nov 2008, David P. Quigley wrote:

> + * @inode_getsecctx:
> + * Returns a string containing all relavent security context information
> + *
> + * @inode we wish to set the security context of.
> + * @ctx is a pointer in which to place the allocated security context.
> + * @ctxlen points to the place to put the length of @ctx.
> * This is the main security structure.
> */
> struct security_operations {
> @@ -1479,6 +1514,10 @@ struct security_operations {
> int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
> void (*release_secctx) (char *secdata, u32 seclen);
>
> + int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
> + int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
> + int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);

For inode_getsecctx(), you're returning the length via the return value,
so you should not also need to pass in a pointer to ctxlen, right?

IMHO, it's clearer and simpler to always only return error status from
these kinds of functions, and to pass things like size back via pointer
args, although it seems that a few mixed return functions have crept in to
the code over time. My preference would be to convert it to return value
is error status only, with the length entirely separate as a pointer arg.


- James
--
James Morris
<[email protected]>

2008-12-05 10:01:22

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 14/14] NFSD: Server implementation of MAC Labeling

On Wed, 26 Nov 2008, David P. Quigley wrote:

> + err = 0;
> + (void)security_inode_getsecctx(dentry->d_inode, &context, &len);
> + if (len < 0)
> + return nfserrno(len);

So, with the simpler version:

err = security_inode_getsecctx(dentry->d_inode, &context, &len);
if (err)
return nfserrno(err);




--
James Morris
<[email protected]>

2008-12-05 15:47:20

by David P. Quigley

[permalink] [raw]
Subject: Re: [PATCH 03/14] LSM/SELinux: inode_{get,set,notify}secctx hooks to access LSM security context information.

On Fri, 2008-12-05 at 20:58 +1100, James Morris wrote:
> On Wed, 26 Nov 2008, David P. Quigley wrote:
>
> > + * @inode_getsecctx:
> > + * Returns a string containing all relavent security context information
> > + *
> > + * @inode we wish to set the security context of.
> > + * @ctx is a pointer in which to place the allocated security context.
> > + * @ctxlen points to the place to put the length of @ctx.
> > * This is the main security structure.
> > */
> > struct security_operations {
> > @@ -1479,6 +1514,10 @@ struct security_operations {
> > int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
> > void (*release_secctx) (char *secdata, u32 seclen);
> >
> > + int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
> > + int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
> > + int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
>
> For inode_getsecctx(), you're returning the length via the return value,
> so you should not also need to pass in a pointer to ctxlen, right?
>
> IMHO, it's clearer and simpler to always only return error status from
> these kinds of functions, and to pass things like size back via pointer
> args, although it seems that a few mixed return functions have crept in to
> the code over time. My preference would be to convert it to return value
> is error status only, with the length entirely separate as a pointer arg.
>
>
> - James

I'll have to look into why we did it this way. The discussion for these
patches happened many months ago so I don't remember why it was done
this way. I remember at the time getting an approval for the approach
but a desire not to merge the patch while there were no users of it.

Dave

2008-12-05 17:55:01

by David P. Quigley

[permalink] [raw]
Subject: Re: [PATCH 03/14] LSM/SELinux: inode_{get,set,notify}secctx hooks to access LSM security context information.

On Fri, 2008-12-05 at 10:25 -0500, David P. Quigley wrote:
> On Fri, 2008-12-05 at 20:58 +1100, James Morris wrote:
> > On Wed, 26 Nov 2008, David P. Quigley wrote:
> >
> > > + * @inode_getsecctx:
> > > + * Returns a string containing all relavent security context information
> > > + *
> > > + * @inode we wish to set the security context of.
> > > + * @ctx is a pointer in which to place the allocated security context.
> > > + * @ctxlen points to the place to put the length of @ctx.
> > > * This is the main security structure.
> > > */
> > > struct security_operations {
> > > @@ -1479,6 +1514,10 @@ struct security_operations {
> > > int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
> > > void (*release_secctx) (char *secdata, u32 seclen);
> > >
> > > + int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
> > > + int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
> > > + int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
> >
> > For inode_getsecctx(), you're returning the length via the return value,
> > so you should not also need to pass in a pointer to ctxlen, right?
> >
> > IMHO, it's clearer and simpler to always only return error status from
> > these kinds of functions, and to pass things like size back via pointer
> > args, although it seems that a few mixed return functions have crept in to
> > the code over time. My preference would be to convert it to return value
> > is error status only, with the length entirely separate as a pointer arg.
> >
> >
> > - James
>
> I'll have to look into why we did it this way. The discussion for these
> patches happened many months ago so I don't remember why it was done
> this way. I remember at the time getting an approval for the approach
> but a desire not to merge the patch while there were no users of it.
>
> Dave
>
>
> --
> This message was distributed to subscribers of the selinux mailing list.
> If you no longer wish to subscribe, send mail to [email protected] with
> the words "unsubscribe selinux" without quotes as the message.

So I can't find why I designed the interface this way and James' request
seems completely reasonable so we will move over to that.

Dave

2008-12-12 21:57:41

by Matthew N. Dodd

[permalink] [raw]
Subject: Re: [PATCH 03/14] LSM/SELinux: inode_{get,set,notify}secctx hooks to access LSM security context information.

David P. Quigley wrote:
> On Fri, 2008-12-05 at 20:58 +1100, James Morris wrote:
>> On Wed, 26 Nov 2008, David P. Quigley wrote:
>>
>>> + * @inode_getsecctx:
>>> + * Returns a string containing all relavent security context information
>>> + *
>>> + * @inode we wish to set the security context of.
>>> + * @ctx is a pointer in which to place the allocated security context.
>>> + * @ctxlen points to the place to put the length of @ctx.
>>> * This is the main security structure.
>>> */
>>> struct security_operations {
>>> @@ -1479,6 +1514,10 @@ struct security_operations {
>>> int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
>>> void (*release_secctx) (char *secdata, u32 seclen);
>>>
>>> + int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
>>> + int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
>>> + int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
>> For inode_getsecctx(), you're returning the length via the return value,
>> so you should not also need to pass in a pointer to ctxlen, right?
>>
>> IMHO, it's clearer and simpler to always only return error status from
>> these kinds of functions, and to pass things like size back via pointer
>> args, although it seems that a few mixed return functions have crept in to
>> the code over time. My preference would be to convert it to return value
>> is error status only, with the length entirely separate as a pointer arg.
>>
>>
>> - James
>
> I'll have to look into why we did it this way. The discussion for these
> patches happened many months ago so I don't remember why it was done
> this way. I remember at the time getting an approval for the approach
> but a desire not to merge the patch while there were no users of it.

I think it was a result of inode_getsecctx() being a wrapper for
selinux_inode_getsecurity() which returns a length.

2008-12-12 22:54:35

by Matthew N. Dodd

[permalink] [raw]
Subject: Re: [Labeled-nfs] [PATCH 09/14] NFS: Add security_label text mount option and handling code to NFS

James Morris wrote:
> On Wed, 26 Nov 2008, David P. Quigley wrote:
>
>> + { Opt_security_label, "security_label" },
>> + { Opt_nosecurity_label, "nosecurity_label" },
>
> "seclabel" might be easier for admins to type.

Maybe, though the option is on by default on the client (that is, if the
server supports security_labels then the client will use it, unless
instructed not to.)

To explain the choice in a longer name, I've got an RPC auth flavor for
demonstrating client process label transport called 'auth_seclabel' that
is turned on in /etc/exports via sec=seclabel.

I'll make this change if you feel strongly about it though. :)

2009-04-03 08:33:36

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Wed, 26 Nov 2008, David P. Quigley wrote:

> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> index ea03667..144eacf 100644
> --- a/include/linux/nfs4.h
> +++ b/include/linux/nfs4.h
> @@ -21,6 +21,7 @@
> #define NFS4_FHSIZE 128
> #define NFS4_MAXPATHLEN PATH_MAX
> #define NFS4_MAXNAMLEN NAME_MAX
> +#define NFS4_MAXLABELLEN 4096

I can't recall if this has been discussed before, but why is the label
length limited to this value?

SELinux on-disk labels can be up to 64KB in size (XATTR_SIZE_MAX), and I'd
like to ensure that we don't end up with an unnecessary disk vs. network
label size incompatibility.

While it seems unlikely that SELinux (and other forms of MAC) security
labels would currently exceed 4K, we don't know how SELinux might be
extended in the future, and should avoid limiting label flexibility
beyond existing constraints.


- James
--
James Morris
<[email protected]>

2009-04-03 10:04:32

by David P. Quigley

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Fri, 2009-04-03 at 19:31 +1100, James Morris wrote:
> On Wed, 26 Nov 2008, David P. Quigley wrote:
>
> > diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> > index ea03667..144eacf 100644
> > --- a/include/linux/nfs4.h
> > +++ b/include/linux/nfs4.h
> > @@ -21,6 +21,7 @@
> > #define NFS4_FHSIZE 128
> > #define NFS4_MAXPATHLEN PATH_MAX
> > #define NFS4_MAXNAMLEN NAME_MAX
> > +#define NFS4_MAXLABELLEN 4096
>
> I can't recall if this has been discussed before, but why is the label
> length limited to this value?
>
> SELinux on-disk labels can be up to 64KB in size (XATTR_SIZE_MAX), and I'd
> like to ensure that we don't end up with an unnecessary disk vs. network
> label size incompatibility.
>
> While it seems unlikely that SELinux (and other forms of MAC) security
> labels would currently exceed 4K, we don't know how SELinux might be
> extended in the future, and should avoid limiting label flexibility
> beyond existing constraints.
>
>
> - James

We tried to change this to be dynamically allocated based on what was
coming off of the wire but we ran into a problem that it required us to
do allocations where they really shouldn't be done in the rpc/nfsv4
code. Trond suggested to make this static and that if someone really
needed more than a page for their label that something was horrifically
wrong. I'm tempted to agree with him on this but there are people trying
to send contexts with an MLS component with every other compartment set
which tend to be really large.

Dave

2009-04-03 11:33:56

by David P. Quigley

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Fri, 2009-04-03 at 19:31 +1100, James Morris wrote:
> On Wed, 26 Nov 2008, David P. Quigley wrote:
>
> > diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> > index ea03667..144eacf 100644
> > --- a/include/linux/nfs4.h
> > +++ b/include/linux/nfs4.h
> > @@ -21,6 +21,7 @@
> > #define NFS4_FHSIZE 128
> > #define NFS4_MAXPATHLEN PATH_MAX
> > #define NFS4_MAXNAMLEN NAME_MAX
> > +#define NFS4_MAXLABELLEN 4096
>
> I can't recall if this has been discussed before, but why is the label
> length limited to this value?
>
> SELinux on-disk labels can be up to 64KB in size (XATTR_SIZE_MAX), and I'd
> like to ensure that we don't end up with an unnecessary disk vs. network
> label size incompatibility.
>
> While it seems unlikely that SELinux (and other forms of MAC) security
> labels would currently exceed 4K, we don't know how SELinux might be
> extended in the future, and should avoid limiting label flexibility
> beyond existing constraints.
>
>
> - James

Also there is nothing in the specification that limits it to 4k. This is
a specific implementation detail of the Linux prototype and can be
changed at a later date if necessary without having to modify any
documents.

Dave

2009-04-03 11:44:09

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Fri, 3 Apr 2009, David P. Quigley wrote:

> We tried to change this to be dynamically allocated based on what was
> coming off of the wire but we ran into a problem that it required us to
> do allocations where they really shouldn't be done in the rpc/nfsv4
> code. Trond suggested to make this static and that if someone really
> needed more than a page for their label that something was horrifically
> wrong. I'm tempted to agree with him on this but there are people trying
> to send contexts with an MLS component with every other compartment set
> which tend to be really large.

Well, future labels might include cryptographic information, for example.

--
James Morris
<[email protected]>

2009-04-03 12:28:27

by David P. Quigley

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Fri, 2009-04-03 at 22:43 +1100, James Morris wrote:
> On Fri, 3 Apr 2009, David P. Quigley wrote:
>
> > We tried to change this to be dynamically allocated based on what was
> > coming off of the wire but we ran into a problem that it required us to
> > do allocations where they really shouldn't be done in the rpc/nfsv4
> > code. Trond suggested to make this static and that if someone really
> > needed more than a page for their label that something was horrifically
> > wrong. I'm tempted to agree with him on this but there are people trying
> > to send contexts with an MLS component with every other compartment set
> > which tend to be really large.
>
> Well, future labels might include cryptographic information, for example.
>

<removing people from the CC who probably don't care about this>

Could you expand on why this might be needed or what applications would
use this? It's unclear to me what sort of crypto information would be in
a context. I know the ecryptfs guys were trying to make crypto decisions
based on SELinux context in some cases but I never heard of wanting to
put that kind of information into the context.

Dave

2009-04-05 23:34:06

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 08/14] NFSv4: Add label recommended attribute and NFSv4 flags

On Fri, 3 Apr 2009, David P. Quigley wrote:

> On Fri, 2009-04-03 at 22:43 +1100, James Morris wrote:
> > On Fri, 3 Apr 2009, David P. Quigley wrote:
> >
> > > We tried to change this to be dynamically allocated based on what was
> > > coming off of the wire but we ran into a problem that it required us to
> > > do allocations where they really shouldn't be done in the rpc/nfsv4
> > > code. Trond suggested to make this static and that if someone really
> > > needed more than a page for their label that something was horrifically
> > > wrong. I'm tempted to agree with him on this but there are people trying
> > > to send contexts with an MLS component with every other compartment set
> > > which tend to be really large.
> >
> > Well, future labels might include cryptographic information, for example.
> >
>
> <removing people from the CC who probably don't care about this>
>
> Could you expand on why this might be needed or what applications would
> use this? It's unclear to me what sort of crypto information would be in
> a context. I know the ecryptfs guys were trying to make crypto decisions
> based on SELinux context in some cases but I never heard of wanting to
> put that kind of information into the context.

Potentially as part of a mandatory cryptographic policy, although the
exact form of the labeling is unknown.

But the main point is that we should not needlessly limit the flexibility
of the system.

--
James Morris
<[email protected]>