[RFC 08/10] selinux: support per-namespace superblock security structures

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Extend the superblock security structure to include a reference
to the associated selinux namespace, and turn it into a list so
that we can maintain per-superblock security state for each namespace.
This is necessary because the superblock SIDs and labeling behavior
are per selinux namespace.  It further enables one to context-mount
a filesystem with a particular context in one namespace while using
xattrs in another, e.g. one might context mount a container filesystem
in the init selinux namespace to provide MCS-style isolation of the
containers while using per-file xattrs within the container to support
conventional SELinux targeted policy.

Introduce a superblock_security() helper to return the superblock
security blob for the current selinux namespace and replace direct uses
of sb->s_security with calls to it.

Also revert the changes made by
commit a64c54cf0811b8032fdab8c9d52576f0370837fa ("SELinux: pass a
superblock to security_fs_use") so that access to the superblock
security structure is properly encapsulated and we can support
per-namespace structures.

This change has similar problems as with the inode security structure
change, see the list of issues in that commit.

Not-signed-off-by: Stephen Smalley <sds@xxxxxxxxxxxxx>
---
 security/selinux/hooks.c            | 109 ++++++++++++++++++++++++++++--------
 security/selinux/include/objsec.h   |   5 +-
 security/selinux/include/security.h |   3 +-
 security/selinux/ss/services.c      |  19 ++++---
 4 files changed, 102 insertions(+), 34 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8a52e71..3daad14 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -478,33 +478,90 @@ static void file_free_security(struct file *file)
 	kmem_cache_free(file_security_cache, fsec);
 }
 
-static int superblock_alloc_security(struct super_block *sb)
+static struct superblock_security_struct *sbsec_alloc(
+	const struct super_block *sb)
 {
 	struct superblock_security_struct *sbsec;
 
-	sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
+	sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_NOFS);
 	if (!sbsec)
-		return -ENOMEM;
+		return NULL;
 
 	mutex_init(&sbsec->lock);
 	INIT_LIST_HEAD(&sbsec->isec_head);
 	spin_lock_init(&sbsec->isec_lock);
-	sbsec->sb = sb;
 	sbsec->sid = SECINITSID_UNLABELED;
 	sbsec->def_sid = SECINITSID_FILE;
 	sbsec->mntpoint_sid = SECINITSID_UNLABELED;
-	sb->s_security = sbsec;
+	sbsec->sb = sb;
+	sbsec->ns = get_selinux_ns(current_selinux_ns);
+	INIT_LIST_HEAD(&sbsec->sbsec_list);
+	return sbsec;
+}
+
+static int superblock_alloc_security(struct super_block *sb)
+{
+	struct superblock_security_struct *sbsec = sbsec_alloc(sb);
+
+	if (!sbsec)
+		return -ENOMEM;
 
+	sb->s_security = sbsec;
 	return 0;
 }
 
 static void superblock_free_security(struct super_block *sb)
 {
-	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *sbsec = sb->s_security, *entry, *tmp;
 	sb->s_security = NULL;
+	put_selinux_ns(sbsec->ns);
+	list_for_each_entry_safe(entry, tmp, &sbsec->sbsec_list, sbsec_list) {
+		put_selinux_ns(entry->ns);
+		kfree(entry);
+	}
 	kfree(sbsec);
 }
 
