aboutsummaryrefslogtreecommitdiffstats
path: root/security/apparmor
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
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')
-rw-r--r--security/apparmor/domain.c2
-rw-r--r--security/apparmor/include/policy.h80
-rw-r--r--security/apparmor/policy.c68
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;
86struct aa_policy { 86struct 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 */
190struct aa_profile { 190struct 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);
223struct aa_namespace *aa_find_namespace(struct aa_namespace *root, 224struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
224 const char *name); 225 const char *name);
225 226
226static 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 */
241static 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 */
255static 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
261void aa_free_replacedby_kref(struct kref *kref); 228void aa_free_replacedby_kref(struct kref *kref);
262struct aa_profile *aa_alloc_profile(const char *name); 229struct aa_profile *aa_alloc_profile(const char *name);
@@ -285,7 +252,7 @@ ssize_t aa_remove_profiles(char *name, size_t size);
285static inline struct aa_profile *aa_get_profile(struct aa_profile *p) 252static 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 */
300static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) 267static 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 */
351static inline void aa_put_profile(struct aa_profile *p) 318static 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
357static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) 328static 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 */
359static 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 */
373static inline void aa_put_namespace(struct aa_namespace *ns)
374{
375 if (ns)
376 aa_put_profile(ns->unconfined);
377}
378
381static inline int AUDIT_MODE(struct aa_profile *profile) 379static 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
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;