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 <[email protected]>
---
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