diff options
-rw-r--r-- | security/apparmor/context.c | 91 | ||||
-rw-r--r-- | security/apparmor/domain.c | 14 | ||||
-rw-r--r-- | security/apparmor/include/context.h | 31 | ||||
-rw-r--r-- | security/apparmor/lsm.c | 48 |
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) |