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