Merge tag 'for-linus-5.12b-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[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 "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 }