Skip to content

Commit

Permalink
LSM: Infrastructure management of the superblock
Browse files Browse the repository at this point in the history
Move management of the superblock->sb_security blob out of the
individual security modules and into the security infrastructure.
Instead of allocating the blobs from within the modules, the modules
tell the infrastructure how much space is required, and the space is
allocated there.

Cc: John Johansen <john.johansen@canonical.com>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Mickaël Salaün <mic@linux.microsoft.com>
Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20210422154123.13086-6-mic@digikod.net
Signed-off-by: James Morris <jamorris@linux.microsoft.com>
  • Loading branch information
Casey Schaufler authored and James Morris committed Apr 22, 2021
1 parent afe81f7 commit 1aea780
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 70 deletions.
1 change: 1 addition & 0 deletions include/linux/lsm_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -1573,6 +1573,7 @@ struct lsm_blob_sizes {
int lbs_cred;
int lbs_file;
int lbs_inode;
int lbs_superblock;
int lbs_ipc;
int lbs_msg_msg;
int lbs_task;
Expand Down
46 changes: 39 additions & 7 deletions security/security.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
}

Expand Down Expand Up @@ -333,12 +334,13 @@ static void __init ordered_lsm_init(void)
for (lsm = ordered_lsms; *lsm; lsm++)
prepare_lsm(*lsm);

init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
init_debug("task blob size = %d\n", blob_sizes.lbs_task);

/*
* Create any kmem_caches needed for blobs
Expand Down Expand Up @@ -670,6 +672,27 @@ static void __init lsm_early_task(struct task_struct *task)
panic("%s: Early task alloc failed.\n", __func__);
}

/**
* lsm_superblock_alloc - allocate a composite superblock blob
* @sb: the superblock that needs a blob
*
* Allocate the superblock blob for all the modules
*
* Returns 0, or -ENOMEM if memory can't be allocated.
*/
static int lsm_superblock_alloc(struct super_block *sb)
{
if (blob_sizes.lbs_superblock == 0) {
sb->s_security = NULL;
return 0;
}

sb->s_security = kzalloc(blob_sizes.lbs_superblock, GFP_KERNEL);
if (sb->s_security == NULL)
return -ENOMEM;
return 0;
}

