1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AppArmor security module |
4 | * |
5 | * This file contains AppArmor policy manipulation functions |
6 | * |
7 | * Copyright (C) 1998-2008 Novell/SUSE |
8 | * Copyright 2009-2017 Canonical Ltd. |
9 | * |
10 | * AppArmor policy namespaces, allow for different sets of policies |
11 | * to be loaded for tasks within the namespace. |
12 | */ |
13 | |
14 | #include <linux/list.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/string.h> |
18 | |
19 | #include "include/apparmor.h" |
20 | #include "include/cred.h" |
21 | #include "include/policy_ns.h" |
22 | #include "include/label.h" |
23 | #include "include/policy.h" |
24 | |
25 | /* kernel label */ |
26 | struct aa_label *kernel_t; |
27 | |
28 | /* root profile namespace */ |
29 | struct aa_ns *root_ns; |
30 | const char *aa_hidden_ns_name = "---" ; |
31 | |
32 | /** |
33 | * aa_ns_visible - test if @view is visible from @curr |
34 | * @curr: namespace to treat as the parent (NOT NULL) |
35 | * @view: namespace to test if visible from @curr (NOT NULL) |
36 | * @subns: whether view of a subns is allowed |
37 | * |
38 | * Returns: true if @view is visible from @curr else false |
39 | */ |
40 | bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns) |
41 | { |
42 | if (curr == view) |
43 | return true; |
44 | |
45 | if (!subns) |
46 | return false; |
47 | |
48 | for ( ; view; view = view->parent) { |
49 | if (view->parent == curr) |
50 | return true; |
51 | } |
52 | |
53 | return false; |
54 | } |
55 | |
56 | /** |
57 | * aa_ns_name - Find the ns name to display for @view from @curr |
58 | * @curr: current namespace (NOT NULL) |
59 | * @view: namespace attempting to view (NOT NULL) |
60 | * @subns: are subns visible |
61 | * |
62 | * Returns: name of @view visible from @curr |
63 | */ |
64 | const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns) |
65 | { |
66 | /* if view == curr then the namespace name isn't displayed */ |
67 | if (curr == view) |
68 | return "" ; |
69 | |
70 | if (aa_ns_visible(curr, view, subns)) { |
71 | /* at this point if a ns is visible it is in a view ns |
72 | * thus the curr ns.hname is a prefix of its name. |
73 | * Only output the virtualized portion of the name |
74 | * Add + 2 to skip over // separating curr hname prefix |
75 | * from the visible tail of the views hname |
76 | */ |
77 | return view->base.hname + strlen(curr->base.hname) + 2; |
78 | } |
79 | |
80 | return aa_hidden_ns_name; |
81 | } |
82 | |
83 | static struct aa_profile *alloc_unconfined(const char *name) |
84 | { |
85 | struct aa_profile *profile; |
86 | |
87 | profile = aa_alloc_null(NULL, name, GFP_KERNEL); |
88 | if (!profile) |
89 | return NULL; |
90 | |
91 | profile->label.flags |= FLAG_IX_ON_NAME_ERROR | |
92 | FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; |
93 | profile->mode = APPARMOR_UNCONFINED; |
94 | |
95 | return profile; |
96 | } |
97 | |
98 | /** |
99 | * alloc_ns - allocate, initialize and return a new namespace |
100 | * @prefix: parent namespace name (MAYBE NULL) |
101 | * @name: a preallocated name (NOT NULL) |
102 | * |
103 | * Returns: refcounted namespace or NULL on failure. |
104 | */ |
105 | static struct aa_ns *alloc_ns(const char *prefix, const char *name) |
106 | { |
107 | struct aa_ns *ns; |
108 | |
109 | ns = kzalloc(size: sizeof(*ns), GFP_KERNEL); |
110 | AA_DEBUG("%s(%p)\n" , __func__, ns); |
111 | if (!ns) |
112 | return NULL; |
113 | if (!aa_policy_init(policy: &ns->base, prefix, name, GFP_KERNEL)) |
114 | goto fail_ns; |
115 | |
116 | INIT_LIST_HEAD(list: &ns->sub_ns); |
117 | INIT_LIST_HEAD(list: &ns->rawdata_list); |
118 | mutex_init(&ns->lock); |
119 | init_waitqueue_head(&ns->wait); |
120 | |
121 | /* released by aa_free_ns() */ |
122 | ns->unconfined = alloc_unconfined(name: "unconfined" ); |
123 | if (!ns->unconfined) |
124 | goto fail_unconfined; |
125 | /* ns and ns->unconfined share ns->unconfined refcount */ |
126 | ns->unconfined->ns = ns; |
127 | |
128 | atomic_set(v: &ns->uniq_null, i: 0); |
129 | |
130 | aa_labelset_init(ls: &ns->labels); |
131 | |
132 | return ns; |
133 | |
134 | fail_unconfined: |
135 | aa_policy_destroy(policy: &ns->base); |
136 | fail_ns: |
137 | kfree_sensitive(objp: ns); |
138 | return NULL; |
139 | } |
140 | |
141 | /** |
142 | * aa_free_ns - free a profile namespace |
143 | * @ns: the namespace to free (MAYBE NULL) |
144 | * |
145 | * Requires: All references to the namespace must have been put, if the |
146 | * namespace was referenced by a profile confining a task, |
147 | */ |
148 | void aa_free_ns(struct aa_ns *ns) |
149 | { |
150 | if (!ns) |
151 | return; |
152 | |
153 | aa_policy_destroy(policy: &ns->base); |
154 | aa_labelset_destroy(ls: &ns->labels); |
155 | aa_put_ns(ns: ns->parent); |
156 | |
157 | ns->unconfined->ns = NULL; |
158 | aa_free_profile(profile: ns->unconfined); |
159 | kfree_sensitive(objp: ns); |
160 | } |
161 | |
162 | /** |
163 | * __aa_lookupn_ns - lookup the namespace matching @hname |
164 | * @view: namespace to search in (NOT NULL) |
165 | * @hname: hierarchical ns name (NOT NULL) |
166 | * @n: length of @hname |
167 | * |
168 | * Requires: rcu_read_lock be held |
169 | * |
170 | * Returns: unrefcounted ns pointer or NULL if not found |
171 | * |
172 | * Do a relative name lookup, recursing through profile tree. |
173 | */ |
174 | struct aa_ns *__aa_lookupn_ns(struct aa_ns *view, const char *hname, size_t n) |
175 | { |
176 | struct aa_ns *ns = view; |
177 | const char *split; |
178 | |
179 | for (split = strnstr(hname, "//" , n); split; |
180 | split = strnstr(hname, "//" , n)) { |
181 | ns = __aa_findn_ns(head: &ns->sub_ns, name: hname, n: split - hname); |
182 | if (!ns) |
183 | return NULL; |
184 | |
185 | n -= split + 2 - hname; |
186 | hname = split + 2; |
187 | } |
188 | |
189 | if (n) |
190 | return __aa_findn_ns(head: &ns->sub_ns, name: hname, n); |
191 | return NULL; |
192 | } |
193 | |
194 | /** |
195 | * aa_lookupn_ns - look up a policy namespace relative to @view |
196 | * @view: namespace to search in (NOT NULL) |
197 | * @name: name of namespace to find (NOT NULL) |
198 | * @n: length of @name |
199 | * |
200 | * Returns: a refcounted namespace on the list, or NULL if no namespace |
201 | * called @name exists. |
202 | * |
203 | * refcount released by caller |
204 | */ |
205 | struct aa_ns *aa_lookupn_ns(struct aa_ns *view, const char *name, size_t n) |
206 | { |
207 | struct aa_ns *ns = NULL; |
208 | |
209 | rcu_read_lock(); |
210 | ns = aa_get_ns(ns: __aa_lookupn_ns(view, hname: name, n)); |
211 | rcu_read_unlock(); |
212 | |
213 | return ns; |
214 | } |
215 | |
216 | static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, |
217 | struct dentry *dir) |
218 | { |
219 | struct aa_ns *ns; |
220 | int error; |
221 | |
222 | AA_BUG(!parent); |
223 | AA_BUG(!name); |
224 | AA_BUG(!mutex_is_locked(&parent->lock)); |
225 | |
226 | ns = alloc_ns(prefix: parent->base.hname, name); |
227 | if (!ns) |
228 | return ERR_PTR(error: -ENOMEM); |
229 | ns->level = parent->level + 1; |
230 | mutex_lock_nested(lock: &ns->lock, subclass: ns->level); |
231 | error = __aafs_ns_mkdir(ns, ns_subns_dir(parent), name, dent: dir); |
232 | if (error) { |
233 | AA_ERROR("Failed to create interface for ns %s\n" , |
234 | ns->base.name); |
235 | mutex_unlock(lock: &ns->lock); |
236 | aa_free_ns(ns); |
237 | return ERR_PTR(error); |
238 | } |
239 | ns->parent = aa_get_ns(ns: parent); |
240 | list_add_rcu(new: &ns->base.list, head: &parent->sub_ns); |
241 | /* add list ref */ |
242 | aa_get_ns(ns); |
243 | mutex_unlock(lock: &ns->lock); |
244 | |
245 | return ns; |
246 | } |
247 | |
248 | /** |
249 | * __aa_find_or_create_ns - create an ns, fail if it already exists |
250 | * @parent: the parent of the namespace being created |
251 | * @name: the name of the namespace |
252 | * @dir: if not null the dir to put the ns entries in |
253 | * |
254 | * Returns: the a refcounted ns that has been add or an ERR_PTR |
255 | */ |
256 | struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, |
257 | struct dentry *dir) |
258 | { |
259 | struct aa_ns *ns; |
260 | |
261 | AA_BUG(!mutex_is_locked(&parent->lock)); |
262 | |
263 | /* try and find the specified ns */ |
264 | /* released by caller */ |
265 | ns = aa_get_ns(ns: __aa_find_ns(head: &parent->sub_ns, name)); |
266 | if (!ns) |
267 | ns = __aa_create_ns(parent, name, dir); |
268 | else |
269 | ns = ERR_PTR(error: -EEXIST); |
270 | |
271 | /* return ref */ |
272 | return ns; |
273 | } |
274 | |
275 | /** |
276 | * aa_prepare_ns - find an existing or create a new namespace of @name |
277 | * @parent: ns to treat as parent |
278 | * @name: the namespace to find or add (NOT NULL) |
279 | * |
280 | * Returns: refcounted namespace or PTR_ERR if failed to create one |
281 | */ |
282 | struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name) |
283 | { |
284 | struct aa_ns *ns; |
285 | |
286 | mutex_lock_nested(lock: &parent->lock, subclass: parent->level); |
287 | /* try and find the specified ns and if it doesn't exist create it */ |
288 | /* released by caller */ |
289 | ns = aa_get_ns(ns: __aa_find_ns(head: &parent->sub_ns, name)); |
290 | if (!ns) |
291 | ns = __aa_create_ns(parent, name, NULL); |
292 | mutex_unlock(lock: &parent->lock); |
293 | |
294 | /* return ref */ |
295 | return ns; |
296 | } |
297 | |
298 | static void __ns_list_release(struct list_head *head); |
299 | |
300 | /** |
301 | * destroy_ns - remove everything contained by @ns |
302 | * @ns: namespace to have it contents removed (NOT NULL) |
303 | */ |
304 | static void destroy_ns(struct aa_ns *ns) |
305 | { |
306 | if (!ns) |
307 | return; |
308 | |
309 | mutex_lock_nested(lock: &ns->lock, subclass: ns->level); |
310 | /* release all profiles in this namespace */ |
311 | __aa_profile_list_release(head: &ns->base.profiles); |
312 | |
313 | /* release all sub namespaces */ |
314 | __ns_list_release(head: &ns->sub_ns); |
315 | |
316 | if (ns->parent) { |
317 | unsigned long flags; |
318 | |
319 | write_lock_irqsave(&ns->labels.lock, flags); |
320 | __aa_proxy_redirect(ns_unconfined(ns), |
321 | ns_unconfined(ns->parent)); |
322 | write_unlock_irqrestore(&ns->labels.lock, flags); |
323 | } |
324 | __aafs_ns_rmdir(ns); |
325 | mutex_unlock(lock: &ns->lock); |
326 | } |
327 | |
328 | /** |
329 | * __aa_remove_ns - remove a namespace and all its children |
330 | * @ns: namespace to be removed (NOT NULL) |
331 | * |
332 | * Requires: ns->parent->lock be held and ns removed from parent. |
333 | */ |
334 | void __aa_remove_ns(struct aa_ns *ns) |
335 | { |
336 | /* remove ns from namespace list */ |
337 | list_del_rcu(entry: &ns->base.list); |
338 | destroy_ns(ns); |
339 | aa_put_ns(ns); |
340 | } |
341 | |
342 | /** |
343 | * __ns_list_release - remove all profile namespaces on the list put refs |
344 | * @head: list of profile namespaces (NOT NULL) |
345 | * |
346 | * Requires: namespace lock be held |
347 | */ |
348 | static void __ns_list_release(struct list_head *head) |
349 | { |
350 | struct aa_ns *ns, *tmp; |
351 | |
352 | list_for_each_entry_safe(ns, tmp, head, base.list) |
353 | __aa_remove_ns(ns); |
354 | |
355 | } |
356 | |
357 | /** |
358 | * aa_alloc_root_ns - allocate the root profile namespace |
359 | * |
360 | * Returns: %0 on success else error |
361 | * |
362 | */ |
363 | int __init aa_alloc_root_ns(void) |
364 | { |
365 | struct aa_profile *kernel_p; |
366 | |
367 | /* released by aa_free_root_ns - used as list ref*/ |
368 | root_ns = alloc_ns(NULL, name: "root" ); |
369 | if (!root_ns) |
370 | return -ENOMEM; |
371 | |
372 | kernel_p = alloc_unconfined(name: "kernel_t" ); |
373 | if (!kernel_p) { |
374 | destroy_ns(ns: root_ns); |
375 | aa_free_ns(ns: root_ns); |
376 | return -ENOMEM; |
377 | } |
378 | kernel_t = &kernel_p->label; |
379 | root_ns->unconfined->ns = aa_get_ns(ns: root_ns); |
380 | |
381 | return 0; |
382 | } |
383 | |
384 | /** |
385 | * aa_free_root_ns - free the root profile namespace |
386 | */ |
387 | void __init aa_free_root_ns(void) |
388 | { |
389 | struct aa_ns *ns = root_ns; |
390 | |
391 | root_ns = NULL; |
392 | |
393 | aa_label_free(label: kernel_t); |
394 | destroy_ns(ns); |
395 | aa_put_ns(ns); |
396 | } |
397 | |