aboutsummaryrefslogtreecommitdiffstats
path: root/security/apparmor/policy.c
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2013-07-11 00:08:43 -0400
committerJohn Johansen <john.johansen@canonical.com>2013-08-14 14:42:06 -0400
commitfa2ac468db510c653499a47c1ec3deb045bf4763 (patch)
tree561c1c638d4e2c337712db0f2daf856a19560e2f /security/apparmor/policy.c
parent77b071b34045a0c65d0e1f85f3d47fd2b8b7a8a1 (diff)
apparmor: update how unconfined is handled
ns->unconfined is being used read side without locking, nor rcu but is being updated when a namespace is removed. This works for the root ns which is never removed but has a race window and can cause failures when children namespaces are removed. Also ns and ns->unconfined have a circular refcounting dependency that is problematic and must be broken. Currently this is done incorrectly when the namespace is destroyed. Fix this by forward referencing unconfined via the replacedby infrastructure instead of directly updating the ns->unconfined pointer. Remove the circular refcount dependency by making the ns and its unconfined profile share the same refcount. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
Diffstat (limited to 'security/apparmor/policy.c')
-rw-r--r--security/apparmor/policy.c68
1 files changed, 27 insertions, 41 deletions
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
310static 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 */
337static 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 */
341void aa_free_namespace_kref(struct kref *kref) 349void 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 */
495static void destroy_namespace(struct aa_namespace *ns) 504static 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
525static 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 */
657void aa_free_profile_kref(struct kref *kref) 643void 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;