@@ -205,20 +205,6 @@ static int selinux_lsm_notifier_avc_callback(u32 event)
return 0;
}
-struct selinux_state *init_selinux_state;
-
-/*
- * initialise the security for the init task
- */
-static void cred_init_security(void)
-{
- struct task_security_struct *tsec;
-
- tsec = selinux_cred(unrcu_pointer(current->real_cred));
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
- tsec->state = get_selinux_state(init_selinux_state);
-}
-
/*
* get the security ID of a set of credentials
*/
@@ -7576,10 +7562,11 @@ unsigned int selinux_maxnsdepth = CONFIG_SECURITY_SELINUX_MAXNSDEPTH;
static atomic_t selinux_nsnum = ATOMIC_INIT(0);
-int selinux_state_create(struct selinux_state *parent,
- u32 creator_sid,
- struct selinux_state **state)
+int selinux_state_create(const struct cred *cred)
{
+ struct task_security_struct *tsec = selinux_cred(cred);
+ struct selinux_state *parent = tsec->state;
+ u32 creator_sid = tsec->sid;
struct selinux_state *newstate;
int rc;
@@ -7606,14 +7593,39 @@ int selinux_state_create(struct selinux_state *parent,
goto err;
if (parent) {
- /* Consumes parent reference */
+ /*
+ * The reference to the new state replaces the reference
+ * to the old state (parent) in the cred security blob;
+ * hence, we do not need to use get_selinux_state() below
+ * to increment the parent reference count.
+ */
newstate->parent = parent;
newstate->depth = parent->depth + 1;
}
atomic_inc(&selinux_nsnum);
- *state = newstate;
+ /*
+ * Set the new namespace.
+ * The reference count was initialized to 1 and
+ * this is that reference.
+ */
+ tsec->state = newstate;
+
+ /* Reset the SIDs for the new namespace. */
+ tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ tsec->exec_sid = tsec->create_sid = tsec->keycreate_sid =
+ tsec->sockcreate_sid = SECSID_NULL;
+
+ /*
+ * Save the credential in the parent namespace
+ * for later use in checks in that namespace.
+ */
+ if (parent) {
+ put_cred(tsec->parent_cred);
+ tsec->parent_cred = get_current_cred();
+ }
+
return 0;
err:
kfree(newstate);
@@ -7642,21 +7654,39 @@ void __put_selinux_state(struct selinux_state *state)
schedule_work(&state->work);
}
+struct selinux_state *init_selinux_state;
+
static __init int selinux_init(void)
{
+ const struct cred *cred = unrcu_pointer(current->real_cred);
+ struct task_security_struct *tsec = selinux_cred(cred);
+
pr_info("SELinux: Initializing.\n");
- if (selinux_state_create(NULL, SECINITSID_KERNEL,
- &init_selinux_state))
+ /*
+ * Initialize the first cred with the kernel SID and
+ * NULL state since selinux_state_create() expects
+ * these two fields to be set. The rest is handled by
+ * selinux_state_create(), which will update the state
+ * field to refer to the new state and set the parent
+ * pointer to the old state value (NULL).
+ */
+ tsec->sid = SECINITSID_KERNEL;
+ tsec->state = NULL;
+ if (selinux_state_create(cred))
panic("SELinux: Could not create initial namespace\n");
+
+ /*
+ * Save a reference to the initial SELinux namespace
+ * for use in various other functions.
+ */
+ init_selinux_state = get_selinux_state(tsec->state);
+
enforcing_set(init_selinux_state, selinux_enforcing_boot);
if (global_sidtab_init())
panic("SELinux: Could not create global SID table\n");
- /* Set the security state for the initial task. */
- cred_init_security();
-
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
if (!default_noexec)
pr_notice("SELinux: virtual memory is executable by default\n");
@@ -117,8 +117,7 @@ struct selinux_state {
extern struct selinux_state *init_selinux_state;
extern unsigned int selinux_maxns, selinux_maxnsdepth;
-int selinux_state_create(struct selinux_state *parent, u32 creator_sid,
- struct selinux_state **state);
+int selinux_state_create(const struct cred *cred);
void __put_selinux_state(struct selinux_state *state);
void selinux_policy_free(struct selinux_policy __rcu *policy);
@@ -370,25 +370,16 @@ static ssize_t sel_write_unshare(struct file *file, const char __user *buf,
if (set) {
struct cred *cred = prepare_creds();
- struct task_security_struct *tsec;
if (!cred) {
length = -ENOMEM;
goto out;
}
- tsec = selinux_cred(cred);
- if (selinux_state_create(state, current_sid(),
- &tsec->state)) {
+ if (selinux_state_create(cred)) {
abort_creds(cred);
length = -ENOMEM;
goto out;
}
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
- tsec->exec_sid = tsec->create_sid = tsec->keycreate_sid =
- tsec->sockcreate_sid = SECSID_NULL;
- if (tsec->parent_cred)
- put_cred(tsec->parent_cred);
- tsec->parent_cred = get_current_cred();
commit_creds(cred);
}
Refactor selinux_state_create() to be more like create_user_ns() after which it was originally modeled. In particular, pass in a single cred argument and update the cred SELinux blob with the new state. This makes the reference counting situation clearer with regard to the old state / parent reference and simplifies the callers. Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com> --- security/selinux/hooks.c | 78 ++++++++++++++++++++--------- security/selinux/include/security.h | 3 +- security/selinux/selinuxfs.c | 11 +--- 3 files changed, 56 insertions(+), 36 deletions(-)