diff options
Diffstat (limited to 'security/apparmor')
-rw-r--r-- | security/apparmor/domain.c | 2 | ||||
-rw-r--r-- | security/apparmor/include/policy.h | 80 | ||||
-rw-r--r-- | security/apparmor/policy.c | 68 |
3 files changed, 67 insertions, 83 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 5488d095af6f..bc28f2670ee4 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c | |||
@@ -434,7 +434,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |||
434 | new_profile = aa_get_profile(profile); | 434 | new_profile = aa_get_profile(profile); |
435 | goto x_clear; | 435 | goto x_clear; |
436 | } else if (perms.xindex & AA_X_UNCONFINED) { | 436 | } else if (perms.xindex & AA_X_UNCONFINED) { |
437 | new_profile = aa_get_profile(ns->unconfined); | 437 | new_profile = aa_get_newest_profile(ns->unconfined); |
438 | info = "ux fallback"; | 438 | info = "ux fallback"; |
439 | } else { | 439 | } else { |
440 | error = -ENOENT; | 440 | error = -ENOENT; |
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index e9f2baf4467e..1ddd5e5728b8 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
@@ -68,6 +68,7 @@ enum profile_flags { | |||
68 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | 68 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ |
69 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | 69 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ |
70 | PFLAG_INVALID = 0x200, /* profile replaced/removed */ | 70 | PFLAG_INVALID = 0x200, /* profile replaced/removed */ |
71 | PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ | ||
71 | 72 | ||
72 | /* These flags must correspond with PATH_flags */ | 73 | /* These flags must correspond with PATH_flags */ |
73 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | 74 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ |
@@ -78,7 +79,6 @@ struct aa_profile; | |||
78 | /* struct aa_policy - common part of both namespaces and profiles | 79 | /* struct aa_policy - common part of both namespaces and profiles |
79 | * @name: name of the object | 80 | * @name: name of the object |
80 | * @hname - The hierarchical name | 81 | * @hname - The hierarchical name |
81 | * @count: reference count of the obj | ||
82 | * @list: list policy object is on | 82 | * @list: list policy object is on |
83 | * @rcu: rcu head used when removing from @list | 83 | * @rcu: rcu head used when removing from @list |
84 | * @profiles: head of the profiles list contained in the object | 84 | * @profiles: head of the profiles list contained in the object |
@@ -86,7 +86,6 @@ struct aa_profile; | |||
86 | struct aa_policy { | 86 | struct aa_policy { |
87 | char *name; | 87 | char *name; |
88 | char *hname; | 88 | char *hname; |
89 | struct kref count; | ||
90 | struct list_head list; | 89 | struct list_head list; |
91 | struct list_head profiles; | 90 | struct list_head profiles; |
92 | struct rcu_head rcu; | 91 | struct rcu_head rcu; |
@@ -157,6 +156,7 @@ struct aa_replacedby { | |||
157 | 156 | ||
158 | /* struct aa_profile - basic confinement data | 157 | /* struct aa_profile - basic confinement data |
159 | * @base - base components of the profile (name, refcount, lists, lock ...) | 158 | * @base - base components of the profile (name, refcount, lists, lock ...) |
159 | * @count: reference count of the obj | ||
160 | * @parent: parent of profile | 160 | * @parent: parent of profile |
161 | * @ns: namespace the profile is in | 161 | * @ns: namespace the profile is in |
162 | * @replacedby: is set to the profile that replaced this profile | 162 | * @replacedby: is set to the profile that replaced this profile |
@@ -189,6 +189,7 @@ struct aa_replacedby { | |||
189 | */ | 189 | */ |
190 | struct aa_profile { | 190 | struct aa_profile { |
191 | struct aa_policy base; | 191 | struct aa_policy base; |
192 | struct kref count; | ||
192 | struct aa_profile __rcu *parent; | 193 | struct aa_profile __rcu *parent; |
193 | 194 | ||
194 | struct aa_namespace *ns; | 195 | struct aa_namespace *ns; |
@@ -223,40 +224,6 @@ void aa_free_namespace_kref(struct kref *kref); | |||
223 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | 224 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, |
224 | const char *name); | 225 | const char *name); |
225 | 226 | ||
226 | static inline struct aa_policy *aa_get_common(struct aa_policy *c) | ||
227 | { | ||
228 | if (c) | ||
229 | kref_get(&c->count); | ||
230 | |||
231 | return c; | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * aa_get_namespace - increment references count on @ns | ||
236 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
237 | * | ||
238 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
239 | * Requires: @ns must be held with valid refcount when called | ||
240 | */ | ||
241 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
242 | { | ||
243 | if (ns) | ||
244 | kref_get(&(ns->base.count)); | ||
245 | |||
246 | return ns; | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * aa_put_namespace - decrement refcount on @ns | ||
251 | * @ns: namespace to put reference of | ||
252 | * | ||
253 | * Decrement reference count of @ns and if no longer in use free it | ||
254 | */ | ||
255 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
256 | { | ||
257 | if (ns) | ||
258 | kref_put(&ns->base.count, aa_free_namespace_kref); | ||
259 | } | ||
260 | 227 | ||
261 | void aa_free_replacedby_kref(struct kref *kref); | 228 | void aa_free_replacedby_kref(struct kref *kref); |
262 | struct aa_profile *aa_alloc_profile(const char *name); | 229 | struct aa_profile *aa_alloc_profile(const char *name); |
@@ -285,7 +252,7 @@ ssize_t aa_remove_profiles(char *name, size_t size); | |||
285 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | 252 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) |
286 | { | 253 | { |
287 | if (p) | 254 | if (p) |
288 | kref_get(&(p->base.count)); | 255 | kref_get(&(p->count)); |
289 | 256 | ||
290 | return p; | 257 | return p; |
291 | } | 258 | } |
@@ -299,7 +266,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | |||
299 | */ | 266 | */ |
300 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) | 267 | static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) |
301 | { | 268 | { |
302 | if (p && kref_get_not0(&p->base.count)) | 269 | if (p && kref_get_not0(&p->count)) |
303 | return p; | 270 | return p; |
304 | 271 | ||
305 | return NULL; | 272 | return NULL; |
@@ -319,7 +286,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) | |||
319 | rcu_read_lock(); | 286 | rcu_read_lock(); |
320 | do { | 287 | do { |
321 | c = rcu_dereference(*p); | 288 | c = rcu_dereference(*p); |
322 | } while (c && !kref_get_not0(&c->base.count)); | 289 | } while (c && !kref_get_not0(&c->count)); |
323 | rcu_read_unlock(); | 290 | rcu_read_unlock(); |
324 | 291 | ||
325 | return c; | 292 | return c; |
@@ -350,8 +317,12 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) | |||
350 | */ | 317 | */ |
351 | static inline void aa_put_profile(struct aa_profile *p) | 318 | static inline void aa_put_profile(struct aa_profile *p) |
352 | { | 319 | { |
353 | if (p) | 320 | if (p) { |
354 | kref_put(&p->base.count, aa_free_profile_kref); | 321 | if (p->flags & PFLAG_NS_COUNT) |
322 | kref_put(&p->count, aa_free_namespace_kref); | ||
323 | else | ||
324 | kref_put(&p->count, aa_free_profile_kref); | ||
325 | } | ||
355 | } | 326 | } |
356 | 327 | ||
357 | static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) | 328 | static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) |
@@ -378,6 +349,33 @@ static inline void __aa_update_replacedby(struct aa_profile *orig, | |||
378 | aa_put_profile(tmp); | 349 | aa_put_profile(tmp); |
379 | } | 350 | } |
380 | 351 | ||
352 | /** | ||
353 | * aa_get_namespace - increment references count on @ns | ||
354 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
355 | * | ||
356 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
357 | * Requires: @ns must be held with valid refcount when called | ||
358 | */ | ||
359 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
360 | { | ||
361 | if (ns) | ||
362 | aa_get_profile(ns->unconfined); | ||
363 | |||
364 | return ns; | ||
365 | } | ||
366 | |||
367 | /** | ||
368 | * aa_put_namespace - decrement refcount on @ns | ||
369 | * @ns: namespace to put reference of | ||
370 | * | ||
371 | * Decrement reference count of @ns and if no longer in use free it | ||
372 | */ | ||
373 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
374 | { | ||
375 | if (ns) | ||
376 | aa_put_profile(ns->unconfined); | ||
377 | } | ||
378 | |||
381 | static inline int AUDIT_MODE(struct aa_profile *profile) | 379 | static inline int AUDIT_MODE(struct aa_profile *profile) |
382 | { | 380 | { |
383 | if (aa_g_audit != AUDIT_NORMAL) | 381 | if (aa_g_audit != AUDIT_NORMAL) |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 41b8f275c626..0ceee967434c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -141,7 +141,6 @@ static bool policy_init(struct aa_policy *policy, const char *prefix, | |||
141 | policy->name = (char *)hname_tail(policy->hname); | 141 | policy->name = (char *)hname_tail(policy->hname); |
142 | INIT_LIST_HEAD(&policy->list); | 142 | INIT_LIST_HEAD(&policy->list); |
143 | INIT_LIST_HEAD(&policy->profiles); | 143 | INIT_LIST_HEAD(&policy->profiles); |
144 | kref_init(&policy->count); | ||
145 | 144 | ||
146 | return 1; | 145 | return 1; |
147 | } | 146 | } |
@@ -292,14 +291,10 @@ static struct aa_namespace *alloc_namespace(const char *prefix, | |||
292 | goto fail_unconfined; | 291 | goto fail_unconfined; |
293 | 292 | ||
294 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | | 293 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | |
295 | PFLAG_IMMUTABLE; | 294 | PFLAG_IMMUTABLE | PFLAG_NS_COUNT; |
296 | 295 | ||
297 | /* | 296 | /* ns and ns->unconfined share ns->unconfined refcount */ |
298 | * released by free_namespace, however __remove_namespace breaks | 297 | ns->unconfined->ns = ns; |
299 | * the cyclic references (ns->unconfined, and unconfined->ns) and | ||
300 | * replaces with refs to parent namespace unconfined | ||
301 | */ | ||
302 | ns->unconfined->ns = aa_get_namespace(ns); | ||
303 | 298 | ||
304 | atomic_set(&ns->uniq_null, 0); | 299 | atomic_set(&ns->uniq_null, 0); |
305 | 300 | ||
@@ -312,6 +307,7 @@ fail_ns: | |||
312 | return NULL; | 307 | return NULL; |
313 | } | 308 | } |
314 | 309 | ||
310 | static void free_profile(struct aa_profile *profile); | ||
315 | /** | 311 | /** |
316 | * free_namespace - free a profile namespace | 312 | * free_namespace - free a profile namespace |
317 | * @ns: the namespace to free (MAYBE NULL) | 313 | * @ns: the namespace to free (MAYBE NULL) |
@@ -327,20 +323,33 @@ static void free_namespace(struct aa_namespace *ns) | |||
327 | policy_destroy(&ns->base); | 323 | policy_destroy(&ns->base); |
328 | aa_put_namespace(ns->parent); | 324 | aa_put_namespace(ns->parent); |
329 | 325 | ||
330 | if (ns->unconfined && ns->unconfined->ns == ns) | 326 | ns->unconfined->ns = NULL; |
331 | ns->unconfined->ns = NULL; | 327 | free_profile(ns->unconfined); |
332 | |||
333 | aa_put_profile(ns->unconfined); | ||
334 | kzfree(ns); | 328 | kzfree(ns); |
335 | } | 329 | } |
336 | 330 | ||
337 | /** | 331 | /** |
332 | * aa_free_namespace_rcu - free aa_namespace by rcu | ||
333 | * @head: rcu_head callback for freeing of a profile (NOT NULL) | ||
334 | * | ||
335 | * rcu_head is to the unconfined profile associated with the namespace | ||
336 | */ | ||
337 | static void aa_free_namespace_rcu(struct rcu_head *head) | ||
338 | { | ||
339 | struct aa_profile *p = container_of(head, struct aa_profile, base.rcu); | ||
340 | free_namespace(p->ns); | ||
341 | } | ||
342 | |||
343 | /** | ||
338 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | 344 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) |
339 | * @kr: kref callback for freeing of a namespace (NOT NULL) | 345 | * @kr: kref callback for freeing of a namespace (NOT NULL) |
346 | * | ||
347 | * kref is to the unconfined profile associated with the namespace | ||
340 | */ | 348 | */ |
341 | void aa_free_namespace_kref(struct kref *kref) | 349 | void aa_free_namespace_kref(struct kref *kref) |
342 | { | 350 | { |
343 | free_namespace(container_of(kref, struct aa_namespace, base.count)); | 351 | struct aa_profile *p = container_of(kref, struct aa_profile, count); |
352 | call_rcu(&p->base.rcu, aa_free_namespace_rcu); | ||
344 | } | 353 | } |
345 | 354 | ||
346 | /** | 355 | /** |
@@ -494,8 +503,6 @@ static void __ns_list_release(struct list_head *head); | |||
494 | */ | 503 | */ |
495 | static void destroy_namespace(struct aa_namespace *ns) | 504 | static void destroy_namespace(struct aa_namespace *ns) |
496 | { | 505 | { |
497 | struct aa_profile *unconfined; | ||
498 | |||
499 | if (!ns) | 506 | if (!ns) |
500 | return; | 507 | return; |
501 | 508 | ||
@@ -506,30 +513,11 @@ static void destroy_namespace(struct aa_namespace *ns) | |||
506 | /* release all sub namespaces */ | 513 | /* release all sub namespaces */ |
507 | __ns_list_release(&ns->sub_ns); | 514 | __ns_list_release(&ns->sub_ns); |
508 | 515 | ||
509 | unconfined = ns->unconfined; | ||
510 | /* | ||
511 | * break the ns, unconfined profile cyclic reference and forward | ||
512 | * all new unconfined profiles requests to the parent namespace | ||
513 | * This will result in all confined tasks that have a profile | ||
514 | * being removed, inheriting the parent->unconfined profile. | ||
515 | */ | ||
516 | if (ns->parent) | 516 | if (ns->parent) |
517 | ns->unconfined = aa_get_profile(ns->parent->unconfined); | 517 | __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); |
518 | |||
519 | /* release original ns->unconfined ref */ | ||
520 | aa_put_profile(unconfined); | ||
521 | |||
522 | mutex_unlock(&ns->lock); | 518 | mutex_unlock(&ns->lock); |
523 | } | 519 | } |
524 | 520 | ||
525 | static void aa_put_ns_rcu(struct rcu_head *head) | ||
526 | { | ||
527 | struct aa_namespace *ns = container_of(head, struct aa_namespace, | ||
528 | base.rcu); | ||
529 | /* release ns->base.list ref */ | ||
530 | aa_put_namespace(ns); | ||
531 | } | ||
532 | |||
533 | /** | 521 | /** |
534 | * __remove_namespace - remove a namespace and all its children | 522 | * __remove_namespace - remove a namespace and all its children |
535 | * @ns: namespace to be removed (NOT NULL) | 523 | * @ns: namespace to be removed (NOT NULL) |
@@ -540,10 +528,8 @@ static void __remove_namespace(struct aa_namespace *ns) | |||
540 | { | 528 | { |
541 | /* remove ns from namespace list */ | 529 | /* remove ns from namespace list */ |
542 | list_del_rcu(&ns->base.list); | 530 | list_del_rcu(&ns->base.list); |
543 | |||
544 | destroy_namespace(ns); | 531 | destroy_namespace(ns); |
545 | 532 | aa_put_namespace(ns); | |
546 | call_rcu(&ns->base.rcu, aa_put_ns_rcu); | ||
547 | } | 533 | } |
548 | 534 | ||
549 | /** | 535 | /** |
@@ -656,8 +642,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) | |||
656 | */ | 642 | */ |
657 | void aa_free_profile_kref(struct kref *kref) | 643 | void aa_free_profile_kref(struct kref *kref) |
658 | { | 644 | { |
659 | struct aa_profile *p = container_of(kref, struct aa_profile, | 645 | struct aa_profile *p = container_of(kref, struct aa_profile, count); |
660 | base.count); | ||
661 | call_rcu(&p->base.rcu, aa_free_profile_rcu); | 646 | call_rcu(&p->base.rcu, aa_free_profile_rcu); |
662 | } | 647 | } |
663 | 648 | ||
@@ -683,6 +668,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) | |||
683 | 668 | ||
684 | if (!policy_init(&profile->base, NULL, hname)) | 669 | if (!policy_init(&profile->base, NULL, hname)) |
685 | goto fail; | 670 | goto fail; |
671 | kref_init(&profile->count); | ||
686 | 672 | ||
687 | /* refcount released by caller */ | 673 | /* refcount released by caller */ |
688 | return profile; | 674 | return profile; |
@@ -884,7 +870,7 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | |||
884 | 870 | ||
885 | /* the unconfined profile is not in the regular profile list */ | 871 | /* the unconfined profile is not in the regular profile list */ |
886 | if (!profile && strcmp(hname, "unconfined") == 0) | 872 | if (!profile && strcmp(hname, "unconfined") == 0) |
887 | profile = aa_get_profile(ns->unconfined); | 873 | profile = aa_get_newest_profile(ns->unconfined); |
888 | 874 | ||
889 | /* refcount released by caller */ | 875 | /* refcount released by caller */ |
890 | return profile; | 876 | return profile; |