+static struct superblock_security_struct *superblock_security(
+	const struct super_block *sb)
+{
+	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *cur, *new;
+
+	if (sbsec->ns == current_selinux_ns)
+		return sbsec;
+
+	spin_lock(&sbsec->sbsec_list_lock);
+
+	list_for_each_entry(cur, &sbsec->sbsec_list, sbsec_list) {
+		if (cur->ns == current_selinux_ns)
+			goto out;
+	}
+
+	spin_unlock(&sbsec->sbsec_list_lock);
+
+	new = sbsec_alloc(sb);
+	if (!new) {
+		cur = NULL;
+		goto out;
+	}
+
+	spin_lock(&sbsec->sbsec_list_lock);
+
+	list_for_each_entry(cur, &sbsec->sbsec_list, sbsec_list) {
+		if (cur->ns == current_selinux_ns) {
+			kfree(new);
+			goto out;
+		}
+	}
+
+	list_add(&new->sbsec_list, &sbsec->sbsec_list);
+	cur = new;
+out:
+	spin_unlock(&sbsec->sbsec_list_lock);
+	return cur;
+}
+
 static inline int inode_doinit(struct inode *inode)
 {
 	return inode_doinit_with_dentry(inode, NULL, find_isec(inode, true));
@@ -572,7 +629,7 @@ static int may_context_mount_inode_relabel(u32 sid,
 
 static int selinux_is_sblabel_mnt(struct super_block *sb)
 {
-	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *sbsec = superblock_security(sb);
 
 	return sbsec->behavior == SECURITY_FS_USE_XATTR ||
 		sbsec->behavior == SECURITY_FS_USE_TRANS ||
@@ -591,7 +648,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
 
 static int sb_finish_set_opts(struct super_block *sb)
 {
-	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *sbsec = superblock_security(sb);
 	struct dentry *root = sb->s_root;
 	struct inode *root_inode = d_backing_inode(root);
 	int rc = 0;
@@ -675,7 +732,7 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
 				struct security_mnt_opts *opts)
 {
 	int rc = 0, i;
-	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *sbsec = superblock_security(sb);
 	char *context = NULL;
 	u32 len;
 	char tmp;
@@ -796,7 +853,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 {
 	const struct cred *cred = current_cred();
 	int rc = 0, i;
-	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *sbsec = superblock_security(sb);
 	const char *name = sb->s_type->name;
 	struct dentry *root = sbsec->sb->s_root;
 	struct inode_security_struct *root_isec;
@@ -932,7 +989,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 		 * Determine the labeling behavior to use for this
 		 * filesystem type.
 		 */
-		rc = security_fs_use(current_selinux_ns, sb);
+		rc = security_fs_use(current_selinux_ns, sb->s_type->name,
+				     &sbsec->behavior, &sbsec->sid);
 		if (rc) {
 			printk(KERN_WARNING
 				"%s: security_fs_use(%s) returned %d\n",
@@ -1051,8 +1109,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 static int selinux_cmp_sb_context(const struct super_block *oldsb,
 				    const struct super_block *newsb)
 {
-	struct superblock_security_struct *old = oldsb->s_security;
-	struct superblock_security_struct *new = newsb->s_security;
+	struct superblock_security_struct *old = superblock_security(oldsb);
+	struct superblock_security_struct *new = superblock_security(newsb);
 	char oldflags = old->flags & SE_MNTMASK;
 	char newflags = new->flags & SE_MNTMASK;
 
@@ -1084,8 +1142,10 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
 					unsigned long *set_kern_flags)
 {
 	int rc = 0;
-	const struct superblock_security_struct *oldsbsec = oldsb->s_security;
-	struct superblock_security_struct *newsbsec = newsb->s_security;
+	const struct superblock_security_struct *oldsbsec =
+		superblock_security(oldsb);
+	struct superblock_security_struct *newsbsec =
+		superblock_security(newsb);
 
 	int set_fscontext =	(oldsbsec->flags & FSCONTEXT_MNT);
 	int set_context =	(oldsbsec->flags & CONTEXT_MNT);
@@ -1122,7 +1182,8 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
 
 	if (newsbsec->behavior == SECURITY_FS_USE_NATIVE &&
 		!(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) {
-		rc = security_fs_use(current_selinux_ns, newsb);
+		rc = security_fs_use(current_selinux_ns, newsb->s_type->name,
+				     &newsbsec->behavior, &newsbsec->sid);
 		if (rc)
 			goto out;
 	}
@@ -1603,6 +1664,8 @@ static int inode_doinit_with_dentry(struct inode *inode,
 	if (isec->initialized == LABEL_INITIALIZED)
 		return 0;
 
+	sbsec = superblock_security(inode->i_sb);
+
 	spin_lock(&isec->lock);
 	if (isec->initialized == LABEL_INITIALIZED)
 		goto out_unlock;
@@ -1610,7 +1673,6 @@ static int inode_doinit_with_dentry(struct inode *inode,
 	if (isec->sclass == SECCLASS_FILE)
 		isec->sclass = inode_mode_to_security_class(inode->i_mode);
 
-	sbsec = inode->i_sb->s_security;
 	if (!current_selinux_ns->initialized ||
 	    !(sbsec->flags & SE_SBINITIALIZED)) {
 		/* Defer initialization until selinux_complete_init,
@@ -1980,7 +2042,8 @@ selinux_determine_inode_label(const struct task_security_struct *tsec,
 				 const struct qstr *name, u16 tclass,
 				 u32 *_new_isid)
 {
-	const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
+	const struct superblock_security_struct *sbsec =
+		superblock_security(dir->i_sb);
 
 	if ((sbsec->flags & SE_SBINITIALIZED) &&
 	    (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
@@ -2011,7 +2074,7 @@ static int may_create(struct inode *dir,
 	int rc;
 
 	dsec = inode_security(dir);
-	sbsec = dir->i_sb->s_security;
+	sbsec = superblock_security(dir->i_sb);
 
 	sid = tsec->sid;
 
@@ -2160,7 +2223,7 @@ static int superblock_has_perm(const struct cred *cred,
 	struct superblock_security_struct *sbsec;
 	u32 sid = cred_sid(cred);
 
-	sbsec = sb->s_security;
+	sbsec = superblock_security(sb);
 	return avc_has_perm(cred_selinux_ns(cred),
 			    sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
 }
@@ -2885,7 +2948,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
 	int rc, i, *flags;
 	struct security_mnt_opts opts;
 	char *secdata, **mount_options;
-	struct superblock_security_struct *sbsec = sb->s_security;
+	struct superblock_security_struct *sbsec = superblock_security(sb);
 
 	if (!(sbsec->flags & SE_SBINITIALIZED))
 		return 0;
@@ -3079,7 +3142,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
 	int rc;
 	char *context;
 
-	sbsec = dir->i_sb->s_security;
+	sbsec = superblock_security(dir->i_sb);
 
 	sid = tsec->sid;
 	newsid = tsec->create_sid;
@@ -3332,7 +3395,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
 	if (strcmp(name, XATTR_NAME_SELINUX))
 		return selinux_inode_setotherxattr(dentry, name);
 
-	sbsec = inode->i_sb->s_security;
+	sbsec = superblock_security(inode->i_sb);
 	if (!(sbsec->flags & SBLABEL_MNT))
 		return -EOPNOTSUPP;
 
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 04514ee..dba80d3 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -68,7 +68,7 @@ struct file_security_struct {
 };
 
 struct superblock_security_struct {
-	struct super_block *sb;		/* back pointer to sb object */
+	const struct super_block *sb;	/* back pointer to sb object */
 	u32 sid;			/* SID of file system superblock */
 	u32 def_sid;			/* default SID for labeling */
 	u32 mntpoint_sid;		/* SECURITY_FS_USE_MNTPOINT context for files */
@@ -77,6 +77,9 @@ struct superblock_security_struct {
 	struct mutex lock;
 	struct list_head isec_head;
 	spinlock_t isec_lock;
+	struct selinux_ns *ns;
+	struct list_head sbsec_list;
+	spinlock_t sbsec_list_lock;
 };
 
 struct msg_security_struct {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 005d65c..b80f9bd 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -324,7 +324,8 @@ int security_get_allow_unknown(struct selinux_ns *ns);
 #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(struct selinux_ns *ns, struct super_block *sb);
+int security_fs_use(struct selinux_ns *ns,
+		    const char *fstype, unsigned short *behavior, u32 *sid);
 
 int security_genfs_sid(struct selinux_ns *ns,
 		       const char *fstype, char *name, u16 sclass,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e1c3881..abc5383 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2770,16 +2770,17 @@ int security_genfs_sid(struct selinux_ns *ns,
 
 /**
  * security_fs_use - Determine how to handle labeling for a filesystem.
- * @sb: superblock in question
+ * @fstype: filesystem type
+ * @behavior: labeling behavior
+ * @sid: SID for filesystem (superblock)
  */
-int security_fs_use(struct selinux_ns *ns, struct super_block *sb)
+int security_fs_use(struct selinux_ns *ns, const char *fstype,
+		    unsigned short *behavior, u32 *sid)
 {
 	struct policydb *policydb;
 	struct sidtab *sidtab;
 	int rc = 0;
 	struct ocontext *c;
-	struct superblock_security_struct *sbsec = sb->s_security;
-	const char *fstype = sb->s_type->name;
 
 	read_lock(&ns->ss->policy_rwlock);
 
@@ -2794,22 +2795,22 @@ int security_fs_use(struct selinux_ns *ns, struct super_block *sb)
 	}
 
 	if (c) {
-		sbsec->behavior = c->v.behavior;
+		*behavior = c->v.behavior;
 		if (!c->sid[0]) {
 			rc = sidtab_context_to_sid(sidtab, &c->context[0],
 						   &c->sid[0]);
 			if (rc)
 				goto out;
 		}
-		sbsec->sid = c->sid[0];
+		*sid = c->sid[0];
 	} else {
 		rc = __security_genfs_sid(ns, fstype, "/", SECCLASS_DIR,
-					  &sbsec->sid);
+					  sid);
 		if (rc) {
-			sbsec->behavior = SECURITY_FS_USE_NONE;
+			*behavior = SECURITY_FS_USE_NONE;
 			rc = 0;
 		} else {
-			sbsec->behavior = SECURITY_FS_USE_GENFS;
+			*behavior = SECURITY_FS_USE_GENFS;
 		}
 	}
 
-- 
2.9.5




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux