xref: /linux/security/apparmor/task.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * AppArmor security module
4  *
5  * This file contains AppArmor task related definitions and mediation
6  *
7  * Copyright 2017 Canonical Ltd.
8  *
9  * TODO
10  * If a task uses change_hat it currently does not return to the old
11  * cred or task context but instead creates a new one.  Ideally the task
12  * should return to the previous cred if it has not been modified.
13  */
14 
15 #include "include/cred.h"
16 #include "include/task.h"
17 
18 /**
19  * aa_get_task_label - Get another task's label
20  * @task: task to query  (NOT NULL)
21  *
22  * Returns: counted reference to @task's label
23  */
24 struct aa_label *aa_get_task_label(struct task_struct *task)
25 {
26 	struct aa_label *p;
27 
28 	rcu_read_lock();
29 	p = aa_get_newest_label(__aa_task_raw_label(task));
30 	rcu_read_unlock();
31 
32 	return p;
33 }
34 
35 /**
36  * aa_replace_current_label - replace the current tasks label
37  * @label: new label  (NOT NULL)
38  *
39  * Returns: 0 or error on failure
40  */
41 int aa_replace_current_label(struct aa_label *label)
42 {
43 	struct aa_label *old = aa_current_raw_label();
44 	struct aa_task_ctx *ctx = task_ctx(current);
45 	struct cred *new;
46 
47 	AA_BUG(!label);
48 
49 	if (old == label)
50 		return 0;
51 
52 	if (current_cred() != current_real_cred())
53 		return -EBUSY;
54 
55 	new  = prepare_creds();
56 	if (!new)
57 		return -ENOMEM;
58 
59 	if (ctx->nnp && label_is_stale(ctx->nnp)) {
60 		struct aa_label *tmp = ctx->nnp;
61 
62 		ctx->nnp = aa_get_newest_label(tmp);
63 		aa_put_label(tmp);
64 	}
65 	if (unconfined(label) || (labels_ns(old) != labels_ns(label)))
66 		/*
67 		 * if switching to unconfined or a different label namespace
68 		 * clear out context state
69 		 */
70 		aa_clear_task_ctx_trans(task_ctx(current));
71 
72 	/*
73 	 * be careful switching cred label, when racing replacement it
74 	 * is possible that the cred labels's->proxy->label is the reference
75 	 * keeping @label valid, so make sure to get its reference before
76 	 * dropping the reference on the cred's label
77 	 */
78 	aa_get_label(label);
79 	aa_put_label(cred_label(new));
80 	set_cred_label(new, label);
81 
82 	commit_creds(new);
83 	return 0;
84 }
85 
86 
87 /**
88  * aa_set_current_onexec - set the tasks change_profile to happen onexec
89  * @label: system label to set at exec  (MAYBE NULL to clear value)
90  * @stack: whether stacking should be done
91  * Returns: 0 or error on failure
92  */
93 int aa_set_current_onexec(struct aa_label *label, bool stack)
94 {
95 	struct aa_task_ctx *ctx = task_ctx(current);
96 
97 	aa_get_label(label);
98 	aa_put_label(ctx->onexec);
99 	ctx->onexec = label;
100 	ctx->token = stack;
101 
102 	return 0;
103 }
104 
105 /**
106  * aa_set_current_hat - set the current tasks hat
107  * @label: label to set as the current hat  (NOT NULL)
108  * @token: token value that must be specified to change from the hat
109  *
110  * Do switch of tasks hat.  If the task is currently in a hat
111  * validate the token to match.
112  *
113  * Returns: 0 or error on failure
114  */
115 int aa_set_current_hat(struct aa_label *label, u64 token)
116 {
117 	struct aa_task_ctx *ctx = task_ctx(current);
118 	struct cred *new;
119 
120 	new = prepare_creds();
121 	if (!new)
122 		return -ENOMEM;
123 	AA_BUG(!label);
124 
125 	if (!ctx->previous) {
126 		/* transfer refcount */
127 		ctx->previous = cred_label(new);
128 		ctx->token = token;
129 	} else if (ctx->token == token) {
130 		aa_put_label(cred_label(new));
131 	} else {
132 		/* previous_profile && ctx->token != token */
133 		abort_creds(new);
134 		return -EACCES;
135 	}
136 
137 	set_cred_label(new, aa_get_newest_label(label));
138 	/* clear exec on switching context */
139 	aa_put_label(ctx->onexec);
140 	ctx->onexec = NULL;
141 
142 	commit_creds(new);
143 	return 0;
144 }
145 
146 /**
147  * aa_restore_previous_label - exit from hat context restoring previous label
148  * @token: the token that must be matched to exit hat context
149  *
150  * Attempt to return out of a hat to the previous label.  The token
151  * must match the stored token value.
152  *
153  * Returns: 0 or error of failure
154  */
155 int aa_restore_previous_label(u64 token)
156 {
157 	struct aa_task_ctx *ctx = task_ctx(current);
158 	struct cred *new;
159 
160 	if (ctx->token != token)
161 		return -EACCES;
162 	/* ignore restores when there is no saved label */
163 	if (!ctx->previous)
164 		return 0;
165 
166 	new = prepare_creds();
167 	if (!new)
168 		return -ENOMEM;
169 
170 	aa_put_label(cred_label(new));
171 	set_cred_label(new, aa_get_newest_label(ctx->previous));
172 	AA_BUG(!cred_label(new));
173 	/* clear exec && prev information when restoring to previous context */
174 	aa_clear_task_ctx_trans(ctx);
175 
176 	commit_creds(new);
177 
178 	return 0;
179 }
180