[PATCH] security: enable atomic inode security labeling

The following patch set enables atomic security labeling of newly created
inodes by altering the fs code to invoke a new LSM hook to obtain the security
attribute to apply to a newly created inode and to set up the incore inode
security state during the inode creation transaction.  This parallels the
existing processing for setting ACLs on newly created inodes.  Otherwise, it
is possible for new inodes to be accessed by another thread via the dcache
prior to complete security setup (presently handled by the
post_create/mkdir/...  LSM hooks in the VFS) and a newly created inode may be
left unlabeled on the disk in the event of a crash.  SELinux presently works
around the issue by ensuring that the incore inode security label is
initialized to a special SID that is inaccessible to unprivileged processes
(in accordance with policy), thereby preventing inappropriate access but
potentially causing false denials on legitimate accesses.  A simple test
program demonstrates such false denials on SELinux, and the patch solves the
problem.  Similar such false denials have been encountered in real
applications.

This patch defines a new inode_init_security LSM hook to obtain the security
attribute to apply to a newly created inode and to set up the incore inode
security state for it, and adds a corresponding hook function implementation
to SELinux.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8641f88..63701fe 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1274,6 +1274,7 @@
 	struct inode *inode;
 	struct inode_security_struct *dsec;
 	struct superblock_security_struct *sbsec;
+	struct inode_security_struct *isec;
 	u32 newsid;
 	char *context;
 	unsigned int len;
@@ -1293,6 +1294,11 @@
 		return 0;
 	}
 
+	isec = inode->i_security;
+
+	if (isec->security_attr_init)
+		return 0;
+
 	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
 		newsid = tsec->create_sid;
 	} else {
@@ -2018,6 +2024,58 @@
 	inode_free_security(inode);
 }
 
+static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
+				       char **name, void **value,
+				       size_t *len)
+{
+	struct task_security_struct *tsec;
+	struct inode_security_struct *dsec;
+	struct superblock_security_struct *sbsec;
+	struct inode_security_struct *isec;
+	u32 newsid;
+	int rc;
+	char *namep, *context;
+
+	tsec = current->security;
+	dsec = dir->i_security;
+	sbsec = dir->i_sb->s_security;
+	isec = inode->i_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(inode->i_mode),
+					     &newsid);
+		if (rc) {
+			printk(KERN_WARNING "%s:  "
+			       "security_transition_sid failed, rc=%d (dev=%s "
+			       "ino=%ld)\n",
+			       __FUNCTION__,
+			       -rc, inode->i_sb->s_id, inode->i_ino);
+			return rc;
+		}
+	}
+
+	inode_security_set_sid(inode, newsid);
+
+	namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_KERNEL);
+	if (!namep)
+		return -ENOMEM;
+	*name = namep;
+
+	rc = security_sid_to_context(newsid, &context, len);
+	if (rc) {
+		kfree(namep);
+		return rc;
+	}
+	*value = context;
+
+	isec->security_attr_init = 1;
+
+	return 0;
+}
+
 static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
 {
 	return may_create(dir, dentry, SECCLASS_FILE);
@@ -4298,6 +4356,7 @@
 
 	.inode_alloc_security =		selinux_inode_alloc_security,
 	.inode_free_security =		selinux_inode_free_security,
+	.inode_init_security =		selinux_inode_init_security,
 	.inode_create =			selinux_inode_create,
 	.inode_post_create =		selinux_inode_post_create,
 	.inode_link =			selinux_inode_link,