@@ -613,7 +613,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
{
const struct cred *cred = current_cred();
struct superblock_security_struct *sbsec = selinux_superblock(sb);
- struct dentry *root = sbsec->sb->s_root;
+ struct dentry *root = sb->s_root;
struct selinux_mnt_opts *opts = mnt_opts;
struct inode_security_struct *root_isec;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
@@ -730,7 +730,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_state, sb);
+ rc = security_fs_use(current_selinux_state, sb->s_type->name,
+ &sbsec->behavior, &sbsec->sid);
if (rc) {
pr_warn("%s: security_fs_use(%s) returned %d\n",
__func__, sb->s_type->name, rc);
@@ -924,7 +925,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_state, newsb);
+ rc = security_fs_use(current_selinux_state, newsb->s_type->name,
+ &newsbsec->behavior, &newsbsec->sid);
if (rc)
goto out;
}
@@ -1094,7 +1096,7 @@ static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
return rc;
}
if (sbsec->flags & ROOTCONTEXT_MNT) {
- struct dentry *root = sbsec->sb->s_root;
+ struct dentry *root = sb->s_root;
struct inode_security_struct *isec = backing_inode_security(root);
seq_putc(m, ',');
seq_puts(m, ROOTCONTEXT_STR);
@@ -2564,21 +2566,94 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
/* superblock security operations */
-static int selinux_sb_alloc_security(struct super_block *sb)
+static void sbsec_head_init(struct super_block *sb,
+ struct superblock_security_head *sbsech)
{
- struct superblock_security_struct *sbsec = selinux_superblock(sb);
+ INIT_LIST_HEAD(&sbsech->head);
+ spin_lock_init(&sbsech->lock);
+}
+static struct superblock_security_struct *
+sbsec_alloc(struct superblock_security_head *sbsech)
+{
+ struct superblock_security_struct *sbsec;
+
+ sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_NOFS);
+ if (!sbsec)
+ return NULL;
mutex_init(&sbsec->lock);
INIT_LIST_HEAD(&sbsec->isec_head);
spin_lock_init(&sbsec->isec_lock);
- sbsec->sb = sb;
+ sbsec->sbsech = sbsech;
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
sbsec->mntpoint_sid = SECINITSID_UNLABELED;
+ sbsec->state = get_selinux_state(current_selinux_state);
+
+ return sbsec;
+}
+
+struct superblock_security_struct *
+find_sbsec(struct superblock_security_head *sbsech)
+{
+ struct superblock_security_struct *cur, *new;
+
+ cur = container_of(sbsech->head.next, struct superblock_security_struct, sbsec_list);
+ if (cur->state == current_selinux_state)
+ return cur;
+ spin_lock(&sbsech->lock);
+ list_for_each_entry(cur, &sbsech->head, sbsec_list) {
+ if (cur->state == current_selinux_state)
+ goto unlock;
+ }
+ spin_unlock(&sbsech->lock);
+
+ new = sbsec_alloc(sbsech);
+ if (!new) {
+ cur = NULL;
+ goto out;
+ }
+
+ spin_lock(&sbsech->lock);
+ list_for_each_entry(cur, &sbsech->head, sbsec_list) {
+ if (cur->state == current_selinux_state)
+ goto unlock;
+ }
+ list_add(&new->sbsec_list, &sbsech->head);
+ cur = new;
+unlock:
+ spin_unlock(&sbsech->lock);
+out:
+ return cur;
+}
+
+static int selinux_sb_alloc_security(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec;
+ struct superblock_security_head *sbsech = selinux_head_of_superblock(sb);
+
+ sbsec_head_init(sb, sbsech);
+ sbsec = sbsec_alloc(sbsech);
+ if (!sbsec)
+ return -ENOMEM;
+ spin_lock(&sbsech->lock);
+ list_add(&sbsec->sbsec_list, &sbsech->head);
+ spin_unlock(&sbsech->lock);
return 0;
}
+static void selinux_sb_free_security(struct super_block *sb)
+{
+ struct superblock_security_struct *entry, *tmp;
+ struct superblock_security_head *sbsech = selinux_head_of_superblock(sb);
+
+ list_for_each_entry_safe(entry, tmp, &sbsech->head, sbsec_list) {
+ put_selinux_state(entry->state);
+ kfree(entry);
+ }
+}
+
static inline int opt_len(const char *s)
{
bool open_quote = false;
@@ -7224,6 +7299,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
selinux_msg_queue_alloc_security),
LSM_HOOK_INIT(shm_alloc_security, selinux_shm_alloc_security),
LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
+ LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security),
LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security),
LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
@@ -51,8 +51,13 @@ struct file_security_struct {
u32 pseqno; /* Policy seqno at the time of file open */
};
+struct superblock_security_head {
+ struct list_head head; /* list head of superblock_security_struct */
+ spinlock_t lock;
+};
+
struct superblock_security_struct {
- struct super_block *sb; /* back pointer to sb object */
+ struct superblock_security_head *sbsech; /* back pointer to superbock_security_head */
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
@@ -61,6 +66,8 @@ struct superblock_security_struct {
struct mutex lock;
struct list_head isec_head;
spinlock_t isec_lock;
+ struct selinux_state *state; /* pointer to selinux_state */
+ struct list_head sbsec_list;
};
struct msg_security_struct {
@@ -176,10 +183,19 @@ static inline u32 current_sid(void)
return tsec->sid;
}
-static inline struct superblock_security_struct *selinux_superblock(
+static inline struct superblock_security_head *selinux_head_of_superblock(
const struct super_block *superblock)
{
return superblock->s_security + selinux_blob_sizes.lbs_superblock;
}
+extern struct superblock_security_struct *
+find_sbsec(struct superblock_security_head *sbsech);
+
+static inline struct superblock_security_struct *selinux_superblock(
+ const struct super_block *superblock)
+{
+ return find_sbsec(selinux_head_of_superblock(superblock));
+}
+
#endif /* _SELINUX_OBJSEC_H_ */
@@ -410,7 +410,8 @@ int security_get_allow_unknown(struct selinux_state *state);
#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_state *state, struct super_block *sb);
+int security_fs_use(struct selinux_state *state, const char *fstype,
+ unsigned short *behavior, u32 *sid);
int security_genfs_sid(struct selinux_state *state,
const char *fstype, char *name, u16 sclass,
@@ -2868,19 +2868,18 @@ int selinux_policy_genfs_sid(struct selinux_policy *policy,
* security_fs_use - Determine how to handle labeling for a filesystem.
* @sb: superblock in question
*/
-int security_fs_use(struct selinux_state *state, struct super_block *sb)
+int security_fs_use(struct selinux_state *state, const char *fstype,
+ unsigned short *behavior, u32 *sid)
{
struct selinux_policy *policy;
struct policydb *policydb;
struct sidtab *sidtab;
int rc = 0;
struct ocontext *c;
- struct superblock_security_struct *sbsec = selinux_superblock(sb);
- const char *fstype = sb->s_type->name;
if (!selinux_initialized(state)) {
- sbsec->behavior = SECURITY_FS_USE_NONE;
- sbsec->sid = SECINITSID_UNLABELED;
+ *behavior = SECURITY_FS_USE_NONE;
+ *sid = SECINITSID_UNLABELED;
return 0;
}
@@ -2897,22 +2896,22 @@ int security_fs_use(struct selinux_state *state, 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(policy, fstype, "/",
- SECCLASS_DIR, &sbsec->sid);
+ SECCLASS_DIR, 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;
}
}