Commit | Line | Data |
---|---|---|
c75afcd1 JJ |
1 | /* |
2 | * AppArmor security module | |
3 | * | |
4 | * This file contains AppArmor functions used to manipulate object security | |
5 | * contexts. | |
6 | * | |
7 | * Copyright (C) 1998-2008 Novell/SUSE | |
8 | * Copyright 2009-2010 Canonical Ltd. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU General Public License as | |
12 | * published by the Free Software Foundation, version 2 of the | |
13 | * License. | |
14 | * | |
15 | * | |
16 | * AppArmor sets confinement on every task, via the the aa_task_cxt and | |
17 | * the aa_task_cxt.profile, both of which are required and are not allowed | |
18 | * to be NULL. The aa_task_cxt is not reference counted and is unique | |
19 | * to each cred (which is reference count). The profile pointed to by | |
20 | * the task_cxt is reference counted. | |
21 | * | |
22 | * TODO | |
23 | * If a task uses change_hat it currently does not return to the old | |
24 | * cred or task context but instead creates a new one. Ideally the task | |
25 | * should return to the previous cred if it has not been modified. | |
26 | * | |
27 | */ | |
28 | ||
29 | #include "include/context.h" | |
30 | #include "include/policy.h" | |
31 | ||
32 | /** | |
33 | * aa_alloc_task_context - allocate a new task_cxt | |
34 | * @flags: gfp flags for allocation | |
35 | * | |
36 | * Returns: allocated buffer or NULL on failure | |
37 | */ | |
38 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) | |
39 | { | |
40 | return kzalloc(sizeof(struct aa_task_cxt), flags); | |
41 | } | |
42 | ||
43 | /** | |
44 | * aa_free_task_context - free a task_cxt | |
45 | * @cxt: task_cxt to free (MAYBE NULL) | |
46 | */ | |
47 | void aa_free_task_context(struct aa_task_cxt *cxt) | |
48 | { | |
49 | if (cxt) { | |
50 | aa_put_profile(cxt->profile); | |
51 | aa_put_profile(cxt->previous); | |
52 | aa_put_profile(cxt->onexec); | |
53 | ||
54 | kzfree(cxt); | |
55 | } | |
56 | } | |
57 | ||
58 | /** | |
59 | * aa_dup_task_context - duplicate a task context, incrementing reference counts | |
60 | * @new: a blank task context (NOT NULL) | |
61 | * @old: the task context to copy (NOT NULL) | |
62 | */ | |
63 | void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) | |
64 | { | |
65 | *new = *old; | |
66 | aa_get_profile(new->profile); | |
67 | aa_get_profile(new->previous); | |
68 | aa_get_profile(new->onexec); | |
69 | } | |
70 | ||
71 | /** | |
72 | * aa_replace_current_profile - replace the current tasks profiles | |
73 | * @profile: new profile (NOT NULL) | |
74 | * | |
75 | * Returns: 0 or error on failure | |
76 | */ | |
77 | int aa_replace_current_profile(struct aa_profile *profile) | |
78 | { | |
79 | struct aa_task_cxt *cxt = current_cred()->security; | |
80 | struct cred *new; | |
81 | BUG_ON(!profile); | |
82 | ||
83 | if (cxt->profile == profile) | |
84 | return 0; | |
85 | ||
86 | new = prepare_creds(); | |
87 | if (!new) | |
88 | return -ENOMEM; | |
89 | ||
90 | cxt = new->security; | |
91 | if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { | |
92 | /* if switching to unconfined or a different profile namespace | |
93 | * clear out context state | |
94 | */ | |
95 | aa_put_profile(cxt->previous); | |
96 | aa_put_profile(cxt->onexec); | |
97 | cxt->previous = NULL; | |
98 | cxt->onexec = NULL; | |
99 | cxt->token = 0; | |
100 | } | |
101 | /* be careful switching cxt->profile, when racing replacement it | |
102 | * is possible that cxt->profile->replacedby is the reference keeping | |
103 | * @profile valid, so make sure to get its reference before dropping | |
104 | * the reference on cxt->profile */ | |
105 | aa_get_profile(profile); | |
106 | aa_put_profile(cxt->profile); | |
107 | cxt->profile = profile; | |
108 | ||
109 | commit_creds(new); | |
110 | return 0; | |
111 | } | |
112 | ||
113 | /** | |
114 | * aa_set_current_onexec - set the tasks change_profile to happen onexec | |
115 | * @profile: system profile to set at exec (MAYBE NULL to clear value) | |
116 | * | |
117 | * Returns: 0 or error on failure | |
118 | */ | |
119 | int aa_set_current_onexec(struct aa_profile *profile) | |
120 | { | |
121 | struct aa_task_cxt *cxt; | |
122 | struct cred *new = prepare_creds(); | |
123 | if (!new) | |
124 | return -ENOMEM; | |
125 | ||
126 | cxt = new->security; | |
127 | aa_get_profile(profile); | |
128 | aa_put_profile(cxt->onexec); | |
129 | cxt->onexec = profile; | |
130 | ||
131 | commit_creds(new); | |
132 | return 0; | |
133 | } | |
134 | ||
135 | /** | |
136 | * aa_set_current_hat - set the current tasks hat | |
137 | * @profile: profile 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 | */ | |
145 | int aa_set_current_hat(struct aa_profile *profile, u64 token) | |
146 | { | |
147 | struct aa_task_cxt *cxt; | |
148 | struct cred *new = prepare_creds(); | |
149 | if (!new) | |
150 | return -ENOMEM; | |
151 | BUG_ON(!profile); | |
152 | ||
153 | cxt = new->security; | |
154 | if (!cxt->previous) { | |
155 | /* transfer refcount */ | |
156 | cxt->previous = cxt->profile; | |
157 | cxt->token = token; | |
158 | } else if (cxt->token == token) { | |
159 | aa_put_profile(cxt->profile); | |
160 | } else { | |
161 | /* previous_profile && cxt->token != token */ | |
162 | abort_creds(new); | |
163 | return -EACCES; | |
164 | } | |
165 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | |
166 | /* clear exec on switching context */ | |
167 | aa_put_profile(cxt->onexec); | |
168 | cxt->onexec = NULL; | |
169 | ||
170 | commit_creds(new); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | /** | |
175 | * aa_restore_previous_profile - exit from hat context restoring the profile | |
176 | * @token: the token that must be matched to exit hat context | |
177 | * | |
178 | * Attempt to return out of a hat to the previous profile. The token | |
179 | * must match the stored token value. | |
180 | * | |
181 | * Returns: 0 or error of failure | |
182 | */ | |
183 | int aa_restore_previous_profile(u64 token) | |
184 | { | |
185 | struct aa_task_cxt *cxt; | |
186 | struct cred *new = prepare_creds(); | |
187 | if (!new) | |
188 | return -ENOMEM; | |
189 | ||
190 | cxt = new->security; | |
191 | if (cxt->token != token) { | |
192 | abort_creds(new); | |
193 | return -EACCES; | |
194 | } | |
195 | /* ignore restores when there is no saved profile */ | |
196 | if (!cxt->previous) { | |
197 | abort_creds(new); | |
198 | return 0; | |
199 | } | |
200 | ||
201 | aa_put_profile(cxt->profile); | |
202 | cxt->profile = aa_newest_version(cxt->previous); | |
203 | BUG_ON(!cxt->profile); | |
204 | if (unlikely(cxt->profile != cxt->previous)) { | |
205 | aa_get_profile(cxt->profile); | |
206 | aa_put_profile(cxt->previous); | |
207 | } | |
208 | /* clear exec && prev information when restoring to previous context */ | |
209 | cxt->previous = NULL; | |
210 | cxt->token = 0; | |
211 | aa_put_profile(cxt->onexec); | |
212 | cxt->onexec = NULL; | |
213 | ||
214 | commit_creds(new); | |
215 | return 0; | |
216 | } |