apparmor: move task domain change info to task security

The task domain change info is task specific and its and abuse of
the cred to store the information in there. Now that a task->security
field exists store it in the proper place.

Signed-off-by: John Johansen <john.johansen@canonical.com>
diff --git a/security/apparmor/context.c b/security/apparmor/context.c
index 89c0305..432672b 100644
--- a/security/apparmor/context.c
+++ b/security/apparmor/context.c
@@ -48,8 +48,6 @@
 {
 	if (ctx) {
 		aa_put_label(ctx->label);
-		aa_put_label(ctx->previous);
-		aa_put_label(ctx->onexec);
 
 		kzfree(ctx);
 	}
@@ -64,8 +62,6 @@
 {
 	*new = *old;
 	aa_get_label(new->label);
-	aa_get_label(new->previous);
-	aa_get_label(new->onexec);
 }
 
 /**
@@ -86,6 +82,43 @@
 }
 
 /**
+ * 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 @@
 		/* 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_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_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_restore_previous_label(u64 token)
 {
+	struct aa_task_ctx *tctx = current_task_ctx();
 	struct aa_cred_ctx *ctx;
-	struct cred *new = prepare_creds();
+	struct cred *new;
+
+	if (tctx->token != token)
+		return -EACCES;
+	/* ignore restores when there is no saved label */
+	if (!tctx->previous)
+		return 0;
+
+	new = prepare_creds();
 	if (!new)
 		return -ENOMEM;
-
 	ctx = cred_ctx(new);
-	if (ctx->token != token) {
-		abort_creds(new);
-		return -EACCES;
-	}
-	/* ignore restores when there is no saved label */
-	if (!ctx->previous) {
-		abort_creds(new);
-		return 0;
-	}
 
 	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 90967de..b90759a 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -780,6 +780,7 @@
 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 @@
 		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 @@
 	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 @@
 {
 	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 @@
 	/* 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 0622fcf..c3b51d8 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 *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 @@
 }
 
 /**
- * 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 0624eb2..a1d63d9 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -102,6 +102,27 @@
 	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 @@
 	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_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 @@
 	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 @@
 {
 	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)