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-2010 Canonical Ltd. |
9 | * |
10 | * AppArmor policy is based around profiles, which contain the rules a |
11 | * task is confined by. Every task in the system has a profile attached |
12 | * to it determined either by matching "unconfined" tasks against the |
13 | * visible set of profiles or by following a profiles attachment rules. |
14 | * |
15 | * Each profile exists in a profile namespace which is a container of |
16 | * visible profiles. Each namespace contains a special "unconfined" profile, |
17 | * which doesn't enforce any confinement on a task beyond DAC. |
18 | * |
19 | * Namespace and profile names can be written together in either |
20 | * of two syntaxes. |
21 | * :namespace:profile - used by kernel interfaces for easy detection |
22 | * namespace://profile - used by policy |
23 | * |
24 | * Profile names can not start with : or @ or ^ and may not contain \0 |
25 | * |
26 | * Reserved profile names |
27 | * unconfined - special automatically generated unconfined profile |
28 | * inherit - special name to indicate profile inheritance |
29 | * null-XXXX-YYYY - special automatically generated learning profiles |
30 | * |
31 | * Namespace names may not start with / or @ and may not contain \0 or : |
32 | * Reserved namespace names |
33 | * user-XXXX - user defined profiles |
34 | * |
35 | * a // in a profile or namespace name indicates a hierarchical name with the |
36 | * name before the // being the parent and the name after the child. |
37 | * |
38 | * Profile and namespace hierarchies serve two different but similar purposes. |
39 | * The namespace contains the set of visible profiles that are considered |
40 | * for attachment. The hierarchy of namespaces allows for virtualizing |
41 | * the namespace so that for example a chroot can have its own set of profiles |
42 | * which may define some local user namespaces. |
43 | * The profile hierarchy severs two distinct purposes, |
44 | * - it allows for sub profiles or hats, which allows an application to run |
45 | * subprograms under its own profile with different restriction than it |
46 | * self, and not have it use the system profile. |
47 | * eg. if a mail program starts an editor, the policy might make the |
48 | * restrictions tighter on the editor tighter than the mail program, |
49 | * and definitely different than general editor restrictions |
50 | * - it allows for binary hierarchy of profiles, so that execution history |
51 | * is preserved. This feature isn't exploited by AppArmor reference policy |
52 | * but is allowed. NOTE: this is currently suboptimal because profile |
53 | * aliasing is not currently implemented so that a profile for each |
54 | * level must be defined. |
55 | * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started |
56 | * from /bin/bash |
57 | * |
58 | * A profile or namespace name that can contain one or more // separators |
59 | * is referred to as an hname (hierarchical). |
60 | * eg. /bin/bash//bin/ls |
61 | * |
62 | * An fqname is a name that may contain both namespace and profile hnames. |
63 | * eg. :ns:/bin/bash//bin/ls |
64 | * |
65 | * NOTES: |
66 | * - locking of profile lists is currently fairly coarse. All profile |
67 | * lists within a namespace use the namespace lock. |
68 | * FIXME: move profile lists to using rcu_lists |
69 | */ |
70 | |
71 | #include <linux/slab.h> |
72 | #include <linux/spinlock.h> |
73 | #include <linux/string.h> |
74 | #include <linux/cred.h> |
75 | #include <linux/rculist.h> |
76 | #include <linux/user_namespace.h> |
77 | |
78 | #include "include/apparmor.h" |
79 | #include "include/capability.h" |
80 | #include "include/cred.h" |
81 | #include "include/file.h" |
82 | #include "include/ipc.h" |
83 | #include "include/match.h" |
84 | #include "include/path.h" |
85 | #include "include/policy.h" |
86 | #include "include/policy_ns.h" |
87 | #include "include/policy_unpack.h" |
88 | #include "include/resource.h" |
89 | |
90 | int unprivileged_userns_apparmor_policy = 1; |
91 | int aa_unprivileged_unconfined_restricted; |
92 | |
93 | const char *const aa_profile_mode_names[] = { |
94 | "enforce" , |
95 | "complain" , |
96 | "kill" , |
97 | "unconfined" , |
98 | "user" , |
99 | }; |
100 | |
101 | |
102 | static void aa_free_pdb(struct aa_policydb *pdb) |
103 | { |
104 | if (pdb) { |
105 | aa_put_dfa(dfa: pdb->dfa); |
106 | if (pdb->perms) |
107 | kvfree(addr: pdb->perms); |
108 | aa_free_str_table(table: &pdb->trans); |
109 | kfree(objp: pdb); |
110 | } |
111 | } |
112 | |
113 | /** |
114 | * aa_pdb_free_kref - free aa_policydb by kref (called by aa_put_pdb) |
115 | * @kref: kref callback for freeing of a dfa (NOT NULL) |
116 | */ |
117 | void aa_pdb_free_kref(struct kref *kref) |
118 | { |
119 | struct aa_policydb *pdb = container_of(kref, struct aa_policydb, count); |
120 | |
121 | aa_free_pdb(pdb); |
122 | } |
123 | |
124 | |
125 | struct aa_policydb *aa_alloc_pdb(gfp_t gfp) |
126 | { |
127 | struct aa_policydb *pdb = kzalloc(size: sizeof(struct aa_policydb), flags: gfp); |
128 | |
129 | if (!pdb) |
130 | return NULL; |
131 | |
132 | kref_init(kref: &pdb->count); |
133 | |
134 | return pdb; |
135 | } |
136 | |
137 | |
138 | /** |
139 | * __add_profile - add a profiles to list and label tree |
140 | * @list: list to add it to (NOT NULL) |
141 | * @profile: the profile to add (NOT NULL) |
142 | * |
143 | * refcount @profile, should be put by __list_remove_profile |
144 | * |
145 | * Requires: namespace lock be held, or list not be shared |
146 | */ |
147 | static void __add_profile(struct list_head *list, struct aa_profile *profile) |
148 | { |
149 | struct aa_label *l; |
150 | |
151 | AA_BUG(!list); |
152 | AA_BUG(!profile); |
153 | AA_BUG(!profile->ns); |
154 | AA_BUG(!mutex_is_locked(&profile->ns->lock)); |
155 | |
156 | list_add_rcu(new: &profile->base.list, head: list); |
157 | /* get list reference */ |
158 | aa_get_profile(p: profile); |
159 | l = aa_label_insert(ls: &profile->ns->labels, l: &profile->label); |
160 | AA_BUG(l != &profile->label); |
161 | aa_put_label(l); |
162 | } |
163 | |
164 | /** |
165 | * __list_remove_profile - remove a profile from the list it is on |
166 | * @profile: the profile to remove (NOT NULL) |
167 | * |
168 | * remove a profile from the list, warning generally removal should |
169 | * be done with __replace_profile as most profile removals are |
170 | * replacements to the unconfined profile. |
171 | * |
172 | * put @profile list refcount |
173 | * |
174 | * Requires: namespace lock be held, or list not have been live |
175 | */ |
176 | static void __list_remove_profile(struct aa_profile *profile) |
177 | { |
178 | AA_BUG(!profile); |
179 | AA_BUG(!profile->ns); |
180 | AA_BUG(!mutex_is_locked(&profile->ns->lock)); |
181 | |
182 | list_del_rcu(entry: &profile->base.list); |
183 | aa_put_profile(p: profile); |
184 | } |
185 | |
186 | /** |
187 | * __remove_profile - remove old profile, and children |
188 | * @profile: profile to be replaced (NOT NULL) |
189 | * |
190 | * Requires: namespace list lock be held, or list not be shared |
191 | */ |
192 | static void __remove_profile(struct aa_profile *profile) |
193 | { |
194 | AA_BUG(!profile); |
195 | AA_BUG(!profile->ns); |
196 | AA_BUG(!mutex_is_locked(&profile->ns->lock)); |
197 | |
198 | /* release any children lists first */ |
199 | __aa_profile_list_release(head: &profile->base.profiles); |
200 | /* released by free_profile */ |
201 | aa_label_remove(label: &profile->label); |
202 | __aafs_profile_rmdir(profile); |
203 | __list_remove_profile(profile); |
204 | } |
205 | |
206 | /** |
207 | * __aa_profile_list_release - remove all profiles on the list and put refs |
208 | * @head: list of profiles (NOT NULL) |
209 | * |
210 | * Requires: namespace lock be held |
211 | */ |
212 | void __aa_profile_list_release(struct list_head *head) |
213 | { |
214 | struct aa_profile *profile, *tmp; |
215 | list_for_each_entry_safe(profile, tmp, head, base.list) |
216 | __remove_profile(profile); |
217 | } |
218 | |
219 | /** |
220 | * aa_free_data - free a data blob |
221 | * @ptr: data to free |
222 | * @arg: unused |
223 | */ |
224 | static void aa_free_data(void *ptr, void *arg) |
225 | { |
226 | struct aa_data *data = ptr; |
227 | |
228 | kfree_sensitive(objp: data->data); |
229 | kfree_sensitive(objp: data->key); |
230 | kfree_sensitive(objp: data); |
231 | } |
232 | |
233 | static void free_attachment(struct aa_attachment *attach) |
234 | { |
235 | int i; |
236 | |
237 | for (i = 0; i < attach->xattr_count; i++) |
238 | kfree_sensitive(objp: attach->xattrs[i]); |
239 | kfree_sensitive(objp: attach->xattrs); |
240 | aa_put_pdb(pdb: attach->xmatch); |
241 | } |
242 | |
243 | static void free_ruleset(struct aa_ruleset *rules) |
244 | { |
245 | int i; |
246 | |
247 | aa_put_pdb(pdb: rules->file); |
248 | aa_put_pdb(pdb: rules->policy); |
249 | aa_free_cap_rules(caps: &rules->caps); |
250 | aa_free_rlimit_rules(rlims: &rules->rlimits); |
251 | |
252 | for (i = 0; i < rules->secmark_count; i++) |
253 | kfree_sensitive(objp: rules->secmark[i].label); |
254 | kfree_sensitive(objp: rules->secmark); |
255 | kfree_sensitive(objp: rules); |
256 | } |
257 | |
258 | struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp) |
259 | { |
260 | struct aa_ruleset *rules; |
261 | |
262 | rules = kzalloc(size: sizeof(*rules), flags: gfp); |
263 | if (rules) |
264 | INIT_LIST_HEAD(list: &rules->list); |
265 | |
266 | return rules; |
267 | } |
268 | |
269 | /** |
270 | * aa_free_profile - free a profile |
271 | * @profile: the profile to free (MAYBE NULL) |
272 | * |
273 | * Free a profile, its hats and null_profile. All references to the profile, |
274 | * its hats and null_profile must have been put. |
275 | * |
276 | * If the profile was referenced from a task context, free_profile() will |
277 | * be called from an rcu callback routine, so we must not sleep here. |
278 | */ |
279 | void aa_free_profile(struct aa_profile *profile) |
280 | { |
281 | struct aa_ruleset *rule, *tmp; |
282 | struct rhashtable *rht; |
283 | |
284 | AA_DEBUG("%s(%p)\n" , __func__, profile); |
285 | |
286 | if (!profile) |
287 | return; |
288 | |
289 | /* free children profiles */ |
290 | aa_policy_destroy(policy: &profile->base); |
291 | aa_put_profile(rcu_access_pointer(profile->parent)); |
292 | |
293 | aa_put_ns(ns: profile->ns); |
294 | kfree_sensitive(objp: profile->rename); |
295 | kfree_sensitive(objp: profile->disconnected); |
296 | |
297 | free_attachment(attach: &profile->attach); |
298 | |
299 | /* |
300 | * at this point there are no tasks that can have a reference |
301 | * to rules |
302 | */ |
303 | list_for_each_entry_safe(rule, tmp, &profile->rules, list) { |
304 | list_del_init(entry: &rule->list); |
305 | free_ruleset(rules: rule); |
306 | } |
307 | kfree_sensitive(objp: profile->dirname); |
308 | |
309 | if (profile->data) { |
310 | rht = profile->data; |
311 | profile->data = NULL; |
312 | rhashtable_free_and_destroy(ht: rht, free_fn: aa_free_data, NULL); |
313 | kfree_sensitive(objp: rht); |
314 | } |
315 | |
316 | kfree_sensitive(objp: profile->hash); |
317 | aa_put_loaddata(data: profile->rawdata); |
318 | aa_label_destroy(label: &profile->label); |
319 | |
320 | kfree_sensitive(objp: profile); |
321 | } |
322 | |
323 | /** |
324 | * aa_alloc_profile - allocate, initialize and return a new profile |
325 | * @hname: name of the profile (NOT NULL) |
326 | * @proxy: proxy to use OR null if to allocate a new one |
327 | * @gfp: allocation type |
328 | * |
329 | * Returns: refcount profile or NULL on failure |
330 | */ |
331 | struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, |
332 | gfp_t gfp) |
333 | { |
334 | struct aa_profile *profile; |
335 | struct aa_ruleset *rules; |
336 | |
337 | /* freed by free_profile - usually through aa_put_profile */ |
338 | profile = kzalloc(struct_size(profile, label.vec, 2), flags: gfp); |
339 | if (!profile) |
340 | return NULL; |
341 | |
342 | if (!aa_policy_init(policy: &profile->base, NULL, name: hname, gfp)) |
343 | goto fail; |
344 | if (!aa_label_init(label: &profile->label, size: 1, gfp)) |
345 | goto fail; |
346 | |
347 | INIT_LIST_HEAD(list: &profile->rules); |
348 | |
349 | /* allocate the first ruleset, but leave it empty */ |
350 | rules = aa_alloc_ruleset(gfp); |
351 | if (!rules) |
352 | goto fail; |
353 | list_add(new: &rules->list, head: &profile->rules); |
354 | |
355 | /* update being set needed by fs interface */ |
356 | if (!proxy) { |
357 | proxy = aa_alloc_proxy(l: &profile->label, gfp); |
358 | if (!proxy) |
359 | goto fail; |
360 | } else |
361 | aa_get_proxy(proxy); |
362 | profile->label.proxy = proxy; |
363 | |
364 | profile->label.hname = profile->base.hname; |
365 | profile->label.flags |= FLAG_PROFILE; |
366 | profile->label.vec[0] = profile; |
367 | |
368 | /* refcount released by caller */ |
369 | return profile; |
370 | |
371 | fail: |
372 | aa_free_profile(profile); |
373 | |
374 | return NULL; |
375 | } |
376 | |
377 | /* TODO: profile accounting - setup in remove */ |
378 | |
379 | /** |
380 | * __strn_find_child - find a profile on @head list using substring of @name |
381 | * @head: list to search (NOT NULL) |
382 | * @name: name of profile (NOT NULL) |
383 | * @len: length of @name substring to match |
384 | * |
385 | * Requires: rcu_read_lock be held |
386 | * |
387 | * Returns: unrefcounted profile ptr, or NULL if not found |
388 | */ |
389 | static struct aa_profile *__strn_find_child(struct list_head *head, |
390 | const char *name, int len) |
391 | { |
392 | return (struct aa_profile *)__policy_strn_find(head, str: name, len); |
393 | } |
394 | |
395 | /** |
396 | * __find_child - find a profile on @head list with a name matching @name |
397 | * @head: list to search (NOT NULL) |
398 | * @name: name of profile (NOT NULL) |
399 | * |
400 | * Requires: rcu_read_lock be held |
401 | * |
402 | * Returns: unrefcounted profile ptr, or NULL if not found |
403 | */ |
404 | static struct aa_profile *__find_child(struct list_head *head, const char *name) |
405 | { |
406 | return __strn_find_child(head, name, strlen(name)); |
407 | } |
408 | |
409 | /** |
410 | * aa_find_child - find a profile by @name in @parent |
411 | * @parent: profile to search (NOT NULL) |
412 | * @name: profile name to search for (NOT NULL) |
413 | * |
414 | * Returns: a refcounted profile or NULL if not found |
415 | */ |
416 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) |
417 | { |
418 | struct aa_profile *profile; |
419 | |
420 | rcu_read_lock(); |
421 | do { |
422 | profile = __find_child(head: &parent->base.profiles, name); |
423 | } while (profile && !aa_get_profile_not0(p: profile)); |
424 | rcu_read_unlock(); |
425 | |
426 | /* refcount released by caller */ |
427 | return profile; |
428 | } |
429 | |
430 | /** |
431 | * __lookup_parent - lookup the parent of a profile of name @hname |
432 | * @ns: namespace to lookup profile in (NOT NULL) |
433 | * @hname: hierarchical profile name to find parent of (NOT NULL) |
434 | * |
435 | * Lookups up the parent of a fully qualified profile name, the profile |
436 | * that matches hname does not need to exist, in general this |
437 | * is used to load a new profile. |
438 | * |
439 | * Requires: rcu_read_lock be held |
440 | * |
441 | * Returns: unrefcounted policy or NULL if not found |
442 | */ |
443 | static struct aa_policy *__lookup_parent(struct aa_ns *ns, |
444 | const char *hname) |
445 | { |
446 | struct aa_policy *policy; |
447 | struct aa_profile *profile = NULL; |
448 | char *split; |
449 | |
450 | policy = &ns->base; |
451 | |
452 | for (split = strstr(hname, "//" ); split;) { |
453 | profile = __strn_find_child(head: &policy->profiles, name: hname, |
454 | len: split - hname); |
455 | if (!profile) |
456 | return NULL; |
457 | policy = &profile->base; |
458 | hname = split + 2; |
459 | split = strstr(hname, "//" ); |
460 | } |
461 | if (!profile) |
462 | return &ns->base; |
463 | return &profile->base; |
464 | } |
465 | |
466 | /** |
467 | * __create_missing_ancestors - create place holders for missing ancestores |
468 | * @ns: namespace to lookup profile in (NOT NULL) |
469 | * @hname: hierarchical profile name to find parent of (NOT NULL) |
470 | * @gfp: type of allocation. |
471 | * |
472 | * Requires: ns mutex lock held |
473 | * |
474 | * Return: unrefcounted parent policy on success or %NULL if error creating |
475 | * place holder profiles. |
476 | */ |
477 | static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns, |
478 | const char *hname, |
479 | gfp_t gfp) |
480 | { |
481 | struct aa_policy *policy; |
482 | struct aa_profile *parent, *profile = NULL; |
483 | char *split; |
484 | |
485 | AA_BUG(!ns); |
486 | AA_BUG(!hname); |
487 | |
488 | policy = &ns->base; |
489 | |
490 | for (split = strstr(hname, "//" ); split;) { |
491 | parent = profile; |
492 | profile = __strn_find_child(head: &policy->profiles, name: hname, |
493 | len: split - hname); |
494 | if (!profile) { |
495 | const char *name = kstrndup(s: hname, len: split - hname, |
496 | gfp); |
497 | if (!name) |
498 | return NULL; |
499 | profile = aa_alloc_null(parent, name, gfp); |
500 | kfree(objp: name); |
501 | if (!profile) |
502 | return NULL; |
503 | if (!parent) |
504 | profile->ns = aa_get_ns(ns); |
505 | } |
506 | policy = &profile->base; |
507 | hname = split + 2; |
508 | split = strstr(hname, "//" ); |
509 | } |
510 | if (!profile) |
511 | return &ns->base; |
512 | return &profile->base; |
513 | } |
514 | |
515 | /** |
516 | * __lookupn_profile - lookup the profile matching @hname |
517 | * @base: base list to start looking up profile name from (NOT NULL) |
518 | * @hname: hierarchical profile name (NOT NULL) |
519 | * @n: length of @hname |
520 | * |
521 | * Requires: rcu_read_lock be held |
522 | * |
523 | * Returns: unrefcounted profile pointer or NULL if not found |
524 | * |
525 | * Do a relative name lookup, recursing through profile tree. |
526 | */ |
527 | static struct aa_profile *__lookupn_profile(struct aa_policy *base, |
528 | const char *hname, size_t n) |
529 | { |
530 | struct aa_profile *profile = NULL; |
531 | const char *split; |
532 | |
533 | for (split = strnstr(hname, "//" , n); split; |
534 | split = strnstr(hname, "//" , n)) { |
535 | profile = __strn_find_child(head: &base->profiles, name: hname, |
536 | len: split - hname); |
537 | if (!profile) |
538 | return NULL; |
539 | |
540 | base = &profile->base; |
541 | n -= split + 2 - hname; |
542 | hname = split + 2; |
543 | } |
544 | |
545 | if (n) |
546 | return __strn_find_child(head: &base->profiles, name: hname, len: n); |
547 | return NULL; |
548 | } |
549 | |
550 | static struct aa_profile *__lookup_profile(struct aa_policy *base, |
551 | const char *hname) |
552 | { |
553 | return __lookupn_profile(base, hname, strlen(hname)); |
554 | } |
555 | |
556 | /** |
557 | * aa_lookupn_profile - find a profile by its full or partial name |
558 | * @ns: the namespace to start from (NOT NULL) |
559 | * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) |
560 | * @n: size of @hname |
561 | * |
562 | * Returns: refcounted profile or NULL if not found |
563 | */ |
564 | struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, |
565 | size_t n) |
566 | { |
567 | struct aa_profile *profile; |
568 | |
569 | rcu_read_lock(); |
570 | do { |
571 | profile = __lookupn_profile(base: &ns->base, hname, n); |
572 | } while (profile && !aa_get_profile_not0(p: profile)); |
573 | rcu_read_unlock(); |
574 | |
575 | /* the unconfined profile is not in the regular profile list */ |
576 | if (!profile && strncmp(hname, "unconfined" , n) == 0) |
577 | profile = aa_get_newest_profile(p: ns->unconfined); |
578 | |
579 | /* refcount released by caller */ |
580 | return profile; |
581 | } |
582 | |
583 | struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) |
584 | { |
585 | return aa_lookupn_profile(ns, hname, strlen(hname)); |
586 | } |
587 | |
588 | struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, |
589 | const char *fqname, size_t n) |
590 | { |
591 | struct aa_profile *profile; |
592 | struct aa_ns *ns; |
593 | const char *name, *ns_name; |
594 | size_t ns_len; |
595 | |
596 | name = aa_splitn_fqname(fqname, n, ns_name: &ns_name, ns_len: &ns_len); |
597 | if (ns_name) { |
598 | ns = aa_lookupn_ns(labels_ns(base), name: ns_name, n: ns_len); |
599 | if (!ns) |
600 | return NULL; |
601 | } else |
602 | ns = aa_get_ns(labels_ns(base)); |
603 | |
604 | if (name) |
605 | profile = aa_lookupn_profile(ns, hname: name, n: n - (name - fqname)); |
606 | else if (ns) |
607 | /* default profile for ns, currently unconfined */ |
608 | profile = aa_get_newest_profile(p: ns->unconfined); |
609 | else |
610 | profile = NULL; |
611 | aa_put_ns(ns); |
612 | |
613 | return profile; |
614 | } |
615 | |
616 | |
617 | struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, |
618 | gfp_t gfp) |
619 | { |
620 | struct aa_profile *profile; |
621 | struct aa_ruleset *rules; |
622 | |
623 | profile = aa_alloc_profile(hname: name, NULL, gfp); |
624 | if (!profile) |
625 | return NULL; |
626 | |
627 | /* TODO: ideally we should inherit abi from parent */ |
628 | profile->label.flags |= FLAG_NULL; |
629 | rules = list_first_entry(&profile->rules, typeof(*rules), list); |
630 | rules->file = aa_get_pdb(pdb: nullpdb); |
631 | rules->policy = aa_get_pdb(pdb: nullpdb); |
632 | |
633 | if (parent) { |
634 | profile->path_flags = parent->path_flags; |
635 | |
636 | /* released on free_profile */ |
637 | rcu_assign_pointer(profile->parent, aa_get_profile(parent)); |
638 | profile->ns = aa_get_ns(ns: parent->ns); |
639 | } |
640 | |
641 | return profile; |
642 | } |
643 | |
644 | /** |
645 | * aa_new_learning_profile - create or find a null-X learning profile |
646 | * @parent: profile that caused this profile to be created (NOT NULL) |
647 | * @hat: true if the null- learning profile is a hat |
648 | * @base: name to base the null profile off of |
649 | * @gfp: type of allocation |
650 | * |
651 | * Find/Create a null- complain mode profile used in learning mode. The |
652 | * name of the profile is unique and follows the format of parent//null-XXX. |
653 | * where XXX is based on the @name or if that fails or is not supplied |
654 | * a unique number |
655 | * |
656 | * null profiles are added to the profile list but the list does not |
657 | * hold a count on them so that they are automatically released when |
658 | * not in use. |
659 | * |
660 | * Returns: new refcounted profile else NULL on failure |
661 | */ |
662 | struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat, |
663 | const char *base, gfp_t gfp) |
664 | { |
665 | struct aa_profile *p, *profile; |
666 | const char *bname; |
667 | char *name = NULL; |
668 | |
669 | AA_BUG(!parent); |
670 | |
671 | if (base) { |
672 | name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), |
673 | flags: gfp); |
674 | if (name) { |
675 | sprintf(buf: name, fmt: "%s//null-%s" , parent->base.hname, base); |
676 | goto name; |
677 | } |
678 | /* fall through to try shorter uniq */ |
679 | } |
680 | |
681 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, flags: gfp); |
682 | if (!name) |
683 | return NULL; |
684 | sprintf(buf: name, fmt: "%s//null-%x" , parent->base.hname, |
685 | atomic_inc_return(v: &parent->ns->uniq_null)); |
686 | |
687 | name: |
688 | /* lookup to see if this is a dup creation */ |
689 | bname = basename(hname: name); |
690 | profile = aa_find_child(parent, name: bname); |
691 | if (profile) |
692 | goto out; |
693 | |
694 | profile = aa_alloc_null(parent, name, gfp); |
695 | if (!profile) |
696 | goto fail; |
697 | profile->mode = APPARMOR_COMPLAIN; |
698 | if (hat) |
699 | profile->label.flags |= FLAG_HAT; |
700 | |
701 | mutex_lock_nested(lock: &profile->ns->lock, subclass: profile->ns->level); |
702 | p = __find_child(head: &parent->base.profiles, name: bname); |
703 | if (p) { |
704 | aa_free_profile(profile); |
705 | profile = aa_get_profile(p); |
706 | } else { |
707 | __add_profile(list: &parent->base.profiles, profile); |
708 | } |
709 | mutex_unlock(lock: &profile->ns->lock); |
710 | |
711 | /* refcount released by caller */ |
712 | out: |
713 | kfree(objp: name); |
714 | |
715 | return profile; |
716 | |
717 | fail: |
718 | kfree(objp: name); |
719 | aa_free_profile(profile); |
720 | return NULL; |
721 | } |
722 | |
723 | /** |
724 | * replacement_allowed - test to see if replacement is allowed |
725 | * @profile: profile to test if it can be replaced (MAYBE NULL) |
726 | * @noreplace: true if replacement shouldn't be allowed but addition is okay |
727 | * @info: Returns - info about why replacement failed (NOT NULL) |
728 | * |
729 | * Returns: %0 if replacement allowed else error code |
730 | */ |
731 | static int replacement_allowed(struct aa_profile *profile, int noreplace, |
732 | const char **info) |
733 | { |
734 | if (profile) { |
735 | if (profile->label.flags & FLAG_IMMUTIBLE) { |
736 | *info = "cannot replace immutable profile" ; |
737 | return -EPERM; |
738 | } else if (noreplace) { |
739 | *info = "profile already exists" ; |
740 | return -EEXIST; |
741 | } |
742 | } |
743 | return 0; |
744 | } |
745 | |
746 | /* audit callback for net specific fields */ |
747 | static void audit_cb(struct audit_buffer *ab, void *va) |
748 | { |
749 | struct common_audit_data *sa = va; |
750 | struct apparmor_audit_data *ad = aad(sa); |
751 | |
752 | if (ad->iface.ns) { |
753 | audit_log_format(ab, fmt: " ns=" ); |
754 | audit_log_untrustedstring(ab, string: ad->iface.ns); |
755 | } |
756 | } |
757 | |
758 | /** |
759 | * audit_policy - Do auditing of policy changes |
760 | * @subj_label: label to check if it can manage policy |
761 | * @op: policy operation being performed |
762 | * @ns_name: name of namespace being manipulated |
763 | * @name: name of profile being manipulated (NOT NULL) |
764 | * @info: any extra information to be audited (MAYBE NULL) |
765 | * @error: error code |
766 | * |
767 | * Returns: the error to be returned after audit is done |
768 | */ |
769 | static int audit_policy(struct aa_label *subj_label, const char *op, |
770 | const char *ns_name, const char *name, |
771 | const char *info, int error) |
772 | { |
773 | DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, op); |
774 | |
775 | ad.iface.ns = ns_name; |
776 | ad.name = name; |
777 | ad.info = info; |
778 | ad.error = error; |
779 | ad.subj_label = subj_label; |
780 | |
781 | aa_audit_msg(type: AUDIT_APPARMOR_STATUS, ad: &ad, cb: audit_cb); |
782 | |
783 | return error; |
784 | } |
785 | |
786 | /* don't call out to other LSMs in the stack for apparmor policy admin |
787 | * permissions |
788 | */ |
789 | static int policy_ns_capable(const struct cred *subj_cred, |
790 | struct aa_label *label, |
791 | struct user_namespace *userns, int cap) |
792 | { |
793 | int err; |
794 | |
795 | /* check for MAC_ADMIN cap in cred */ |
796 | err = cap_capable(cred: subj_cred, ns: userns, cap, CAP_OPT_NONE); |
797 | if (!err) |
798 | err = aa_capable(subj_cred, label, cap, CAP_OPT_NONE); |
799 | |
800 | return err; |
801 | } |
802 | |
803 | /** |
804 | * aa_policy_view_capable - check if viewing policy in at @ns is allowed |
805 | * @subj_cred: cred of subject |
806 | * @label: label that is trying to view policy in ns |
807 | * @ns: namespace being viewed by @label (may be NULL if @label's ns) |
808 | * |
809 | * Returns: true if viewing policy is allowed |
810 | * |
811 | * If @ns is NULL then the namespace being viewed is assumed to be the |
812 | * tasks current namespace. |
813 | */ |
814 | bool aa_policy_view_capable(const struct cred *subj_cred, |
815 | struct aa_label *label, struct aa_ns *ns) |
816 | { |
817 | struct user_namespace *user_ns = subj_cred->user_ns; |
818 | struct aa_ns *view_ns = labels_view(label); |
819 | bool root_in_user_ns = uid_eq(current_euid(), right: make_kuid(from: user_ns, uid: 0)) || |
820 | in_egroup_p(make_kgid(from: user_ns, gid: 0)); |
821 | bool response = false; |
822 | if (!ns) |
823 | ns = view_ns; |
824 | |
825 | if (root_in_user_ns && aa_ns_visible(curr: view_ns, view: ns, subns: true) && |
826 | (user_ns == &init_user_ns || |
827 | (unprivileged_userns_apparmor_policy != 0 && |
828 | user_ns->level == view_ns->level))) |
829 | response = true; |
830 | |
831 | return response; |
832 | } |
833 | |
834 | bool aa_policy_admin_capable(const struct cred *subj_cred, |
835 | struct aa_label *label, struct aa_ns *ns) |
836 | { |
837 | struct user_namespace *user_ns = subj_cred->user_ns; |
838 | bool capable = policy_ns_capable(subj_cred, label, userns: user_ns, |
839 | CAP_MAC_ADMIN) == 0; |
840 | |
841 | AA_DEBUG("cap_mac_admin? %d\n" , capable); |
842 | AA_DEBUG("policy locked? %d\n" , aa_g_lock_policy); |
843 | |
844 | return aa_policy_view_capable(subj_cred, label, ns) && capable && |
845 | !aa_g_lock_policy; |
846 | } |
847 | |
848 | bool aa_current_policy_view_capable(struct aa_ns *ns) |
849 | { |
850 | struct aa_label *label; |
851 | bool res; |
852 | |
853 | label = __begin_current_label_crit_section(); |
854 | res = aa_policy_view_capable(current_cred(), label, ns); |
855 | __end_current_label_crit_section(label); |
856 | |
857 | return res; |
858 | } |
859 | |
860 | bool aa_current_policy_admin_capable(struct aa_ns *ns) |
861 | { |
862 | struct aa_label *label; |
863 | bool res; |
864 | |
865 | label = __begin_current_label_crit_section(); |
866 | res = aa_policy_admin_capable(current_cred(), label, ns); |
867 | __end_current_label_crit_section(label); |
868 | |
869 | return res; |
870 | } |
871 | |
872 | /** |
873 | * aa_may_manage_policy - can the current task manage policy |
874 | * @subj_cred: subjects cred |
875 | * @label: label to check if it can manage policy |
876 | * @ns: namespace being managed by @label (may be NULL if @label's ns) |
877 | * @mask: contains the policy manipulation operation being done |
878 | * |
879 | * Returns: 0 if the task is allowed to manipulate policy else error |
880 | */ |
881 | int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label, |
882 | struct aa_ns *ns, u32 mask) |
883 | { |
884 | const char *op; |
885 | |
886 | if (mask & AA_MAY_REMOVE_POLICY) |
887 | op = OP_PROF_RM; |
888 | else if (mask & AA_MAY_REPLACE_POLICY) |
889 | op = OP_PROF_REPL; |
890 | else |
891 | op = OP_PROF_LOAD; |
892 | |
893 | /* check if loading policy is locked out */ |
894 | if (aa_g_lock_policy) |
895 | return audit_policy(subj_label: label, op, NULL, NULL, info: "policy_locked" , |
896 | error: -EACCES); |
897 | |
898 | if (!aa_policy_admin_capable(subj_cred, label, ns)) |
899 | return audit_policy(subj_label: label, op, NULL, NULL, info: "not policy admin" , |
900 | error: -EACCES); |
901 | |
902 | /* TODO: add fine grained mediation of policy loads */ |
903 | return 0; |
904 | } |
905 | |
906 | static struct aa_profile *__list_lookup_parent(struct list_head *lh, |
907 | struct aa_profile *profile) |
908 | { |
909 | const char *base = basename(hname: profile->base.hname); |
910 | long len = base - profile->base.hname; |
911 | struct aa_load_ent *ent; |
912 | |
913 | /* parent won't have trailing // so remove from len */ |
914 | if (len <= 2) |
915 | return NULL; |
916 | len -= 2; |
917 | |
918 | list_for_each_entry(ent, lh, list) { |
919 | if (ent->new == profile) |
920 | continue; |
921 | if (strncmp(ent->new->base.hname, profile->base.hname, len) == |
922 | 0 && ent->new->base.hname[len] == 0) |
923 | return ent->new; |
924 | } |
925 | |
926 | return NULL; |
927 | } |
928 | |
929 | /** |
930 | * __replace_profile - replace @old with @new on a list |
931 | * @old: profile to be replaced (NOT NULL) |
932 | * @new: profile to replace @old with (NOT NULL) |
933 | * |
934 | * Will duplicate and refcount elements that @new inherits from @old |
935 | * and will inherit @old children. |
936 | * |
937 | * refcount @new for list, put @old list refcount |
938 | * |
939 | * Requires: namespace list lock be held, or list not be shared |
940 | */ |
941 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) |
942 | { |
943 | struct aa_profile *child, *tmp; |
944 | |
945 | if (!list_empty(head: &old->base.profiles)) { |
946 | LIST_HEAD(lh); |
947 | list_splice_init_rcu(list: &old->base.profiles, head: &lh, sync: synchronize_rcu); |
948 | |
949 | list_for_each_entry_safe(child, tmp, &lh, base.list) { |
950 | struct aa_profile *p; |
951 | |
952 | list_del_init(entry: &child->base.list); |
953 | p = __find_child(head: &new->base.profiles, name: child->base.name); |
954 | if (p) { |
955 | /* @p replaces @child */ |
956 | __replace_profile(old: child, new: p); |
957 | continue; |
958 | } |
959 | |
960 | /* inherit @child and its children */ |
961 | /* TODO: update hname of inherited children */ |
962 | /* list refcount transferred to @new */ |
963 | p = aa_deref_parent(p: child); |
964 | rcu_assign_pointer(child->parent, aa_get_profile(new)); |
965 | list_add_rcu(new: &child->base.list, head: &new->base.profiles); |
966 | aa_put_profile(p); |
967 | } |
968 | } |
969 | |
970 | if (!rcu_access_pointer(new->parent)) { |
971 | struct aa_profile *parent = aa_deref_parent(p: old); |
972 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); |
973 | } |
974 | aa_label_replace(old: &old->label, new: &new->label); |
975 | /* migrate dents must come after label replacement b/c update */ |
976 | __aafs_profile_migrate_dents(old, new); |
977 | |
978 | if (list_empty(head: &new->base.list)) { |
979 | /* new is not on a list already */ |
980 | list_replace_rcu(old: &old->base.list, new: &new->base.list); |
981 | aa_get_profile(p: new); |
982 | aa_put_profile(p: old); |
983 | } else |
984 | __list_remove_profile(profile: old); |
985 | } |
986 | |
987 | /** |
988 | * __lookup_replace - lookup replacement information for a profile |
989 | * @ns: namespace the lookup occurs in |
990 | * @hname: name of profile to lookup |
991 | * @noreplace: true if not replacing an existing profile |
992 | * @p: Returns - profile to be replaced |
993 | * @info: Returns - info string on why lookup failed |
994 | * |
995 | * Returns: profile to replace (no ref) on success else ptr error |
996 | */ |
997 | static int __lookup_replace(struct aa_ns *ns, const char *hname, |
998 | bool noreplace, struct aa_profile **p, |
999 | const char **info) |
1000 | { |
1001 | *p = aa_get_profile(p: __lookup_profile(base: &ns->base, hname)); |
1002 | if (*p) { |
1003 | int error = replacement_allowed(profile: *p, noreplace, info); |
1004 | if (error) { |
1005 | *info = "profile can not be replaced" ; |
1006 | return error; |
1007 | } |
1008 | } |
1009 | |
1010 | return 0; |
1011 | } |
1012 | |
1013 | static void share_name(struct aa_profile *old, struct aa_profile *new) |
1014 | { |
1015 | aa_put_str(str: new->base.hname); |
1016 | aa_get_str(str: old->base.hname); |
1017 | new->base.hname = old->base.hname; |
1018 | new->base.name = old->base.name; |
1019 | new->label.hname = old->label.hname; |
1020 | } |
1021 | |
1022 | /* Update to newest version of parent after previous replacements |
1023 | * Returns: unrefcount newest version of parent |
1024 | */ |
1025 | static struct aa_profile *update_to_newest_parent(struct aa_profile *new) |
1026 | { |
1027 | struct aa_profile *parent, *newest; |
1028 | |
1029 | parent = rcu_dereference_protected(new->parent, |
1030 | mutex_is_locked(&new->ns->lock)); |
1031 | newest = aa_get_newest_profile(p: parent); |
1032 | |
1033 | /* parent replaced in this atomic set? */ |
1034 | if (newest != parent) { |
1035 | aa_put_profile(p: parent); |
1036 | rcu_assign_pointer(new->parent, newest); |
1037 | } else |
1038 | aa_put_profile(p: newest); |
1039 | |
1040 | return newest; |
1041 | } |
1042 | |
1043 | /** |
1044 | * aa_replace_profiles - replace profile(s) on the profile list |
1045 | * @policy_ns: namespace load is occurring on |
1046 | * @label: label that is attempting to load/replace policy |
1047 | * @mask: permission mask |
1048 | * @udata: serialized data stream (NOT NULL) |
1049 | * |
1050 | * unpack and replace a profile on the profile list and uses of that profile |
1051 | * by any task creds via invalidating the old version of the profile, which |
1052 | * tasks will notice to update their own cred. If the profile does not exist |
1053 | * on the profile list it is added. |
1054 | * |
1055 | * Returns: size of data consumed else error code on failure. |
1056 | */ |
1057 | ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, |
1058 | u32 mask, struct aa_loaddata *udata) |
1059 | { |
1060 | const char *ns_name = NULL, *info = NULL; |
1061 | struct aa_ns *ns = NULL; |
1062 | struct aa_load_ent *ent, *tmp; |
1063 | struct aa_loaddata *rawdata_ent; |
1064 | const char *op; |
1065 | ssize_t count, error; |
1066 | LIST_HEAD(lh); |
1067 | |
1068 | op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD; |
1069 | aa_get_loaddata(data: udata); |
1070 | /* released below */ |
1071 | error = aa_unpack(udata, lh: &lh, ns: &ns_name); |
1072 | if (error) |
1073 | goto out; |
1074 | |
1075 | /* ensure that profiles are all for the same ns |
1076 | * TODO: update locking to remove this constaint. All profiles in |
1077 | * the load set must succeed as a set or the load will |
1078 | * fail. Sort ent list and take ns locks in hierarchy order |
1079 | */ |
1080 | count = 0; |
1081 | list_for_each_entry(ent, &lh, list) { |
1082 | if (ns_name) { |
1083 | if (ent->ns_name && |
1084 | strcmp(ent->ns_name, ns_name) != 0) { |
1085 | info = "policy load has mixed namespaces" ; |
1086 | error = -EACCES; |
1087 | goto fail; |
1088 | } |
1089 | } else if (ent->ns_name) { |
1090 | if (count) { |
1091 | info = "policy load has mixed namespaces" ; |
1092 | error = -EACCES; |
1093 | goto fail; |
1094 | } |
1095 | ns_name = ent->ns_name; |
1096 | } else |
1097 | count++; |
1098 | } |
1099 | if (ns_name) { |
1100 | ns = aa_prepare_ns(root: policy_ns ? policy_ns : labels_ns(label), |
1101 | name: ns_name); |
1102 | if (IS_ERR(ptr: ns)) { |
1103 | op = OP_PROF_LOAD; |
1104 | info = "failed to prepare namespace" ; |
1105 | error = PTR_ERR(ptr: ns); |
1106 | ns = NULL; |
1107 | ent = NULL; |
1108 | goto fail; |
1109 | } |
1110 | } else |
1111 | ns = aa_get_ns(ns: policy_ns ? policy_ns : labels_ns(label)); |
1112 | |
1113 | mutex_lock_nested(lock: &ns->lock, subclass: ns->level); |
1114 | /* check for duplicate rawdata blobs: space and file dedup */ |
1115 | if (!list_empty(head: &ns->rawdata_list)) { |
1116 | list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { |
1117 | if (aa_rawdata_eq(l: rawdata_ent, r: udata)) { |
1118 | struct aa_loaddata *tmp; |
1119 | |
1120 | tmp = __aa_get_loaddata(data: rawdata_ent); |
1121 | /* check we didn't fail the race */ |
1122 | if (tmp) { |
1123 | aa_put_loaddata(data: udata); |
1124 | udata = tmp; |
1125 | break; |
1126 | } |
1127 | } |
1128 | } |
1129 | } |
1130 | /* setup parent and ns info */ |
1131 | list_for_each_entry(ent, &lh, list) { |
1132 | struct aa_policy *policy; |
1133 | struct aa_profile *p; |
1134 | |
1135 | if (aa_g_export_binary) |
1136 | ent->new->rawdata = aa_get_loaddata(data: udata); |
1137 | error = __lookup_replace(ns, hname: ent->new->base.hname, |
1138 | noreplace: !(mask & AA_MAY_REPLACE_POLICY), |
1139 | p: &ent->old, info: &info); |
1140 | if (error) |
1141 | goto fail_lock; |
1142 | |
1143 | if (ent->new->rename) { |
1144 | error = __lookup_replace(ns, hname: ent->new->rename, |
1145 | noreplace: !(mask & AA_MAY_REPLACE_POLICY), |
1146 | p: &ent->rename, info: &info); |
1147 | if (error) |
1148 | goto fail_lock; |
1149 | } |
1150 | |
1151 | /* released when @new is freed */ |
1152 | ent->new->ns = aa_get_ns(ns); |
1153 | |
1154 | if (ent->old || ent->rename) |
1155 | continue; |
1156 | |
1157 | /* no ref on policy only use inside lock */ |
1158 | p = NULL; |
1159 | policy = __lookup_parent(ns, hname: ent->new->base.hname); |
1160 | if (!policy) { |
1161 | /* first check for parent in the load set */ |
1162 | p = __list_lookup_parent(lh: &lh, profile: ent->new); |
1163 | if (!p) { |
1164 | /* |
1165 | * fill in missing parent with null |
1166 | * profile that doesn't have |
1167 | * permissions. This allows for |
1168 | * individual profile loading where |
1169 | * the child is loaded before the |
1170 | * parent, and outside of the current |
1171 | * atomic set. This unfortunately can |
1172 | * happen with some userspaces. The |
1173 | * null profile will be replaced once |
1174 | * the parent is loaded. |
1175 | */ |
1176 | policy = __create_missing_ancestors(ns, |
1177 | hname: ent->new->base.hname, |
1178 | GFP_KERNEL); |
1179 | if (!policy) { |
1180 | error = -ENOENT; |
1181 | info = "parent does not exist" ; |
1182 | goto fail_lock; |
1183 | } |
1184 | } |
1185 | } |
1186 | if (!p && policy != &ns->base) |
1187 | /* released on profile replacement or free_profile */ |
1188 | p = (struct aa_profile *) policy; |
1189 | rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); |
1190 | } |
1191 | |
1192 | /* create new fs entries for introspection if needed */ |
1193 | if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) { |
1194 | error = __aa_fs_create_rawdata(ns, rawdata: udata); |
1195 | if (error) { |
1196 | info = "failed to create raw_data dir and files" ; |
1197 | ent = NULL; |
1198 | goto fail_lock; |
1199 | } |
1200 | } |
1201 | list_for_each_entry(ent, &lh, list) { |
1202 | if (!ent->old) { |
1203 | struct dentry *parent; |
1204 | if (rcu_access_pointer(ent->new->parent)) { |
1205 | struct aa_profile *p; |
1206 | p = aa_deref_parent(p: ent->new); |
1207 | parent = prof_child_dir(p); |
1208 | } else |
1209 | parent = ns_subprofs_dir(ent->new->ns); |
1210 | error = __aafs_profile_mkdir(profile: ent->new, parent); |
1211 | } |
1212 | |
1213 | if (error) { |
1214 | info = "failed to create" ; |
1215 | goto fail_lock; |
1216 | } |
1217 | } |
1218 | |
1219 | /* Done with checks that may fail - do actual replacement */ |
1220 | __aa_bump_ns_revision(ns); |
1221 | if (aa_g_export_binary) |
1222 | __aa_loaddata_update(data: udata, revision: ns->revision); |
1223 | list_for_each_entry_safe(ent, tmp, &lh, list) { |
1224 | list_del_init(entry: &ent->list); |
1225 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; |
1226 | |
1227 | if (ent->old && ent->old->rawdata == ent->new->rawdata && |
1228 | ent->new->rawdata) { |
1229 | /* dedup actual profile replacement */ |
1230 | audit_policy(subj_label: label, op, ns_name, name: ent->new->base.hname, |
1231 | info: "same as current profile, skipping" , |
1232 | error); |
1233 | /* break refcount cycle with proxy. */ |
1234 | aa_put_proxy(proxy: ent->new->label.proxy); |
1235 | ent->new->label.proxy = NULL; |
1236 | goto skip; |
1237 | } |
1238 | |
1239 | /* |
1240 | * TODO: finer dedup based on profile range in data. Load set |
1241 | * can differ but profile may remain unchanged |
1242 | */ |
1243 | audit_policy(subj_label: label, op, ns_name, name: ent->new->base.hname, NULL, |
1244 | error); |
1245 | |
1246 | if (ent->old) { |
1247 | share_name(old: ent->old, new: ent->new); |
1248 | __replace_profile(old: ent->old, new: ent->new); |
1249 | } else { |
1250 | struct list_head *lh; |
1251 | |
1252 | if (rcu_access_pointer(ent->new->parent)) { |
1253 | struct aa_profile *parent; |
1254 | |
1255 | parent = update_to_newest_parent(new: ent->new); |
1256 | lh = &parent->base.profiles; |
1257 | } else |
1258 | lh = &ns->base.profiles; |
1259 | __add_profile(list: lh, profile: ent->new); |
1260 | } |
1261 | skip: |
1262 | aa_load_ent_free(ent); |
1263 | } |
1264 | __aa_labelset_update_subtree(ns); |
1265 | mutex_unlock(lock: &ns->lock); |
1266 | |
1267 | out: |
1268 | aa_put_ns(ns); |
1269 | aa_put_loaddata(data: udata); |
1270 | kfree(objp: ns_name); |
1271 | |
1272 | if (error) |
1273 | return error; |
1274 | return udata->size; |
1275 | |
1276 | fail_lock: |
1277 | mutex_unlock(lock: &ns->lock); |
1278 | |
1279 | /* audit cause of failure */ |
1280 | op = (ent && !ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; |
1281 | fail: |
1282 | audit_policy(subj_label: label, op, ns_name, name: ent ? ent->new->base.hname : NULL, |
1283 | info, error); |
1284 | /* audit status that rest of profiles in the atomic set failed too */ |
1285 | info = "valid profile in failed atomic policy load" ; |
1286 | list_for_each_entry(tmp, &lh, list) { |
1287 | if (tmp == ent) { |
1288 | info = "unchecked profile in failed atomic policy load" ; |
1289 | /* skip entry that caused failure */ |
1290 | continue; |
1291 | } |
1292 | op = (!tmp->old) ? OP_PROF_LOAD : OP_PROF_REPL; |
1293 | audit_policy(subj_label: label, op, ns_name, name: tmp->new->base.hname, info, |
1294 | error); |
1295 | } |
1296 | list_for_each_entry_safe(ent, tmp, &lh, list) { |
1297 | list_del_init(entry: &ent->list); |
1298 | aa_load_ent_free(ent); |
1299 | } |
1300 | |
1301 | goto out; |
1302 | } |
1303 | |
1304 | /** |
1305 | * aa_remove_profiles - remove profile(s) from the system |
1306 | * @policy_ns: namespace the remove is being done from |
1307 | * @subj: label attempting to remove policy |
1308 | * @fqname: name of the profile or namespace to remove (NOT NULL) |
1309 | * @size: size of the name |
1310 | * |
1311 | * Remove a profile or sub namespace from the current namespace, so that |
1312 | * they can not be found anymore and mark them as replaced by unconfined |
1313 | * |
1314 | * NOTE: removing confinement does not restore rlimits to preconfinement values |
1315 | * |
1316 | * Returns: size of data consume else error code if fails |
1317 | */ |
1318 | ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj, |
1319 | char *fqname, size_t size) |
1320 | { |
1321 | struct aa_ns *ns = NULL; |
1322 | struct aa_profile *profile = NULL; |
1323 | const char *name = fqname, *info = NULL; |
1324 | const char *ns_name = NULL; |
1325 | ssize_t error = 0; |
1326 | |
1327 | if (*fqname == 0) { |
1328 | info = "no profile specified" ; |
1329 | error = -ENOENT; |
1330 | goto fail; |
1331 | } |
1332 | |
1333 | if (fqname[0] == ':') { |
1334 | size_t ns_len; |
1335 | |
1336 | name = aa_splitn_fqname(fqname, n: size, ns_name: &ns_name, ns_len: &ns_len); |
1337 | /* released below */ |
1338 | ns = aa_lookupn_ns(view: policy_ns ? policy_ns : labels_ns(subj), |
1339 | name: ns_name, n: ns_len); |
1340 | if (!ns) { |
1341 | info = "namespace does not exist" ; |
1342 | error = -ENOENT; |
1343 | goto fail; |
1344 | } |
1345 | } else |
1346 | /* released below */ |
1347 | ns = aa_get_ns(ns: policy_ns ? policy_ns : labels_ns(subj)); |
1348 | |
1349 | if (!name) { |
1350 | /* remove namespace - can only happen if fqname[0] == ':' */ |
1351 | mutex_lock_nested(lock: &ns->parent->lock, subclass: ns->parent->level); |
1352 | __aa_bump_ns_revision(ns); |
1353 | __aa_remove_ns(ns); |
1354 | mutex_unlock(lock: &ns->parent->lock); |
1355 | } else { |
1356 | /* remove profile */ |
1357 | mutex_lock_nested(lock: &ns->lock, subclass: ns->level); |
1358 | profile = aa_get_profile(p: __lookup_profile(base: &ns->base, hname: name)); |
1359 | if (!profile) { |
1360 | error = -ENOENT; |
1361 | info = "profile does not exist" ; |
1362 | goto fail_ns_lock; |
1363 | } |
1364 | name = profile->base.hname; |
1365 | __aa_bump_ns_revision(ns); |
1366 | __remove_profile(profile); |
1367 | __aa_labelset_update_subtree(ns); |
1368 | mutex_unlock(lock: &ns->lock); |
1369 | } |
1370 | |
1371 | /* don't fail removal if audit fails */ |
1372 | (void) audit_policy(subj_label: subj, OP_PROF_RM, ns_name, name, info, |
1373 | error); |
1374 | aa_put_ns(ns); |
1375 | aa_put_profile(p: profile); |
1376 | return size; |
1377 | |
1378 | fail_ns_lock: |
1379 | mutex_unlock(lock: &ns->lock); |
1380 | aa_put_ns(ns); |
1381 | |
1382 | fail: |
1383 | (void) audit_policy(subj_label: subj, OP_PROF_RM, ns_name, name, info, |
1384 | error); |
1385 | return error; |
1386 | } |
1387 | |