Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c75afcd1 JJ |
2 | /* |
3 | * AppArmor security module | |
4 | * | |
de62de59 | 5 | * This file contains AppArmor task related definitions and mediation |
c75afcd1 | 6 | * |
de62de59 | 7 | * Copyright 2017 Canonical Ltd. |
c75afcd1 | 8 | * |
c75afcd1 JJ |
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. | |
c75afcd1 JJ |
13 | */ |
14 | ||
eac93125 JJ |
15 | #include <linux/gfp.h> |
16 | #include <linux/ptrace.h> | |
17 | ||
18 | #include "include/audit.h" | |
d8889d49 | 19 | #include "include/cred.h" |
eac93125 | 20 | #include "include/policy.h" |
de62de59 | 21 | #include "include/task.h" |
c75afcd1 | 22 | |
3cfcc19e | 23 | /** |
637f688d | 24 | * aa_get_task_label - Get another task's label |
3cfcc19e JJ |
25 | * @task: task to query (NOT NULL) |
26 | * | |
637f688d | 27 | * Returns: counted reference to @task's label |
3cfcc19e | 28 | */ |
637f688d | 29 | struct aa_label *aa_get_task_label(struct task_struct *task) |
3cfcc19e | 30 | { |
637f688d | 31 | struct aa_label *p; |
3cfcc19e JJ |
32 | |
33 | rcu_read_lock(); | |
adaa9a3f | 34 | p = aa_get_newest_cred_label(__task_cred(task)); |
3cfcc19e JJ |
35 | rcu_read_unlock(); |
36 | ||
37 | return p; | |
38 | } | |
39 | ||
c75afcd1 | 40 | /** |
637f688d JJ |
41 | * aa_replace_current_label - replace the current tasks label |
42 | * @label: new label (NOT NULL) | |
c75afcd1 JJ |
43 | * |
44 | * Returns: 0 or error on failure | |
45 | */ | |
637f688d | 46 | int aa_replace_current_label(struct aa_label *label) |
c75afcd1 | 47 | { |
d9087c49 | 48 | struct aa_label *old = aa_current_raw_label(); |
9fcf78cc | 49 | struct aa_task_ctx *ctx = task_ctx(current); |
c75afcd1 | 50 | struct cred *new; |
d9087c49 | 51 | |
637f688d | 52 | AA_BUG(!label); |
c75afcd1 | 53 | |
d9087c49 | 54 | if (old == label) |
c75afcd1 JJ |
55 | return 0; |
56 | ||
a20aa95f JJ |
57 | if (current_cred() != current_real_cred()) |
58 | return -EBUSY; | |
59 | ||
c75afcd1 JJ |
60 | new = prepare_creds(); |
61 | if (!new) | |
62 | return -ENOMEM; | |
63 | ||
9fcf78cc JJ |
64 | if (ctx->nnp && label_is_stale(ctx->nnp)) { |
65 | struct aa_label *tmp = ctx->nnp; | |
66 | ||
67 | ctx->nnp = aa_get_newest_label(tmp); | |
68 | aa_put_label(tmp); | |
69 | } | |
d9087c49 JJ |
70 | if (unconfined(label) || (labels_ns(old) != labels_ns(label))) |
71 | /* | |
72 | * if switching to unconfined or a different label namespace | |
c75afcd1 JJ |
73 | * clear out context state |
74 | */ | |
de62de59 | 75 | aa_clear_task_ctx_trans(task_ctx(current)); |
7a2871b5 | 76 | |
55a26ebf | 77 | /* |
d9087c49 JJ |
78 | * be careful switching cred label, when racing replacement it |
79 | * is possible that the cred labels's->proxy->label is the reference | |
80 | * keeping @label valid, so make sure to get its reference before | |
81 | * dropping the reference on the cred's label | |
55a26ebf | 82 | */ |
637f688d | 83 | aa_get_label(label); |
d9087c49 | 84 | aa_put_label(cred_label(new)); |
69b5a44a | 85 | set_cred_label(new, label); |
c75afcd1 JJ |
86 | |
87 | commit_creds(new); | |
88 | return 0; | |
89 | } | |
90 | ||
de62de59 | 91 | |
c75afcd1 JJ |
92 | /** |
93 | * aa_set_current_onexec - set the tasks change_profile to happen onexec | |
637f688d JJ |
94 | * @label: system label to set at exec (MAYBE NULL to clear value) |
95 | * @stack: whether stacking should be done | |
c75afcd1 JJ |
96 | * Returns: 0 or error on failure |
97 | */ | |
637f688d | 98 | int aa_set_current_onexec(struct aa_label *label, bool stack) |
c75afcd1 | 99 | { |
de62de59 | 100 | struct aa_task_ctx *ctx = task_ctx(current); |
c75afcd1 | 101 | |
637f688d | 102 | aa_get_label(label); |
3b529a76 | 103 | aa_put_label(ctx->onexec); |
637f688d JJ |
104 | ctx->onexec = label; |
105 | ctx->token = stack; | |
c75afcd1 | 106 | |
c75afcd1 JJ |
107 | return 0; |
108 | } | |
109 | ||
110 | /** | |
111 | * aa_set_current_hat - set the current tasks hat | |
637f688d | 112 | * @label: label to set as the current hat (NOT NULL) |
c75afcd1 JJ |
113 | * @token: token value that must be specified to change from the hat |
114 | * | |
115 | * Do switch of tasks hat. If the task is currently in a hat | |
116 | * validate the token to match. | |
117 | * | |
118 | * Returns: 0 or error on failure | |
119 | */ | |
637f688d | 120 | int aa_set_current_hat(struct aa_label *label, u64 token) |
c75afcd1 | 121 | { |
de62de59 | 122 | struct aa_task_ctx *ctx = task_ctx(current); |
d9087c49 | 123 | struct cred *new; |
3b529a76 | 124 | |
d9087c49 | 125 | new = prepare_creds(); |
c75afcd1 JJ |
126 | if (!new) |
127 | return -ENOMEM; | |
637f688d | 128 | AA_BUG(!label); |
c75afcd1 | 129 | |
f175221a | 130 | if (!ctx->previous) { |
c75afcd1 | 131 | /* transfer refcount */ |
f175221a JJ |
132 | ctx->previous = cred_label(new); |
133 | ctx->token = token; | |
134 | } else if (ctx->token == token) { | |
d9087c49 | 135 | aa_put_label(cred_label(new)); |
c75afcd1 | 136 | } else { |
55a26ebf | 137 | /* previous_profile && ctx->token != token */ |
c75afcd1 JJ |
138 | abort_creds(new); |
139 | return -EACCES; | |
140 | } | |
3b529a76 | 141 | |
69b5a44a | 142 | set_cred_label(new, aa_get_newest_label(label)); |
c75afcd1 | 143 | /* clear exec on switching context */ |
f175221a JJ |
144 | aa_put_label(ctx->onexec); |
145 | ctx->onexec = NULL; | |
c75afcd1 JJ |
146 | |
147 | commit_creds(new); | |
148 | return 0; | |
149 | } | |
150 | ||
151 | /** | |
637f688d | 152 | * aa_restore_previous_label - exit from hat context restoring previous label |
c75afcd1 JJ |
153 | * @token: the token that must be matched to exit hat context |
154 | * | |
637f688d | 155 | * Attempt to return out of a hat to the previous label. The token |
c75afcd1 JJ |
156 | * must match the stored token value. |
157 | * | |
158 | * Returns: 0 or error of failure | |
159 | */ | |
637f688d | 160 | int aa_restore_previous_label(u64 token) |
c75afcd1 | 161 | { |
de62de59 | 162 | struct aa_task_ctx *ctx = task_ctx(current); |
3b529a76 | 163 | struct cred *new; |
c75afcd1 | 164 | |
f175221a | 165 | if (ctx->token != token) |
c75afcd1 | 166 | return -EACCES; |
637f688d | 167 | /* ignore restores when there is no saved label */ |
f175221a | 168 | if (!ctx->previous) |
c75afcd1 | 169 | return 0; |
3b529a76 JJ |
170 | |
171 | new = prepare_creds(); | |
172 | if (!new) | |
173 | return -ENOMEM; | |
c75afcd1 | 174 | |
d9087c49 | 175 | aa_put_label(cred_label(new)); |
69b5a44a | 176 | set_cred_label(new, aa_get_newest_label(ctx->previous)); |
d9087c49 | 177 | AA_BUG(!cred_label(new)); |
7a2871b5 | 178 | /* clear exec && prev information when restoring to previous context */ |
f175221a | 179 | aa_clear_task_ctx_trans(ctx); |
c75afcd1 JJ |
180 | |
181 | commit_creds(new); | |
3b529a76 | 182 | |
c75afcd1 JJ |
183 | return 0; |
184 | } | |
eac93125 JJ |
185 | |
186 | /** | |
187 | * audit_ptrace_mask - convert mask to permission string | |
188 | * @mask: permission mask to convert | |
189 | * | |
190 | * Returns: pointer to static string | |
191 | */ | |
192 | static const char *audit_ptrace_mask(u32 mask) | |
193 | { | |
194 | switch (mask) { | |
195 | case MAY_READ: | |
196 | return "read"; | |
197 | case MAY_WRITE: | |
198 | return "trace"; | |
199 | case AA_MAY_BE_READ: | |
200 | return "readby"; | |
201 | case AA_MAY_BE_TRACED: | |
202 | return "tracedby"; | |
203 | } | |
204 | return ""; | |
205 | } | |
206 | ||
207 | /* call back to audit ptrace fields */ | |
208 | static void audit_ptrace_cb(struct audit_buffer *ab, void *va) | |
209 | { | |
210 | struct common_audit_data *sa = va; | |
211 | ||
212 | if (aad(sa)->request & AA_PTRACE_PERM_MASK) { | |
213 | audit_log_format(ab, " requested_mask=\"%s\"", | |
214 | audit_ptrace_mask(aad(sa)->request)); | |
215 | ||
216 | if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { | |
217 | audit_log_format(ab, " denied_mask=\"%s\"", | |
218 | audit_ptrace_mask(aad(sa)->denied)); | |
219 | } | |
220 | } | |
221 | audit_log_format(ab, " peer="); | |
222 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, | |
223 | FLAGS_NONE, GFP_ATOMIC); | |
224 | } | |
225 | ||
217af7e2 | 226 | /* assumes check for RULE_MEDIATES is already done */ |
eac93125 JJ |
227 | /* TODO: conditionals */ |
228 | static int profile_ptrace_perm(struct aa_profile *profile, | |
229 | struct aa_label *peer, u32 request, | |
230 | struct common_audit_data *sa) | |
231 | { | |
1ad22fcc JJ |
232 | struct aa_ruleset *rules = list_first_entry(&profile->rules, |
233 | typeof(*rules), list); | |
eac93125 JJ |
234 | struct aa_perms perms = { }; |
235 | ||
236 | aad(sa)->peer = peer; | |
1ad22fcc JJ |
237 | aa_profile_match_label(profile, rules, peer, AA_CLASS_PTRACE, request, |
238 | &perms); | |
eac93125 JJ |
239 | aa_apply_modes_to_perms(profile, &perms); |
240 | return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); | |
241 | } | |
242 | ||
243 | static int profile_tracee_perm(struct aa_profile *tracee, | |
244 | struct aa_label *tracer, u32 request, | |
245 | struct common_audit_data *sa) | |
246 | { | |
247 | if (profile_unconfined(tracee) || unconfined(tracer) || | |
1ad22fcc | 248 | !ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE)) |
eac93125 JJ |
249 | return 0; |
250 | ||
251 | return profile_ptrace_perm(tracee, tracer, request, sa); | |
252 | } | |
253 | ||
254 | static int profile_tracer_perm(struct aa_profile *tracer, | |
255 | struct aa_label *tracee, u32 request, | |
256 | struct common_audit_data *sa) | |
257 | { | |
258 | if (profile_unconfined(tracer)) | |
259 | return 0; | |
260 | ||
1ad22fcc | 261 | if (ANY_RULE_MEDIATES(&tracer->rules, AA_CLASS_PTRACE)) |
eac93125 JJ |
262 | return profile_ptrace_perm(tracer, tracee, request, sa); |
263 | ||
264 | /* profile uses the old style capability check for ptrace */ | |
265 | if (&tracer->label == tracee) | |
266 | return 0; | |
267 | ||
268 | aad(sa)->label = &tracer->label; | |
269 | aad(sa)->peer = tracee; | |
270 | aad(sa)->request = 0; | |
271 | aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, | |
272 | CAP_OPT_NONE); | |
273 | ||
274 | return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); | |
275 | } | |
276 | ||
277 | /** | |
278 | * aa_may_ptrace - test if tracer task can trace the tracee | |
279 | * @tracer: label of the task doing the tracing (NOT NULL) | |
280 | * @tracee: task label to be traced | |
281 | * @request: permission request | |
282 | * | |
283 | * Returns: %0 else error code if permission denied or error | |
284 | */ | |
285 | int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, | |
286 | u32 request) | |
287 | { | |
288 | struct aa_profile *profile; | |
289 | u32 xrequest = request << PTRACE_PERM_SHIFT; | |
8c4b785a | 290 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_PTRACE, OP_PTRACE); |
eac93125 JJ |
291 | |
292 | return xcheck_labels(tracer, tracee, profile, | |
293 | profile_tracer_perm(profile, tracee, request, &sa), | |
294 | profile_tracee_perm(profile, tracer, xrequest, &sa)); | |
295 | } |