apparmor: move ptrace mediation to more logical task.{h,c}
[linux-2.6-microblaze.git] / security / apparmor / task.c
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
18 #include "include/audit.h"
19 #include "include/cred.h"
20 #include "include/policy.h"
21 #include "include/task.h"
22
23 /**
24  * aa_get_task_label - Get another task's label
25  * @task: task to query  (NOT NULL)
26  *
27  * Returns: counted reference to @task's label
28  */
29 struct aa_label *aa_get_task_label(struct task_struct *task)
30 {
31         struct aa_label *p;
32
33         rcu_read_lock();
34         p = aa_get_newest_label(__aa_task_raw_label(task));
35         rcu_read_unlock();
36
37         return p;
38 }
39
40 /**
41  * aa_replace_current_label - replace the current tasks label
42  * @label: new label  (NOT NULL)
43  *
44  * Returns: 0 or error on failure
45  */
46 int aa_replace_current_label(struct aa_label *label)
47 {
48         struct aa_label *old = aa_current_raw_label();
49         struct aa_task_ctx *ctx = task_ctx(current);
50         struct cred *new;
51
52         AA_BUG(!label);
53
54         if (old == label)
55                 return 0;
56
57         if (current_cred() != current_real_cred())
58                 return -EBUSY;
59
60         new  = prepare_creds();
61         if (!new)
62                 return -ENOMEM;
63
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         }
70         if (unconfined(label) || (labels_ns(old) != labels_ns(label)))
71                 /*
72                  * if switching to unconfined or a different label namespace
73                  * clear out context state
74                  */
75                 aa_clear_task_ctx_trans(task_ctx(current));
76
77         /*
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
82          */
83         aa_get_label(label);
84         aa_put_label(cred_label(new));
85         set_cred_label(new, label);
86
87         commit_creds(new);
88         return 0;
89 }
90
91
92 /**
93  * aa_set_current_onexec - set the tasks change_profile to happen onexec
94  * @label: system label to set at exec  (MAYBE NULL to clear value)
95  * @stack: whether stacking should be done
96  * Returns: 0 or error on failure
97  */
98 int aa_set_current_onexec(struct aa_label *label, bool stack)
99 {
100         struct aa_task_ctx *ctx = task_ctx(current);
101
102         aa_get_label(label);
103         aa_put_label(ctx->onexec);
104         ctx->onexec = label;
105         ctx->token = stack;
106
107         return 0;
108 }
109
110 /**
111  * aa_set_current_hat - set the current tasks hat
112  * @label: label to set as the current hat  (NOT NULL)
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  */
120 int aa_set_current_hat(struct aa_label *label, u64 token)
121 {
122         struct aa_task_ctx *ctx = task_ctx(current);
123         struct cred *new;
124
125         new = prepare_creds();
126         if (!new)
127                 return -ENOMEM;
128         AA_BUG(!label);
129
130         if (!ctx->previous) {
131                 /* transfer refcount */
132                 ctx->previous = cred_label(new);
133                 ctx->token = token;
134         } else if (ctx->token == token) {
135                 aa_put_label(cred_label(new));
136         } else {
137                 /* previous_profile && ctx->token != token */
138                 abort_creds(new);
139                 return -EACCES;
140         }
141
142         set_cred_label(new, aa_get_newest_label(label));
143         /* clear exec on switching context */
144         aa_put_label(ctx->onexec);
145         ctx->onexec = NULL;
146
147         commit_creds(new);
148         return 0;
149 }
150
151 /**
152  * aa_restore_previous_label - exit from hat context restoring previous label
153  * @token: the token that must be matched to exit hat context
154  *
155  * Attempt to return out of a hat to the previous label.  The token
156  * must match the stored token value.
157  *
158  * Returns: 0 or error of failure
159  */
160 int aa_restore_previous_label(u64 token)
161 {
162         struct aa_task_ctx *ctx = task_ctx(current);
163         struct cred *new;
164
165         if (ctx->token != token)
166                 return -EACCES;
167         /* ignore restores when there is no saved label */
168         if (!ctx->previous)
169                 return 0;
170
171         new = prepare_creds();
172         if (!new)
173                 return -ENOMEM;
174
175         aa_put_label(cred_label(new));
176         set_cred_label(new, aa_get_newest_label(ctx->previous));
177         AA_BUG(!cred_label(new));
178         /* clear exec && prev information when restoring to previous context */
179         aa_clear_task_ctx_trans(ctx);
180
181         commit_creds(new);
182
183         return 0;
184 }
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
226 /* assumes check for PROFILE_MEDIATES is already done */
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 {
232         struct aa_perms perms = { };
233
234         aad(sa)->peer = peer;
235         aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request,
236                                &perms);
237         aa_apply_modes_to_perms(profile, &perms);
238         return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
239 }
240
241 static int profile_tracee_perm(struct aa_profile *tracee,
242                                struct aa_label *tracer, u32 request,
243                                struct common_audit_data *sa)
244 {
245         if (profile_unconfined(tracee) || unconfined(tracer) ||
246             !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE))
247                 return 0;
248
249         return profile_ptrace_perm(tracee, tracer, request, sa);
250 }
251
252 static int profile_tracer_perm(struct aa_profile *tracer,
253                                struct aa_label *tracee, u32 request,
254                                struct common_audit_data *sa)
255 {
256         if (profile_unconfined(tracer))
257                 return 0;
258
259         if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE))
260                 return profile_ptrace_perm(tracer, tracee, request, sa);
261
262         /* profile uses the old style capability check for ptrace */
263         if (&tracer->label == tracee)
264                 return 0;
265
266         aad(sa)->label = &tracer->label;
267         aad(sa)->peer = tracee;
268         aad(sa)->request = 0;
269         aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE,
270                                     CAP_OPT_NONE);
271
272         return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
273 }
274
275 /**
276  * aa_may_ptrace - test if tracer task can trace the tracee
277  * @tracer: label of the task doing the tracing  (NOT NULL)
278  * @tracee: task label to be traced
279  * @request: permission request
280  *
281  * Returns: %0 else error code if permission denied or error
282  */
283 int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
284                   u32 request)
285 {
286         struct aa_profile *profile;
287         u32 xrequest = request << PTRACE_PERM_SHIFT;
288         DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
289
290         return xcheck_labels(tracer, tracee, profile,
291                         profile_tracer_perm(profile, tracee, request, &sa),
292                         profile_tracee_perm(profile, tracer, xrequest, &sa));
293 }