summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/context.c91
-rw-r--r--security/apparmor/domain.c14
-rw-r--r--security/apparmor/include/context.h31
-rw-r--r--security/apparmor/lsm.c48
4 files changed, 132 insertions, 52 deletions
diff --git a/security/apparmor/context.c b/security/apparmor/context.c
index 89c03053303e..432672b18945 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/context.c
@@ -48,8 +48,6 @@ void aa_free_cred_ctx(struct aa_cred_ctx *ctx)
{
if (ctx) {
aa_put_label(ctx->label);
- aa_put_label(ctx->previous);
- aa_put_label(ctx->onexec);
kzfree(ctx);
}
@@ -64,8 +62,6 @@ void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old)
{
*new = *old;
aa_get_label(new->label);
- aa_get_label(new->previous);
- aa_get_label(new->onexec);
}
/**
@@ -86,6 +82,43 @@ struct aa_label *aa_get_task_label(struct task_struct *task)
}
/**
+ * aa_alloc_task_ctx - allocate a new task_ctx
+ * @flags: gfp flags for allocation
+ *
+ * Returns: allocated buffer or NULL on failure
+ */
+struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags)
+{
+ return kzalloc(sizeof(struct aa_task_ctx), flags);
+}
+
+/**
+ * aa_free_task_ctx - free a task_ctx
+ * @ctx: task_ctx to free (MAYBE NULL)
+ */
+void aa_free_task_ctx(struct aa_task_ctx *ctx)
+{
+ if (ctx) {
+ aa_put_label(ctx->previous);
+ aa_put_label(ctx->onexec);
+
+ kzfree(ctx);
+ }
+}
+
+/**
+ * aa_dup_task_ctx - duplicate a task context, incrementing reference counts
+ * @new: a blank task context (NOT NULL)
+ * @old: the task context to copy (NOT NULL)
+ */
+void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old)
+{
+ *new = *old;
+ aa_get_label(new->previous);
+ aa_get_label(new->onexec);
+}
+
+/**
* aa_replace_current_label - replace the current tasks label
* @label: new label (NOT NULL)
*
@@ -112,7 +145,7 @@ int aa_replace_current_label(struct aa_label *label)
/* if switching to unconfined or a different label namespace
* clear out context state
*/
- aa_clear_cred_ctx_trans(ctx);
+ aa_clear_task_ctx_trans(current_task_ctx());
/*
* be careful switching ctx->profile, when racing replacement it
@@ -136,18 +169,13 @@ int aa_replace_current_label(struct aa_label *label)
*/
int aa_set_current_onexec(struct aa_label *label, bool stack)
{
- struct aa_cred_ctx *ctx;
- struct cred *new = prepare_creds();
- if (!new)
- return -ENOMEM;
+ struct aa_task_ctx *ctx = current_task_ctx();
- ctx = cred_ctx(new);
aa_get_label(label);
- aa_clear_cred_ctx_trans(ctx);
+ aa_put_label(ctx->onexec);
ctx->onexec = label;
ctx->token = stack;
- commit_creds(new);
return 0;
}
@@ -163,28 +191,31 @@ int aa_set_current_onexec(struct aa_label *label, bool stack)
*/
int aa_set_current_hat(struct aa_label *label, u64 token)
{
+ struct aa_task_ctx *tctx = current_task_ctx();
struct aa_cred_ctx *ctx;
struct cred *new = prepare_creds();
+
if (!new)
return -ENOMEM;
AA_BUG(!label);
ctx = cred_ctx(new);
- if (!ctx->previous) {
+ if (!tctx->previous) {
/* transfer refcount */
- ctx->previous = ctx->label;
- ctx->token = token;
- } else if (ctx->token == token) {
+ tctx->previous = ctx->label;
+ tctx->token = token;
+ } else if (tctx->token == token) {
aa_put_label(ctx->label);
} else {
/* previous_profile && ctx->token != token */
abort_creds(new);
return -EACCES;
}
+
ctx->label = aa_get_newest_label(label);
/* clear exec on switching context */
- aa_put_label(ctx->onexec);
- ctx->onexec = NULL;
+ aa_put_label(tctx->onexec);
+ tctx->onexec = NULL;
commit_creds(new);
return 0;
@@ -201,28 +232,28 @@ int aa_set_current_hat(struct aa_label *label, u64 token)
*/
int aa_restore_previous_label(u64 token)
{
+ struct aa_task_ctx *tctx = current_task_ctx();
struct aa_cred_ctx *ctx;
- struct cred *new = prepare_creds();
- if (!new)
- return -ENOMEM;
+ struct cred *new;
- ctx = cred_ctx(new);
- if (ctx->token != token) {
- abort_creds(new);
+ if (tctx->token != token)
return -EACCES;
- }
/* ignore restores when there is no saved label */
- if (!ctx->previous) {
- abort_creds(new);
+ if (!tctx->previous)
return 0;
- }
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ ctx = cred_ctx(new);
aa_put_label(ctx->label);
- ctx->label = aa_get_newest_label(ctx->previous);
+ ctx->label = aa_get_newest_label(tctx->previous);
AA_BUG(!ctx->label);
/* clear exec && prev information when restoring to previous context */
- aa_clear_cred_ctx_trans(ctx);
+ aa_clear_task_ctx_trans(tctx);
commit_creds(new);
+
return 0;
}
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 90967de96be0..b90759a765b5 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -780,6 +780,7 @@ static struct aa_label *handle_onexec(struct aa_label *label,
int apparmor_bprm_set_creds(struct linux_binprm *bprm)
{
struct aa_cred_ctx *ctx;
+ struct aa_task_ctx *tctx;
struct aa_label *label, *new = NULL;
struct aa_profile *profile;
char *buffer = NULL;
@@ -795,15 +796,17 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
return 0;
ctx = cred_ctx(bprm->cred);
+ tctx = current_task_ctx();
AA_BUG(!ctx);
+ AA_BUG(!tctx);
label = aa_get_newest_label(ctx->label);
/* buffer freed below, name is pointer into buffer */
get_buffers(buffer);
/* Test for onexec first as onexec override other x transitions. */
- if (ctx->onexec)
- new = handle_onexec(label, ctx->onexec, ctx->token,
+ if (tctx->onexec)
+ new = handle_onexec(label, tctx->onexec, tctx->token,
bprm, buffer, &cond, &unsafe);
else
new = fn_label_build(label, profile, GFP_ATOMIC,
@@ -858,9 +861,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
ctx->label = new;
done:
- /* clear out temporary/transitional state from the context */
- aa_clear_cred_ctx_trans(ctx);
-
aa_put_label(label);
put_buffers(buffer);
@@ -1050,6 +1050,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
{
const struct cred *cred;
struct aa_cred_ctx *ctx;
+ struct aa_task_ctx *tctx;
struct aa_label *label, *previous, *new = NULL, *target = NULL;
struct aa_profile *profile;
struct aa_perms perms = {};
@@ -1070,8 +1071,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags)
/* released below */
cred = get_current_cred();
ctx = cred_ctx(cred);
+ tctx = current_task_ctx();
label = aa_get_newest_cred_label(cred);
- previous = aa_get_newest_label(ctx->previous);
+ previous = aa_get_newest_label(tctx->previous);
if (unconfined(label)) {
info = "unconfined can not change_hat";
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h
index 0622fcf2a695..c3b51d88275b 100644
--- a/security/apparmor/include/context.h
+++ b/security/apparmor/include/context.h
@@ -25,20 +25,24 @@
#define cred_ctx(X) ((X)->security)
#define current_cred_ctx() cred_ctx(current_cred())
+#define task_ctx(X) ((X)->security)
+#define current_task_ctx() (task_ctx(current))
+
/**
* struct aa_cred_ctx - primary label for confined tasks
* @label: the current label (NOT NULL)
- * @exec: label to transition to on next exec (MAYBE NULL)
- * @previous: label the task may return to (MAYBE NULL)
- * @token: magic value the task must know for returning to @previous
- *
- * Contains the task's current label (which could change due to
- * change_hat). Plus the hat_magic needed during change_hat.
- *
- * TODO: make so a task can be confined by a stack of contexts
*/
struct aa_cred_ctx {
struct aa_label *label;
+};
+
+/**
+ * struct aa_task_ctx - information for current task label change
+ * @onexec: profile to transition to on next exec (MAY BE NULL)
+ * @previous: profile the task may return to (MAY BE NULL)
+ * @token: magic value the task must know for returning to @previous_profile
+ */
+struct aa_task_ctx {
struct aa_label *onexec;
struct aa_label *previous;
u64 token;
@@ -47,6 +51,11 @@ struct aa_cred_ctx {
struct aa_cred_ctx *aa_alloc_cred_ctx(gfp_t flags);
void aa_free_cred_ctx(struct aa_cred_ctx *ctx);
void aa_dup_cred_ctx(struct aa_cred_ctx *new, const struct aa_cred_ctx *old);
+
+struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags);
+void aa_free_task_ctx(struct aa_task_ctx *ctx);
+void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old);
+
int aa_replace_current_label(struct aa_label *label);
int aa_set_current_onexec(struct aa_label *label, bool stack);
int aa_set_current_hat(struct aa_label *label, u64 token);
@@ -213,11 +222,13 @@ static inline struct aa_ns *aa_get_current_ns(void)
}
/**
- * aa_clear_cred_ctx_trans - clear transition tracking info from the ctx
+ * aa_clear_task_ctx_trans - clear transition tracking info from the ctx
* @ctx: task context to clear (NOT NULL)
*/
-static inline void aa_clear_cred_ctx_trans(struct aa_cred_ctx *ctx)
+static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx)
{
+ AA_BUG(!ctx);
+
aa_put_label(ctx->previous);
aa_put_label(ctx->onexec);
ctx->previous = NULL;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 0624eb2081f3..a1d63d93b862 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -102,6 +102,27 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
aa_dup_cred_ctx(new_ctx, old_ctx);
}
+static void apparmor_task_free(struct task_struct *task)
+{
+
+ aa_free_task_ctx(task_ctx(task));
+ task_ctx(task) = NULL;
+}
+
+static int apparmor_task_alloc(struct task_struct *task,
+ unsigned long clone_flags)
+{
+ struct aa_task_ctx *new = aa_alloc_task_ctx(GFP_KERNEL);
+
+ if (!new)
+ return -ENOMEM;
+
+ aa_dup_task_ctx(new, current_task_ctx());
+ task_ctx(task) = new;
+
+ return 0;
+}
+
static int apparmor_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
@@ -577,15 +598,16 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
int error = -ENOENT;
/* released below */
const struct cred *cred = get_task_cred(task);
+ struct aa_task_ctx *tctx = current_task_ctx();
struct aa_cred_ctx *ctx = cred_ctx(cred);
struct aa_label *label = NULL;
if (strcmp(name, "current") == 0)
label = aa_get_newest_label(ctx->label);
- else if (strcmp(name, "prev") == 0 && ctx->previous)
- label = aa_get_newest_label(ctx->previous);
- else if (strcmp(name, "exec") == 0 && ctx->onexec)
- label = aa_get_newest_label(ctx->onexec);
+ else if (strcmp(name, "prev") == 0 && tctx->previous)
+ label = aa_get_newest_label(tctx->previous);
+ else if (strcmp(name, "exec") == 0 && tctx->onexec)
+ label = aa_get_newest_label(tctx->onexec);
else
error = -EINVAL;
@@ -699,7 +721,9 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
*/
static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
{
- /* TODO: cleanup signals - ipc mediation */
+ /* clear out temporary/transitional state from the context */
+ aa_clear_task_ctx_trans(current_task_ctx());
+
return;
}
@@ -779,6 +803,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),
+ LSM_HOOK_INIT(task_free, apparmor_task_free),
+ LSM_HOOK_INIT(task_alloc, apparmor_task_alloc),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
LSM_HOOK_INIT(task_kill, apparmor_task_kill),
};
@@ -1025,15 +1051,25 @@ static int __init set_init_ctx(void)
{
struct cred *cred = (struct cred *)current->real_cred;
struct aa_cred_ctx *ctx;
+ struct aa_task_ctx *tctx;
ctx = aa_alloc_cred_ctx(GFP_KERNEL);
if (!ctx)
- return -ENOMEM;
+ goto fail_cred;
+ tctx = aa_alloc_task_ctx(GFP_KERNEL);
+ if (!tctx)
+ goto fail_task;
ctx->label = aa_get_label(ns_unconfined(root_ns));
cred_ctx(cred) = ctx;
+ task_ctx(current) = tctx;
return 0;
+
+fail_task:
+ aa_free_cred_ctx(ctx);
+fail_cred:
+ return -ENOMEM;
}
static void destroy_buffers(void)