projects/linux·blob master · refs

399 lines · 10.0 KiB

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 <linux/gfp.h>
16#include <linux/ptrace.h>
17#include <linux/task_work.h>
18
19#include "include/path.h"
20#include "include/audit.h"
21#include "include/cred.h"
22#include "include/policy.h"
23#include "include/task.h"
24
25/**
26 * aa_get_task_label - Get another task's label
27 * @task: task to query (NOT NULL)
28 *
29 * Returns: counted reference to @task's label
30 */
31struct aa_label *aa_get_task_label(struct task_struct *task)
32{
33 struct aa_label *p;
34
35 rcu_read_lock();
36 p = aa_get_newest_cred_label(__task_cred(task));
37 rcu_read_unlock();
38
39 return p;
40}
41
42/**
43 * aa_replace_current_label - replace the current tasks label
44 * @label: new label (NOT NULL)
45 *
46 * Returns: 0 or error on failure
47 */
48int aa_replace_current_label(struct aa_label *label)
49{
50 struct aa_label *old = aa_current_raw_label();
51 struct aa_task_ctx *ctx = task_ctx(current);
52 struct cred *new;
53
54 AA_BUG(!label);
55
56 if (old == label)
57 return 0;
58
59 if (current_cred() != current_real_cred())
60 return -EBUSY;
61
62 new = prepare_creds();
63 if (!new)
64 return -ENOMEM;
65
66 if (ctx->nnp && label_is_stale(ctx->nnp)) {
67 struct aa_label *tmp = ctx->nnp;
68
69 ctx->nnp = aa_get_newest_label(tmp);
70 aa_put_label(tmp);
71 }
72 if (unconfined(label) || (labels_ns(old) != labels_ns(label)))
73 /*
74 * if switching to unconfined or a different label namespace
75 * clear out context state
76 */
77 aa_clear_task_ctx_trans(task_ctx(current));
78
79 /*
80 * be careful switching cred label, when racing replacement it
81 * is possible that the cred labels's->proxy->label is the reference
82 * keeping @label valid, so make sure to get its reference before
83 * dropping the reference on the cred's label
84 */
85 aa_get_label(label);
86 aa_put_label(cred_label(new));
87 set_cred_label(new, label);
88
89 commit_creds(new);
90 return 0;
91}
92
93static void aa_replace_stale_label_tw_func(struct callback_head *tw)
94{
95 struct aa_task_ctx *ctx = task_ctx(current);
96 struct aa_label *label;
97
98 ctx->label_replacement_pending = false;
99 label = aa_current_raw_label();
100 if (!label_is_stale(label))
101 return;
102 label = aa_get_newest_label(label);
103 aa_replace_current_label(label);
104 aa_put_label(label);
105}
106
107/* replace the current task's stale label on syscall return */
108void aa_schedule_stale_label_replacement(void)
109{
110 struct aa_task_ctx *ctx = task_ctx(current);
111
112 if (ctx->label_replacement_pending)
113 return;
114 init_task_work(&ctx->label_replacement_tw, aa_replace_stale_label_tw_func);
115 if (task_work_add(current, &ctx->label_replacement_tw, TWA_RESUME) == 0)
116 ctx->label_replacement_pending = true;
117}
118
119
120/**
121 * aa_set_current_onexec - set the tasks change_profile to happen onexec
122 * @label: system label to set at exec (MAYBE NULL to clear value)
123 * @stack: whether stacking should be done
124 */
125void aa_set_current_onexec(struct aa_label *label, bool stack)
126{
127 struct aa_task_ctx *ctx = task_ctx(current);
128
129 aa_get_label(label);
130 aa_put_label(ctx->onexec);
131 ctx->onexec = label;
132 ctx->token = stack;
133}
134
135/**
136 * aa_set_current_hat - set the current tasks hat
137 * @label: label to set as the current hat (NOT NULL)
138 * @token: token value that must be specified to change from the hat
139 *
140 * Do switch of tasks hat. If the task is currently in a hat
141 * validate the token to match.
142 *
143 * Returns: 0 or error on failure
144 */
145int aa_set_current_hat(struct aa_label *label, u64 token)
146{
147 struct aa_task_ctx *ctx = task_ctx(current);
148 struct cred *new;
149
150 new = prepare_creds();
151 if (!new)
152 return -ENOMEM;
153 AA_BUG(!label);
154
155 if (!ctx->previous) {
156 /* transfer refcount */
157 ctx->previous = cred_label(new);
158 ctx->token = token;
159 } else if (ctx->token == token) {
160 aa_put_label(cred_label(new));
161 } else {
162 /* previous_profile && ctx->token != token */
163 abort_creds(new);
164 return -EACCES;
165 }
166
167 set_cred_label(new, aa_get_newest_label(label));
168 /* clear exec on switching context */
169 aa_put_label(ctx->onexec);
170 ctx->onexec = NULL;
171
172 commit_creds(new);
173 return 0;
174}
175
176/**
177 * aa_restore_previous_label - exit from hat context restoring previous label
178 * @token: the token that must be matched to exit hat context
179 *
180 * Attempt to return out of a hat to the previous label. The token
181 * must match the stored token value.
182 *
183 * Returns: 0 or error of failure
184 */
185int aa_restore_previous_label(u64 token)
186{
187 struct aa_task_ctx *ctx = task_ctx(current);
188 struct cred *new;
189
190 if (ctx->token != token)
191 return -EACCES;
192 /* ignore restores when there is no saved label */
193 if (!ctx->previous)
194 return 0;
195
196 new = prepare_creds();
197 if (!new)
198 return -ENOMEM;
199
200 aa_put_label(cred_label(new));
201 set_cred_label(new, aa_get_newest_label(ctx->previous));
202 AA_BUG(!cred_label(new));
203 /* clear exec && prev information when restoring to previous context */
204 aa_clear_task_ctx_trans(ctx);
205
206 commit_creds(new);
207
208 return 0;
209}
210
211/**
212 * audit_ptrace_mask - convert mask to permission string
213 * @mask: permission mask to convert
214 *
215 * Returns: pointer to static string
216 */
217static const char *audit_ptrace_mask(u32 mask)
218{
219 switch (mask) {
220 case MAY_READ:
221 return "read";
222 case MAY_WRITE:
223 return "trace";
224 case AA_MAY_BE_READ:
225 return "readby";
226 case AA_MAY_BE_TRACED:
227 return "tracedby";
228 }
229 return "";
230}
231
232/* call back to audit ptrace fields */
233static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
234{
235 struct common_audit_data *sa = va;
236 struct apparmor_audit_data *ad = aad(sa);
237
238 if (ad->request & AA_PTRACE_PERM_MASK) {
239 audit_log_format(ab, " requested_mask=\"%s\"",
240 audit_ptrace_mask(ad->request));
241
242 if (ad->denied & AA_PTRACE_PERM_MASK) {
243 audit_log_format(ab, " denied_mask=\"%s\"",
244 audit_ptrace_mask(ad->denied));
245 }
246 }
247 audit_log_format(ab, " peer=");
248 aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
249 FLAGS_NONE, GFP_ATOMIC);
250}
251
252/* assumes check for RULE_MEDIATES is already done */
253/* TODO: conditionals */
254static int profile_ptrace_perm(const struct cred *cred,
255 struct aa_profile *profile,
256 struct aa_label *peer, u32 request,
257 struct apparmor_audit_data *ad)
258{
259 struct aa_ruleset *rules = profile->label.rules[0];
260 struct aa_perms perms = { };
261
262 ad->subj_cred = cred;
263 ad->peer = peer;
264 aa_profile_match_label(profile, rules, peer, AA_CLASS_PTRACE, request,
265 &perms);
266 aa_apply_modes_to_perms(profile, &perms);
267 return aa_check_perms(profile, &perms, request, ad, audit_ptrace_cb);
268}
269
270static int profile_tracee_perm(const struct cred *cred,
271 struct aa_profile *tracee,
272 struct aa_label *tracer, u32 request,
273 struct apparmor_audit_data *ad)
274{
275 if (profile_unconfined(tracee) || unconfined(tracer) ||
276 !label_mediates(&tracee->label, AA_CLASS_PTRACE))
277 return 0;
278
279 return profile_ptrace_perm(cred, tracee, tracer, request, ad);
280}
281
282static int profile_tracer_perm(const struct cred *cred,
283 struct aa_profile *tracer,
284 struct aa_label *tracee, u32 request,
285 struct apparmor_audit_data *ad)
286{
287 if (profile_unconfined(tracer))
288 return 0;
289
290 if (label_mediates(&tracer->label, AA_CLASS_PTRACE))
291 return profile_ptrace_perm(cred, tracer, tracee, request, ad);
292
293 /* profile uses the old style capability check for ptrace */
294 if (&tracer->label == tracee)
295 return 0;
296
297 ad->subj_label = &tracer->label;
298 ad->peer = tracee;
299 ad->request = 0;
300 ad->error = aa_capable(cred, &tracer->label, CAP_SYS_PTRACE,
301 CAP_OPT_NONE);
302
303 return aa_audit(AUDIT_APPARMOR_AUTO, tracer, ad, audit_ptrace_cb);
304}
305
306/**
307 * aa_may_ptrace - test if tracer task can trace the tracee
308 * @tracer_cred: cred of task doing the tracing (NOT NULL)
309 * @tracer: label of the task doing the tracing (NOT NULL)
310 * @tracee_cred: cred of task to be traced
311 * @tracee: task label to be traced
312 * @request: permission request
313 *
314 * Returns: %0 else error code if permission denied or error
315 */
316int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer,
317 const struct cred *tracee_cred, struct aa_label *tracee,
318 u32 request)
319{
320 struct aa_profile *profile;
321 u32 xrequest = request << PTRACE_PERM_SHIFT;
322 DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_PTRACE, OP_PTRACE);
323
324 return xcheck_labels(tracer, tracee, profile,
325 profile_tracer_perm(tracer_cred, profile, tracee,
326 request, &sa),
327 profile_tracee_perm(tracee_cred, profile, tracer,
328 xrequest, &sa));
329}
330
331static const char *get_current_exe_path(char *buffer, int buffer_size)
332{
333 struct file *exe_file;
334 struct path p;
335 const char *path_str;
336
337 exe_file = get_task_exe_file(current);
338 if (!exe_file)
339 return ERR_PTR(-ENOENT);
340 p = exe_file->f_path;
341 path_get(&p);
342
343 if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL))
344 path_str = ERR_PTR(-ENOMEM);
345
346 fput(exe_file);
347 path_put(&p);
348
349 return path_str;
350}
351
352/* call back to audit ptrace fields */
353static void audit_ns_cb(struct audit_buffer *ab, void *va)
354{
355 struct apparmor_audit_data *ad = aad_of_va(va);
356 char *buffer;
357 const char *path;
358
359 if (ad->request & AA_USERNS_CREATE)
360 audit_log_format(ab, " requested=\"userns_create\"");
361
362 if (ad->denied & AA_USERNS_CREATE)
363 audit_log_format(ab, " denied=\"userns_create\"");
364
365 buffer = aa_get_buffer(false);
366 if (!buffer)
367 return; // OOM
368 path = get_current_exe_path(buffer, aa_g_path_max);
369 if (!IS_ERR(path))
370 audit_log_format(ab, " execpath=\"%s\"", path);
371 aa_put_buffer(buffer);
372}
373
374int aa_profile_ns_perm(struct aa_profile *profile,
375 struct apparmor_audit_data *ad,
376 u32 request)
377{
378 struct aa_perms perms = { };
379 int error = 0;
380
381 ad->subj_label = &profile->label;
382 ad->request = request;
383
384 if (!profile_unconfined(profile)) {
385 struct aa_ruleset *rules = profile->label.rules[0];
386 aa_state_t state;
387
388 state = RULE_MEDIATES(rules, ad->class);
389 if (!state)
390 /* TODO: add flag to complain about unmediated */
391 return 0;
392 perms = *aa_lookup_perms(rules->policy, state);
393 aa_apply_modes_to_perms(profile, &perms);
394 error = aa_check_perms(profile, &perms, request, ad,
395 audit_ns_cb);
396 }
397
398 return error;
399}