480 lines
17 KiB
Diff
480 lines
17 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Casey Schaufler <casey@schaufler-ca.com>
|
|
Date: Thu, 10 May 2018 14:25:23 -0700
|
|
Subject: [PATCH] LSM: Infrastructure management of the superblock security
|
|
blob
|
|
|
|
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.
|
|
|
|
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
|
|
---
|
|
include/linux/lsm_hooks.h | 1 +
|
|
security/security.c | 34 ++++++++++++++++++-
|
|
security/selinux/hooks.c | 56 ++++++++++++-------------------
|
|
security/selinux/include/objsec.h | 6 ++++
|
|
security/selinux/ss/services.c | 3 +-
|
|
security/smack/smack.h | 6 ++++
|
|
security/smack/smack_lsm.c | 35 +++++--------------
|
|
7 files changed, 78 insertions(+), 63 deletions(-)
|
|
|
|
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
|
|
index ee77a6dc1c37..ddd18e6b2eec 100644
|
|
--- a/include/linux/lsm_hooks.h
|
|
+++ b/include/linux/lsm_hooks.h
|
|
@@ -2033,6 +2033,7 @@ struct lsm_blob_sizes {
|
|
int lbs_cred;
|
|
int lbs_file;
|
|
int lbs_inode;
|
|
+ int lbs_superblock;
|
|
int lbs_task;
|
|
};
|
|
|
|
diff --git a/security/security.c b/security/security.c
|
|
index 4d2f1d88eef8..f6238c8489be 100644
|
|
--- a/security/security.c
|
|
+++ b/security/security.c
|
|
@@ -117,6 +117,7 @@ int __init security_init(void)
|
|
pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred);
|
|
pr_info("LSM: file blob size = %d\n", blob_sizes.lbs_file);
|
|
pr_info("LSM: inode blob size = %d\n", blob_sizes.lbs_inode);
|
|
+ pr_info("LSM: superblock blob size = %d\n", blob_sizes.lbs_superblock);
|
|
pr_info("LSM: task blob size = %d\n", blob_sizes.lbs_task);
|
|
#endif /* CONFIG_SECURITY_LSM_DEBUG */
|
|
|
|
@@ -295,6 +296,7 @@ void __init security_add_blobs(struct lsm_blob_sizes *needed)
|
|
{
|
|
lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
|
|
lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file);
|
|
+ lsm_set_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
|
|
lsm_set_size(&needed->lbs_task, &blob_sizes.lbs_task);
|
|
/*
|
|
* The inode blob gets an rcu_head in addition to
|
|
@@ -406,6 +408,27 @@ void lsm_early_inode(struct inode *inode)
|
|
panic("%s: Early inode 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.
|
|
+ */
|
|
+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;
|
|
+}
|
|
+
|
|
/*
|
|
* Hook list operation macros.
|
|
*
|
|
@@ -569,12 +592,21 @@ void security_bprm_committed_creds(struct linux_binprm *bprm)
|
|
|
|
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;
|
|
}
|
|
|
|
int security_sb_copy_data(char *orig, char *copy)
|
|
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
|
|
index 77e1663a15b7..c59135f92089 100644
|
|
--- a/security/selinux/hooks.c
|
|
+++ b/security/selinux/hooks.c
|
|
@@ -333,7 +333,8 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr
|
|
static void inode_free_security(struct inode *inode)
|
|
{
|
|
struct inode_security_struct *isec = selinux_inode(inode);
|
|
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
|
|
+ struct superblock_security_struct *sbsec =
|
|
+ selinux_superblock(inode->i_sb);
|
|
|
|
/*
|
|
* As not all inode security structures are in a list, we check for
|
|
@@ -365,11 +366,7 @@ static int file_alloc_security(struct file *file)
|
|
|
|
static int superblock_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);
|
|
@@ -378,18 +375,10 @@ static int superblock_alloc_security(struct super_block *sb)
|
|
sbsec->sid = SECINITSID_UNLABELED;
|
|
sbsec->def_sid = SECINITSID_FILE;
|
|
sbsec->mntpoint_sid = SECINITSID_UNLABELED;
|
|
- sb->s_security = sbsec;
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static void superblock_free_security(struct super_block *sb)
|
|
-{
|
|
- struct superblock_security_struct *sbsec = sb->s_security;
|
|
- sb->s_security = NULL;
|
|
- kfree(sbsec);
|
|
-}
|
|
-
|
|
static inline int inode_doinit(struct inode *inode)
|
|
{
|
|
return inode_doinit_with_dentry(inode, NULL);
|
|
@@ -470,7 +459,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
|
|
@@ -498,7 +487,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 = selinux_superblock(sb);
|
|
struct dentry *root = sb->s_root;
|
|
struct inode *root_inode = d_backing_inode(root);
|
|
int rc = 0;
|
|
@@ -581,7 +570,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 = selinux_superblock(sb);
|
|
char *context = NULL;
|
|
u32 len;
|
|
char tmp;
|
|
@@ -702,7 +691,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 = selinux_superblock(sb);
|
|
const char *name = sb->s_type->name;
|
|
struct dentry *root = sbsec->sb->s_root;
|
|
struct inode_security_struct *root_isec;
|
|
@@ -956,8 +945,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;
|
|
|
|
@@ -989,8 +978,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);
|
|
@@ -1524,7 +1514,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
|
|
@@ -1922,7 +1912,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)) {
|
|
@@ -1953,7 +1944,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;
|
|
|
|
@@ -2102,7 +2093,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);
|
|
}
|
|
@@ -2733,11 +2724,6 @@ static int selinux_sb_alloc_security(struct super_block *sb)
|
|
return superblock_alloc_security(sb);
|
|
}
|
|
|
|
-static void selinux_sb_free_security(struct super_block *sb)
|
|
-{
|
|
- superblock_free_security(sb);
|
|
-}
|
|
-
|
|
static inline int match_prefix(char *prefix, int plen, char *option, int olen)
|
|
{
|
|
if (plen > olen)
|
|
@@ -2834,7 +2820,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 = selinux_superblock(sb);
|
|
|
|
if (!(sbsec->flags & SE_SBINITIALIZED))
|
|
return 0;
|
|
@@ -3028,7 +3014,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;
|
|
|
|
@@ -3273,7 +3259,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
|
if (!selinux_state.initialized)
|
|
return (inode_owner_or_capable(inode) ? 0 : -EPERM);
|
|
|
|
- sbsec = inode->i_sb->s_security;
|
|
+ sbsec = selinux_superblock(inode->i_sb);
|
|
if (!(sbsec->flags & SBLABEL_MNT))
|
|
return -EOPNOTSUPP;
|
|
|
|
@@ -6904,6 +6890,7 @@ struct lsm_blob_sizes selinux_blob_sizes = {
|
|
.lbs_cred = sizeof(struct task_security_struct),
|
|
.lbs_file = sizeof(struct file_security_struct),
|
|
.lbs_inode = sizeof(struct inode_security_struct),
|
|
+ .lbs_superblock = sizeof(struct superblock_security_struct),
|
|
};
|
|
|
|
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
|
@@ -6929,7 +6916,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
|
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
|
|
|
|
LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
|
|
- LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
|
|
LSM_HOOK_INIT(sb_copy_data, selinux_sb_copy_data),
|
|
LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
|
|
LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
|
|
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
|
|
index 7a3d18fa9b13..d64c4f6adb35 100644
|
|
--- a/security/selinux/include/objsec.h
|
|
+++ b/security/selinux/include/objsec.h
|
|
@@ -175,4 +175,10 @@ static inline struct inode_security_struct *selinux_inode(
|
|
return inode->i_security;
|
|
}
|
|
|
|
+static inline struct superblock_security_struct *selinux_superblock(
|
|
+ const struct super_block *superblock)
|
|
+{
|
|
+ return superblock->s_security;
|
|
+}
|
|
+
|
|
#endif /* _SELINUX_OBJSEC_H_ */
|
|
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
|
|
index a9f2bc8443bd..1180ba133861 100644
|
|
--- a/security/selinux/ss/services.c
|
|
+++ b/security/selinux/ss/services.c
|
|
@@ -52,6 +52,7 @@
|
|
#include <linux/selinux.h>
|
|
#include <linux/flex_array.h>
|
|
#include <linux/vmalloc.h>
|
|
+#include <linux/lsm_hooks.h>
|
|
#include <net/netlabel.h>
|
|
|
|
#include "flask.h"
|
|
@@ -2765,7 +2766,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;
|
|
|
|
read_lock(&state->ss->policy_rwlock);
|
|
diff --git a/security/smack/smack.h b/security/smack/smack.h
|
|
index 5da5bd1b9b47..d73e9def586c 100644
|
|
--- a/security/smack/smack.h
|
|
+++ b/security/smack/smack.h
|
|
@@ -372,6 +372,12 @@ static inline struct inode_smack *smack_inode(const struct inode *inode)
|
|
return inode->i_security;
|
|
}
|
|
|
|
+static inline struct superblock_smack *smack_superblock(
|
|
+ const struct super_block *superblock)
|
|
+{
|
|
+ return superblock->s_security;
|
|
+}
|
|
+
|
|
/*
|
|
* Is the directory transmuting?
|
|
*/
|
|
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
|
|
index cf2a441b7a00..70ff833200cc 100644
|
|
--- a/security/smack/smack_lsm.c
|
|
+++ b/security/smack/smack_lsm.c
|
|
@@ -522,12 +522,7 @@ static int smack_syslog(int typefrom_file)
|
|
*/
|
|
static int smack_sb_alloc_security(struct super_block *sb)
|
|
{
|
|
- struct superblock_smack *sbsp;
|
|
-
|
|
- sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL);
|
|
-
|
|
- if (sbsp == NULL)
|
|
- return -ENOMEM;
|
|
+ struct superblock_smack *sbsp = smack_superblock(sb);
|
|
|
|
sbsp->smk_root = &smack_known_floor;
|
|
sbsp->smk_default = &smack_known_floor;
|
|
@@ -536,22 +531,10 @@ static int smack_sb_alloc_security(struct super_block *sb)
|
|
/*
|
|
* SMK_SB_INITIALIZED will be zero from kzalloc.
|
|
*/
|
|
- sb->s_security = sbsp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
-/**
|
|
- * smack_sb_free_security - free a superblock blob
|
|
- * @sb: the superblock getting the blob
|
|
- *
|
|
- */
|
|
-static void smack_sb_free_security(struct super_block *sb)
|
|
-{
|
|
- kfree(sb->s_security);
|
|
- sb->s_security = NULL;
|
|
-}
|
|
-
|
|
/**
|
|
* smack_sb_copy_data - copy mount options data for processing
|
|
* @orig: where to start
|
|
@@ -742,7 +725,7 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
|
{
|
|
struct dentry *root = sb->s_root;
|
|
struct inode *inode = d_backing_inode(root);
|
|
- struct superblock_smack *sp = sb->s_security;
|
|
+ struct superblock_smack *sp = smack_superblock(sb);
|
|
struct inode_smack *isp;
|
|
struct smack_known *skp;
|
|
int i;
|
|
@@ -871,7 +854,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
|
*/
|
|
static int smack_sb_statfs(struct dentry *dentry)
|
|
{
|
|
- struct superblock_smack *sbp = dentry->d_sb->s_security;
|
|
+ struct superblock_smack *sbp = smack_superblock(dentry->d_sb);
|
|
int rc;
|
|
struct smk_audit_info ad;
|
|
|
|
@@ -908,7 +891,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
|
|
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
|
|
return 0;
|
|
|
|
- sbsp = inode->i_sb->s_security;
|
|
+ sbsp = smack_superblock(inode->i_sb);
|
|
if ((sbsp->smk_flags & SMK_SB_UNTRUSTED) &&
|
|
isp->smk_task != sbsp->smk_root)
|
|
return 0;
|
|
@@ -1160,7 +1143,7 @@ static int smack_inode_rename(struct inode *old_inode,
|
|
*/
|
|
static int smack_inode_permission(struct inode *inode, int mask)
|
|
{
|
|
- struct superblock_smack *sbsp = inode->i_sb->s_security;
|
|
+ struct superblock_smack *sbsp = smack_superblock(inode->i_sb);
|
|
struct smk_audit_info ad;
|
|
int no_block = mask & MAY_NOT_BLOCK;
|
|
int rc;
|
|
@@ -1402,7 +1385,7 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
|
|
*/
|
|
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
|
|
struct super_block *sbp = dentry->d_sb;
|
|
- struct superblock_smack *sbsp = sbp->s_security;
|
|
+ struct superblock_smack *sbsp = smack_superblock(sbp);
|
|
|
|
isp->smk_inode = sbsp->smk_default;
|
|
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
|
|
@@ -1672,7 +1655,7 @@ static int smack_mmap_file(struct file *file,
|
|
isp = smack_inode(file_inode(file));
|
|
if (isp->smk_mmap == NULL)
|
|
return 0;
|
|
- sbsp = file_inode(file)->i_sb->s_security;
|
|
+ sbsp = smack_superblock(file_inode(file)->i_sb);
|
|
if (sbsp->smk_flags & SMK_SB_UNTRUSTED &&
|
|
isp->smk_mmap != sbsp->smk_root)
|
|
return -EACCES;
|
|
@@ -3298,7 +3281,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
|
|
goto unlockandout;
|
|
|
|
sbp = inode->i_sb;
|
|
- sbsp = sbp->s_security;
|
|
+ sbsp = smack_superblock(sbp);
|
|
/*
|
|
* We're going to use the superblock default label
|
|
* if there's no label on the file.
|
|
@@ -4588,6 +4571,7 @@ struct lsm_blob_sizes smack_blob_sizes = {
|
|
.lbs_cred = sizeof(struct task_smack),
|
|
.lbs_file = sizeof(struct smack_known *),
|
|
.lbs_inode = sizeof(struct inode_smack),
|
|
+ .lbs_superblock = sizeof(struct superblock_smack),
|
|
};
|
|
|
|
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
|
@@ -4596,7 +4580,6 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
|
LSM_HOOK_INIT(syslog, smack_syslog),
|
|
|
|
LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
|
|
- LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
|
|
LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
|
|
LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
|
|
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
|
|
--
|
|
https://clearlinux.org
|
|
|