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 <s...@tycho.nsa.gov>
---
 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

Reply via email to