mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-05 17:01:00 +01:00
475 lines
14 KiB
Diff
475 lines
14 KiB
Diff
Index: b/security/apparmor/apparmor.h
|
|
===================================================================
|
|
--- a/security/apparmor/apparmor.h
|
|
+++ b/security/apparmor/apparmor.h
|
|
@@ -119,6 +119,9 @@ struct aa_profile {
|
|
struct kref count;
|
|
};
|
|
|
|
+extern struct list_head profile_list;
|
|
+extern rwlock_t profile_list_lock;
|
|
+
|
|
/**
|
|
* struct aa_task_context - primary label for confined tasks
|
|
* @profile: the current profile
|
|
@@ -231,15 +234,11 @@ extern int aa_register(struct linux_binp
|
|
extern void aa_release(struct task_struct *task);
|
|
extern int aa_change_hat(const char *id, u32 hat_magic);
|
|
extern int aa_associate_filp(struct file *filp);
|
|
+extern struct aa_profile *__aa_find_profile(const char *name,
|
|
+ struct list_head *list);
|
|
|
|
/* list.c */
|
|
-extern struct aa_profile *aa_profilelist_find(const char *name);
|
|
-extern int aa_profilelist_add(struct aa_profile *profile);
|
|
-extern struct aa_profile *aa_profilelist_remove(const char *name);
|
|
extern void aa_profilelist_release(void);
|
|
-extern struct aa_profile *aa_profilelist_replace(struct aa_profile *profile);
|
|
-extern void aa_profile_dump(struct aa_profile *);
|
|
-extern void aa_profilelist_dump(void);
|
|
extern void aa_task_context_list_add(struct aa_task_context *);
|
|
extern void aa_task_context_list_remove(struct aa_task_context *);
|
|
extern void aa_task_context_list_iterate(aa_iter, void *);
|
|
Index: b/security/apparmor/list.c
|
|
===================================================================
|
|
--- a/security/apparmor/list.c
|
|
+++ b/security/apparmor/list.c
|
|
@@ -14,125 +14,31 @@
|
|
#include "inline.h"
|
|
|
|
/* list of all profiles and lock */
|
|
-static LIST_HEAD(profile_list);
|
|
-static rwlock_t profile_list_lock = RW_LOCK_UNLOCKED;
|
|
+LIST_HEAD(profile_list);
|
|
+rwlock_t profile_list_lock = RW_LOCK_UNLOCKED;
|
|
|
|
/* list of all task_contexts and lock */
|
|
static LIST_HEAD(task_context_list);
|
|
static rwlock_t task_context_list_lock = RW_LOCK_UNLOCKED;
|
|
|
|
/**
|
|
- * aa_profilelist_find
|
|
- * @name: profile name (program name)
|
|
+ * __aa_find_profile - look up a profile on the profile list
|
|
+ * @name: name of profile to find
|
|
+ * @head: list to search
|
|
*
|
|
- * Search the profile list for profile @name. Return refcounted profile on
|
|
- * success, NULL on failure.
|
|
+ * Returns a pointer to the profile on the list, or NULL if no profile
|
|
+ * called @name exists. The caller must hold the profile_list_lock.
|
|
*/
|
|
-struct aa_profile *aa_profilelist_find(const char *name)
|
|
+struct aa_profile *__aa_find_profile(const char *name, struct list_head *head)
|
|
{
|
|
- struct aa_profile *p = NULL;
|
|
- if (name) {
|
|
- read_lock(&profile_list_lock);
|
|
- p = __aa_find_profile(name, &profile_list);
|
|
- read_unlock(&profile_list_lock);
|
|
- }
|
|
- return p;
|
|
-}
|
|
-
|
|
-/**
|
|
- * aa_profilelist_add - add new profile to list
|
|
- * @profile: new profile to add to list
|
|
- *
|
|
- * NOTE: Caller must allocate necessary reference count that will be used
|
|
- * by the profile_list. This is because profile allocation alloc_aa_profile()
|
|
- * returns an unreferenced object with a initial count of %1.
|
|
- *
|
|
- * Return %1 on success, %0 on failure (already exists)
|
|
- */
|
|
-int aa_profilelist_add(struct aa_profile *profile)
|
|
-{
|
|
- struct aa_profile *old_profile;
|
|
- int ret = 0;
|
|
-
|
|
- if (!profile)
|
|
- goto out;
|
|
-
|
|
- write_lock(&profile_list_lock);
|
|
- old_profile = __aa_find_profile(profile->name, &profile_list);
|
|
- if (old_profile) {
|
|
- aa_put_profile(old_profile);
|
|
- goto out;
|
|
- }
|
|
-
|
|
- list_add(&profile->list, &profile_list);
|
|
- ret = 1;
|
|
- out:
|
|
- write_unlock(&profile_list_lock);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/**
|
|
- * aa_profilelist_remove - remove a profile from the list by name
|
|
- * @name: name of profile to be removed
|
|
- *
|
|
- * If the profile exists remove profile from list and return its reference.
|
|
- * The reference count on profile is not decremented and should be decremented
|
|
- * when the profile is no longer needed
|
|
- */
|
|
-struct aa_profile *aa_profilelist_remove(const char *name)
|
|
-{
|
|
- struct aa_profile *profile = NULL;
|
|
- struct aa_profile *p, *tmp;
|
|
-
|
|
- if (!name)
|
|
- goto out;
|
|
+ struct aa_profile *profile;
|
|
|
|
- write_lock(&profile_list_lock);
|
|
- list_for_each_entry_safe(p, tmp, &profile_list, list) {
|
|
- if (!strcmp(p->name, name)) {
|
|
- list_del_init(&p->list);
|
|
- /* mark old profile as stale */
|
|
- p->isstale = 1;
|
|
- profile = p;
|
|
- break;
|
|
- }
|
|
+ list_for_each_entry(profile, head, list) {
|
|
+ if (!strcmp(profile->name, name))
|
|
+ return profile;
|
|
}
|
|
- write_unlock(&profile_list_lock);
|
|
|
|
-out:
|
|
- return profile;
|
|
-}
|
|
-
|
|
-/**
|
|
- * aa_profilelist_replace - replace a profile on the list
|
|
- * @profile: new profile
|
|
- *
|
|
- * Replace a profile on the profile list. Find the old profile by name in
|
|
- * the list, and replace it with the new profile. NOTE: Caller must allocate
|
|
- * necessary initial reference count for new profile as aa_profilelist_add().
|
|
- *
|
|
- * This is an atomic list operation. Returns the old profile (which is still
|
|
- * refcounted) if there was one, or NULL.
|
|
- */
|
|
-struct aa_profile *aa_profilelist_replace(struct aa_profile *profile)
|
|
-{
|
|
- struct aa_profile *oldprofile;
|
|
-
|
|
- write_lock(&profile_list_lock);
|
|
- oldprofile = __aa_find_profile(profile->name, &profile_list);
|
|
- if (oldprofile) {
|
|
- list_del_init(&oldprofile->list);
|
|
- /* mark old profile as stale */
|
|
- oldprofile->isstale = 1;
|
|
-
|
|
- /* __aa_find_profile incremented count, so adjust down */
|
|
- aa_put_profile(oldprofile);
|
|
- }
|
|
-
|
|
- list_add(&profile->list, &profile_list);
|
|
- write_unlock(&profile_list_lock);
|
|
-
|
|
- return oldprofile;
|
|
+ return NULL;
|
|
}
|
|
|
|
/**
|
|
Index: b/security/apparmor/inline.h
|
|
===================================================================
|
|
--- a/security/apparmor/inline.h
|
|
+++ b/security/apparmor/inline.h
|
|
@@ -50,6 +50,17 @@ static inline struct aa_profile *aa_get_
|
|
return profile;
|
|
}
|
|
|
|
+static inline struct aa_profile *aa_find_profile(const char *name)
|
|
+{
|
|
+ struct aa_profile *profile = NULL;
|
|
+
|
|
+ read_lock(&profile_list_lock);
|
|
+ profile = aa_dup_profile(__aa_find_profile(name, &profile_list));
|
|
+ read_unlock(&profile_list_lock);
|
|
+
|
|
+ return profile;
|
|
+}
|
|
+
|
|
/**
|
|
* aa_switch_to_profile - change aa_task_context to use a new profile
|
|
* @cxt: aa_task_context to switch the profile on
|
|
@@ -132,32 +143,4 @@ static inline struct aa_profile *alloc_a
|
|
}
|
|
return profile;
|
|
}
|
|
-
|
|
-/** __aa_find_profile
|
|
- * @name: name of profile to find
|
|
- * @head: list to search
|
|
- *
|
|
- * Return reference counted handle to profile. NULL if not found
|
|
- * Caller must hold any necessary locks
|
|
- */
|
|
-static inline struct aa_profile *__aa_find_profile(const char *name,
|
|
- struct list_head *head)
|
|
-{
|
|
- struct aa_profile *p;
|
|
-
|
|
- if (!name || !head)
|
|
- return NULL;
|
|
-
|
|
- AA_DEBUG("%s: finding profile %s\n", __FUNCTION__, name);
|
|
- list_for_each_entry(p, head, list) {
|
|
- if (!strcmp(p->name, name)) {
|
|
- /* return refcounted object */
|
|
- p = aa_dup_profile(p);
|
|
- return p;
|
|
- } else {
|
|
- AA_DEBUG("%s: skipping %s\n", __FUNCTION__, p->name);
|
|
- }
|
|
- }
|
|
- return NULL;
|
|
-}
|
|
#endif /* __INLINE_H__ */
|
|
Index: b/security/apparmor/main.c
|
|
===================================================================
|
|
--- a/security/apparmor/main.c
|
|
+++ b/security/apparmor/main.c
|
|
@@ -778,7 +778,7 @@ aa_register_find(const char *name, int m
|
|
struct aa_profile *profile;
|
|
|
|
/* Locate new profile */
|
|
- profile = aa_profilelist_find(name);
|
|
+ profile = aa_find_profile(name);
|
|
if (profile) {
|
|
AA_DEBUG("%s: setting profile %s\n",
|
|
__FUNCTION__, profile->name);
|
|
@@ -931,7 +931,7 @@ repeat:
|
|
* the transition occured before replacement.
|
|
*
|
|
* - If newprofile points to an actual profile (result of
|
|
- * aa_profilelist_find above), this profile may have been
|
|
+ * aa_find_profile above), this profile may have been
|
|
* replaced. We need to fix it up. Doing this to avoid
|
|
* having to hold a lock around all this code.
|
|
*/
|
|
@@ -972,7 +972,7 @@ repeat:
|
|
/* drop refcnt obtained from earlier aa_dup_profile */
|
|
aa_put_profile(newprofile);
|
|
|
|
- newprofile = aa_profilelist_find(filename);
|
|
+ newprofile = aa_find_profile(filename);
|
|
|
|
if (!newprofile) {
|
|
/* Race, profile was removed, not replaced.
|
|
@@ -1063,12 +1063,14 @@ static inline int do_change_hat(const ch
|
|
struct aa_profile *sub;
|
|
int error = 0;
|
|
|
|
+ /*
|
|
+ * Note: the profile and sub-profiles cannot go away under us here;
|
|
+ * no need to grab an additional reference count.
|
|
+ */
|
|
sub = __aa_find_profile(hat_name, &BASE_PROFILE(cxt->profile)->sub);
|
|
-
|
|
if (sub) {
|
|
/* change hat */
|
|
aa_switch_to_profile(cxt, sub, hat_magic);
|
|
- aa_put_profile(sub);
|
|
} else {
|
|
if (APPARMOR_COMPLAIN(cxt)) {
|
|
LOG_HINT(cxt->profile, GFP_ATOMIC, HINT_UNKNOWN_HAT,
|
|
Index: b/security/apparmor/procattr.c
|
|
===================================================================
|
|
--- a/security/apparmor/procattr.c
|
|
+++ b/security/apparmor/procattr.c
|
|
@@ -208,7 +208,7 @@ int aa_setprocattr_setprofile(struct tas
|
|
|
|
repeat:
|
|
if (strcmp(name, "unconstrained") != 0) {
|
|
- profile = aa_profilelist_find(name);
|
|
+ profile = aa_find_profile(name);
|
|
if (!profile) {
|
|
AA_WARN("%s: Unable to switch task %s(%d) to profile"
|
|
"'%s'. No such profile.\n",
|
|
@@ -283,7 +283,7 @@ int aa_setprocattr_setprofile(struct tas
|
|
|
|
/* drop refcnt obtained from earlier aa_dup_profile */
|
|
aa_put_profile(profile);
|
|
- profile = aa_profilelist_find(name);
|
|
+ profile = aa_find_profile(name);
|
|
|
|
if (!profile) {
|
|
/* Race, profile was removed. */
|
|
Index: b/security/apparmor/module_interface.c
|
|
===================================================================
|
|
--- a/security/apparmor/module_interface.c
|
|
+++ b/security/apparmor/module_interface.c
|
|
@@ -476,16 +476,13 @@ fail:
|
|
}
|
|
|
|
/**
|
|
- * aa_file_prof_add - add a new profile to the profile list
|
|
+ * aa_file_prof_add - Unpack and add a new profile to the profile list
|
|
* @data: serialized data stream
|
|
* @size: size of the serialized data stream
|
|
- *
|
|
- * unpack and add a profile to the profile list. Return %0 or error
|
|
*/
|
|
ssize_t aa_file_prof_add(void *data, size_t size)
|
|
{
|
|
struct aa_profile *profile = NULL;
|
|
-
|
|
struct aa_ext e = {
|
|
.start = data,
|
|
.end = data + size,
|
|
@@ -494,26 +491,20 @@ ssize_t aa_file_prof_add(void *data, siz
|
|
ssize_t error;
|
|
|
|
profile = aa_activate_top_profile(&e, &error);
|
|
- if (!profile) {
|
|
- AA_DEBUG("couldn't activate profile\n");
|
|
- goto out;
|
|
- }
|
|
+ if (!profile)
|
|
+ return error;
|
|
|
|
- /* aa_activate_top_profile allocates profile with initial 1 count
|
|
- * aa_profilelist_add transfers that ref to profile list without
|
|
- * further incrementing
|
|
- */
|
|
- if (aa_profilelist_add(profile)) {
|
|
- error = size;
|
|
- } else {
|
|
- AA_WARN("trying to add profile (%s) that already exists.\n",
|
|
- profile->name);
|
|
+ write_lock(&profile_list_lock);
|
|
+ if (__aa_find_profile(profile->name, &profile_list)) {
|
|
+ /* A profile with this name exists already. */
|
|
+ write_unlock(&profile_list_lock);
|
|
aa_put_profile(profile);
|
|
- error = -EEXIST;
|
|
+ return -EEXIST;
|
|
}
|
|
+ list_add(&profile->list, &profile_list);
|
|
+ write_unlock(&profile_list_lock);
|
|
|
|
-out:
|
|
- return error;
|
|
+ return size;
|
|
}
|
|
|
|
/**
|
|
@@ -533,55 +524,31 @@ ssize_t aa_file_prof_repl(void *udata, s
|
|
.end = udata + size,
|
|
.pos = udata
|
|
};
|
|
-
|
|
ssize_t error;
|
|
|
|
data.new_profile = aa_activate_top_profile(&e, &error);
|
|
- if (!data.new_profile) {
|
|
- AA_DEBUG("couldn't activate profile\n");
|
|
- goto out;
|
|
- }
|
|
-
|
|
- /* Refcount on data.new_profile is 1 (aa_activate_top_profile).
|
|
- *
|
|
- * This reference will be inherited by aa_profilelist_replace for it's
|
|
- * profile list reference but this isn't sufficient.
|
|
- *
|
|
- * Another replace (*for-same-profile*) may race us here.
|
|
- * Task A calls aa_profilelist_replace(new_profile) and is interrupted.
|
|
- * Task B old_profile = aa_profilelist_replace() will return task A's
|
|
- * new_profile with the count of 1. If task B proceeeds to put this
|
|
- * profile it will dissapear from under task A.
|
|
- *
|
|
- * Grab extra reference on new_profile to prevent this
|
|
- */
|
|
-
|
|
- aa_dup_profile(data.new_profile);
|
|
-
|
|
- data.old_profile = aa_profilelist_replace(data.new_profile);
|
|
+ if (!data.new_profile)
|
|
+ return error;
|
|
|
|
- /* If there was an old profile, find all currently executing tasks
|
|
- * using this profile and replace the old profile with the new.
|
|
- */
|
|
+ write_lock(&profile_list_lock);
|
|
+ data.old_profile = __aa_find_profile(data.new_profile->name,
|
|
+ &profile_list);
|
|
if (data.old_profile) {
|
|
- AA_DEBUG("%s: try to replace profile (%p)%s\n",
|
|
- __FUNCTION__,
|
|
- data.old_profile,
|
|
- data.old_profile->name);
|
|
+ list_del_init(&data.old_profile->list);
|
|
|
|
- aa_task_context_list_iterate(taskreplace_iter, (void *)&data);
|
|
+ data.old_profile->isstale = 1;
|
|
|
|
- /* it's off global list, and we are done replacing */
|
|
+ /*
|
|
+ * Find all tasks using the old profile and replace the old
|
|
+ * profile with the new.
|
|
+ */
|
|
+ aa_task_context_list_iterate(taskreplace_iter, &data);
|
|
aa_put_profile(data.old_profile);
|
|
}
|
|
+ list_add(&data.new_profile->list, &profile_list);
|
|
+ write_unlock(&profile_list_lock);
|
|
|
|
- /* release extra reference obtained above (race) */
|
|
- aa_put_profile(data.new_profile);
|
|
-
|
|
- error = size;
|
|
-
|
|
-out:
|
|
- return error;
|
|
+ return size;
|
|
}
|
|
|
|
/**
|
|
@@ -590,30 +557,27 @@ out:
|
|
* @size: size of the name
|
|
*
|
|
* remove a profile from the profile list and all aa_task_context references
|
|
- * to said profile. Return %0 on success, else error.
|
|
+ * to said profile.
|
|
*/
|
|
ssize_t aa_file_prof_remove(const char *name, size_t size)
|
|
{
|
|
- struct aa_profile *old_profile;
|
|
+ struct aa_profile *profile;
|
|
|
|
- /* if the old profile exists it will be removed from the list and
|
|
- * a reference is returned.
|
|
- */
|
|
- old_profile = aa_profilelist_remove(name);
|
|
-
|
|
- if (old_profile) {
|
|
- /* remove profile from any tasks using it */
|
|
- aa_task_context_list_iterate(taskremove_iter,
|
|
- (void *)old_profile);
|
|
-
|
|
- /* drop reference obtained by aa_profilelist_remove */
|
|
- aa_put_profile(old_profile);
|
|
- } else {
|
|
- AA_WARN("%s: trying to remove profile (%s) that "
|
|
- "doesn't exist - skipping.\n", __FUNCTION__, name);
|
|
+ write_lock(&profile_list_lock);
|
|
+ profile = __aa_find_profile(name, &profile_list);
|
|
+ if (!profile) {
|
|
+ write_unlock(&profile_list_lock);
|
|
return -ENOENT;
|
|
}
|
|
|
|
+ list_del_init(&profile->list);
|
|
+
|
|
+ profile->isstale = 1;
|
|
+
|
|
+ aa_task_context_list_iterate(taskremove_iter, profile);
|
|
+ aa_put_profile(profile);
|
|
+ write_unlock(&profile_list_lock);
|
|
+
|
|
return size;
|
|
}
|
|
|