/*
* The default value of the LSM hook is defined in linux/lsm_hook_defs.h and
* can be accessed with:
Expand Down Expand Up @@ -867,12 +890,21 @@ int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *

int security_sb_alloc(struct super_block *sb)
{
return call_int_hook(sb_alloc_security, 0, sb);
int rc = lsm_superblock_alloc(sb);

if (unlikely(rc))
return rc;
rc = call_int_hook(sb_alloc_security, 0, sb);
if (unlikely(rc))
security_sb_free(sb);
return rc;
}

void security_sb_free(struct super_block *sb)
{
call_void_hook(sb_free_security, sb);
kfree(sb->s_security);
sb->s_security = NULL;
}

void security_free_mnt_opts(void **mnt_opts)
Expand Down
58 changes: 22 additions & 36 deletions security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ static void inode_free_security(struct inode *inode)

if (!isec)
return;
sbsec = inode->i_sb->s_security;
sbsec = selinux_superblock(inode->i_sb);
/*
* As not all inode security structures are in a list, we check for
* empty list outside of the lock to make sure that we won't waste
Expand All @@ -340,13 +340,6 @@ static void inode_free_security(struct inode *inode)
}
}

static void superblock_free_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
sb->s_security = NULL;
kfree(sbsec);
}

struct selinux_mnt_opts {
const char *fscontext, *context, *rootcontext, *defcontext;
};
Expand Down Expand Up @@ -458,7 +451,7 @@ static int selinux_is_genfs_special_handling(struct super_block *sb)

static int selinux_is_sblabel_mnt(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
struct superblock_security_struct *sbsec = selinux_superblock(sb);

/*
* IMPORTANT: Double-check logic in this function when adding a new
Expand Down Expand Up @@ -535,7 +528,7 @@ static int sb_check_xattr_support(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 = selinux_superblock(sb);
struct dentry *root = sb->s_root;
struct inode *root_inode = d_backing_inode(root);
int rc = 0;
Expand Down Expand Up @@ -626,7 +619,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
unsigned long *set_kern_flags)
{
const struct cred *cred = current_cred();
struct superblock_security_struct *sbsec = sb->s_security;
struct superblock_security_struct *sbsec = selinux_superblock(sb);
struct dentry *root = sb->s_root;
struct selinux_mnt_opts *opts = mnt_opts;
struct inode_security_struct *root_isec;
Expand Down Expand Up @@ -863,8 +856,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 = selinux_superblock(oldsb);
struct superblock_security_struct *new = selinux_superblock(newsb);
char oldflags = old->flags & SE_MNTMASK;
char newflags = new->flags & SE_MNTMASK;

Expand Down Expand Up @@ -896,8 +889,9 @@ 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 =
selinux_superblock(oldsb);
struct superblock_security_struct *newsbsec = selinux_superblock(newsb);

int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
int set_context = (oldsbsec->flags & CONTEXT_MNT);
Expand Down Expand Up @@ -1076,7 +1070,7 @@ static int show_sid(struct seq_file *m, u32 sid)

static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
struct superblock_security_struct *sbsec = selinux_superblock(sb);
int rc;

if (!(sbsec->flags & SE_SBINITIALIZED))
Expand Down Expand Up @@ -1427,7 +1421,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
if (isec->sclass == SECCLASS_FILE)
isec->sclass = inode_mode_to_security_class(inode->i_mode);

sbsec = inode->i_sb->s_security;
sbsec = selinux_superblock(inode->i_sb);
if (!(sbsec->flags & SE_SBINITIALIZED)) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
Expand Down Expand Up @@ -1778,7 +1772,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 =
selinux_superblock(dir->i_sb);

if ((sbsec->flags & SE_SBINITIALIZED) &&
(sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
Expand Down Expand Up @@ -1809,7 +1804,7 @@ static int may_create(struct inode *dir,
int rc;

dsec = inode_security(dir);
sbsec = dir->i_sb->s_security;
sbsec = selinux_superblock(dir->i_sb);

sid = tsec->sid;

Expand Down Expand Up @@ -1958,7 +1953,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 = selinux_superblock(sb);
return avc_has_perm(&selinux_state,
sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
}
Expand Down Expand Up @@ -2587,28 +2582,18 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)

static int selinux_sb_alloc_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec;

sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
if (!sbsec)
return -ENOMEM;
struct superblock_security_struct *sbsec = selinux_superblock(sb);

mutex_init(&sbsec->lock);
INIT_LIST_HEAD(&sbsec->isec_head);
spin_lock_init(&sbsec->isec_lock);
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
sbsec->mntpoint_sid = SECINITSID_UNLABELED;
sb->s_security = sbsec;

return 0;
}

static void selinux_sb_free_security(struct super_block *sb)
{
superblock_free_security(sb);
}

static inline int opt_len(const char *s)
{
bool open_quote = false;
Expand Down Expand Up @@ -2687,7 +2672,7 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
{
struct selinux_mnt_opts *opts = mnt_opts;
struct superblock_security_struct *sbsec = sb->s_security;
struct superblock_security_struct *sbsec = selinux_superblock(sb);
u32 sid;
int rc;

Expand Down Expand Up @@ -2925,7 +2910,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
int rc;
char *context;

sbsec = dir->i_sb->s_security;
sbsec = selinux_superblock(dir->i_sb);

newsid = tsec->create_sid;

Expand Down Expand Up @@ -3227,7 +3212,7 @@ static int selinux_inode_setxattr(struct user_namespace *mnt_userns,
if (!selinux_initialized(&selinux_state))
return (inode_owner_or_capable(mnt_userns, inode) ? 0 : -EPERM);

sbsec = inode->i_sb->s_security;
sbsec = selinux_superblock(inode->i_sb);
if (!(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;

Expand Down Expand Up @@ -3472,13 +3457,14 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
struct inode_security_struct *isec = inode_security_novalidate(inode);
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
struct superblock_security_struct *sbsec;
u32 newsid;
int rc;

if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;

sbsec = selinux_superblock(inode->i_sb);
if (!(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;

Expand Down Expand Up @@ -6975,6 +6961,7 @@ struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
.lbs_inode = sizeof(struct inode_security_struct),
.lbs_ipc = sizeof(struct ipc_security_struct),
.lbs_msg_msg = sizeof(struct msg_security_struct),
.lbs_superblock = sizeof(struct superblock_security_struct),
};

#ifdef CONFIG_PERF_EVENTS
Expand Down Expand Up @@ -7075,7 +7062,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),

LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
Expand Down
6 changes: 6 additions & 0 deletions security/selinux/include/objsec.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,10 @@ static inline u32 current_sid(void)
return tsec->sid;
}

static inline struct superblock_security_struct *selinux_superblock(
const struct super_block *superblock)
{
return superblock->s_security + selinux_blob_sizes.lbs_superblock;
}

#endif /* _SELINUX_OBJSEC_H_ */
3 changes: 2 additions & 1 deletion security/selinux/ss/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <linux/sched.h>
#include <linux/audit.h>
#include <linux/vmalloc.h>
#include <linux/lsm_hooks.h>
#include <net/netlabel.h>

#include "flask.h"
Expand Down Expand Up @@ -2875,7 +2876,7 @@ int security_fs_use(struct selinux_state *state, struct super_block *sb)
struct sidtab *sidtab;
int rc = 0;
struct ocontext *c;
struct superblock_security_struct *sbsec = sb->s_security;
struct superblock_security_struct *sbsec = selinux_superblock(sb);
const char *fstype = sb->s_type->name;

if (!selinux_initialized(state)) {
Expand Down
6 changes: 6 additions & 0 deletions security/smack/smack.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ static inline struct smack_known **smack_ipc(const struct kern_ipc_perm *ipc)
return ipc->security + smack_blob_sizes.lbs_ipc;
}

static inline struct superblock_smack *smack_superblock(
const struct super_block *superblock)
{
return superblock->s_security + smack_blob_sizes.lbs_superblock;
}

/*
* Is the directory transmuting?
*/
Expand Down
Loading

0 comments on commit 1aea780

Please sign in to comment.