From b21507e272627c434e8dd74e8d51fd8245281b59 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: proc,security: move restriction on writing /proc/pid/attr nodes to proc Processes can only alter their own security attributes via /proc/pid/attr nodes. This is presently enforced by each individual security module and is also imposed by the Linux credentials implementation, which only allows a task to alter its own credentials. Move the check enforcing this restriction from the individual security modules to proc_pid_attr_write() before calling the security hook, and drop the unnecessary task argument to the security hook since it can only ever be the current task. Signed-off-by: Stephen Smalley Acked-by: Casey Schaufler Acked-by: John Johansen Signed-off-by: Paul Moore --- security/apparmor/lsm.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb1..8202e55 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -495,8 +495,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, return error; } -static int apparmor_setprocattr(struct task_struct *task, char *name, - void *value, size_t size) +static int apparmor_setprocattr(const char *name, void *value, + size_t size) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; @@ -506,9 +506,6 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (size == 0) return -EINVAL; - /* task can only write its own attributes */ - if (current != task) - return -EACCES; /* AppArmor requires that the buffer must be null terminated atm */ if (args[size - 1] != '\0') { -- cgit v1.1 From a7f6c1b63b863d29f126d9b163ad5b40008544b2 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 14 Nov 2016 20:11:52 +0900 Subject: AppArmor: Use GFP_KERNEL for __aa_kvmalloc(). Calling kmalloc(GFP_NOIO) with order == PAGE_ALLOC_COSTLY_ORDER is not recommended because it might fall into infinite retry loop without invoking the OOM killer. Since aa_dfa_unpack() is the only caller of kvzalloc() and aa_dfa_unpack() which is calling kvzalloc() via unpack_table() is doing kzalloc(GFP_KERNEL), it is safe to use GFP_KERNEL from __aa_kvmalloc(). Since aa_simple_write_to_buffer() is the only caller of kvmalloc() and aa_simple_write_to_buffer() is calling copy_from_user() which is GFP_KERNEL context (see memdup_user_nul()), it is safe to use GFP_KERNEL from __aa_kvmalloc(). Therefore, replace GFP_NOIO with GFP_KERNEL. Also, since we have vmalloc() fallback, add __GFP_NORETRY so that we don't invoke the OOM killer by kmalloc(GFP_KERNEL) with order == PAGE_ALLOC_COSTLY_ORDER. Signed-off-by: Tetsuo Handa Signed-off-by: John Johansen --- security/apparmor/lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security/apparmor') diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index c1827e0..2ef422a 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -95,7 +95,8 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) /* do not attempt kmalloc if we need more than 16 pages at once */ if (size <= (16*PAGE_SIZE)) - buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); + buffer = kmalloc(size, flags | GFP_KERNEL | __GFP_NORETRY | + __GFP_NOWARN); if (!buffer) { if (flags & __GFP_ZERO) buffer = vzalloc(size); -- cgit v1.1 From 8486adf0d755062611968ecc1632a13ebce71660 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 16 Dec 2016 17:04:13 -0800 Subject: apparmor: use designated initializers Prepare to mark sensitive kernel structures for randomization by making sure they're using designated initializers. These were identified during allyesconfig builds of x86, arm, and arm64, with most initializer fixes extracted from grsecurity. Signed-off-by: Kees Cook Signed-off-by: John Johansen --- security/apparmor/file.c | 4 ++-- security/apparmor/lsm.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 4d2af4b..608971a 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -349,8 +349,8 @@ static inline bool xindex_is_subset(u32 link, u32 target) int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) { - struct path link = { new_dir->mnt, new_dentry }; - struct path target = { new_dir->mnt, old_dentry }; + struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry }; + struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry }; struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, d_backing_inode(old_dentry)->i_mode diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 41b8cb1..f76738b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -176,7 +176,7 @@ static int common_perm_dir_dentry(int op, const struct path *dir, struct dentry *dentry, u32 mask, struct path_cond *cond) { - struct path path = { dir->mnt, dentry }; + struct path path = { .mnt = dir->mnt, .dentry = dentry }; return common_perm(op, &path, mask, cond); } @@ -306,8 +306,10 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d profile = aa_current_profile(); if (!unconfined(profile)) { - struct path old_path = { old_dir->mnt, old_dentry }; - struct path new_path = { new_dir->mnt, new_dentry }; + struct path old_path = { .mnt = old_dir->mnt, + .dentry = old_dentry }; + struct path new_path = { .mnt = new_dir->mnt, + .dentry = new_dentry }; struct path_cond cond = { d_backing_inode(old_dentry)->i_uid, d_backing_inode(old_dentry)->i_mode }; -- cgit v1.1 From 12557dcba21b015f470076da6947e68bc70fff64 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:13 -0800 Subject: apparmor: move lib definitions into separate lib include Signed-off-by: John Johansen --- security/apparmor/include/apparmor.h | 82 +------------------------------ security/apparmor/include/lib.h | 94 ++++++++++++++++++++++++++++++++++++ security/apparmor/include/policy.h | 1 + security/apparmor/lib.c | 2 +- security/apparmor/match.c | 2 +- 5 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 security/apparmor/include/lib.h (limited to 'security/apparmor') diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 5d721e9..1750cc0 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -1,7 +1,7 @@ /* * AppArmor security module * - * This file contains AppArmor basic global and lib definitions + * This file contains AppArmor basic global * * Copyright (C) 1998-2008 Novell/SUSE * Copyright 2009-2010 Canonical Ltd. @@ -15,10 +15,7 @@ #ifndef __APPARMOR_H #define __APPARMOR_H -#include -#include - -#include "match.h" +#include /* * Class of mediation types in the AppArmor policy db @@ -43,79 +40,4 @@ extern bool aa_g_logsyscall; extern bool aa_g_paranoid_load; extern unsigned int aa_g_path_max; -/* - * DEBUG remains global (no per profile flag) since it is mostly used in sysctl - * which is not related to profile accesses. - */ - -#define AA_DEBUG(fmt, args...) \ - do { \ - if (aa_g_debug && printk_ratelimit()) \ - printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ - } while (0) - -#define AA_ERROR(fmt, args...) \ - do { \ - if (printk_ratelimit()) \ - printk(KERN_ERR "AppArmor: " fmt, ##args); \ - } while (0) - -/* Flag indicating whether initialization completed */ -extern int apparmor_initialized __initdata; - -/* fn's in lib */ -char *aa_split_fqname(char *args, char **ns_name); -void aa_info_message(const char *str); -void *__aa_kvmalloc(size_t size, gfp_t flags); - -static inline void *kvmalloc(size_t size) -{ - return __aa_kvmalloc(size, 0); -} - -static inline void *kvzalloc(size_t size) -{ - return __aa_kvmalloc(size, __GFP_ZERO); -} - -/* returns 0 if kref not incremented */ -static inline int kref_get_not0(struct kref *kref) -{ - return atomic_inc_not_zero(&kref->refcount); -} - -/** - * aa_strneq - compare null terminated @str to a non null terminated substring - * @str: a null terminated string - * @sub: a substring, not necessarily null terminated - * @len: length of @sub to compare - * - * The @str string must be full consumed for this to be considered a match - */ -static inline bool aa_strneq(const char *str, const char *sub, int len) -{ - return !strncmp(str, sub, len) && !str[len]; -} - -/** - * aa_dfa_null_transition - step to next state after null character - * @dfa: the dfa to match against - * @start: the state of the dfa to start matching in - * - * aa_dfa_null_transition transitions to the next state after a null - * character which is not used in standard matching and is only - * used to separate pairs. - */ -static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, - unsigned int start) -{ - /* the null transition only needs the string's null terminator byte */ - return aa_dfa_next(dfa, start, 0); -} - -static inline bool mediated_filesystem(struct dentry *dentry) -{ - return !(dentry->d_sb->s_flags & MS_NOUSER); -} - #endif /* __APPARMOR_H */ diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h new file mode 100644 index 0000000..71fd6d2 --- /dev/null +++ b/security/apparmor/include/lib.h @@ -0,0 +1,94 @@ +/* + * AppArmor security module + * + * This file contains AppArmor lib definitions + * + * 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_LIB_H +#define __AA_LIB_H + +#include +#include + +#include "match.h" + +/* + * DEBUG remains global (no per profile flag) since it is mostly used in sysctl + * which is not related to profile accesses. + */ + +#define AA_DEBUG(fmt, args...) \ + do { \ + if (aa_g_debug) \ + pr_debug_ratelimited("AppArmor: " fmt, ##args); \ + } while (0) + +#define AA_ERROR(fmt, args...) \ + pr_err_ratelimited("AppArmor: " fmt, ##args) + +/* Flag indicating whether initialization completed */ +extern int apparmor_initialized __initdata; + +/* fn's in lib */ +char *aa_split_fqname(char *args, char **ns_name); +void aa_info_message(const char *str); +void *__aa_kvmalloc(size_t size, gfp_t flags); + +static inline void *kvmalloc(size_t size) +{ + return __aa_kvmalloc(size, 0); +} + +static inline void *kvzalloc(size_t size) +{ + return __aa_kvmalloc(size, __GFP_ZERO); +} + +/* returns 0 if kref not incremented */ +static inline int kref_get_not0(struct kref *kref) +{ + return atomic_inc_not_zero(&kref->refcount); +} + +/** + * aa_strneq - compare null terminated @str to a non null terminated substring + * @str: a null terminated string + * @sub: a substring, not necessarily null terminated + * @len: length of @sub to compare + * + * The @str string must be full consumed for this to be considered a match + */ +static inline bool aa_strneq(const char *str, const char *sub, int len) +{ + return !strncmp(str, sub, len) && !str[len]; +} + +/** + * aa_dfa_null_transition - step to next state after null character + * @dfa: the dfa to match against + * @start: the state of the dfa to start matching in + * + * aa_dfa_null_transition transitions to the next state after a null + * character which is not used in standard matching and is only + * used to separate pairs. + */ +static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, + unsigned int start) +{ + /* the null transition only needs the string's null terminator byte */ + return aa_dfa_next(dfa, start, 0); +} + +static inline bool mediated_filesystem(struct dentry *dentry) +{ + return !(dentry->d_sb->s_flags & MS_NOUSER); +} + +#endif /* AA_LIB_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 52275f0..190fe37 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,6 +27,7 @@ #include "capability.h" #include "domain.h" #include "file.h" +#include "lib.h" #include "resource.h" extern const char *const aa_profile_mode_names[]; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 2ef422a..6028ffc 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -19,7 +19,7 @@ #include "include/audit.h" #include "include/apparmor.h" - +#include "include/lib.h" /** * aa_split_fqname - split a fqname into a profile and namespace name diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 3f900fc..0e04bcf 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -20,7 +20,7 @@ #include #include -#include "include/apparmor.h" +#include "include/lib.h" #include "include/match.h" #define base_idx(X) ((X) & 0xffffff) -- cgit v1.1 From fe6bb31f590c9cd9c8d3ddbdfd4301f72db91718 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:14 -0800 Subject: apparmor: split out shared policy_XXX fns to lib Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 81 ++++++++++++++++++++++++ security/apparmor/include/policy.h | 13 ---- security/apparmor/lib.c | 52 ++++++++++++++++ security/apparmor/policy.c | 123 ++----------------------------------- 4 files changed, 137 insertions(+), 132 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 71fd6d2..74cc68e 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -91,4 +91,85 @@ static inline bool mediated_filesystem(struct dentry *dentry) return !(dentry->d_sb->s_flags & MS_NOUSER); } +/* struct aa_policy - common part of both namespaces and profiles + * @name: name of the object + * @hname - The hierarchical name + * @list: list policy object is on + * @profiles: head of the profiles list contained in the object + */ +struct aa_policy { + char *name; + char *hname; + struct list_head list; + struct list_head profiles; +}; + +/** + * hname_tail - find the last component of an hname + * @name: hname to find the base profile name component of (NOT NULL) + * + * Returns: the tail (base profile name) name component of an hname + */ +static inline const char *hname_tail(const char *hname) +{ + char *split; + + hname = strim((char *)hname); + for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) + hname = split + 2; + + return hname; +} + +/** + * __policy_find - find a policy by @name on a policy list + * @head: list to search (NOT NULL) + * @name: name to search for (NOT NULL) + * + * Requires: rcu_read_lock be held + * + * Returns: unrefcounted policy that match @name or NULL if not found + */ +static inline struct aa_policy *__policy_find(struct list_head *head, + const char *name) +{ + struct aa_policy *policy; + + list_for_each_entry_rcu(policy, head, list) { + if (!strcmp(policy->name, name)) + return policy; + } + return NULL; +} + +/** + * __policy_strn_find - find a policy that's name matches @len chars of @str + * @head: list to search (NOT NULL) + * @str: string to search for (NOT NULL) + * @len: length of match required + * + * Requires: rcu_read_lock be held + * + * Returns: unrefcounted policy that match @str or NULL if not found + * + * if @len == strlen(@strlen) then this is equiv to __policy_find + * other wise it allows searching for policy by a partial match of name + */ +static inline struct aa_policy *__policy_strn_find(struct list_head *head, + const char *str, int len) +{ + struct aa_policy *policy; + + list_for_each_entry_rcu(policy, head, list) { + if (aa_strneq(policy->name, str, len)) + return policy; + } + + return NULL; +} + +bool aa_policy_init(struct aa_policy *policy, const char *prefix, + const char *name); +void aa_policy_destroy(struct aa_policy *policy); + #endif /* AA_LIB_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 190fe37..9b19967 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -77,19 +77,6 @@ enum profile_flags { struct aa_profile; -/* struct aa_policy - common part of both namespaces and profiles - * @name: name of the object - * @hname - The hierarchical name - * @list: list policy object is on - * @profiles: head of the profiles list contained in the object - */ -struct aa_policy { - char *name; - char *hname; - struct list_head list; - struct list_head profiles; -}; - /* struct aa_ns_acct - accounting of profiles in namespace * @max_size: maximum space allowed for all profiles in namespace * @max_count: maximum number of profiles that can be in this namespace diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 6028ffc..e29ccdb 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -20,6 +20,7 @@ #include "include/audit.h" #include "include/apparmor.h" #include "include/lib.h" +#include "include/policy.h" /** * aa_split_fqname - split a fqname into a profile and namespace name @@ -105,3 +106,54 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) } return buffer; } + +/** + * aa_policy_init - initialize a policy structure + * @policy: policy to initialize (NOT NULL) + * @prefix: prefix name if any is required. (MAYBE NULL) + * @name: name of the policy, init will make a copy of it (NOT NULL) + * + * Note: this fn creates a copy of strings passed in + * + * Returns: true if policy init successful + */ +bool aa_policy_init(struct aa_policy *policy, const char *prefix, + const char *name) +{ + /* freed by policy_free */ + if (prefix) { + policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, + GFP_KERNEL); + if (policy->hname) + sprintf(policy->hname, "%s//%s", prefix, name); + } else + policy->hname = kstrdup(name, GFP_KERNEL); + if (!policy->hname) + return 0; + /* base.name is a substring of fqname */ + policy->name = (char *)hname_tail(policy->hname); + INIT_LIST_HEAD(&policy->list); + INIT_LIST_HEAD(&policy->profiles); + + return 1; +} + +/** + * aa_policy_destroy - free the elements referenced by @policy + * @policy: policy that is to have its elements freed (NOT NULL) + */ +void aa_policy_destroy(struct aa_policy *policy) +{ + /* still contains profiles -- invalid */ + if (on_list_rcu(&policy->profiles)) { + AA_ERROR("%s: internal error, policy '%s' contains profiles\n", + __func__, policy->name); + } + if (on_list_rcu(&policy->list)) { + AA_ERROR("%s: internal error, policy '%s' still on list\n", + __func__, policy->name); + } + + /* don't free name as its a subset of hname */ + kzfree(policy->hname); +} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 179e68d..a331149 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -99,121 +99,6 @@ const char *const aa_profile_mode_names[] = { "unconfined", }; -/** - * hname_tail - find the last component of an hname - * @name: hname to find the base profile name component of (NOT NULL) - * - * Returns: the tail (base profile name) name component of an hname - */ -static const char *hname_tail(const char *hname) -{ - char *split; - hname = strim((char *)hname); - for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) - hname = split + 2; - - return hname; -} - -/** - * policy_init - initialize a policy structure - * @policy: policy to initialize (NOT NULL) - * @prefix: prefix name if any is required. (MAYBE NULL) - * @name: name of the policy, init will make a copy of it (NOT NULL) - * - * Note: this fn creates a copy of strings passed in - * - * Returns: true if policy init successful - */ -static bool policy_init(struct aa_policy *policy, const char *prefix, - const char *name) -{ - /* freed by policy_free */ - if (prefix) { - policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, - GFP_KERNEL); - if (policy->hname) - sprintf(policy->hname, "%s//%s", prefix, name); - } else - policy->hname = kstrdup(name, GFP_KERNEL); - if (!policy->hname) - return 0; - /* base.name is a substring of fqname */ - policy->name = (char *)hname_tail(policy->hname); - INIT_LIST_HEAD(&policy->list); - INIT_LIST_HEAD(&policy->profiles); - - return 1; -} - -/** - * policy_destroy - free the elements referenced by @policy - * @policy: policy that is to have its elements freed (NOT NULL) - */ -static void policy_destroy(struct aa_policy *policy) -{ - /* still contains profiles -- invalid */ - if (on_list_rcu(&policy->profiles)) { - AA_ERROR("%s: internal error, " - "policy '%s' still contains profiles\n", - __func__, policy->name); - BUG(); - } - if (on_list_rcu(&policy->list)) { - AA_ERROR("%s: internal error, policy '%s' still on list\n", - __func__, policy->name); - BUG(); - } - - /* don't free name as its a subset of hname */ - kzfree(policy->hname); -} - -/** - * __policy_find - find a policy by @name on a policy list - * @head: list to search (NOT NULL) - * @name: name to search for (NOT NULL) - * - * Requires: rcu_read_lock be held - * - * Returns: unrefcounted policy that match @name or NULL if not found - */ -static struct aa_policy *__policy_find(struct list_head *head, const char *name) -{ - struct aa_policy *policy; - - list_for_each_entry_rcu(policy, head, list) { - if (!strcmp(policy->name, name)) - return policy; - } - return NULL; -} - -/** - * __policy_strn_find - find a policy that's name matches @len chars of @str - * @head: list to search (NOT NULL) - * @str: string to search for (NOT NULL) - * @len: length of match required - * - * Requires: rcu_read_lock be held - * - * Returns: unrefcounted policy that match @str or NULL if not found - * - * if @len == strlen(@strlen) then this is equiv to __policy_find - * other wise it allows searching for policy by a partial match of name - */ -static struct aa_policy *__policy_strn_find(struct list_head *head, - const char *str, int len) -{ - struct aa_policy *policy; - - list_for_each_entry_rcu(policy, head, list) { - if (aa_strneq(policy->name, str, len)) - return policy; - } - - return NULL; -} /* * Routines for AppArmor namespaces @@ -280,7 +165,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix, AA_DEBUG("%s(%p)\n", __func__, ns); if (!ns) return NULL; - if (!policy_init(&ns->base, prefix, name)) + if (!aa_policy_init(&ns->base, prefix, name)) goto fail_ns; INIT_LIST_HEAD(&ns->sub_ns); @@ -321,7 +206,7 @@ static void free_namespace(struct aa_namespace *ns) if (!ns) return; - policy_destroy(&ns->base); + aa_policy_destroy(&ns->base); aa_put_namespace(ns->parent); ns->unconfined->ns = NULL; @@ -595,7 +480,7 @@ void aa_free_profile(struct aa_profile *profile) return; /* free children profiles */ - policy_destroy(&profile->base); + aa_policy_destroy(&profile->base); aa_put_profile(rcu_access_pointer(profile->parent)); aa_put_namespace(profile->ns); @@ -657,7 +542,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) goto fail; kref_init(&profile->replacedby->count); - if (!policy_init(&profile->base, NULL, hname)) + if (!aa_policy_init(&profile->base, NULL, hname)) goto fail; kref_init(&profile->count); -- cgit v1.1 From cff281f6861e72f1416927aaa0c10a08bb7b2d3f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:15 -0800 Subject: apparmor: split apparmor policy namespaces code into its own file Policy namespaces will be diverging from profile management and expanding so put it in its own file. Signed-off-by: John Johansen --- security/apparmor/Makefile | 2 +- security/apparmor/apparmorfs.c | 1 + security/apparmor/audit.c | 1 + security/apparmor/domain.c | 1 + security/apparmor/include/policy.h | 112 +------------ security/apparmor/include/policy_ns.h | 137 ++++++++++++++++ security/apparmor/lsm.c | 1 + security/apparmor/policy.c | 298 ++-------------------------------- security/apparmor/policy_ns.c | 291 +++++++++++++++++++++++++++++++++ security/apparmor/procattr.c | 1 + 10 files changed, 454 insertions(+), 391 deletions(-) create mode 100644 security/apparmor/include/policy_ns.h create mode 100644 security/apparmor/policy_ns.c (limited to 'security/apparmor') diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index d693df8..3485f49 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o + resource.o sid.o file.o policy_ns.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5923d56..efac1a9 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -28,6 +28,7 @@ #include "include/context.h" #include "include/crypto.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/resource.h" /** diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3a7f1da..42101c4 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -18,6 +18,7 @@ #include "include/apparmor.h" #include "include/audit.h" #include "include/policy.h" +#include "include/policy_ns.h" const char *const op_table[] = { "null", diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index a4d90aa..02d2f01 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -29,6 +29,7 @@ #include "include/match.h" #include "include/path.h" #include "include/policy.h" +#include "include/policy_ns.h" /** * aa_free_domain_entries - free entries in a domain table diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 9b19967..a1b1d8a 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -30,6 +30,9 @@ #include "lib.h" #include "resource.h" + +struct aa_namespace; + extern const char *const aa_profile_mode_names[]; #define APPARMOR_MODE_NAMES_MAX_INDEX 4 @@ -77,57 +80,6 @@ enum profile_flags { struct aa_profile; -/* struct aa_ns_acct - accounting of profiles in namespace - * @max_size: maximum space allowed for all profiles in namespace - * @max_count: maximum number of profiles that can be in this namespace - * @size: current size of profiles - * @count: current count of profiles (includes null profiles) - */ -struct aa_ns_acct { - int max_size; - int max_count; - int size; - int count; -}; - -/* struct aa_namespace - namespace for a set of profiles - * @base: common policy - * @parent: parent of namespace - * @lock: lock for modifying the object - * @acct: accounting for the namespace - * @unconfined: special unconfined profile for the namespace - * @sub_ns: list of namespaces under the current namespace. - * @uniq_null: uniq value used for null learning profiles - * @uniq_id: a unique id count for the profiles in the namespace - * @dents: dentries for the namespaces file entries in apparmorfs - * - * An aa_namespace defines the set profiles that are searched to determine - * which profile to attach to a task. Profiles can not be shared between - * aa_namespaces and profile names within a namespace are guaranteed to be - * unique. When profiles in separate namespaces have the same name they - * are NOT considered to be equivalent. - * - * Namespaces are hierarchical and only namespaces and profiles below the - * current namespace are visible. - * - * Namespace names must be unique and can not contain the characters :/\0 - * - * FIXME TODO: add vserver support of namespaces (can it all be done in - * userspace?) - */ -struct aa_namespace { - struct aa_policy base; - struct aa_namespace *parent; - struct mutex lock; - struct aa_ns_acct acct; - struct aa_profile *unconfined; - struct list_head sub_ns; - atomic_t uniq_null; - long uniq_id; - - struct dentry *dents[AAFS_NS_SIZEOF]; -}; - /* struct aa_policydb - match engine for a policy * dfa: dfa pattern match * start: set of start states for the different classes of data @@ -212,19 +164,11 @@ struct aa_profile { struct dentry *dents[AAFS_PROF_SIZEOF]; }; -extern struct aa_namespace *root_ns; extern enum profile_mode aa_g_profile_mode; -void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); - -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); -const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); -int aa_alloc_root_ns(void); -void aa_free_root_ns(void); -void aa_free_namespace_kref(struct kref *kref); +void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new); -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name); +void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_replacedby_kref(struct kref *kref); @@ -238,6 +182,7 @@ struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); ssize_t aa_remove_profiles(char *name, size_t size); +void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 #define PROF_REPLACE 0 @@ -245,12 +190,6 @@ ssize_t aa_remove_profiles(char *name, size_t size); #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) -static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) -{ - return rcu_dereference_protected(p->parent, - mutex_is_locked(&p->ns->lock)); -} - /** * aa_get_profile - increment refcount on profile @p * @p: profile (MAYBE NULL) @@ -344,45 +283,6 @@ static inline void aa_put_replacedby(struct aa_replacedby *p) kref_put(&p->count, aa_free_replacedby_kref); } -/* requires profile list write lock held */ -static inline void __aa_update_replacedby(struct aa_profile *orig, - struct aa_profile *new) -{ - struct aa_profile *tmp; - tmp = rcu_dereference_protected(orig->replacedby->profile, - mutex_is_locked(&orig->ns->lock)); - rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); - orig->flags |= PFLAG_INVALID; - aa_put_profile(tmp); -} - -/** - * aa_get_namespace - increment references count on @ns - * @ns: namespace to increment reference count of (MAYBE NULL) - * - * Returns: pointer to @ns, if @ns is NULL returns NULL - * Requires: @ns must be held with valid refcount when called - */ -static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) -{ - if (ns) - aa_get_profile(ns->unconfined); - - return ns; -} - -/** - * aa_put_namespace - decrement refcount on @ns - * @ns: namespace to put reference of - * - * Decrement reference count of @ns and if no longer in use free it - */ -static inline void aa_put_namespace(struct aa_namespace *ns) -{ - if (ns) - aa_put_profile(ns->unconfined); -} - static inline int AUDIT_MODE(struct aa_profile *profile) { if (aa_g_audit != AUDIT_NORMAL) diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h new file mode 100644 index 0000000..4b9e8c7 --- /dev/null +++ b/security/apparmor/include/policy_ns.h @@ -0,0 +1,137 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_NAMESPACE_H +#define __AA_NAMESPACE_H + +#include + +#include "apparmor.h" +#include "apparmorfs.h" +#include "policy.h" + + +/* struct aa_ns_acct - accounting of profiles in namespace + * @max_size: maximum space allowed for all profiles in namespace + * @max_count: maximum number of profiles that can be in this namespace + * @size: current size of profiles + * @count: current count of profiles (includes null profiles) + */ +struct aa_ns_acct { + int max_size; + int max_count; + int size; + int count; +}; + +/* struct aa_namespace - namespace for a set of profiles + * @base: common policy + * @parent: parent of namespace + * @lock: lock for modifying the object + * @acct: accounting for the namespace + * @unconfined: special unconfined profile for the namespace + * @sub_ns: list of namespaces under the current namespace. + * @uniq_null: uniq value used for null learning profiles + * @uniq_id: a unique id count for the profiles in the namespace + * @dents: dentries for the namespaces file entries in apparmorfs + * + * An aa_namespace defines the set profiles that are searched to determine + * which profile to attach to a task. Profiles can not be shared between + * aa_namespaces and profile names within a namespace are guaranteed to be + * unique. When profiles in separate namespaces have the same name they + * are NOT considered to be equivalent. + * + * Namespaces are hierarchical and only namespaces and profiles below the + * current namespace are visible. + * + * Namespace names must be unique and can not contain the characters :/\0 + */ +struct aa_namespace { + struct aa_policy base; + struct aa_namespace *parent; + struct mutex lock; + struct aa_ns_acct acct; + struct aa_profile *unconfined; + struct list_head sub_ns; + atomic_t uniq_null; + long uniq_id; + + struct dentry *dents[AAFS_NS_SIZEOF]; +}; + +extern struct aa_namespace *root_ns; + +extern const char *aa_hidden_ns_name; + +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); +const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); +void aa_free_namespace(struct aa_namespace *ns); +int aa_alloc_root_ns(void); +void aa_free_root_ns(void); +void aa_free_namespace_kref(struct kref *kref); + +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, + const char *name); +struct aa_namespace *aa_prepare_namespace(const char *name); +void __aa_remove_namespace(struct aa_namespace *ns); + +static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) +{ + return rcu_dereference_protected(p->parent, + mutex_is_locked(&p->ns->lock)); +} + +/** + * aa_get_namespace - increment references count on @ns + * @ns: namespace to increment reference count of (MAYBE NULL) + * + * Returns: pointer to @ns, if @ns is NULL returns NULL + * Requires: @ns must be held with valid refcount when called + */ +static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) +{ + if (ns) + aa_get_profile(ns->unconfined); + + return ns; +} + +/** + * aa_put_namespace - decrement refcount on @ns + * @ns: namespace to put reference of + * + * Decrement reference count of @ns and if no longer in use free it + */ +static inline void aa_put_namespace(struct aa_namespace *ns) +{ + if (ns) + aa_put_profile(ns->unconfined); +} + +/** + * __aa_find_namespace - find a namespace on a list by @name + * @head: list to search for namespace on (NOT NULL) + * @name: name of namespace to look for (NOT NULL) + * + * Returns: unrefcounted namespace + * + * Requires: rcu_read_lock be held + */ +static inline struct aa_namespace *__aa_find_namespace(struct list_head *head, + const char *name) +{ + return (struct aa_namespace *)__policy_find(head, name); +} + +#endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f76738b..1dae66b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -34,6 +34,7 @@ #include "include/ipc.h" #include "include/path.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/procattr.h" /* Flag indicating whether initialization completed */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a331149..2a86182 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -85,13 +85,11 @@ #include "include/match.h" #include "include/path.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/policy_unpack.h" #include "include/resource.h" -/* root profile namespace */ -struct aa_namespace *root_ns; - const char *const aa_profile_mode_names[] = { "enforce", "complain", @@ -100,202 +98,16 @@ const char *const aa_profile_mode_names[] = { }; -/* - * Routines for AppArmor namespaces - */ - -static const char *hidden_ns_name = "---"; -/** - * aa_ns_visible - test if @view is visible from @curr - * @curr: namespace to treat as the parent (NOT NULL) - * @view: namespace to test if visible from @curr (NOT NULL) - * - * Returns: true if @view is visible from @curr else false - */ -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) -{ - if (curr == view) - return true; - - for ( ; view; view = view->parent) { - if (view->parent == curr) - return true; - } - return false; -} - -/** - * aa_na_name - Find the ns name to display for @view from @curr - * @curr - current namespace (NOT NULL) - * @view - namespace attempting to view (NOT NULL) - * - * Returns: name of @view visible from @curr - */ -const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) -{ - /* if view == curr then the namespace name isn't displayed */ - if (curr == view) - return ""; - - if (aa_ns_visible(curr, view)) { - /* at this point if a ns is visible it is in a view ns - * thus the curr ns.hname is a prefix of its name. - * Only output the virtualized portion of the name - * Add + 2 to skip over // separating curr hname prefix - * from the visible tail of the views hname - */ - return view->base.hname + strlen(curr->base.hname) + 2; - } else - return hidden_ns_name; -} - -/** - * alloc_namespace - allocate, initialize and return a new namespace - * @prefix: parent namespace name (MAYBE NULL) - * @name: a preallocated name (NOT NULL) - * - * Returns: refcounted namespace or NULL on failure. - */ -static struct aa_namespace *alloc_namespace(const char *prefix, - const char *name) -{ - struct aa_namespace *ns; - - ns = kzalloc(sizeof(*ns), GFP_KERNEL); - AA_DEBUG("%s(%p)\n", __func__, ns); - if (!ns) - return NULL; - if (!aa_policy_init(&ns->base, prefix, name)) - goto fail_ns; - - INIT_LIST_HEAD(&ns->sub_ns); - mutex_init(&ns->lock); - - /* released by free_namespace */ - ns->unconfined = aa_alloc_profile("unconfined"); - if (!ns->unconfined) - goto fail_unconfined; - - ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | - PFLAG_IMMUTABLE | PFLAG_NS_COUNT; - ns->unconfined->mode = APPARMOR_UNCONFINED; - - /* ns and ns->unconfined share ns->unconfined refcount */ - ns->unconfined->ns = ns; - - atomic_set(&ns->uniq_null, 0); - - return ns; - -fail_unconfined: - kzfree(ns->base.hname); -fail_ns: - kzfree(ns); - return NULL; -} - -/** - * free_namespace - free a profile namespace - * @ns: the namespace to free (MAYBE NULL) - * - * Requires: All references to the namespace must have been put, if the - * namespace was referenced by a profile confining a task, - */ -static void free_namespace(struct aa_namespace *ns) -{ - if (!ns) - return; - - aa_policy_destroy(&ns->base); - aa_put_namespace(ns->parent); - - ns->unconfined->ns = NULL; - aa_free_profile(ns->unconfined); - kzfree(ns); -} - -/** - * __aa_find_namespace - find a namespace on a list by @name - * @head: list to search for namespace on (NOT NULL) - * @name: name of namespace to look for (NOT NULL) - * - * Returns: unrefcounted namespace - * - * Requires: rcu_read_lock be held - */ -static struct aa_namespace *__aa_find_namespace(struct list_head *head, - const char *name) -{ - return (struct aa_namespace *)__policy_find(head, name); -} - -/** - * aa_find_namespace - look up a profile namespace on the namespace list - * @root: namespace to search in (NOT NULL) - * @name: name of namespace to find (NOT NULL) - * - * Returns: a refcounted namespace on the list, or NULL if no namespace - * called @name exists. - * - * refcount released by caller - */ -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name) -{ - struct aa_namespace *ns = NULL; - - rcu_read_lock(); - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); - rcu_read_unlock(); - - return ns; -} - -/** - * aa_prepare_namespace - find an existing or create a new namespace of @name - * @name: the namespace to find or add (MAYBE NULL) - * - * Returns: refcounted namespace or NULL if failed to create one - */ -static struct aa_namespace *aa_prepare_namespace(const char *name) +/* requires profile list write lock held */ +void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new) { - struct aa_namespace *ns, *root; - - root = aa_current_profile()->ns; - - mutex_lock(&root->lock); - - /* if name isn't specified the profile is loaded to the current ns */ - if (!name) { - /* released by caller */ - ns = aa_get_namespace(root); - goto out; - } + struct aa_profile *tmp; - /* try and find the specified ns and if it doesn't exist create it */ - /* released by caller */ - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); - if (!ns) { - ns = alloc_namespace(root->base.hname, name); - if (!ns) - goto out; - if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { - AA_ERROR("Failed to create interface for ns %s\n", - ns->base.name); - free_namespace(ns); - ns = NULL; - goto out; - } - ns->parent = aa_get_namespace(root); - list_add_rcu(&ns->base.list, &root->sub_ns); - /* add list ref */ - aa_get_namespace(ns); - } -out: - mutex_unlock(&root->lock); - - /* return ref */ - return ns; + tmp = rcu_dereference_protected(orig->replacedby->profile, + mutex_is_locked(&orig->ns->lock)); + rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); + orig->flags |= PFLAG_INVALID; + aa_put_profile(tmp); } /** @@ -333,8 +145,6 @@ static void __list_remove_profile(struct aa_profile *profile) aa_put_profile(profile); } -static void __profile_list_release(struct list_head *head); - /** * __remove_profile - remove old profile, and children * @profile: profile to be replaced (NOT NULL) @@ -344,7 +154,7 @@ static void __profile_list_release(struct list_head *head); static void __remove_profile(struct aa_profile *profile) { /* release any children lists first */ - __profile_list_release(&profile->base.profiles); + __aa_profile_list_release(&profile->base.profiles); /* released by free_profile */ __aa_update_replacedby(profile, profile->ns->unconfined); __aa_fs_profile_rmdir(profile); @@ -352,98 +162,18 @@ static void __remove_profile(struct aa_profile *profile) } /** - * __profile_list_release - remove all profiles on the list and put refs + * __aa_profile_list_release - remove all profiles on the list and put refs * @head: list of profiles (NOT NULL) * * Requires: namespace lock be held */ -static void __profile_list_release(struct list_head *head) +void __aa_profile_list_release(struct list_head *head) { struct aa_profile *profile, *tmp; list_for_each_entry_safe(profile, tmp, head, base.list) __remove_profile(profile); } -static void __ns_list_release(struct list_head *head); - -/** - * destroy_namespace - remove everything contained by @ns - * @ns: namespace to have it contents removed (NOT NULL) - */ -static void destroy_namespace(struct aa_namespace *ns) -{ - if (!ns) - return; - - mutex_lock(&ns->lock); - /* release all profiles in this namespace */ - __profile_list_release(&ns->base.profiles); - - /* release all sub namespaces */ - __ns_list_release(&ns->sub_ns); - - if (ns->parent) - __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); - __aa_fs_namespace_rmdir(ns); - mutex_unlock(&ns->lock); -} - -/** - * __remove_namespace - remove a namespace and all its children - * @ns: namespace to be removed (NOT NULL) - * - * Requires: ns->parent->lock be held and ns removed from parent. - */ -static void __remove_namespace(struct aa_namespace *ns) -{ - /* remove ns from namespace list */ - list_del_rcu(&ns->base.list); - destroy_namespace(ns); - aa_put_namespace(ns); -} - -/** - * __ns_list_release - remove all profile namespaces on the list put refs - * @head: list of profile namespaces (NOT NULL) - * - * Requires: namespace lock be held - */ -static void __ns_list_release(struct list_head *head) -{ - struct aa_namespace *ns, *tmp; - list_for_each_entry_safe(ns, tmp, head, base.list) - __remove_namespace(ns); - -} - -/** - * aa_alloc_root_ns - allocate the root profile namespace - * - * Returns: %0 on success else error - * - */ -int __init aa_alloc_root_ns(void) -{ - /* released by aa_free_root_ns - used as list ref*/ - root_ns = alloc_namespace(NULL, "root"); - if (!root_ns) - return -ENOMEM; - - return 0; -} - - /** - * aa_free_root_ns - free the root profile namespace - */ -void __init aa_free_root_ns(void) - { - struct aa_namespace *ns = root_ns; - root_ns = NULL; - - destroy_namespace(ns); - aa_put_namespace(ns); -} - static void free_replacedby(struct aa_replacedby *r) { @@ -507,7 +237,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) { struct aa_profile *p = container_of(head, struct aa_profile, rcu); if (p->flags & PFLAG_NS_COUNT) - free_namespace(p->ns); + aa_free_namespace(p->ns); else aa_free_profile(p); } @@ -1181,7 +911,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) if (!name) { /* remove namespace - can only happen if fqname[0] == ':' */ mutex_lock(&ns->parent->lock); - __remove_namespace(ns); + __aa_remove_namespace(ns); mutex_unlock(&ns->parent->lock); } else { /* remove profile */ diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c new file mode 100644 index 0000000..d4e9924 --- /dev/null +++ b/security/apparmor/policy_ns.c @@ -0,0 +1,291 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy manipulation functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * AppArmor policy namespaces, allow for different sets of policies + * to be loaded for tasks within the namespace. + */ + +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/context.h" +#include "include/policy_ns.h" +#include "include/policy.h" + +/* root profile namespace */ +struct aa_namespace *root_ns; +const char *aa_hidden_ns_name = "---"; + +/** + * aa_ns_visible - test if @view is visible from @curr + * @curr: namespace to treat as the parent (NOT NULL) + * @view: namespace to test if visible from @curr (NOT NULL) + * + * Returns: true if @view is visible from @curr else false + */ +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) +{ + if (curr == view) + return true; + + for ( ; view; view = view->parent) { + if (view->parent == curr) + return true; + } + return false; +} + +/** + * aa_na_name - Find the ns name to display for @view from @curr + * @curr - current namespace (NOT NULL) + * @view - namespace attempting to view (NOT NULL) + * + * Returns: name of @view visible from @curr + */ +const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) +{ + /* if view == curr then the namespace name isn't displayed */ + if (curr == view) + return ""; + + if (aa_ns_visible(curr, view)) { + /* at this point if a ns is visible it is in a view ns + * thus the curr ns.hname is a prefix of its name. + * Only output the virtualized portion of the name + * Add + 2 to skip over // separating curr hname prefix + * from the visible tail of the views hname + */ + return view->base.hname + strlen(curr->base.hname) + 2; + } + + return aa_hidden_ns_name; +} + +/** + * alloc_namespace - allocate, initialize and return a new namespace + * @prefix: parent namespace name (MAYBE NULL) + * @name: a preallocated name (NOT NULL) + * + * Returns: refcounted namespace or NULL on failure. + */ +static struct aa_namespace *alloc_namespace(const char *prefix, + const char *name) +{ + struct aa_namespace *ns; + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); + AA_DEBUG("%s(%p)\n", __func__, ns); + if (!ns) + return NULL; + if (!aa_policy_init(&ns->base, prefix, name)) + goto fail_ns; + + INIT_LIST_HEAD(&ns->sub_ns); + mutex_init(&ns->lock); + + /* released by free_namespace */ + ns->unconfined = aa_alloc_profile("unconfined"); + if (!ns->unconfined) + goto fail_unconfined; + + ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR | + PFLAG_IMMUTABLE | PFLAG_NS_COUNT; + ns->unconfined->mode = APPARMOR_UNCONFINED; + + /* ns and ns->unconfined share ns->unconfined refcount */ + ns->unconfined->ns = ns; + + atomic_set(&ns->uniq_null, 0); + + return ns; + +fail_unconfined: + kzfree(ns->base.hname); +fail_ns: + kzfree(ns); + return NULL; +} + +/** + * aa_free_namespace - free a profile namespace + * @ns: the namespace to free (MAYBE NULL) + * + * Requires: All references to the namespace must have been put, if the + * namespace was referenced by a profile confining a task, + */ +void aa_free_namespace(struct aa_namespace *ns) +{ + if (!ns) + return; + + aa_policy_destroy(&ns->base); + aa_put_namespace(ns->parent); + + ns->unconfined->ns = NULL; + aa_free_profile(ns->unconfined); + kzfree(ns); +} + +/** + * aa_find_namespace - look up a profile namespace on the namespace list + * @root: namespace to search in (NOT NULL) + * @name: name of namespace to find (NOT NULL) + * + * Returns: a refcounted namespace on the list, or NULL if no namespace + * called @name exists. + * + * refcount released by caller + */ +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, + const char *name) +{ + struct aa_namespace *ns = NULL; + + rcu_read_lock(); + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + rcu_read_unlock(); + + return ns; +} + +/** + * aa_prepare_namespace - find an existing or create a new namespace of @name + * @name: the namespace to find or add (MAYBE NULL) + * + * Returns: refcounted namespace or NULL if failed to create one + */ +struct aa_namespace *aa_prepare_namespace(const char *name) +{ + struct aa_namespace *ns, *root; + + root = aa_current_profile()->ns; + + mutex_lock(&root->lock); + + /* if name isn't specified the profile is loaded to the current ns */ + if (!name) { + /* released by caller */ + ns = aa_get_namespace(root); + goto out; + } + + /* try and find the specified ns and if it doesn't exist create it */ + /* released by caller */ + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + if (!ns) { + ns = alloc_namespace(root->base.hname, name); + if (!ns) + goto out; + if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { + AA_ERROR("Failed to create interface for ns %s\n", + ns->base.name); + aa_free_namespace(ns); + ns = NULL; + goto out; + } + ns->parent = aa_get_namespace(root); + list_add_rcu(&ns->base.list, &root->sub_ns); + /* add list ref */ + aa_get_namespace(ns); + } +out: + mutex_unlock(&root->lock); + + /* return ref */ + return ns; +} + +static void __ns_list_release(struct list_head *head); + +/** + * destroy_namespace - remove everything contained by @ns + * @ns: namespace to have it contents removed (NOT NULL) + */ +static void destroy_namespace(struct aa_namespace *ns) +{ + if (!ns) + return; + + mutex_lock(&ns->lock); + /* release all profiles in this namespace */ + __aa_profile_list_release(&ns->base.profiles); + + /* release all sub namespaces */ + __ns_list_release(&ns->sub_ns); + + if (ns->parent) + __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); + __aa_fs_namespace_rmdir(ns); + mutex_unlock(&ns->lock); +} + +/** + * __aa_remove_namespace - remove a namespace and all its children + * @ns: namespace to be removed (NOT NULL) + * + * Requires: ns->parent->lock be held and ns removed from parent. + */ +void __aa_remove_namespace(struct aa_namespace *ns) +{ + /* remove ns from namespace list */ + list_del_rcu(&ns->base.list); + destroy_namespace(ns); + aa_put_namespace(ns); +} + +/** + * __ns_list_release - remove all profile namespaces on the list put refs + * @head: list of profile namespaces (NOT NULL) + * + * Requires: namespace lock be held + */ +static void __ns_list_release(struct list_head *head) +{ + struct aa_namespace *ns, *tmp; + + list_for_each_entry_safe(ns, tmp, head, base.list) + __aa_remove_namespace(ns); + +} + +/** + * aa_alloc_root_ns - allocate the root profile namespace + * + * Returns: %0 on success else error + * + */ +int __init aa_alloc_root_ns(void) +{ + /* released by aa_free_root_ns - used as list ref*/ + root_ns = alloc_namespace(NULL, "root"); + if (!root_ns) + return -ENOMEM; + + return 0; +} + + /** + * aa_free_root_ns - free the root profile namespace + */ +void __init aa_free_root_ns(void) +{ + struct aa_namespace *ns = root_ns; + + root_ns = NULL; + + destroy_namespace(ns); + aa_put_namespace(ns); +} diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index b125acc..bb2600d 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -15,6 +15,7 @@ #include "include/apparmor.h" #include "include/context.h" #include "include/policy.h" +#include "include/policy_ns.h" #include "include/domain.h" #include "include/procattr.h" -- cgit v1.1 From 98849dff90e270af3b34889b9e08252544f40b5b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:16 -0800 Subject: apparmor: rename namespace to ns to improve code line lengths Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 49 ++++++++++---------- security/apparmor/domain.c | 24 +++++----- security/apparmor/include/apparmorfs.h | 8 ++-- security/apparmor/include/policy.h | 8 ++-- security/apparmor/include/policy_ns.h | 43 +++++++++--------- security/apparmor/policy.c | 32 ++++++------- security/apparmor/policy_ns.c | 82 +++++++++++++++++----------------- security/apparmor/procattr.c | 4 +- 8 files changed, 122 insertions(+), 128 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index efac1a9..4409b63 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -478,9 +478,9 @@ fail2: return error; } -void __aa_fs_namespace_rmdir(struct aa_namespace *ns) +void __aa_fs_ns_rmdir(struct aa_ns *ns) { - struct aa_namespace *sub; + struct aa_ns *sub; struct aa_profile *child; int i; @@ -492,7 +492,7 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns) list_for_each_entry(sub, &ns->sub_ns, base.list) { mutex_lock(&sub->lock); - __aa_fs_namespace_rmdir(sub); + __aa_fs_ns_rmdir(sub); mutex_unlock(&sub->lock); } @@ -502,10 +502,9 @@ void __aa_fs_namespace_rmdir(struct aa_namespace *ns) } } -int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, - const char *name) +int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) { - struct aa_namespace *sub; + struct aa_ns *sub; struct aa_profile *child; struct dentry *dent, *dir; int error; @@ -536,7 +535,7 @@ int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, list_for_each_entry(sub, &ns->sub_ns, base.list) { mutex_lock(&sub->lock); - error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); + error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL); mutex_unlock(&sub->lock); if (error) goto fail2; @@ -548,7 +547,7 @@ fail: error = PTR_ERR(dent); fail2: - __aa_fs_namespace_rmdir(ns); + __aa_fs_ns_rmdir(ns); return error; } @@ -557,7 +556,7 @@ fail2: #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** - * __next_namespace - find the next namespace to list + * __next_ns - find the next namespace to list * @root: root namespace to stop search at (NOT NULL) * @ns: current ns position (NOT NULL) * @@ -568,10 +567,9 @@ fail2: * Requires: ns->parent->lock to be held * NOTE: will not unlock root->lock */ -static struct aa_namespace *__next_namespace(struct aa_namespace *root, - struct aa_namespace *ns) +static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) { - struct aa_namespace *parent, *next; + struct aa_ns *parent, *next; /* is next namespace a child */ if (!list_empty(&ns->sub_ns)) { @@ -604,10 +602,10 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, * Returns: unrefcounted profile or NULL if no profile * Requires: profile->ns.lock to be held */ -static struct aa_profile *__first_profile(struct aa_namespace *root, - struct aa_namespace *ns) +static struct aa_profile *__first_profile(struct aa_ns *root, + struct aa_ns *ns) { - for (; ns; ns = __next_namespace(root, ns)) { + for (; ns; ns = __next_ns(root, ns)) { if (!list_empty(&ns->base.profiles)) return list_first_entry(&ns->base.profiles, struct aa_profile, base.list); @@ -627,7 +625,7 @@ static struct aa_profile *__first_profile(struct aa_namespace *root, static struct aa_profile *__next_profile(struct aa_profile *p) { struct aa_profile *parent; - struct aa_namespace *ns = p->ns; + struct aa_ns *ns = p->ns; /* is next profile a child */ if (!list_empty(&p->base.profiles)) @@ -661,7 +659,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) * * Returns: next profile or NULL if there isn't one */ -static struct aa_profile *next_profile(struct aa_namespace *root, +static struct aa_profile *next_profile(struct aa_ns *root, struct aa_profile *profile) { struct aa_profile *next = __next_profile(profile); @@ -669,7 +667,7 @@ static struct aa_profile *next_profile(struct aa_namespace *root, return next; /* finished all profiles in namespace move to next namespace */ - return __first_profile(root, __next_namespace(root, profile->ns)); + return __first_profile(root, __next_ns(root, profile->ns)); } /** @@ -684,9 +682,9 @@ static struct aa_profile *next_profile(struct aa_namespace *root, static void *p_start(struct seq_file *f, loff_t *pos) { struct aa_profile *profile = NULL; - struct aa_namespace *root = aa_current_profile()->ns; + struct aa_ns *root = aa_current_profile()->ns; loff_t l = *pos; - f->private = aa_get_namespace(root); + f->private = aa_get_ns(root); /* find the first profile */ @@ -713,7 +711,7 @@ static void *p_start(struct seq_file *f, loff_t *pos) static void *p_next(struct seq_file *f, void *p, loff_t *pos) { struct aa_profile *profile = p; - struct aa_namespace *ns = f->private; + struct aa_ns *ns = f->private; (*pos)++; return next_profile(ns, profile); @@ -729,14 +727,14 @@ static void *p_next(struct seq_file *f, void *p, loff_t *pos) static void p_stop(struct seq_file *f, void *p) { struct aa_profile *profile = p; - struct aa_namespace *root = f->private, *ns; + struct aa_ns *root = f->private, *ns; if (profile) { for (ns = profile->ns; ns && ns != root; ns = ns->parent) mutex_unlock(&ns->lock); } mutex_unlock(&root->lock); - aa_put_namespace(root); + aa_put_ns(root); } /** @@ -749,7 +747,7 @@ static void p_stop(struct seq_file *f, void *p) static int seq_show_profile(struct seq_file *f, void *p) { struct aa_profile *profile = (struct aa_profile *)p; - struct aa_namespace *root = f->private; + struct aa_ns *root = f->private; if (profile->ns != root) seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); @@ -951,8 +949,7 @@ static int __init aa_create_aafs(void) if (error) goto error; - error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, - "policy"); + error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy"); if (error) goto error; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 02d2f01..503cb2c 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -94,7 +94,7 @@ out: * Returns: permission set */ static struct file_perms change_profile_perms(struct aa_profile *profile, - struct aa_namespace *ns, + struct aa_ns *ns, const char *name, u32 request, unsigned int start) { @@ -171,7 +171,7 @@ static struct aa_profile *__attach_match(const char *name, * * Returns: profile or NULL if no match found */ -static struct aa_profile *find_attach(struct aa_namespace *ns, +static struct aa_profile *find_attach(struct aa_ns *ns, struct list_head *list, const char *name) { struct aa_profile *profile; @@ -240,7 +240,7 @@ static const char *next_name(int xtype, const char *name) static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; - struct aa_namespace *ns = profile->ns; + struct aa_ns *ns = profile->ns; u32 xtype = xindex & AA_X_TYPE_MASK; int index = xindex & AA_X_INDEX_MASK; const char *name; @@ -248,7 +248,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) /* index is guaranteed to be in range, validated at load time */ for (name = profile->file.trans.table[index]; !new_profile && name; name = next_name(xtype, name)) { - struct aa_namespace *new_ns; + struct aa_ns *new_ns; const char *xname = NULL; new_ns = NULL; @@ -268,7 +268,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) ; } /* released below */ - new_ns = aa_find_namespace(ns, ns_name); + new_ns = aa_find_ns(ns, ns_name); if (!new_ns) continue; } else if (*name == '@') { @@ -281,7 +281,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) /* released by caller */ new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); - aa_put_namespace(new_ns); + aa_put_ns(new_ns); } /* released by caller */ @@ -302,7 +302,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile, const char *name, u32 xindex) { struct aa_profile *new_profile = NULL; - struct aa_namespace *ns = profile->ns; + struct aa_ns *ns = profile->ns; u32 xtype = xindex & AA_X_TYPE_MASK; switch (xtype) { @@ -339,7 +339,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { struct aa_task_cxt *cxt; struct aa_profile *profile, *new_profile = NULL; - struct aa_namespace *ns; + struct aa_ns *ns; char *buffer = NULL; unsigned int state; struct file_perms perms = {}; @@ -746,7 +746,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, { const struct cred *cred; struct aa_profile *profile, *target = NULL; - struct aa_namespace *ns = NULL; + struct aa_ns *ns = NULL; struct file_perms perms = {}; const char *name = NULL, *info = NULL; int op, error = 0; @@ -780,7 +780,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, if (ns_name) { /* released below */ - ns = aa_find_namespace(profile->ns, ns_name); + ns = aa_find_ns(profile->ns, ns_name); if (!ns) { /* we don't create new namespace in complain mode */ name = ns_name; @@ -790,7 +790,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } } else /* released below */ - ns = aa_get_namespace(profile->ns); + ns = aa_get_ns(profile->ns); /* if the name was not specified, use the name of the current profile */ if (!hname) { @@ -843,7 +843,7 @@ audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, hname, GLOBAL_ROOT_UID, info, error); - aa_put_namespace(ns); + aa_put_ns(ns); aa_put_profile(target); put_cred(cred); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 414e568..5626bd4 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -62,7 +62,7 @@ extern const struct file_operations aa_fs_seq_file_ops; extern void __init aa_destroy_aafs(void); struct aa_profile; -struct aa_namespace; +struct aa_ns; enum aafs_ns_type { AAFS_NS_DIR, @@ -97,8 +97,8 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile); void __aa_fs_profile_migrate_dents(struct aa_profile *old, struct aa_profile *new); int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); -void __aa_fs_namespace_rmdir(struct aa_namespace *ns); -int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, - const char *name); +void __aa_fs_ns_rmdir(struct aa_ns *ns); +int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, + const char *name); #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index a1b1d8a..415f8ab 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -31,7 +31,7 @@ #include "resource.h" -struct aa_namespace; +struct aa_ns; extern const char *const aa_profile_mode_names[]; #define APPARMOR_MODE_NAMES_MAX_INDEX 4 @@ -141,7 +141,7 @@ struct aa_profile { struct rcu_head rcu; struct aa_profile __rcu *parent; - struct aa_namespace *ns; + struct aa_ns *ns; struct aa_replacedby *replacedby; const char *rename; @@ -177,8 +177,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); -struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); -struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); +struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); ssize_t aa_remove_profiles(char *name, size_t size); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 4b9e8c7..323752c 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -35,7 +35,7 @@ struct aa_ns_acct { int count; }; -/* struct aa_namespace - namespace for a set of profiles +/* struct aa_ns - namespace for a set of profiles * @base: common policy * @parent: parent of namespace * @lock: lock for modifying the object @@ -46,9 +46,9 @@ struct aa_ns_acct { * @uniq_id: a unique id count for the profiles in the namespace * @dents: dentries for the namespaces file entries in apparmorfs * - * An aa_namespace defines the set profiles that are searched to determine + * An aa_ns defines the set profiles that are searched to determine * which profile to attach to a task. Profiles can not be shared between - * aa_namespaces and profile names within a namespace are guaranteed to be + * aa_nss and profile names within a namespace are guaranteed to be * unique. When profiles in separate namespaces have the same name they * are NOT considered to be equivalent. * @@ -57,9 +57,9 @@ struct aa_ns_acct { * * Namespace names must be unique and can not contain the characters :/\0 */ -struct aa_namespace { +struct aa_ns { struct aa_policy base; - struct aa_namespace *parent; + struct aa_ns *parent; struct mutex lock; struct aa_ns_acct acct; struct aa_profile *unconfined; @@ -70,21 +70,20 @@ struct aa_namespace { struct dentry *dents[AAFS_NS_SIZEOF]; }; -extern struct aa_namespace *root_ns; +extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); -const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); -void aa_free_namespace(struct aa_namespace *ns); +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view); +const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child); +void aa_free_ns(struct aa_ns *ns); int aa_alloc_root_ns(void); void aa_free_root_ns(void); -void aa_free_namespace_kref(struct kref *kref); +void aa_free_ns_kref(struct kref *kref); -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name); -struct aa_namespace *aa_prepare_namespace(const char *name); -void __aa_remove_namespace(struct aa_namespace *ns); +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); +struct aa_ns *aa_prepare_ns(const char *name); +void __aa_remove_ns(struct aa_ns *ns); static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) { @@ -93,13 +92,13 @@ static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) } /** - * aa_get_namespace - increment references count on @ns + * aa_get_ns - increment references count on @ns * @ns: namespace to increment reference count of (MAYBE NULL) * * Returns: pointer to @ns, if @ns is NULL returns NULL * Requires: @ns must be held with valid refcount when called */ -static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) +static inline struct aa_ns *aa_get_ns(struct aa_ns *ns) { if (ns) aa_get_profile(ns->unconfined); @@ -108,19 +107,19 @@ static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) } /** - * aa_put_namespace - decrement refcount on @ns + * aa_put_ns - decrement refcount on @ns * @ns: namespace to put reference of * * Decrement reference count of @ns and if no longer in use free it */ -static inline void aa_put_namespace(struct aa_namespace *ns) +static inline void aa_put_ns(struct aa_ns *ns) { if (ns) aa_put_profile(ns->unconfined); } /** - * __aa_find_namespace - find a namespace on a list by @name + * __aa_find_ns - find a namespace on a list by @name * @head: list to search for namespace on (NOT NULL) * @name: name of namespace to look for (NOT NULL) * @@ -128,10 +127,10 @@ static inline void aa_put_namespace(struct aa_namespace *ns) * * Requires: rcu_read_lock be held */ -static inline struct aa_namespace *__aa_find_namespace(struct list_head *head, - const char *name) +static inline struct aa_ns *__aa_find_ns(struct list_head *head, + const char *name) { - return (struct aa_namespace *)__policy_find(head, name); + return (struct aa_ns *)__policy_find(head, name); } #endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 2a86182..2dd8717 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -213,7 +213,7 @@ void aa_free_profile(struct aa_profile *profile) aa_policy_destroy(&profile->base); aa_put_profile(rcu_access_pointer(profile->parent)); - aa_put_namespace(profile->ns); + aa_put_ns(profile->ns); kzfree(profile->rename); aa_free_file_rules(&profile->file); @@ -237,7 +237,7 @@ static void aa_free_profile_rcu(struct rcu_head *head) { struct aa_profile *p = container_of(head, struct aa_profile, rcu); if (p->flags & PFLAG_NS_COUNT) - aa_free_namespace(p->ns); + aa_free_ns(p->ns); else aa_free_profile(p); } @@ -324,7 +324,7 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); - profile->ns = aa_get_namespace(parent->ns); + profile->ns = aa_get_ns(parent->ns); mutex_lock(&profile->ns->lock); __list_add_profile(&parent->base.profiles, profile); @@ -403,7 +403,7 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) * * Returns: unrefcounted policy or NULL if not found */ -static struct aa_policy *__lookup_parent(struct aa_namespace *ns, +static struct aa_policy *__lookup_parent(struct aa_ns *ns, const char *hname) { struct aa_policy *policy; @@ -466,7 +466,7 @@ static struct aa_profile *__lookup_profile(struct aa_policy *base, * * Returns: refcounted profile or NULL if not found */ -struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) { struct aa_profile *profile; @@ -670,7 +670,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, * * Returns: profile to replace (no ref) on success else ptr error */ -static int __lookup_replace(struct aa_namespace *ns, const char *hname, +static int __lookup_replace(struct aa_ns *ns, const char *hname, bool noreplace, struct aa_profile **p, const char **info) { @@ -701,7 +701,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { const char *ns_name, *info = NULL; - struct aa_namespace *ns = NULL; + struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; ssize_t error; @@ -713,7 +713,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) goto out; /* released below */ - ns = aa_prepare_namespace(ns_name); + ns = aa_prepare_ns(ns_name); if (!ns) { error = audit_policy(op, GFP_KERNEL, ns_name, "failed to prepare namespace", -ENOMEM); @@ -738,7 +738,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) } /* released when @new is freed */ - ent->new->ns = aa_get_namespace(ns); + ent->new->ns = aa_get_ns(ns); if (ent->old || ent->rename) continue; @@ -835,7 +835,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) mutex_unlock(&ns->lock); out: - aa_put_namespace(ns); + aa_put_ns(ns); if (error) return error; @@ -881,7 +881,7 @@ free: */ ssize_t aa_remove_profiles(char *fqname, size_t size) { - struct aa_namespace *root, *ns = NULL; + struct aa_ns *root, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; ssize_t error = 0; @@ -898,7 +898,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) char *ns_name; name = aa_split_fqname(fqname, &ns_name); /* released below */ - ns = aa_find_namespace(root, ns_name); + ns = aa_find_ns(root, ns_name); if (!ns) { info = "namespace does not exist"; error = -ENOENT; @@ -906,12 +906,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) } } else /* released below */ - ns = aa_get_namespace(root); + ns = aa_get_ns(root); if (!name) { /* remove namespace - can only happen if fqname[0] == ':' */ mutex_lock(&ns->parent->lock); - __aa_remove_namespace(ns); + __aa_remove_ns(ns); mutex_unlock(&ns->parent->lock); } else { /* remove profile */ @@ -929,13 +929,13 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); - aa_put_namespace(ns); + aa_put_ns(ns); aa_put_profile(profile); return size; fail_ns_lock: mutex_unlock(&ns->lock); - aa_put_namespace(ns); + aa_put_ns(ns); fail: (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index d4e9924..88b3b3c 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -26,7 +26,7 @@ #include "include/policy.h" /* root profile namespace */ -struct aa_namespace *root_ns; +struct aa_ns *root_ns; const char *aa_hidden_ns_name = "---"; /** @@ -36,7 +36,7 @@ const char *aa_hidden_ns_name = "---"; * * Returns: true if @view is visible from @curr else false */ -bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view) { if (curr == view) return true; @@ -55,7 +55,7 @@ bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) * * Returns: name of @view visible from @curr */ -const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) +const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view) { /* if view == curr then the namespace name isn't displayed */ if (curr == view) @@ -75,16 +75,15 @@ const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) } /** - * alloc_namespace - allocate, initialize and return a new namespace + * alloc_ns - allocate, initialize and return a new namespace * @prefix: parent namespace name (MAYBE NULL) * @name: a preallocated name (NOT NULL) * * Returns: refcounted namespace or NULL on failure. */ -static struct aa_namespace *alloc_namespace(const char *prefix, - const char *name) +static struct aa_ns *alloc_ns(const char *prefix, const char *name) { - struct aa_namespace *ns; + struct aa_ns *ns; ns = kzalloc(sizeof(*ns), GFP_KERNEL); AA_DEBUG("%s(%p)\n", __func__, ns); @@ -96,7 +95,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix, INIT_LIST_HEAD(&ns->sub_ns); mutex_init(&ns->lock); - /* released by free_namespace */ + /* released by aa_free_ns() */ ns->unconfined = aa_alloc_profile("unconfined"); if (!ns->unconfined) goto fail_unconfined; @@ -120,19 +119,19 @@ fail_ns: } /** - * aa_free_namespace - free a profile namespace + * aa_free_ns - free a profile namespace * @ns: the namespace to free (MAYBE NULL) * * Requires: All references to the namespace must have been put, if the * namespace was referenced by a profile confining a task, */ -void aa_free_namespace(struct aa_namespace *ns) +void aa_free_ns(struct aa_ns *ns) { if (!ns) return; aa_policy_destroy(&ns->base); - aa_put_namespace(ns->parent); + aa_put_ns(ns->parent); ns->unconfined->ns = NULL; aa_free_profile(ns->unconfined); @@ -140,7 +139,7 @@ void aa_free_namespace(struct aa_namespace *ns) } /** - * aa_find_namespace - look up a profile namespace on the namespace list + * aa_find_ns - look up a profile namespace on the namespace list * @root: namespace to search in (NOT NULL) * @name: name of namespace to find (NOT NULL) * @@ -149,27 +148,26 @@ void aa_free_namespace(struct aa_namespace *ns) * * refcount released by caller */ -struct aa_namespace *aa_find_namespace(struct aa_namespace *root, - const char *name) +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) { - struct aa_namespace *ns = NULL; + struct aa_ns *ns = NULL; rcu_read_lock(); - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); rcu_read_unlock(); return ns; } /** - * aa_prepare_namespace - find an existing or create a new namespace of @name + * aa_prepare_ns - find an existing or create a new namespace of @name * @name: the namespace to find or add (MAYBE NULL) * - * Returns: refcounted namespace or NULL if failed to create one + * Returns: refcounted ns or NULL if failed to create one */ -struct aa_namespace *aa_prepare_namespace(const char *name) +struct aa_ns *aa_prepare_ns(const char *name) { - struct aa_namespace *ns, *root; + struct aa_ns *ns, *root; root = aa_current_profile()->ns; @@ -178,28 +176,28 @@ struct aa_namespace *aa_prepare_namespace(const char *name) /* if name isn't specified the profile is loaded to the current ns */ if (!name) { /* released by caller */ - ns = aa_get_namespace(root); + ns = aa_get_ns(root); goto out; } /* try and find the specified ns and if it doesn't exist create it */ /* released by caller */ - ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); if (!ns) { - ns = alloc_namespace(root->base.hname, name); + ns = alloc_ns(root->base.hname, name); if (!ns) goto out; - if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { + if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) { AA_ERROR("Failed to create interface for ns %s\n", ns->base.name); - aa_free_namespace(ns); + aa_free_ns(ns); ns = NULL; goto out; } - ns->parent = aa_get_namespace(root); + ns->parent = aa_get_ns(root); list_add_rcu(&ns->base.list, &root->sub_ns); /* add list ref */ - aa_get_namespace(ns); + aa_get_ns(ns); } out: mutex_unlock(&root->lock); @@ -211,10 +209,10 @@ out: static void __ns_list_release(struct list_head *head); /** - * destroy_namespace - remove everything contained by @ns - * @ns: namespace to have it contents removed (NOT NULL) + * destroy_ns - remove everything contained by @ns + * @ns: ns to have it contents removed (NOT NULL) */ -static void destroy_namespace(struct aa_namespace *ns) +static void destroy_ns(struct aa_ns *ns) { if (!ns) return; @@ -228,22 +226,22 @@ static void destroy_namespace(struct aa_namespace *ns) if (ns->parent) __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); - __aa_fs_namespace_rmdir(ns); + __aa_fs_ns_rmdir(ns); mutex_unlock(&ns->lock); } /** - * __aa_remove_namespace - remove a namespace and all its children + * __aa_remove_ns - remove a namespace and all its children * @ns: namespace to be removed (NOT NULL) * * Requires: ns->parent->lock be held and ns removed from parent. */ -void __aa_remove_namespace(struct aa_namespace *ns) +void __aa_remove_ns(struct aa_ns *ns) { /* remove ns from namespace list */ list_del_rcu(&ns->base.list); - destroy_namespace(ns); - aa_put_namespace(ns); + destroy_ns(ns); + aa_put_ns(ns); } /** @@ -254,15 +252,15 @@ void __aa_remove_namespace(struct aa_namespace *ns) */ static void __ns_list_release(struct list_head *head) { - struct aa_namespace *ns, *tmp; + struct aa_ns *ns, *tmp; list_for_each_entry_safe(ns, tmp, head, base.list) - __aa_remove_namespace(ns); + __aa_remove_ns(ns); } /** - * aa_alloc_root_ns - allocate the root profile namespace + * aa_alloc_root_ns - allocate the root profile namespcae * * Returns: %0 on success else error * @@ -270,7 +268,7 @@ static void __ns_list_release(struct list_head *head) int __init aa_alloc_root_ns(void) { /* released by aa_free_root_ns - used as list ref*/ - root_ns = alloc_namespace(NULL, "root"); + root_ns = alloc_ns(NULL, "root"); if (!root_ns) return -ENOMEM; @@ -282,10 +280,10 @@ int __init aa_alloc_root_ns(void) */ void __init aa_free_root_ns(void) { - struct aa_namespace *ns = root_ns; + struct aa_ns *ns = root_ns; root_ns = NULL; - destroy_namespace(ns); - aa_put_namespace(ns); + destroy_ns(ns); + aa_put_ns(ns); } diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index bb2600d..15ddf74 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -40,8 +40,8 @@ int aa_getprocattr(struct aa_profile *profile, char **string) int len = 0, mode_len = 0, ns_len = 0, name_len; const char *mode_str = aa_profile_mode_names[profile->mode]; const char *ns_name = NULL; - struct aa_namespace *ns = profile->ns; - struct aa_namespace *current_ns = __aa_current_profile()->ns; + struct aa_ns *ns = profile->ns; + struct aa_ns *current_ns = __aa_current_profile()->ns; char *s; if (!aa_ns_visible(current_ns, ns)) -- cgit v1.1 From 121d4a91e3c12ddfb167edafb9aa64cc5cc3a406 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:17 -0800 Subject: apparmor: rename sid to secid Move to common terminology with other LSMs and kernel infrastucture Signed-off-by: John Johansen --- security/apparmor/Makefile | 2 +- security/apparmor/include/secid.h | 26 ++++++++++++++++++ security/apparmor/include/sid.h | 26 ------------------ security/apparmor/secid.c | 55 +++++++++++++++++++++++++++++++++++++++ security/apparmor/sid.c | 55 --------------------------------------- 5 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 security/apparmor/include/secid.h delete mode 100644 security/apparmor/include/sid.h create mode 100644 security/apparmor/secid.c delete mode 100644 security/apparmor/sid.c (limited to 'security/apparmor') diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index 3485f49..ad369a7 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o policy_ns.o + resource.o secid.o file.o policy_ns.o apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o clean-files := capability_names.h rlim_names.h diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h new file mode 100644 index 0000000..95ed86a --- /dev/null +++ b/security/apparmor/include/secid.h @@ -0,0 +1,26 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (secid) definitions + * + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_SECID_H +#define __AA_SECID_H + +#include + +/* secid value that will not be allocated */ +#define AA_SECID_INVALID 0 +#define AA_SECID_ALLOC AA_SECID_INVALID + +u32 aa_alloc_secid(void); +void aa_free_secid(u32 secid); + +#endif /* __AA_SECID_H */ diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h deleted file mode 100644 index 513ca0e..0000000 --- a/security/apparmor/include/sid.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * AppArmor security module - * - * This file contains AppArmor security identifier (sid) definitions - * - * Copyright 2009-2010 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - */ - -#ifndef __AA_SID_H -#define __AA_SID_H - -#include - -/* sid value that will not be allocated */ -#define AA_SID_INVALID 0 -#define AA_SID_ALLOC AA_SID_INVALID - -u32 aa_alloc_sid(void); -void aa_free_sid(u32 sid); - -#endif /* __AA_SID_H */ diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c new file mode 100644 index 0000000..3a3edba --- /dev/null +++ b/security/apparmor/secid.c @@ -0,0 +1,55 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (secid) manipulation fns + * + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + * AppArmor allocates a unique secid for every profile loaded. If a profile + * is replaced it receives the secid of the profile it is replacing. + * + * The secid value of 0 is invalid. + */ + +#include +#include +#include + +#include "include/secid.h" + +/* global counter from which secids are allocated */ +static u32 global_secid; +static DEFINE_SPINLOCK(secid_lock); + +/* TODO FIXME: add secid to profile mapping, and secid recycling */ + +/** + * aa_alloc_secid - allocate a new secid for a profile + */ +u32 aa_alloc_secid(void) +{ + u32 secid; + + /* + * TODO FIXME: secid recycling - part of profile mapping table + */ + spin_lock(&secid_lock); + secid = (++global_secid); + spin_unlock(&secid_lock); + return secid; +} + +/** + * aa_free_secid - free a secid + * @secid: secid to free + */ +void aa_free_secid(u32 secid) +{ + ; /* NOP ATM */ +} diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c deleted file mode 100644 index f0b34f7..0000000 --- a/security/apparmor/sid.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * AppArmor security module - * - * This file contains AppArmor security identifier (sid) manipulation fns - * - * Copyright 2009-2010 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - * - * AppArmor allocates a unique sid for every profile loaded. If a profile - * is replaced it receives the sid of the profile it is replacing. - * - * The sid value of 0 is invalid. - */ - -#include -#include -#include - -#include "include/sid.h" - -/* global counter from which sids are allocated */ -static u32 global_sid; -static DEFINE_SPINLOCK(sid_lock); - -/* TODO FIXME: add sid to profile mapping, and sid recycling */ - -/** - * aa_alloc_sid - allocate a new sid for a profile - */ -u32 aa_alloc_sid(void) -{ - u32 sid; - - /* - * TODO FIXME: sid recycling - part of profile mapping table - */ - spin_lock(&sid_lock); - sid = (++global_sid); - spin_unlock(&sid_lock); - return sid; -} - -/** - * aa_free_sid - free a sid - * @sid: sid to free - */ -void aa_free_sid(u32 sid) -{ - ; /* NOP ATM */ -} -- cgit v1.1 From d97d51d253e08e059bba40002407ec3d188feafb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:18 -0800 Subject: apparmor: rename PFLAG_INVALID to PFLAG_STALE Invalid does not convey the meaning of the flag anymore so rename it. Signed-off-by: John Johansen --- security/apparmor/include/context.h | 2 +- security/apparmor/include/policy.h | 6 +++--- security/apparmor/policy.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 6bf6579..a0acc23 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -152,7 +152,7 @@ static inline struct aa_profile *aa_current_profile(void) struct aa_profile *profile; BUG_ON(!cxt || !cxt->profile); - if (PROFILE_INVALID(cxt->profile)) { + if (profile_is_stale(cxt->profile)) { profile = aa_get_newest_profile(cxt->profile); aa_replace_current_profile(profile); aa_put_profile(profile); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 415f8ab..56bef76 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -46,7 +46,7 @@ extern const char *const aa_profile_mode_names[]; #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) -#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID) +#define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) @@ -71,7 +71,7 @@ enum profile_flags { PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ - PFLAG_INVALID = 0x200, /* profile replaced/removed */ + PFLAG_STALE = 0x200, /* profile replaced/removed */ PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ /* These flags must correspond with PATH_flags */ @@ -253,7 +253,7 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) if (!p) return NULL; - if (PROFILE_INVALID(p)) + if (profile_is_stale(p)) return aa_get_profile_rcu(&p->replacedby->profile); return aa_get_profile(p); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 2dd8717..edc81a0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -106,7 +106,7 @@ void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new) tmp = rcu_dereference_protected(orig->replacedby->profile, mutex_is_locked(&orig->ns->lock)); rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); - orig->flags |= PFLAG_INVALID; + orig->flags |= PFLAG_STALE; aa_put_profile(tmp); } -- cgit v1.1 From 8399588a7f9def9195e577f988ad06f2a0ffb1af Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:19 -0800 Subject: apparmor: rename replacedby to proxy Proxy is shorter and a better fit than replaceby, so rename it. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 36 ++++++++++---------- security/apparmor/context.c | 2 +- security/apparmor/include/policy.h | 20 +++++------ security/apparmor/policy.c | 70 +++++++++++++++++++------------------- security/apparmor/policy_ns.c | 2 +- 5 files changed, 65 insertions(+), 65 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 4409b63..0f1a4a2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -228,12 +228,12 @@ const struct file_operations aa_fs_seq_file_ops = { static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, int (*show)(struct seq_file *, void *)) { - struct aa_replacedby *r = aa_get_replacedby(inode->i_private); - int error = single_open(file, show, r); + struct aa_proxy *proxy = aa_get_proxy(inode->i_private); + int error = single_open(file, show, proxy); if (error) { file->private_data = NULL; - aa_put_replacedby(r); + aa_put_proxy(proxy); } return error; @@ -243,14 +243,14 @@ static int aa_fs_seq_profile_release(struct inode *inode, struct file *file) { struct seq_file *seq = (struct seq_file *) file->private_data; if (seq) - aa_put_replacedby(seq->private); + aa_put_proxy(seq->private); return single_release(inode, file); } static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); seq_printf(seq, "%s\n", profile->base.name); aa_put_profile(profile); @@ -272,8 +272,8 @@ static const struct file_operations aa_fs_profname_fops = { static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); aa_put_profile(profile); @@ -295,8 +295,8 @@ static const struct file_operations aa_fs_profmode_fops = { static int aa_fs_seq_profattach_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); if (profile->attach) seq_printf(seq, "%s\n", profile->attach); else if (profile->xmatch) @@ -323,8 +323,8 @@ static const struct file_operations aa_fs_profattach_fops = { static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) { - struct aa_replacedby *r = seq->private; - struct aa_profile *profile = aa_get_profile_rcu(&r->profile); + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); unsigned int i, size = aa_hash_size(); if (profile->hash) { @@ -363,13 +363,13 @@ void __aa_fs_profile_rmdir(struct aa_profile *profile) __aa_fs_profile_rmdir(child); for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { - struct aa_replacedby *r; + struct aa_proxy *proxy; if (!profile->dents[i]) continue; - r = d_inode(profile->dents[i])->i_private; + proxy = d_inode(profile->dents[i])->i_private; securityfs_remove(profile->dents[i]); - aa_put_replacedby(r); + aa_put_proxy(proxy); profile->dents[i] = NULL; } } @@ -391,12 +391,12 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, struct aa_profile *profile, const struct file_operations *fops) { - struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); + struct aa_proxy *proxy = aa_get_proxy(profile->proxy); struct dentry *dent; - dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); + dent = securityfs_create_file(name, S_IFREG | 0444, dir, proxy, fops); if (IS_ERR(dent)) - aa_put_replacedby(r); + aa_put_proxy(proxy); return dent; } diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3064c6c..3c4f534 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -112,7 +112,7 @@ int aa_replace_current_profile(struct aa_profile *profile) aa_clear_task_cxt_trans(cxt); /* be careful switching cxt->profile, when racing replacement it - * is possible that cxt->profile->replacedby->profile is the reference + * is possible that cxt->profile->proxy->profile is the reference * keeping @profile valid, so make sure to get its reference before * dropping the reference on cxt->profile */ aa_get_profile(profile); diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 56bef76..f55ecb8 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -91,7 +91,7 @@ struct aa_policydb { }; -struct aa_replacedby { +struct aa_proxy { struct kref count; struct aa_profile __rcu *profile; }; @@ -103,7 +103,7 @@ struct aa_replacedby { * @rcu: rcu head used when removing from @list * @parent: parent of profile * @ns: namespace the profile is in - * @replacedby: is set to the profile that replaced this profile + * @proxy: is set to the profile that replaced this profile * @rename: optional profile name that this profile renamed * @attach: human readable attachment string * @xmatch: optional extended matching for unconfined executables names @@ -126,7 +126,7 @@ struct aa_replacedby { * used to determine profile attachment against unconfined tasks. All other * attachments are determined by profile X transition rules. * - * The @replacedby struct is write protected by the profile lock. + * The @proxy struct is write protected by the profile lock. * * Profiles have a hierarchy where hats and children profiles keep * a reference to their parent. @@ -142,7 +142,7 @@ struct aa_profile { struct aa_profile __rcu *parent; struct aa_ns *ns; - struct aa_replacedby *replacedby; + struct aa_proxy *proxy; const char *rename; const char *attach; @@ -166,12 +166,12 @@ struct aa_profile { extern enum profile_mode aa_g_profile_mode; -void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new); +void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new); void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); -void aa_free_replacedby_kref(struct kref *kref); +void aa_free_proxy_kref(struct kref *kref); struct aa_profile *aa_alloc_profile(const char *name); struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); @@ -254,7 +254,7 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) return NULL; if (profile_is_stale(p)) - return aa_get_profile_rcu(&p->replacedby->profile); + return aa_get_profile_rcu(&p->proxy->profile); return aa_get_profile(p); } @@ -269,7 +269,7 @@ static inline void aa_put_profile(struct aa_profile *p) kref_put(&p->count, aa_free_profile_kref); } -static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) +static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p) { if (p) kref_get(&(p->count)); @@ -277,10 +277,10 @@ static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p) return p; } -static inline void aa_put_replacedby(struct aa_replacedby *p) +static inline void aa_put_proxy(struct aa_proxy *p) { if (p) - kref_put(&p->count, aa_free_replacedby_kref); + kref_put(&p->count, aa_free_proxy_kref); } static inline int AUDIT_MODE(struct aa_profile *profile) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index edc81a0..a4bf675 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -99,13 +99,13 @@ const char *const aa_profile_mode_names[] = { /* requires profile list write lock held */ -void __aa_update_replacedby(struct aa_profile *orig, struct aa_profile *new) +void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new) { struct aa_profile *tmp; - tmp = rcu_dereference_protected(orig->replacedby->profile, + tmp = rcu_dereference_protected(orig->proxy->profile, mutex_is_locked(&orig->ns->lock)); - rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new)); + rcu_assign_pointer(orig->proxy->profile, aa_get_profile(new)); orig->flags |= PFLAG_STALE; aa_put_profile(tmp); } @@ -156,7 +156,7 @@ static void __remove_profile(struct aa_profile *profile) /* release any children lists first */ __aa_profile_list_release(&profile->base.profiles); /* released by free_profile */ - __aa_update_replacedby(profile, profile->ns->unconfined); + __aa_update_proxy(profile, profile->ns->unconfined); __aa_fs_profile_rmdir(profile); __list_remove_profile(profile); } @@ -175,21 +175,21 @@ void __aa_profile_list_release(struct list_head *head) } -static void free_replacedby(struct aa_replacedby *r) +static void free_proxy(struct aa_proxy *p) { - if (r) { + if (p) { /* r->profile will not be updated any more as r is dead */ - aa_put_profile(rcu_dereference_protected(r->profile, true)); - kzfree(r); + aa_put_profile(rcu_dereference_protected(p->profile, true)); + kzfree(p); } } -void aa_free_replacedby_kref(struct kref *kref) +void aa_free_proxy_kref(struct kref *kref) { - struct aa_replacedby *r = container_of(kref, struct aa_replacedby, - count); - free_replacedby(r); + struct aa_proxy *p = container_of(kref, struct aa_proxy, count); + + free_proxy(p); } /** @@ -223,7 +223,7 @@ void aa_free_profile(struct aa_profile *profile) kzfree(profile->dirname); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); - aa_put_replacedby(profile->replacedby); + aa_put_proxy(profile->proxy); kzfree(profile->hash); kzfree(profile); @@ -267,10 +267,10 @@ struct aa_profile *aa_alloc_profile(const char *hname) if (!profile) return NULL; - profile->replacedby = kzalloc(sizeof(struct aa_replacedby), GFP_KERNEL); - if (!profile->replacedby) + profile->proxy = kzalloc(sizeof(struct aa_proxy), GFP_KERNEL); + if (!profile->proxy) goto fail; - kref_init(&profile->replacedby->count); + kref_init(&profile->proxy->count); if (!aa_policy_init(&profile->base, NULL, hname)) goto fail; @@ -280,7 +280,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) return profile; fail: - kzfree(profile->replacedby); + kzfree(profile->proxy); kzfree(profile); return NULL; @@ -598,7 +598,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, * __replace_profile - replace @old with @new on a list * @old: profile to be replaced (NOT NULL) * @new: profile to replace @old with (NOT NULL) - * @share_replacedby: transfer @old->replacedby to @new + * @share_proxy: transfer @old->proxy to @new * * Will duplicate and refcount elements that @new inherits from @old * and will inherit @old children. @@ -608,7 +608,7 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, * Requires: namespace list lock be held, or list not be shared */ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, - bool share_replacedby) + bool share_proxy) { struct aa_profile *child, *tmp; @@ -623,7 +623,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, p = __find_child(&new->base.profiles, child->base.name); if (p) { /* @p replaces @child */ - __replace_profile(child, p, share_replacedby); + __replace_profile(child, p, share_proxy); continue; } @@ -641,13 +641,13 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, struct aa_profile *parent = aa_deref_parent(old); rcu_assign_pointer(new->parent, aa_get_profile(parent)); } - __aa_update_replacedby(old, new); - if (share_replacedby) { - aa_put_replacedby(new->replacedby); - new->replacedby = aa_get_replacedby(old->replacedby); - } else if (!rcu_access_pointer(new->replacedby->profile)) - /* aafs interface uses replacedby */ - rcu_assign_pointer(new->replacedby->profile, + __aa_update_proxy(old, new); + if (share_proxy) { + aa_put_proxy(new->proxy); + new->proxy = aa_get_proxy(old->proxy); + } else if (!rcu_access_pointer(new->proxy->profile)) + /* aafs interface uses proxy */ + rcu_assign_pointer(new->proxy->profile, aa_get_profile(new)); __aa_fs_profile_migrate_dents(old, new); @@ -797,15 +797,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (ent->old) { __replace_profile(ent->old, ent->new, 1); if (ent->rename) { - /* aafs interface uses replacedby */ - struct aa_replacedby *r = ent->new->replacedby; + /* aafs interface uses proxy */ + struct aa_proxy *r = ent->new->proxy; rcu_assign_pointer(r->profile, aa_get_profile(ent->new)); __replace_profile(ent->rename, ent->new, 0); } } else if (ent->rename) { - /* aafs interface uses replacedby */ - rcu_assign_pointer(ent->new->replacedby->profile, + /* aafs interface uses proxy */ + rcu_assign_pointer(ent->new->proxy->profile, aa_get_profile(ent->new)); __replace_profile(ent->rename, ent->new, 0); } else if (ent->new->parent) { @@ -819,14 +819,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) rcu_assign_pointer(ent->new->parent, newest); aa_put_profile(parent); } - /* aafs interface uses replacedby */ - rcu_assign_pointer(ent->new->replacedby->profile, + /* aafs interface uses proxy */ + rcu_assign_pointer(ent->new->proxy->profile, aa_get_profile(ent->new)); __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { - /* aafs interface uses replacedby */ - rcu_assign_pointer(ent->new->replacedby->profile, + /* aafs interface uses proxy */ + rcu_assign_pointer(ent->new->proxy->profile, aa_get_profile(ent->new)); __list_add_profile(&ns->base.profiles, ent->new); } diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 88b3b3c..71fbd14 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -225,7 +225,7 @@ static void destroy_ns(struct aa_ns *ns) __ns_list_release(&ns->sub_ns); if (ns->parent) - __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); + __aa_update_proxy(ns->unconfined, ns->parent->unconfined); __aa_fs_ns_rmdir(ns); mutex_unlock(&ns->lock); } -- cgit v1.1 From 1741e9eb8c15de0ba6598a342c51d0688cb569d2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:21 -0800 Subject: apparmor: add strn version of lookup_profile fn Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/policy.c | 36 +++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index f55ecb8..c90f4a2 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -177,6 +177,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); +struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, + size_t n); struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a4bf675..2918104 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -427,9 +427,10 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, } /** - * __lookup_profile - lookup the profile matching @hname + * __lookupn_profile - lookup the profile matching @hname * @base: base list to start looking up profile name from (NOT NULL) * @hname: hierarchical profile name (NOT NULL) + * @n: length of @hname * * Requires: rcu_read_lock be held * @@ -437,53 +438,66 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, * * Do a relative name lookup, recursing through profile tree. */ -static struct aa_profile *__lookup_profile(struct aa_policy *base, - const char *hname) +static struct aa_profile *__lookupn_profile(struct aa_policy *base, + const char *hname, size_t n) { struct aa_profile *profile = NULL; - char *split; + const char *split; - for (split = strstr(hname, "//"); split;) { + for (split = strnstr(hname, "//", n); split; + split = strnstr(hname, "//", n)) { profile = __strn_find_child(&base->profiles, hname, split - hname); if (!profile) return NULL; base = &profile->base; + n -= split + 2 - hname; hname = split + 2; - split = strstr(hname, "//"); } - profile = __find_child(&base->profiles, hname); + if (n) + return __strn_find_child(&base->profiles, hname, n); + return NULL; +} - return profile; +static struct aa_profile *__lookup_profile(struct aa_policy *base, + const char *hname) +{ + return __lookupn_profile(base, hname, strlen(hname)); } /** * aa_lookup_profile - find a profile by its full or partial name * @ns: the namespace to start from (NOT NULL) * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) + * @n: size of @hname * * Returns: refcounted profile or NULL if not found */ -struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) +struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, + size_t n) { struct aa_profile *profile; rcu_read_lock(); do { - profile = __lookup_profile(&ns->base, hname); + profile = __lookupn_profile(&ns->base, hname, n); } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* the unconfined profile is not in the regular profile list */ - if (!profile && strcmp(hname, "unconfined") == 0) + if (!profile && strncmp(hname, "unconfined", n) == 0) profile = aa_get_newest_profile(ns->unconfined); /* refcount released by caller */ return profile; } +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) +{ + return aa_lookupn_profile(ns, hname, strlen(hname)); +} /** * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL) -- cgit v1.1 From 9a2d40c12d00ead1b1a3ac8383d2d66e35674fdb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:22 -0800 Subject: apparmor: add strn version of aa_find_ns Signed-off-by: John Johansen --- security/apparmor/include/policy_ns.h | 13 ++++++++++--- security/apparmor/policy_ns.c | 22 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 323752c..381f8b0 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -82,6 +82,7 @@ void aa_free_root_ns(void); void aa_free_ns_kref(struct kref *kref); struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); +struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); struct aa_ns *aa_prepare_ns(const char *name); void __aa_remove_ns(struct aa_ns *ns); @@ -119,18 +120,24 @@ static inline void aa_put_ns(struct aa_ns *ns) } /** - * __aa_find_ns - find a namespace on a list by @name + * __aa_findn_ns - find a namespace on a list by @name * @head: list to search for namespace on (NOT NULL) * @name: name of namespace to look for (NOT NULL) - * + * @n: length of @name * Returns: unrefcounted namespace * * Requires: rcu_read_lock be held */ +static inline struct aa_ns *__aa_findn_ns(struct list_head *head, + const char *name, size_t n) +{ + return (struct aa_ns *)__policy_strn_find(head, name, n); +} + static inline struct aa_ns *__aa_find_ns(struct list_head *head, const char *name) { - return (struct aa_ns *)__policy_find(head, name); + return __aa_findn_ns(head, name, strlen(name)); } #endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 71fbd14..9746643 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -139,27 +139,43 @@ void aa_free_ns(struct aa_ns *ns) } /** - * aa_find_ns - look up a profile namespace on the namespace list + * aa_findn_ns - look up a profile namespace on the namespace list * @root: namespace to search in (NOT NULL) * @name: name of namespace to find (NOT NULL) + * @n: length of @name * * Returns: a refcounted namespace on the list, or NULL if no namespace * called @name exists. * * refcount released by caller */ -struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) +struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n) { struct aa_ns *ns = NULL; rcu_read_lock(); - ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); + ns = aa_get_ns(__aa_findn_ns(&root->sub_ns, name, n)); rcu_read_unlock(); return ns; } /** + * aa_find_ns - look up a profile namespace on the namespace list + * @root: namespace to search in (NOT NULL) + * @name: name of namespace to find (NOT NULL) + * + * Returns: a refcounted namespace on the list, or NULL if no namespace + * called @name exists. + * + * refcount released by caller + */ +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) +{ + return aa_findn_ns(root, name, strlen(name)); +} + +/** * aa_prepare_ns - find an existing or create a new namespace of @name * @name: the namespace to find or add (MAYBE NULL) * -- cgit v1.1 From 3b0aaf5866bf92a3e47627a02ed5e1be6d7cc110 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:23 -0800 Subject: apparmor: add lib fn to find the "split" for fqnames Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 2 ++ security/apparmor/lib.c | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'security/apparmor') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 74cc68e..4699c2b 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -38,6 +38,8 @@ extern int apparmor_initialized __initdata; /* fn's in lib */ char *aa_split_fqname(char *args, char **ns_name); +const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, + size_t *ns_len); void aa_info_message(const char *str); void *__aa_kvmalloc(size_t size, gfp_t flags); diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index e29ccdb..fec78ee 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -12,6 +12,7 @@ * License. */ +#include #include #include #include @@ -61,6 +62,58 @@ char *aa_split_fqname(char *fqname, char **ns_name) } /** + * skipn_spaces - Removes leading whitespace from @str. + * @str: The string to be stripped. + * + * Returns a pointer to the first non-whitespace character in @str. + * if all whitespace will return NULL + */ + +static const char *skipn_spaces(const char *str, size_t n) +{ + for (; n && isspace(*str); --n) + ++str; + if (n) + return (char *)str; + return NULL; +} + +const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, + size_t *ns_len) +{ + const char *end = fqname + n; + const char *name = skipn_spaces(fqname, n); + + if (!name) + return NULL; + *ns_name = NULL; + *ns_len = 0; + if (name[0] == ':') { + char *split = strnchr(&name[1], end - &name[1], ':'); + *ns_name = skipn_spaces(&name[1], end - &name[1]); + if (!*ns_name) + return NULL; + if (split) { + *ns_len = split - *ns_name; + if (*ns_len == 0) + *ns_name = NULL; + split++; + if (end - split > 1 && strncmp(split, "//", 2) == 0) + split += 2; + name = skipn_spaces(split, end - split); + } else { + /* a ns name without a following profile is allowed */ + name = NULL; + *ns_len = end - *ns_name; + } + } + if (name && *name == 0) + name = NULL; + + return name; +} + +/** * aa_info_message - log a none profile related status message * @str: message to log */ -- cgit v1.1 From 31617ddfdd7764a5046f076247208aa324458069 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:24 -0800 Subject: apparmor: add fn to lookup profiles by fqname Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 ++ security/apparmor/include/policy_ns.h | 10 +++++----- security/apparmor/policy.c | 29 +++++++++++++++++++++++++++++ security/apparmor/policy_ns.c | 4 ++-- 4 files changed, 38 insertions(+), 7 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c90f4a2..da62d29 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -180,6 +180,8 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, size_t n); struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); +struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, + const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 381f8b0..ebf9b40 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -46,11 +46,11 @@ struct aa_ns_acct { * @uniq_id: a unique id count for the profiles in the namespace * @dents: dentries for the namespaces file entries in apparmorfs * - * An aa_ns defines the set profiles that are searched to determine - * which profile to attach to a task. Profiles can not be shared between - * aa_nss and profile names within a namespace are guaranteed to be - * unique. When profiles in separate namespaces have the same name they - * are NOT considered to be equivalent. + * An aa_ns defines the set profiles that are searched to determine which + * profile to attach to a task. Profiles can not be shared between aa_ns + * and profile names within a namespace are guaranteed to be unique. When + * profiles in separate namespaces have the same name they are NOT considered + * to be equivalent. * * Namespaces are hierarchical and only namespaces and profiles below the * current namespace are visible. diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 2918104..7fde578 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -498,6 +498,35 @@ struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname) { return aa_lookupn_profile(ns, hname, strlen(hname)); } + +struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, + const char *fqname, size_t n) +{ + struct aa_profile *profile; + struct aa_ns *ns; + const char *name, *ns_name; + size_t ns_len; + + name = aa_splitn_fqname(fqname, n, &ns_name, &ns_len); + if (ns_name) { + ns = aa_findn_ns(base->ns, ns_name, ns_len); + if (!ns) + return NULL; + } else + ns = aa_get_ns(base->ns); + + if (name) + profile = aa_lookupn_profile(ns, name, n - (name - fqname)); + else if (ns) + /* default profile for ns, currently unconfined */ + profile = aa_get_newest_profile(ns->unconfined); + else + profile = NULL; + aa_put_ns(ns); + + return profile; +} + /** * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL) diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 9746643..bab23cc 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -226,7 +226,7 @@ static void __ns_list_release(struct list_head *head); /** * destroy_ns - remove everything contained by @ns - * @ns: ns to have it contents removed (NOT NULL) + * @ns: namespace to have it contents removed (NOT NULL) */ static void destroy_ns(struct aa_ns *ns) { @@ -276,7 +276,7 @@ static void __ns_list_release(struct list_head *head) } /** - * aa_alloc_root_ns - allocate the root profile namespcae + * aa_alloc_root_ns - allocate the root profile namespace * * Returns: %0 on success else error * -- cgit v1.1 From 92b6d8eff55f8dca57ade26e1dde2c3b6acdae02 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:25 -0800 Subject: apparmor: allow ns visibility question to consider subnses Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/include/policy_ns.h | 4 ++-- security/apparmor/policy_ns.c | 12 +++++++++--- security/apparmor/procattr.c | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0f1a4a2..d7cfd79 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -750,7 +750,7 @@ static int seq_show_profile(struct seq_file *f, void *p) struct aa_ns *root = f->private; if (profile->ns != root) - seq_printf(f, ":%s://", aa_ns_name(root, profile->ns)); + seq_printf(f, ":%s://", aa_ns_name(root, profile->ns, true)); seq_printf(f, "%s (%s)\n", profile->base.hname, aa_profile_mode_names[profile->mode]); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index ebf9b40..e4c8765 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -74,8 +74,8 @@ extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; -bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view); -const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child); +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns); +const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns); void aa_free_ns(struct aa_ns *ns); int aa_alloc_root_ns(void); void aa_free_root_ns(void); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index bab23cc..e7b7a82 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -33,18 +33,23 @@ const char *aa_hidden_ns_name = "---"; * aa_ns_visible - test if @view is visible from @curr * @curr: namespace to treat as the parent (NOT NULL) * @view: namespace to test if visible from @curr (NOT NULL) + * @subns: whether view of a subns is allowed * * Returns: true if @view is visible from @curr else false */ -bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view) +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns) { if (curr == view) return true; + if (!subns) + return false; + for ( ; view; view = view->parent) { if (view->parent == curr) return true; } + return false; } @@ -52,16 +57,17 @@ bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view) * aa_na_name - Find the ns name to display for @view from @curr * @curr - current namespace (NOT NULL) * @view - namespace attempting to view (NOT NULL) + * @subns - are subns visible * * Returns: name of @view visible from @curr */ -const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view) +const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns) { /* if view == curr then the namespace name isn't displayed */ if (curr == view) return ""; - if (aa_ns_visible(curr, view)) { + if (aa_ns_visible(curr, view, subns)) { /* at this point if a ns is visible it is in a view ns * thus the curr ns.hname is a prefix of its name. * Only output the virtualized portion of the name diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 15ddf74..1babd36 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -44,10 +44,10 @@ int aa_getprocattr(struct aa_profile *profile, char **string) struct aa_ns *current_ns = __aa_current_profile()->ns; char *s; - if (!aa_ns_visible(current_ns, ns)) + if (!aa_ns_visible(current_ns, ns, true)) return -EACCES; - ns_name = aa_ns_name(current_ns, ns); + ns_name = aa_ns_name(current_ns, ns, true); ns_len = strlen(ns_name); /* if the visible ns_name is > 0 increase size for : :// seperator */ -- cgit v1.1 From 57e36bbd67bd1509f550311b162be78cadfe887b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:26 -0800 Subject: apparmor: add macro for bug asserts to check that a lock is held Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'security/apparmor') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 4699c2b..61dedd7 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -19,6 +19,17 @@ #include "match.h" +/* Provide our own test for whether a write lock is held for asserts + * this is because on none SMP systems write_can_lock will always + * resolve to true, which is what you want for code making decisions + * based on it, but wrong for asserts checking that the lock is held + */ +#ifdef CONFIG_SMP +#define write_is_locked(X) !write_can_lock(X) +#else +#define write_is_locked(X) (1) +#endif /* CONFIG_SMP */ + /* * DEBUG remains global (no per profile flag) since it is mostly used in sysctl * which is not related to profile accesses. -- cgit v1.1 From 680cd62e910d7b7e3c1fcde6ba67c6ca770c2286 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:27 -0800 Subject: apparmor: add debug assert AA_BUG and Kconfig to control debug info Signed-off-by: John Johansen --- security/apparmor/Kconfig | 31 +++++++++++++++++++++++++++++-- security/apparmor/include/lib.h | 14 +++++++++++++- security/apparmor/lsm.c | 2 +- 3 files changed, 43 insertions(+), 4 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index be5e941..b6b68a7 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -36,7 +36,6 @@ config SECURITY_APPARMOR_HASH select CRYPTO select CRYPTO_SHA1 default y - help This option selects whether introspection of loaded policy is available to userspace via the apparmor filesystem. @@ -45,7 +44,6 @@ config SECURITY_APPARMOR_HASH_DEFAULT bool "Enable policy hash introspection by default" depends on SECURITY_APPARMOR_HASH default y - help This option selects whether sha1 hashing of loaded policy is enabled by default. The generation of sha1 hashes for @@ -54,3 +52,32 @@ config SECURITY_APPARMOR_HASH_DEFAULT however it can slow down policy load on some devices. In these cases policy hashing can be disabled by default and enabled only if needed. + +config SECURITY_APPARMOR_DEBUG + bool "Build AppArmor with debug code" + depends on SECURITY_APPARMOR + default n + help + Build apparmor with debugging logic in apparmor. Not all + debugging logic will necessarily be enabled. A submenu will + provide fine grained control of the debug options that are + available. + +config SECURITY_APPARMOR_DEBUG_ASSERTS + bool "Build AppArmor with debugging asserts" + depends on SECURITY_APPARMOR_DEBUG + default y + help + Enable code assertions made with AA_BUG. These are primarily + function entry preconditions but also exist at other key + points. If the assert is triggered it will trigger a WARN + message. + +config SECURITY_APPARMOR_DEBUG_MESSAGES + bool "Debug messages enabled by default" + depends on SECURITY_APPARMOR_DEBUG + default n + help + Set the default value of the apparmor.debug kernel parameter. + When enabled, various debug messages will be logged to + the kernel message buffer. diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 61dedd7..d507c73 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -35,12 +35,24 @@ * which is not related to profile accesses. */ +#define DEBUG_ON (aa_g_debug) +#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) #define AA_DEBUG(fmt, args...) \ do { \ - if (aa_g_debug) \ + if (DEBUG_ON) \ pr_debug_ratelimited("AppArmor: " fmt, ##args); \ } while (0) +#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) + +#define AA_BUG(X, args...) AA_BUG_FMT((X), "" args) +#ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS +#define AA_BUG_FMT(X, fmt, args...) \ + WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) +#else +#define AA_BUG_FMT(X, fmt, args...) +#endif + #define AA_ERROR(fmt, args...) \ pr_err_ratelimited("AppArmor: " fmt, ##args) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 1dae66b..99a6e5e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -681,7 +681,7 @@ module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); #endif /* Debug mode */ -bool aa_g_debug; +bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES); module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); /* Audit mode */ -- cgit v1.1 From efeee83a7060c225fac5ac794e9c11183c267f81 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:28 -0800 Subject: apparmor: rename mediated_filesystem() to path_mediated_fs() Rename to indicate the test is only about whether path mediation is used, not whether other types of mediation might be used. Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 2 +- security/apparmor/lsm.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index d507c73..4ff09ed 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -111,7 +111,7 @@ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, return aa_dfa_next(dfa, start, 0); } -static inline bool mediated_filesystem(struct dentry *dentry) +static inline bool path_mediated_fs(struct dentry *dentry) { return !(dentry->d_sb->s_flags & MS_NOUSER); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 99a6e5e..a757c16 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -195,7 +195,7 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask) struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, d_backing_inode(path->dentry)->i_mode }; - if (!mediated_filesystem(path->dentry)) + if (!path_mediated_fs(path->dentry)) return 0; return common_perm(op, path, mask, &cond); @@ -216,7 +216,7 @@ static int common_perm_rm(int op, const struct path *dir, struct inode *inode = d_backing_inode(dentry); struct path_cond cond = { }; - if (!inode || !mediated_filesystem(dentry)) + if (!inode || !path_mediated_fs(dentry)) return 0; cond.uid = inode->i_uid; @@ -240,7 +240,7 @@ static int common_perm_create(int op, const struct path *dir, { struct path_cond cond = { current_fsuid(), mode }; - if (!mediated_filesystem(dir->dentry)) + if (!path_mediated_fs(dir->dentry)) return 0; return common_perm_dir_dentry(op, dir, dentry, mask, &cond); @@ -287,7 +287,7 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_ struct aa_profile *profile; int error = 0; - if (!mediated_filesystem(old_dentry)) + if (!path_mediated_fs(old_dentry)) return 0; profile = aa_current_profile(); @@ -302,7 +302,7 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d struct aa_profile *profile; int error = 0; - if (!mediated_filesystem(old_dentry)) + if (!path_mediated_fs(old_dentry)) return 0; profile = aa_current_profile(); @@ -349,7 +349,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) struct aa_profile *profile; int error = 0; - if (!mediated_filesystem(file->f_path.dentry)) + if (!path_mediated_fs(file->f_path.dentry)) return 0; /* If in exec, permission is handled by bprm hooks. @@ -402,7 +402,7 @@ static int common_file_perm(int op, struct file *file, u32 mask) BUG_ON(!fprofile); if (!file->f_path.mnt || - !mediated_filesystem(file->f_path.dentry)) + !path_mediated_fs(file->f_path.dentry)) return 0; profile = __aa_current_profile(); -- cgit v1.1 From 6e474e3063eae9767f219d83cf91d8360f63be0c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:29 -0800 Subject: apparmor: rename hname_tail to basename Rename to the shorter and more familiar shell cmd name Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 4 ++-- security/apparmor/lib.c | 2 +- security/apparmor/policy.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 4ff09ed..b5c16d3 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -130,12 +130,12 @@ struct aa_policy { }; /** - * hname_tail - find the last component of an hname + * basename - find the last component of an hname * @name: hname to find the base profile name component of (NOT NULL) * * Returns: the tail (base profile name) name component of an hname */ -static inline const char *hname_tail(const char *hname) +static inline const char *basename(const char *hname) { char *split; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index fec78ee..0220388 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -184,7 +184,7 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, if (!policy->hname) return 0; /* base.name is a substring of fqname */ - policy->name = (char *)hname_tail(policy->hname); + policy->name = (char *)basename(policy->hname); INIT_LIST_HEAD(&policy->list); INIT_LIST_HEAD(&policy->profiles); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7fde578..3b23960 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -617,7 +617,7 @@ bool aa_may_manage_policy(int op) static struct aa_profile *__list_lookup_parent(struct list_head *lh, struct aa_profile *profile) { - const char *base = hname_tail(profile->base.hname); + const char *base = basename(profile->base.hname); long len = base - profile->base.hname; struct aa_load_ent *ent; -- cgit v1.1 From bbe4a7c8733c925b061dcce2d1af8926cefbe539 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:30 -0800 Subject: apparmor: constify policy name and hname Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/include/lib.h | 4 ++-- security/apparmor/lib.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d7cfd79..96a02ee 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -38,7 +38,7 @@ * * Returns: length of mangled name */ -static int mangle_name(char *name, char *target) +static int mangle_name(const char *name, char *target) { char *t = target; diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index b5c16d3..7e81cda 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -123,8 +123,8 @@ static inline bool path_mediated_fs(struct dentry *dentry) * @profiles: head of the profiles list contained in the object */ struct aa_policy { - char *name; - char *hname; + const char *name; + const char *hname; struct list_head list; struct list_head profiles; }; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 0220388..91d5766 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -178,7 +178,7 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, GFP_KERNEL); if (policy->hname) - sprintf(policy->hname, "%s//%s", prefix, name); + sprintf((char *)policy->hname, "%s//%s", prefix, name); } else policy->hname = kstrdup(name, GFP_KERNEL); if (!policy->hname) -- cgit v1.1 From d102d895713c736fd13e21feaab38b52d8ab32ad Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:31 -0800 Subject: apparmor: pass gfp param into aa_policy_init() Signed-off-by: John Johansen --- security/apparmor/include/lib.h | 2 +- security/apparmor/lib.c | 8 ++++---- security/apparmor/policy.c | 2 +- security/apparmor/policy_ns.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 7e81cda..fa281c2 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -194,7 +194,7 @@ static inline struct aa_policy *__policy_strn_find(struct list_head *head, } bool aa_policy_init(struct aa_policy *policy, const char *prefix, - const char *name); + const char *name, gfp_t gfp); void aa_policy_destroy(struct aa_policy *policy); #endif /* AA_LIB_H */ diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 91d5766..bcd598c 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -171,20 +171,20 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) * Returns: true if policy init successful */ bool aa_policy_init(struct aa_policy *policy, const char *prefix, - const char *name) + const char *name, gfp_t gfp) { /* freed by policy_free */ if (prefix) { policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, - GFP_KERNEL); + gfp); if (policy->hname) sprintf((char *)policy->hname, "%s//%s", prefix, name); } else - policy->hname = kstrdup(name, GFP_KERNEL); + policy->hname = kstrdup(name, gfp); if (!policy->hname) return 0; /* base.name is a substring of fqname */ - policy->name = (char *)basename(policy->hname); + policy->name = basename(policy->hname); INIT_LIST_HEAD(&policy->list); INIT_LIST_HEAD(&policy->profiles); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 3b23960..5d99fb7 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -272,7 +272,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) goto fail; kref_init(&profile->proxy->count); - if (!aa_policy_init(&profile->base, NULL, hname)) + if (!aa_policy_init(&profile->base, NULL, hname, GFP_KERNEL)) goto fail; kref_init(&profile->count); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index e7b7a82..8a5632f 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -95,7 +95,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) AA_DEBUG("%s(%p)\n", __func__, ns); if (!ns) return NULL; - if (!aa_policy_init(&ns->base, prefix, name)) + if (!aa_policy_init(&ns->base, prefix, name, GFP_KERNEL)) goto fail_ns; INIT_LIST_HEAD(&ns->sub_ns); -- cgit v1.1 From 5fd1b95fc9b96629d185f5fe3d9342fcff78eb30 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:32 -0800 Subject: apparmor: update policy_destroy to use new debug asserts Signed-off-by: John Johansen --- security/apparmor/lib.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index bcd598c..5d8ef31 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -197,15 +197,8 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, */ void aa_policy_destroy(struct aa_policy *policy) { - /* still contains profiles -- invalid */ - if (on_list_rcu(&policy->profiles)) { - AA_ERROR("%s: internal error, policy '%s' contains profiles\n", - __func__, policy->name); - } - if (on_list_rcu(&policy->list)) { - AA_ERROR("%s: internal error, policy '%s' still on list\n", - __func__, policy->name); - } + AA_BUG(on_list_rcu(&policy->profiles)); + AA_BUG(on_list_rcu(&policy->list)); /* don't free name as its a subset of hname */ kzfree(policy->hname); -- cgit v1.1 From 73688d1ed0b8f800f312f7bc9d583463858da861 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:34 -0800 Subject: apparmor: refactor prepare_ns() and make usable from different views prepare_ns() will need to be called from alternate views, and namespaces will need to be created via different interfaces. So refactor and allow specifying the view ns. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 6 ++- security/apparmor/include/policy.h | 3 +- security/apparmor/include/policy_ns.h | 4 +- security/apparmor/policy.c | 6 ++- security/apparmor/policy_ns.c | 98 +++++++++++++++++++++++------------ 5 files changed, 79 insertions(+), 38 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 96a02ee..2b48be2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -125,7 +125,8 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(data, size, PROF_ADD); + error = aa_replace_profiles(__aa_current_profile()->ns, data, + size, PROF_ADD); kvfree(data); } @@ -147,7 +148,8 @@ static ssize_t profile_replace(struct file *f, const char __user *buf, data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(data, size, PROF_REPLACE); + error = aa_replace_profiles(__aa_current_profile()->ns, data, + size, PROF_REPLACE); kvfree(data); } diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index da62d29..1573cad 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -184,7 +184,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); +ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, + bool noreplace); ssize_t aa_remove_profiles(char *name, size_t size); void __aa_profile_list_release(struct list_head *head); diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index e4c8765..820d86d2 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -83,7 +83,9 @@ void aa_free_ns_kref(struct kref *kref); struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); -struct aa_ns *aa_prepare_ns(const char *name); +struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir); +struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name); void __aa_remove_ns(struct aa_ns *ns); static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 5d99fb7..e02ab20 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -731,6 +731,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list + * @view: namespace load is viewed from * @udata: serialized data stream (NOT NULL) * @size: size of the serialized data stream * @noreplace: true if only doing addition, no replacement allowed @@ -741,7 +742,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * * Returns: size of data consumed else error code on failure. */ -ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) +ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, + bool noreplace) { const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; @@ -756,7 +758,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) goto out; /* released below */ - ns = aa_prepare_ns(ns_name); + ns = aa_prepare_ns(view, ns_name); if (!ns) { error = audit_policy(op, GFP_KERNEL, ns_name, "failed to prepare namespace", -ENOMEM); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 8a5632f..f6cdc73 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -181,48 +181,82 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) return aa_findn_ns(root, name, strlen(name)); } +static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir) +{ + struct aa_ns *ns; + int error; + + AA_BUG(!parent); + AA_BUG(!name); + AA_BUG(!mutex_is_locked(&parent->lock)); + + ns = alloc_ns(parent->base.hname, name); + if (!ns) + return NULL; + mutex_lock(&ns->lock); + error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name); + if (error) { + AA_ERROR("Failed to create interface for ns %s\n", + ns->base.name); + mutex_unlock(&ns->lock); + aa_free_ns(ns); + return ERR_PTR(error); + } + ns->parent = aa_get_ns(parent); + list_add_rcu(&ns->base.list, &parent->sub_ns); + /* add list ref */ + aa_get_ns(ns); + mutex_unlock(&ns->lock); + + return ns; +} + /** - * aa_prepare_ns - find an existing or create a new namespace of @name - * @name: the namespace to find or add (MAYBE NULL) + * aa_create_ns - create an ns, fail if it already exists + * @parent: the parent of the namespace being created + * @name: the name of the namespace + * @dir: if not null the dir to put the ns entries in * - * Returns: refcounted ns or NULL if failed to create one + * Returns: the a refcounted ns that has been add or an ERR_PTR */ -struct aa_ns *aa_prepare_ns(const char *name) +struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir) { - struct aa_ns *ns, *root; + struct aa_ns *ns; - root = aa_current_profile()->ns; + AA_BUG(!mutex_is_locked(&parent->lock)); - mutex_lock(&root->lock); + /* try and find the specified ns */ + /* released by caller */ + ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); + if (!ns) + ns = __aa_create_ns(parent, name, dir); + else + ns = ERR_PTR(-EEXIST); - /* if name isn't specified the profile is loaded to the current ns */ - if (!name) { - /* released by caller */ - ns = aa_get_ns(root); - goto out; - } + /* return ref */ + return ns; +} +/** + * aa_prepare_ns - find an existing or create a new namespace of @name + * @parent: ns to treat as parent + * @name: the namespace to find or add (NOT NULL) + * + * Returns: refcounted namespace or PTR_ERR if failed to create one + */ +struct aa_ns *aa_prepare_ns(struct aa_ns *parent, const char *name) +{ + struct aa_ns *ns; + + mutex_lock(&parent->lock); /* try and find the specified ns and if it doesn't exist create it */ /* released by caller */ - ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name)); - if (!ns) { - ns = alloc_ns(root->base.hname, name); - if (!ns) - goto out; - if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) { - AA_ERROR("Failed to create interface for ns %s\n", - ns->base.name); - aa_free_ns(ns); - ns = NULL; - goto out; - } - ns->parent = aa_get_ns(root); - list_add_rcu(&ns->base.list, &root->sub_ns); - /* add list ref */ - aa_get_ns(ns); - } -out: - mutex_unlock(&root->lock); + ns = aa_get_ns(__aa_find_ns(&parent->sub_ns, name)); + if (!ns) + ns = __aa_create_ns(parent, name, NULL); + mutex_unlock(&parent->lock); /* return ref */ return ns; -- cgit v1.1 From 30b026a8d16bfa15bc24f4cca1604e47ac1a2f64 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:35 -0800 Subject: apparmor: pass gfp_t parameter into profile allocation Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 11 ++++++----- security/apparmor/policy_ns.c | 2 +- security/apparmor/policy_unpack.c | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 1573cad..b44eaea 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -172,7 +172,7 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_proxy_kref(struct kref *kref); -struct aa_profile *aa_alloc_profile(const char *name); +struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp); struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index e02ab20..e310f3b 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -255,24 +255,25 @@ void aa_free_profile_kref(struct kref *kref) /** * aa_alloc_profile - allocate, initialize and return a new profile * @hname: name of the profile (NOT NULL) + * @gfp: allocation type * * Returns: refcount profile or NULL on failure */ -struct aa_profile *aa_alloc_profile(const char *hname) +struct aa_profile *aa_alloc_profile(const char *hname, gfp_t gfp) { struct aa_profile *profile; /* freed by free_profile - usually through aa_put_profile */ - profile = kzalloc(sizeof(*profile), GFP_KERNEL); + profile = kzalloc(sizeof(*profile), gfp); if (!profile) return NULL; - profile->proxy = kzalloc(sizeof(struct aa_proxy), GFP_KERNEL); + profile->proxy = kzalloc(sizeof(struct aa_proxy), gfp); if (!profile->proxy) goto fail; kref_init(&profile->proxy->count); - if (!aa_policy_init(&profile->base, NULL, hname, GFP_KERNEL)) + if (!aa_policy_init(&profile->base, NULL, hname, gfp)) goto fail; kref_init(&profile->count); @@ -312,7 +313,7 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) goto fail; sprintf(name, "%s//null-%x", parent->base.hname, uniq); - profile = aa_alloc_profile(name); + profile = aa_alloc_profile(name, GFP_KERNEL); kfree(name); if (!profile) goto fail; diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index f6cdc73..1e19bd3 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -102,7 +102,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) mutex_init(&ns->lock); /* released by aa_free_ns() */ - ns->unconfined = aa_alloc_profile("unconfined"); + ns->unconfined = aa_alloc_profile("unconfined", GFP_KERNEL); if (!ns->unconfined) goto fail_unconfined; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 1381206..9ddc6b2 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -486,7 +486,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_str(e, &name, NULL)) goto fail; - profile = aa_alloc_profile(name); + profile = aa_alloc_profile(name, GFP_KERNEL); if (!profile) return ERR_PTR(-ENOMEM); -- cgit v1.1 From 181f7c977680dcd86eb71ad4b37239d2a385c3ad Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:36 -0800 Subject: apparmor: name null-XXX profiles after the executable When possible its better to name a learning profile after the missing profile in question. This allows for both more informative names and for profile reuse. Signed-off-by: John Johansen --- security/apparmor/domain.c | 8 +++--- security/apparmor/include/policy.h | 3 ++- security/apparmor/policy.c | 53 ++++++++++++++++++++++++++++---------- 3 files changed, 47 insertions(+), 17 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 503cb2c..1a8ffc5 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -442,7 +442,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) } } else if (COMPLAIN_MODE(profile)) { /* no exec permission - are we in learning mode */ - new_profile = aa_new_null_profile(profile, 0); + new_profile = aa_new_null_profile(profile, false, name, + GFP_ATOMIC); if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; @@ -667,7 +668,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) aa_put_profile(root); target = name; /* released below */ - hat = aa_new_null_profile(profile, 1); + hat = aa_new_null_profile(profile, true, hats[0], + GFP_KERNEL); if (!hat) { info = "failed null profile create"; error = -ENOMEM; @@ -815,7 +817,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, if (permtest || !COMPLAIN_MODE(profile)) goto audit; /* released below */ - target = aa_new_null_profile(profile, 0); + target = aa_new_null_profile(profile, false, hname, GFP_KERNEL); if (!target) { info = "failed null profile create"; error = -ENOMEM; diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b44eaea..3527e3f 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -173,7 +173,8 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_proxy_kref(struct kref *kref); struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp); -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp); void aa_free_profile(struct aa_profile *profile); void aa_free_profile_kref(struct kref *kref); struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index e310f3b..dd63ac9 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -288,12 +288,16 @@ fail: } /** - * aa_new_null_profile - create a new null-X learning profile + * aa_new_null_profile - create or find a null-X learning profile * @parent: profile that caused this profile to be created (NOT NULL) * @hat: true if the null- learning profile is a hat + * @base: name to base the null profile off of + * @gfp: type of allocation * - * Create a null- complain mode profile used in learning mode. The name of - * the profile is unique and follows the format of parent//null-. + * Find/Create a null- complain mode profile used in learning mode. The + * name of the profile is unique and follows the format of parent//null-XXX. + * where XXX is based on the @name or if that fails or is not supplied + * a unique number * * null profiles are added to the profile list but the list does not * hold a count on them so that they are automatically released when @@ -301,27 +305,45 @@ fail: * * Returns: new refcounted profile else NULL on failure */ -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp) { - struct aa_profile *profile = NULL; + struct aa_profile *profile; char *name; - int uniq = atomic_inc_return(&parent->ns->uniq_null); - /* freed below */ - name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); + AA_BUG(!parent); + + if (base) { + name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), + gfp); + if (name) { + sprintf(name, "%s//null-%s", parent->base.hname, base); + goto name; + } + /* fall through to try shorter uniq */ + } + + name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); if (!name) - goto fail; - sprintf(name, "%s//null-%x", parent->base.hname, uniq); + return NULL; + sprintf(name, "%s//null-%x", parent->base.hname, + atomic_inc_return(&parent->ns->uniq_null)); - profile = aa_alloc_profile(name, GFP_KERNEL); - kfree(name); +name: + /* lookup to see if this is a dup creation */ + profile = aa_find_child(parent, basename(name)); + if (profile) + goto out; + + profile = aa_alloc_profile(name, gfp); if (!profile) goto fail; profile->mode = APPARMOR_COMPLAIN; - profile->flags = PFLAG_NULL; + profile->flags |= PFLAG_NULL; if (hat) profile->flags |= PFLAG_HAT; + profile->path_flags = parent->path_flags; /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); @@ -332,9 +354,14 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) mutex_unlock(&profile->ns->lock); /* refcount released by caller */ +out: + kfree(name); + return profile; fail: + kfree(name); + aa_free_profile(profile); return NULL; } -- cgit v1.1 From abbf8734039fe57c72c999e37bd1c30d8aed1943 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:37 -0800 Subject: apparmor: remove paranoid load switch Policy should always under go a full paranoid verification. Signed-off-by: John Johansen --- security/apparmor/lsm.c | 5 +++-- security/apparmor/policy_unpack.c | 21 +++++++-------------- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index a757c16..e40eecb 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -714,10 +714,11 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); /* Determines how paranoid loading of policy is and how much verification * on the loaded policy is done. + * DEPRECATED: read only as strict checking of load is always done now + * that none root users (user namespaces) can load policy. */ bool aa_g_paranoid_load = 1; -module_param_named(paranoid_load, aa_g_paranoid_load, aabool, - S_IRUSR | S_IWUSR); +module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); /* Boot time disable flag */ static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 9ddc6b2..fe73117 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -340,12 +340,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) ((e->pos - e->start) & 7); size_t pad = ALIGN(sz, 8) - sz; int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | - TO_ACCEPT2_FLAG(YYTD_DATA32); - - - if (aa_g_paranoid_load) - flags |= DFA_FLAG_VERIFY_STATES; - + TO_ACCEPT2_FLAG(YYTD_DATA32) | DFA_FLAG_VERIFY_STATES; dfa = aa_dfa_unpack(blob + pad, size - pad, flags); if (IS_ERR(dfa)) @@ -705,14 +700,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) */ static int verify_profile(struct aa_profile *profile) { - if (aa_g_paranoid_load) { - if (profile->file.dfa && - !verify_dfa_xindex(profile->file.dfa, - profile->file.trans.size)) { - audit_iface(profile, NULL, "Invalid named transition", - NULL, -EPROTO); - return -EPROTO; - } + if (profile->file.dfa && + !verify_dfa_xindex(profile->file.dfa, + profile->file.trans.size)) { + audit_iface(profile, NULL, "Invalid named transition", + NULL, -EPROTO); + return -EPROTO; } return 0; -- cgit v1.1 From 5ebfb12822656beec5c56b362d44e4db81c8e1eb Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:38 -0800 Subject: apparmor: add support for force complain flag to support learning mode Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'security/apparmor') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index fe73117..c836a9c 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -29,6 +29,8 @@ #include "include/policy.h" #include "include/policy_unpack.h" +#define FORCE_COMPLAIN_FLAG 0x800 + /* * The AppArmor interface treats data as a type byte followed by the * actual data. The interface has the notion of a a named entry @@ -514,7 +516,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) profile->flags |= PFLAG_HAT; if (!unpack_u32(e, &tmp, NULL)) goto fail; - if (tmp == PACKED_MODE_COMPLAIN) + if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) profile->mode = APPARMOR_COMPLAIN; else if (tmp == PACKED_MODE_KILL) profile->mode = APPARMOR_KILL; -- cgit v1.1 From 474d6b75106229025ab6b7bbabf2f9c246e928e1 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:39 -0800 Subject: apparmor: prepare to support newer versions of policy Newer policy encodes more than just version in the version tag, so add masking to make sure the comparison remains correct. Note: this is fully compatible with older policy as it will never set the bits being masked out. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 10 ++++++++-- security/apparmor/policy_unpack.c | 25 +++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2b48be2..49a5122 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -799,9 +799,15 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { { } }; +static struct aa_fs_entry aa_fs_entry_versions[] = { + AA_FS_FILE_BOOLEAN("v5", 1), + { } +}; + static struct aa_fs_entry aa_fs_entry_policy[] = { - AA_FS_FILE_BOOLEAN("set_load", 1), - {} + AA_FS_DIR("versions", aa_fs_entry_versions), + AA_FS_FILE_BOOLEAN("set_load", 1), + { } }; static struct aa_fs_entry aa_fs_entry_features[] = { diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index c836a9c..6ac292f 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -29,7 +29,14 @@ #include "include/policy.h" #include "include/policy_unpack.h" +#define K_ABI_MASK 0x3ff #define FORCE_COMPLAIN_FLAG 0x800 +#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK)) +#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK)) + +#define v5 5 /* base version */ +#define v6 6 /* per entry policydb mediation check */ +#define v7 7 /* full network masking */ /* * The AppArmor interface treats data as a type byte followed by the @@ -646,19 +653,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { if (required) { - audit_iface(NULL, NULL, "invalid profile format", e, - error); - return error; - } - - /* check that the interface version is currently supported */ - if (e->version != 5) { - audit_iface(NULL, NULL, "unsupported interface version", + audit_iface(NULL, NULL, "invalid profile format", e, error); return error; } } + /* Check that the interface version is currently supported. + * if not specified use previous version + * Mask off everything that is not kernel abi version + */ + if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { + audit_iface(NULL, NULL, "unsupported interface version", + e, error); + return error; + } /* read the namespace if present */ if (unpack_str(e, &name, "namespace")) { -- cgit v1.1 From 293a4886f93f1d4f01ef2642b81c2509a5376ce5 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:40 -0800 Subject: apparmor: add get_dfa() fn The dfa is currently setup to be shared (has the basis of refcounting) but currently can't be because the count can't be increased. Signed-off-by: John Johansen --- security/apparmor/include/match.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'security/apparmor') diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index a1c04fe..d751c8b 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -128,6 +128,21 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, void aa_dfa_free_kref(struct kref *kref); /** + * aa_get_dfa - increment refcount on dfa @p + * @dfa: dfa (MAYBE NULL) + * + * Returns: pointer to @dfa if @dfa is NULL will return NULL + * Requires: @dfa must be held with valid refcount when called + */ +static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa) +{ + if (dfa) + kref_get(&(dfa->count)); + + return dfa; +} + +/** * aa_put_dfa - put a dfa refcount * @dfa: dfa to put refcount (MAYBE NULL) * -- cgit v1.1 From 6604d4c1c1a65d3d1a6a56291d96516d1e9b7041 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:41 -0800 Subject: apparmor: allow policydb to be used as the file dfa Newer policy will combine the file and policydb dfas, allowing for better optimizations. However to support older policy we need to keep the ability to address the "file" dfa separately. So dup the policydb as if it is the file dfa and set the appropriate start state. Signed-off-by: John Johansen --- security/apparmor/policy_unpack.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 6ac292f..7160add 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -611,12 +611,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->file.dfa); profile->file.dfa = NULL; goto fail; + } else if (profile->file.dfa) { + if (!unpack_u32(e, &profile->file.start, "dfa_start")) + /* default start state */ + profile->file.start = DFA_START; + } else if (profile->policy.dfa && + profile->policy.start[AA_CLASS_FILE]) { + profile->file.dfa = aa_get_dfa(profile->policy.dfa); + profile->file.start = profile->policy.start[AA_CLASS_FILE]; } - if (!unpack_u32(e, &profile->file.start, "dfa_start")) - /* default start state */ - profile->file.start = DFA_START; - if (!unpack_trans_table(e, profile)) goto fail; -- cgit v1.1 From 11c236b89d7c26d58c55d5613a858600a4d2ab3a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:42 -0800 Subject: apparmor: add a default null dfa Instead of testing whether a given dfa exists in every code path, have a default null dfa that is used when loaded policy doesn't provide a dfa. This will let us get rid of special casing and avoid dereference bugs when special casing is missed. Signed-off-by: John Johansen --- security/apparmor/include/match.h | 5 +++++ security/apparmor/lsm.c | 7 +++++++ security/apparmor/match.c | 27 +++++++++++++++++++++++++++ security/apparmor/nulldfa.in | 1 + security/apparmor/policy.c | 2 ++ security/apparmor/policy_unpack.c | 6 ++++-- 6 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 security/apparmor/nulldfa.in (limited to 'security/apparmor') diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index d751c8b..a85bb3b 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -100,6 +100,8 @@ struct aa_dfa { struct table_header *tables[YYTD_ID_TSIZE]; }; +extern struct aa_dfa *nulldfa; + #define byte_to_byte(X) (X) #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ @@ -117,6 +119,9 @@ static inline size_t table_size(size_t len, size_t el_size) return ALIGN(sizeof(struct table_header) + len * el_size, 8); } +int aa_setup_dfa_engine(void); +void aa_teardown_dfa_engine(void); + struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, const char *str, int len); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e40eecb..f852cd6 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -878,6 +878,12 @@ static int __init apparmor_init(void) return 0; } + error = aa_setup_dfa_engine(); + if (error) { + AA_ERROR("Unable to setup dfa engine\n"); + goto alloc_out; + } + error = aa_alloc_root_ns(); if (error) { AA_ERROR("Unable to allocate default profile namespace\n"); @@ -905,6 +911,7 @@ static int __init apparmor_init(void) alloc_out: aa_destroy_aafs(); + aa_teardown_dfa_engine(); apparmor_enabled = 0; return error; diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 0e04bcf..8f0806b 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -25,6 +25,33 @@ #define base_idx(X) ((X) & 0xffffff) +static char nulldfa_src[] = { + #include "nulldfa.in" +}; +struct aa_dfa *nulldfa; + +int aa_setup_dfa_engine(void) +{ + int error; + + nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src), + TO_ACCEPT1_FLAG(YYTD_DATA32) | + TO_ACCEPT2_FLAG(YYTD_DATA32)); + if (!IS_ERR(nulldfa)) + return 0; + + error = PTR_ERR(nulldfa); + nulldfa = NULL; + + return error; +} + +void aa_teardown_dfa_engine(void) +{ + aa_put_dfa(nulldfa); + nulldfa = NULL; +} + /** * unpack_table - unpack a dfa table (one of accept, default, base, next check) * @blob: data to unpack (NOT NULL) diff --git a/security/apparmor/nulldfa.in b/security/apparmor/nulldfa.in new file mode 100644 index 0000000..3cb3802 --- /dev/null +++ b/security/apparmor/nulldfa.in @@ -0,0 +1 @@ +0x1B, 0x5E, 0x78, 0x3D, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 0x6E, 0x6F, 0x74, 0x66, 0x6C, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index dd63ac9..046edec 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -348,6 +348,8 @@ name: /* released on free_profile */ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); profile->ns = aa_get_ns(parent->ns); + profile->file.dfa = aa_get_dfa(nulldfa); + profile->policy.dfa = aa_get_dfa(nulldfa); mutex_lock(&profile->ns->lock); __list_add_profile(&parent->base.profiles, profile); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 7160add..51a7f9f 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -603,7 +603,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) } if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; - } + } else + profile->policy.dfa = aa_get_dfa(nulldfa); /* get file rules */ profile->file.dfa = unpack_dfa(e); @@ -619,7 +620,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) profile->policy.start[AA_CLASS_FILE]) { profile->file.dfa = aa_get_dfa(profile->policy.dfa); profile->file.start = profile->policy.start[AA_CLASS_FILE]; - } + } else + profile->file.dfa = aa_get_dfa(nulldfa); if (!unpack_trans_table(e, profile)) goto fail; -- cgit v1.1 From 34c426acb75cc21bdf84685e106db0c1a3565057 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:43 -0800 Subject: apparmor: provide userspace flag indicating binfmt_elf_mmap change Commit 9f834ec18def ("binfmt_elf: switch to new creds when switching to new mm") changed when the creds are installed by the binfmt_elf handler. This affects which creds are used to mmap the executable into the address space. Which can have an affect on apparmor policy. Add a flag to apparmor at /sys/kernel/security/apparmor/features/domain/fix_binfmt_elf_mmap to make it possible to detect this semantic change so that the userspace tools and the regression test suite can correctly deal with the change. BugLink: http://bugs.launchpad.net/bugs/1630069 Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 49a5122..5c000cb 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -796,6 +796,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { AA_FS_FILE_BOOLEAN("change_hatv", 1), AA_FS_FILE_BOOLEAN("change_onexec", 1), AA_FS_FILE_BOOLEAN("change_profile", 1), + AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), { } }; -- cgit v1.1 From a71ada305801e940ff69c2c58489778760e5148b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:45 -0800 Subject: apparmor: add special .null file used to "close" fds at exec Borrow the special null device file from selinux to "close" fds that don't have sufficient permissions at exec time. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 78 +++++++++++++++++++++++++++++++++- security/apparmor/include/apparmorfs.h | 2 + security/apparmor/include/policy_ns.h | 2 + 3 files changed, 81 insertions(+), 1 deletion(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5c000cb..2501a65 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -18,9 +18,12 @@ #include #include #include +#include #include #include #include +#include +#include #include "include/apparmor.h" #include "include/apparmorfs.h" @@ -352,6 +355,28 @@ static const struct file_operations aa_fs_seq_hash_fops = { .release = single_release, }; +static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v) +{ + struct aa_ns *ns = aa_current_profile()->ns; + + seq_printf(seq, "%d\n", ns->level); + + return 0; +} + +static int aa_fs_seq_open_ns_level(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_show_ns_level, inode->i_private); +} + +static const struct file_operations aa_fs_ns_level = { + .owner = THIS_MODULE, + .open = aa_fs_seq_open_ns_level, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -825,6 +850,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), + AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } @@ -934,6 +960,52 @@ void __init aa_destroy_aafs(void) aafs_remove_dir(&aa_fs_entry); } + +#define NULL_FILE_NAME ".null" +struct path aa_null; + +static int aa_mk_null_file(struct dentry *parent) +{ + struct vfsmount *mount = NULL; + struct dentry *dentry; + struct inode *inode; + int count = 0; + int error = simple_pin_fs(parent->d_sb->s_type, &mount, &count); + + if (error) + return error; + + inode_lock(d_inode(parent)); + dentry = lookup_one_len(NULL_FILE_NAME, parent, strlen(NULL_FILE_NAME)); + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); + goto out; + } + inode = new_inode(parent->d_inode->i_sb); + if (!inode) { + error = -ENOMEM; + goto out1; + } + + inode->i_ino = get_next_ino(); + inode->i_mode = S_IFCHR | S_IRUGO | S_IWUGO; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, + MKDEV(MEM_MAJOR, 3)); + d_instantiate(dentry, inode); + aa_null.dentry = dget(dentry); + aa_null.mnt = mntget(mount); + + error = 0; + +out1: + dput(dentry); +out: + inode_unlock(d_inode(parent)); + simple_release_fs(&mount, &count); + return error; +} + /** * aa_create_aafs - create the apparmor security filesystem * @@ -962,7 +1034,11 @@ static int __init aa_create_aafs(void) if (error) goto error; - /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ + error = aa_mk_null_file(aa_fs_entry.dentry); + if (error) + goto error; + + /* TODO: add default profile to apparmorfs */ /* Report that AppArmor fs is enabled */ aa_info_message("AppArmor Filesystem Enabled"); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 5626bd4..eeeae5b 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -15,6 +15,8 @@ #ifndef __AA_APPARMORFS_H #define __AA_APPARMORFS_H +extern struct path aa_null; + enum aa_fs_type { AA_FS_TYPE_BOOLEAN, AA_FS_TYPE_STRING, diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 820d86d2..89cffdd 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -44,6 +44,7 @@ struct aa_ns_acct { * @sub_ns: list of namespaces under the current namespace. * @uniq_null: uniq value used for null learning profiles * @uniq_id: a unique id count for the profiles in the namespace + * @level: level of ns within the tree hierarchy * @dents: dentries for the namespaces file entries in apparmorfs * * An aa_ns defines the set profiles that are searched to determine which @@ -66,6 +67,7 @@ struct aa_ns { struct list_head sub_ns; atomic_t uniq_null; long uniq_id; + int level; struct dentry *dents[AAFS_NS_SIZEOF]; }; -- cgit v1.1 From ee2351e4b07cb7e3609f8661effe0382fb23646b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:46 -0800 Subject: apparmor: track ns level so it can be used to help in view checks Signed-off-by: John Johansen --- security/apparmor/policy_ns.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security/apparmor') diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 1e19bd3..93d1826 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -204,6 +204,7 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, return ERR_PTR(error); } ns->parent = aa_get_ns(parent); + ns->level = parent->level + 1; list_add_rcu(&ns->base.list, &parent->sub_ns); /* add list ref */ aa_get_ns(ns); -- cgit v1.1 From b79473f2de3eb3320e2a145da8a2ea03c7331784 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:47 -0800 Subject: apparmor: Make aa_remove_profile() callable from a different view This is prep work for fs operations being able to remove namespaces. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 3 ++- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2501a65..14b96a4 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -180,7 +180,8 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, error = PTR_ERR(data); if (!IS_ERR(data)) { data[size] = 0; - error = aa_remove_profiles(data, size); + error = aa_remove_profiles(__aa_current_profile()->ns, data, + size); kvfree(data); } diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 3527e3f..8fcfb3c 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -187,7 +187,7 @@ struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, bool noreplace); -ssize_t aa_remove_profiles(char *name, size_t size); +ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 046edec..0314fae 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -944,6 +944,7 @@ free: /** * aa_remove_profiles - remove profile(s) from the system + * @view: namespace the remove is being done from * @fqname: name of the profile or namespace to remove (NOT NULL) * @size: size of the name * @@ -954,9 +955,9 @@ free: * * Returns: size of data consume else error code if fails */ -ssize_t aa_remove_profiles(char *fqname, size_t size) +ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) { - struct aa_ns *root, *ns = NULL; + struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; ssize_t error = 0; @@ -967,7 +968,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) goto fail; } - root = aa_current_profile()->ns; + root = view; if (fqname[0] == ':') { char *ns_name; -- cgit v1.1 From 3e3e569539864d5812ecb03792dc183ebbf81476 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:48 -0800 Subject: apparmor: allow introspecting the policy namespace name Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 14b96a4..9fd7f73 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -356,6 +356,7 @@ static const struct file_operations aa_fs_seq_hash_fops = { .release = single_release, }; + static int aa_fs_seq_show_ns_level(struct seq_file *seq, void *v) { struct aa_ns *ns = aa_current_profile()->ns; @@ -378,6 +379,28 @@ static const struct file_operations aa_fs_ns_level = { .release = single_release, }; +static int aa_fs_seq_show_ns_name(struct seq_file *seq, void *v) +{ + struct aa_ns *ns = aa_current_profile()->ns; + + seq_printf(seq, "%s\n", ns->base.name); + + return 0; +} + +static int aa_fs_seq_open_ns_name(struct inode *inode, struct file *file) +{ + return single_open(file, aa_fs_seq_show_ns_name, inode->i_private); +} + +static const struct file_operations aa_fs_ns_name = { + .owner = THIS_MODULE, + .open = aa_fs_seq_open_ns_name, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -852,6 +875,7 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = { AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), + AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name), AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } -- cgit v1.1 From a6f233003b1af70132619bca386dfae1862a45e8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:49 -0800 Subject: apparmor: allow specifying the profile doing the management Signed-off-by: John Johansen --- security/apparmor/policy.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0314fae..0125de6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -582,6 +582,7 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, /** * aa_audit_policy - Do auditing of policy changes + * @profile: profile to check if it can manage policy * @op: policy operation being performed * @gfp: memory allocation flags * @name: name of profile being manipulated (NOT NULL) @@ -590,8 +591,8 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, * * Returns: the error to be returned after audit is done */ -static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, - int error) +static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, + const char *name, const char *info, int error) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; @@ -602,7 +603,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, aad.info = info; aad.error = error; - return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, + return aa_audit(AUDIT_APPARMOR_STATUS, profile, gfp, &sa, NULL); } @@ -632,12 +633,14 @@ bool aa_may_manage_policy(int op) { /* check if loading policy is locked out */ if (aa_g_lock_policy) { - audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + "policy_locked", -EACCES); return 0; } if (!policy_admin_capable()) { - audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + "not policy admin", -EACCES); return 0; } @@ -762,6 +765,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list * @view: namespace load is viewed from + * @profile: profile that is attempting to load/replace policy * @udata: serialized data stream (NOT NULL) * @size: size of the serialized data stream * @noreplace: true if only doing addition, no replacement allowed @@ -790,7 +794,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, /* released below */ ns = aa_prepare_ns(view, ns_name); if (!ns) { - error = audit_policy(op, GFP_KERNEL, ns_name, + error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, + ns_name, "failed to prepare namespace", -ENOMEM); goto free; } @@ -867,7 +872,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); + audit_policy(__aa_current_profile(), op, GFP_ATOMIC, + ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); @@ -921,7 +927,8 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, + ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; list_for_each_entry(tmp, &lh, list) { @@ -931,7 +938,8 @@ fail_lock: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + audit_policy(__aa_current_profile(), op, GFP_KERNEL, + tmp->new->base.hname, info, error); } free: list_for_each_entry_safe(ent, tmp, &lh, list) { @@ -1004,7 +1012,8 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) } /* don't fail removal if audit fails */ - (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); + (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, + name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1014,6 +1023,7 @@ fail_ns_lock: aa_put_ns(ns); fail: - (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); + (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, + name, info, error); return error; } -- cgit v1.1 From 2bd8dbbf22fe9eb2a99273436f815d49ceb23a8f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:50 -0800 Subject: apparmor: add ns being viewed as a param to policy_view_capable() Prepare for a tighter pairing of user namespaces and apparmor policy namespaces, by making the ns to be viewed available and checking that the user namespace level is the same as the policy ns level. This strict pairing will be relaxed once true support of user namespaces lands. Signed-off-by: John Johansen --- security/apparmor/include/context.h | 6 ++++++ security/apparmor/include/policy.h | 4 +++- security/apparmor/lsm.c | 8 ++++---- security/apparmor/policy.c | 25 ++++++++++++++++++++++--- 4 files changed, 35 insertions(+), 8 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index a0acc23..d378bff 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -20,6 +20,7 @@ #include #include "policy.h" +#include "policy_ns.h" #define cred_cxt(X) (X)->security #define current_cxt() cred_cxt(current_cred()) @@ -162,6 +163,11 @@ static inline struct aa_profile *aa_current_profile(void) return cxt->profile; } +static inline struct aa_ns *aa_get_current_ns(void) +{ + return aa_get_ns(__aa_current_profile()->ns); +} + /** * aa_clear_task_cxt_trans - clear transition tracking info from the cxt * @cxt: task context to clear (NOT NULL) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 8fcfb3c..b0b65c5 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -33,6 +33,8 @@ struct aa_ns; +extern int unprivileged_userns_apparmor_policy; + extern const char *const aa_profile_mode_names[]; #define APPARMOR_MODE_NAMES_MAX_INDEX 4 @@ -297,7 +299,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } -bool policy_view_capable(void); +bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(void); bool aa_may_manage_policy(int op); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f852cd6..f83ba33 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -745,7 +745,7 @@ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; return param_get_bool(buffer, kp); } @@ -759,7 +759,7 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp) static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; return param_get_bool(buffer, kp); } @@ -773,14 +773,14 @@ static int param_set_aauint(const char *val, const struct kernel_param *kp) static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!policy_view_capable()) + if (!policy_view_capable(NULL)) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 0125de6..f092c04 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -76,6 +76,7 @@ #include #include #include +#include #include "include/apparmor.h" #include "include/capability.h" @@ -89,6 +90,7 @@ #include "include/policy_unpack.h" #include "include/resource.h" +int unprivileged_userns_apparmor_policy = 1; const char *const aa_profile_mode_names[] = { "enforce", @@ -607,20 +609,37 @@ static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, &sa, NULL); } -bool policy_view_capable(void) +/** + * policy_view_capable - check if viewing policy in at @ns is allowed + * ns: namespace being viewed by current task (may be NULL) + * Returns: true if viewing policy is allowed + * + * If @ns is NULL then the namespace being viewed is assumed to be the + * tasks current namespace. + */ +bool policy_view_capable(struct aa_ns *ns) { struct user_namespace *user_ns = current_user_ns(); + struct aa_ns *view_ns = aa_get_current_ns(); + bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) || + in_egroup_p(make_kgid(user_ns, 0)); bool response = false; + if (!ns) + ns = view_ns; - if (ns_capable(user_ns, CAP_MAC_ADMIN)) + if (root_in_user_ns && aa_ns_visible(view_ns, ns, true) && + (user_ns == &init_user_ns || + (unprivileged_userns_apparmor_policy != 0 && + user_ns->level == view_ns->level))) response = true; + aa_put_ns(view_ns); return response; } bool policy_admin_capable(void) { - return policy_view_capable() && !aa_g_lock_policy; + return policy_view_capable(NULL) && !aa_g_lock_policy; } /** -- cgit v1.1 From fd2a80438d736012129977bec779db093979057e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:51 -0800 Subject: apparmor: add ns being viewed as a param to policy_admin_capable() Prepare for a tighter pairing of user namespaces and apparmor policy namespaces, by making the ns to be viewed available. Signed-off-by: John Johansen --- security/apparmor/include/policy.h | 2 +- security/apparmor/lsm.c | 12 ++++++------ security/apparmor/policy.c | 12 +++++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b0b65c5..27f9171 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -300,7 +300,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) } bool policy_view_capable(struct aa_ns *ns); -bool policy_admin_capable(void); +bool policy_admin_capable(struct aa_ns *ns); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f83ba33..09f1c40 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -738,7 +738,7 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } @@ -752,7 +752,7 @@ static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } @@ -766,7 +766,7 @@ static int param_get_aabool(char *buffer, const struct kernel_param *kp) static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; return param_set_uint(val, kp); } @@ -792,7 +792,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; if (!apparmor_enabled) @@ -813,7 +813,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!policy_admin_capable()) + if (!policy_view_capable(NULL)) return -EPERM; if (!apparmor_enabled) @@ -825,7 +825,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!policy_admin_capable()) + if (!policy_admin_capable(NULL)) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f092c04..ef64c25 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -637,9 +637,15 @@ bool policy_view_capable(struct aa_ns *ns) return response; } -bool policy_admin_capable(void) +bool policy_admin_capable(struct aa_ns *ns) { - return policy_view_capable(NULL) && !aa_g_lock_policy; + struct user_namespace *user_ns = current_user_ns(); + bool capable = ns_capable(user_ns, CAP_MAC_ADMIN); + + AA_DEBUG("cap_mac_admin? %d\n", capable); + AA_DEBUG("policy locked? %d\n", aa_g_lock_policy); + + return policy_view_capable(ns) && capable && !aa_g_lock_policy; } /** @@ -657,7 +663,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!policy_admin_capable()) { + if (!policy_admin_capable(NULL)) { audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; -- cgit v1.1 From 078c73c63fb2878689da334f112507639c72c14f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:52 -0800 Subject: apparmor: add profile and ns params to aa_may_manage_policy() Policy management will be expanded beyond traditional unconfined root. This will require knowning the profile of the task doing the management and the ns view. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 2 +- security/apparmor/include/policy.h | 2 +- security/apparmor/policy.c | 22 ++++++++++------------ 3 files changed, 12 insertions(+), 14 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 9fd7f73..cc6ee1e 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -100,7 +100,7 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, * Don't allow profile load/replace/remove from profiles that don't * have CAP_MAC_ADMIN */ - if (!aa_may_manage_policy(op)) + if (!aa_may_manage_policy(__aa_current_profile(), NULL, op)) return ERR_PTR(-EACCES); /* freed by caller to simple_write_to_buffer */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 27f9171..95641e2 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -301,6 +301,6 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(struct aa_ns *ns); -bool aa_may_manage_policy(int op); +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ef64c25..27d93aa 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -650,26 +650,24 @@ bool policy_admin_capable(struct aa_ns *ns) /** * aa_may_manage_policy - can the current task manage policy + * @profile: profile to check if it can manage policy * @op: the policy manipulation operation being done * - * Returns: true if the task is allowed to manipulate policy + * Returns: 0 if the task is allowed to manipulate policy else error */ -bool aa_may_manage_policy(int op) +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op) { /* check if loading policy is locked out */ - if (aa_g_lock_policy) { - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + if (aa_g_lock_policy) + return audit_policy(profile, op, GFP_KERNEL, NULL, "policy_locked", -EACCES); - return 0; - } - if (!policy_admin_capable(NULL)) { - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, - "not policy admin", -EACCES); - return 0; - } + if (!policy_admin_capable(ns)) + return audit_policy(profile, op, GFP_KERNEL, NULL, + "not policy admin", -EACCES); - return 1; + /* TODO: add fine grained mediation of policy loads */ + return 0; } static struct aa_profile *__list_lookup_parent(struct list_head *lh, -- cgit v1.1 From fc1c9fd10a53a17abb3348adb2ec5d29813a0397 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:54 -0800 Subject: apparmor: add ns name to the audit data for policy loads Signed-off-by: John Johansen --- security/apparmor/include/audit.h | 1 + security/apparmor/policy.c | 34 ++++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index ba3dfd1..dbfb4a6 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -113,6 +113,7 @@ struct apparmor_audit_data { void *target; struct { long pos; + const char *ns; void *target; } iface; struct { diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 27d93aa..3c5c0b2 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -582,11 +582,23 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, return 0; } +/* audit callback for net specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (sa->aad->iface.ns) { + audit_log_format(ab, " ns="); + audit_log_untrustedstring(ab, sa->aad->iface.ns); + } +} + /** * aa_audit_policy - Do auditing of policy changes * @profile: profile to check if it can manage policy * @op: policy operation being performed * @gfp: memory allocation flags + * @nsname: name of the ns being manipulated (MAY BE NULL) * @name: name of profile being manipulated (NOT NULL) * @info: any extra information to be audited (MAYBE NULL) * @error: error code @@ -594,19 +606,21 @@ static int replacement_allowed(struct aa_profile *profile, int noreplace, * Returns: the error to be returned after audit is done */ static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, - const char *name, const char *info, int error) + const char *nsname, const char *name, + const char *info, int error) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = op; + aad.iface.ns = nsname; aad.name = name; aad.info = info; aad.error = error; return aa_audit(AUDIT_APPARMOR_STATUS, profile, gfp, - &sa, NULL); + &sa, audit_cb); } /** @@ -659,11 +673,11 @@ int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op) { /* check if loading policy is locked out */ if (aa_g_lock_policy) - return audit_policy(profile, op, GFP_KERNEL, NULL, + return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, "policy_locked", -EACCES); if (!policy_admin_capable(ns)) - return audit_policy(profile, op, GFP_KERNEL, NULL, + return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, "not policy admin", -EACCES); /* TODO: add fine grained mediation of policy loads */ @@ -818,7 +832,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, ns = aa_prepare_ns(view, ns_name); if (!ns) { error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, - ns_name, + NULL, ns_name, "failed to prepare namespace", -ENOMEM); goto free; } @@ -895,7 +909,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_ATOMIC, + audit_policy(__aa_current_profile(), op, GFP_ATOMIC, NULL, ent->new->base.hname, NULL, error); if (ent->old) { @@ -950,7 +964,7 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -961,7 +975,7 @@ fail_lock: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, + audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, tmp->new->base.hname, info, error); } free: @@ -1036,7 +1050,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - name, info, error); + NULL, name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1047,6 +1061,6 @@ fail_ns_lock: fail: (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - name, info, error); + NULL, name, info, error); return error; } -- cgit v1.1 From 5ac8c355ae0013d82b3a07b49aebeadfce9b6e52 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:55 -0800 Subject: apparmor: allow introspecting the loaded policy pre internal transform Store loaded policy and allow introspecting it through apparmorfs. This has several uses from debugging, policy validation, and policy checkpoint and restore for containers. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 213 ++++++++++++++++++++++++------ security/apparmor/crypto.c | 39 +++++- security/apparmor/include/apparmorfs.h | 5 + security/apparmor/include/crypto.h | 5 + security/apparmor/include/policy.h | 5 +- security/apparmor/include/policy_unpack.h | 27 +++- security/apparmor/policy.c | 14 +- security/apparmor/policy_unpack.c | 28 +++- 8 files changed, 278 insertions(+), 58 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index cc6ee1e..2e6790c 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -33,6 +33,7 @@ #include "include/policy.h" #include "include/policy_ns.h" #include "include/resource.h" +#include "include/policy_unpack.h" /** * aa_mangle_name - mangle a profile name to std profile layout form @@ -84,11 +85,13 @@ static int mangle_name(const char *name, char *target) * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. */ -static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, - size_t alloc_size, size_t copy_size, - loff_t *pos) +static struct aa_loaddata *aa_simple_write_to_buffer(int op, + const char __user *userbuf, + size_t alloc_size, + size_t copy_size, + loff_t *pos) { - char *data; + struct aa_loaddata *data; BUG_ON(copy_size > alloc_size); @@ -96,19 +99,16 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, /* only writes from pos 0, that is complete writes */ return ERR_PTR(-ESPIPE); - /* - * Don't allow profile load/replace/remove from profiles that don't - * have CAP_MAC_ADMIN - */ - if (!aa_may_manage_policy(__aa_current_profile(), NULL, op)) - return ERR_PTR(-EACCES); - /* freed by caller to simple_write_to_buffer */ - data = kvmalloc(alloc_size); + data = kvmalloc(sizeof(*data) + alloc_size); if (data == NULL) return ERR_PTR(-ENOMEM); + kref_init(&data->count); + data->size = copy_size; + data->hash = NULL; + data->abi = 0; - if (copy_from_user(data, userbuf, copy_size)) { + if (copy_from_user(data->data, userbuf, copy_size)) { kvfree(data); return ERR_PTR(-EFAULT); } @@ -116,26 +116,38 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, return data; } - -/* .load file hook fn to load policy */ -static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, - loff_t *pos) +static ssize_t policy_update(int binop, const char __user *buf, size_t size, + loff_t *pos) { - char *data; ssize_t error; + struct aa_loaddata *data; + struct aa_profile *profile = aa_current_profile(); + int op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; + /* high level check about policy management - fine grained in + * below after unpack + */ + error = aa_may_manage_policy(profile, profile->ns, op); + if (error) + return error; - data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); - + data = aa_simple_write_to_buffer(op, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(__aa_current_profile()->ns, data, - size, PROF_ADD); - kvfree(data); + error = aa_replace_profiles(profile->ns, binop, data); + aa_put_loaddata(data); } return error; } +static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, + loff_t *pos) +{ + int error = policy_update(PROF_ADD, buf, size, pos); + + return error; +} + static const struct file_operations aa_fs_profile_load = { .write = profile_load, .llseek = default_llseek, @@ -145,16 +157,7 @@ static const struct file_operations aa_fs_profile_load = { static ssize_t profile_replace(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - char *data; - ssize_t error; - - data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); - error = PTR_ERR(data); - if (!IS_ERR(data)) { - error = aa_replace_profiles(__aa_current_profile()->ns, data, - size, PROF_REPLACE); - kvfree(data); - } + int error = policy_update(PROF_REPLACE, buf, size, pos); return error; } @@ -164,27 +167,35 @@ static const struct file_operations aa_fs_profile_replace = { .llseek = default_llseek, }; -/* .remove file hook fn to remove loaded policy */ static ssize_t profile_remove(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - char *data; + struct aa_loaddata *data; + struct aa_profile *profile; ssize_t error; + profile = aa_current_profile(); + /* high level check about policy management - fine grained in + * below after unpack + */ + error = aa_may_manage_policy(profile, profile->ns, OP_PROF_RM); + if (error) + goto out; + /* * aa_remove_profile needs a null terminated string so 1 extra * byte is allocated and the copied data is null terminated. */ - data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); + data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, + pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - data[size] = 0; - error = aa_remove_profiles(__aa_current_profile()->ns, data, - size); - kvfree(data); + data->data[size] = 0; + error = aa_remove_profiles(profile->ns, data->data, size); + aa_put_loaddata(data); } - + out: return error; } @@ -401,6 +412,100 @@ static const struct file_operations aa_fs_ns_name = { .release = single_release, }; +static int rawdata_release(struct inode *inode, struct file *file) +{ + /* TODO: switch to loaddata when profile switched to symlink */ + aa_put_loaddata(file->private_data); + + return 0; +} + +static int aa_fs_seq_raw_abi_show(struct seq_file *seq, void *v) +{ + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); + + if (profile->rawdata->abi) { + seq_printf(seq, "v%d", profile->rawdata->abi); + seq_puts(seq, "\n"); + } + aa_put_profile(profile); + + return 0; +} + +static int aa_fs_seq_raw_abi_open(struct inode *inode, struct file *file) +{ + return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_abi_show); +} + +static const struct file_operations aa_fs_seq_raw_abi_fops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_raw_abi_open, + .read = seq_read, + .llseek = seq_lseek, + .release = aa_fs_seq_profile_release, +}; + +static int aa_fs_seq_raw_hash_show(struct seq_file *seq, void *v) +{ + struct aa_proxy *proxy = seq->private; + struct aa_profile *profile = aa_get_profile_rcu(&proxy->profile); + unsigned int i, size = aa_hash_size(); + + if (profile->rawdata->hash) { + for (i = 0; i < size; i++) + seq_printf(seq, "%.2x", profile->rawdata->hash[i]); + seq_puts(seq, "\n"); + } + aa_put_profile(profile); + + return 0; +} + +static int aa_fs_seq_raw_hash_open(struct inode *inode, struct file *file) +{ + return aa_fs_seq_profile_open(inode, file, aa_fs_seq_raw_hash_show); +} + +static const struct file_operations aa_fs_seq_raw_hash_fops = { + .owner = THIS_MODULE, + .open = aa_fs_seq_raw_hash_open, + .read = seq_read, + .llseek = seq_lseek, + .release = aa_fs_seq_profile_release, +}; + +static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, + loff_t *ppos) +{ + struct aa_loaddata *rawdata = file->private_data; + + return simple_read_from_buffer(buf, size, ppos, rawdata->data, + rawdata->size); +} + +static int rawdata_open(struct inode *inode, struct file *file) +{ + struct aa_proxy *proxy = inode->i_private; + struct aa_profile *profile; + + if (!policy_view_capable(NULL)) + return -EACCES; + profile = aa_get_profile_rcu(&proxy->profile); + file->private_data = aa_get_loaddata(profile->rawdata); + aa_put_profile(profile); + + return 0; +} + +static const struct file_operations aa_fs_rawdata_fops = { + .open = rawdata_open, + .read = rawdata_read, + .llseek = generic_file_llseek, + .release = rawdata_release, +}; + /** fns to setup dynamic per profile/namespace files **/ void __aa_fs_profile_rmdir(struct aa_profile *profile) { @@ -512,6 +617,29 @@ int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } + if (profile->rawdata) { + dent = create_profile_file(dir, "raw_sha1", profile, + &aa_fs_seq_raw_hash_fops); + if (IS_ERR(dent)) + goto fail; + profile->dents[AAFS_PROF_RAW_HASH] = dent; + + dent = create_profile_file(dir, "raw_abi", profile, + &aa_fs_seq_raw_abi_fops); + if (IS_ERR(dent)) + goto fail; + profile->dents[AAFS_PROF_RAW_ABI] = dent; + + dent = securityfs_create_file("raw_data", S_IFREG | 0444, dir, + profile->proxy, + &aa_fs_rawdata_fops); + if (IS_ERR(dent)) + goto fail; + profile->dents[AAFS_PROF_RAW_DATA] = dent; + d_inode(dent)->i_size = profile->rawdata->size; + aa_get_proxy(profile->proxy); + } + list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); if (error) @@ -817,6 +945,9 @@ static const struct seq_operations aa_fs_profiles_op = { static int profiles_open(struct inode *inode, struct file *file) { + if (!policy_view_capable(NULL)) + return -EACCES; + return seq_open(file, &aa_fs_profiles_op); } diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index b75dab0..de8dc78 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -29,6 +29,43 @@ unsigned int aa_hash_size(void) return apparmor_hash_size; } +char *aa_calc_hash(void *data, size_t len) +{ + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(apparmor_tfm)]; + } desc; + char *hash = NULL; + int error = -ENOMEM; + + if (!apparmor_tfm) + return NULL; + + hash = kzalloc(apparmor_hash_size, GFP_KERNEL); + if (!hash) + goto fail; + + desc.shash.tfm = apparmor_tfm; + desc.shash.flags = 0; + + error = crypto_shash_init(&desc.shash); + if (error) + goto fail; + error = crypto_shash_update(&desc.shash, (u8 *) data, len); + if (error) + goto fail; + error = crypto_shash_final(&desc.shash, hash); + if (error) + goto fail; + + return hash; + +fail: + kfree(hash); + + return ERR_PTR(error); +} + int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { @@ -37,7 +74,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, char ctx[crypto_shash_descsize(apparmor_tfm)]; } desc; int error = -ENOMEM; - u32 le32_version = cpu_to_le32(version); + __le32 le32_version = cpu_to_le32(version); if (!aa_g_hash_policy) return 0; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index eeeae5b..a593e75 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -70,6 +70,7 @@ enum aafs_ns_type { AAFS_NS_DIR, AAFS_NS_PROFS, AAFS_NS_NS, + AAFS_NS_RAW_DATA, AAFS_NS_COUNT, AAFS_NS_MAX_COUNT, AAFS_NS_SIZE, @@ -85,12 +86,16 @@ enum aafs_prof_type { AAFS_PROF_MODE, AAFS_PROF_ATTACH, AAFS_PROF_HASH, + AAFS_PROF_RAW_DATA, + AAFS_PROF_RAW_HASH, + AAFS_PROF_RAW_ABI, AAFS_PROF_SIZEOF, }; #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) +#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h index dc418e5..c1469f8 100644 --- a/security/apparmor/include/crypto.h +++ b/security/apparmor/include/crypto.h @@ -18,9 +18,14 @@ #ifdef CONFIG_SECURITY_APPARMOR_HASH unsigned int aa_hash_size(void); +char *aa_calc_hash(void *data, size_t len); int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len); #else +static inline char *aa_calc_hash(void *data, size_t len) +{ + return NULL; +} static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 95641e2..fbbc867 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -161,6 +161,7 @@ struct aa_profile { struct aa_caps caps; struct aa_rlimit rlimits; + struct aa_loaddata *rawdata; unsigned char *hash; char *dirname; struct dentry *dents[AAFS_PROF_SIZEOF]; @@ -187,8 +188,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, - bool noreplace); +ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, + struct aa_loaddata *udata); ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); void __aa_profile_list_release(struct list_head *head); diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index c214fb8..7b675b6 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -16,6 +16,7 @@ #define __POLICY_INTERFACE_H #include +#include struct aa_load_ent { struct list_head list; @@ -34,6 +35,30 @@ struct aa_load_ent *aa_load_ent_alloc(void); #define PACKED_MODE_KILL 2 #define PACKED_MODE_UNCONFINED 3 -int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns); +/* struct aa_loaddata - buffer of policy load data set */ +struct aa_loaddata { + struct kref count; + size_t size; + int abi; + unsigned char *hash; + char data[]; +}; + +int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); + +static inline struct aa_loaddata * +aa_get_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_get(&(data->count)); + return data; +} + +void aa_loaddata_kref(struct kref *kref); +static inline void aa_put_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_put(&data->count, aa_loaddata_kref); +} #endif /* __POLICY_INTERFACE_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 3c5c0b2..ff29b60 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -228,6 +228,7 @@ void aa_free_profile(struct aa_profile *profile) aa_put_proxy(profile->proxy); kzfree(profile->hash); + aa_put_loaddata(profile->rawdata); kzfree(profile); } @@ -802,10 +803,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list * @view: namespace load is viewed from - * @profile: profile that is attempting to load/replace policy - * @udata: serialized data stream (NOT NULL) - * @size: size of the serialized data stream * @noreplace: true if only doing addition, no replacement allowed + * @udata: serialized data stream (NOT NULL) * * unpack and replace a profile on the profile list and uses of that profile * by any aa_task_cxt. If the profile does not exist on the profile list @@ -813,8 +812,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * * Returns: size of data consumed else error code on failure. */ -ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, - bool noreplace) +ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, + struct aa_loaddata *udata) { const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; @@ -824,7 +823,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, LIST_HEAD(lh); /* released below */ - error = aa_unpack(udata, size, &lh, &ns_name); + error = aa_unpack(udata, &lh, &ns_name); if (error) goto out; @@ -841,6 +840,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, void *udata, size_t size, /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; + ent->new->rawdata = aa_get_loaddata(udata); error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -957,7 +957,7 @@ out: if (error) return error; - return size; + return udata->size; fail_lock: mutex_unlock(&ns->lock); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 51a7f9f..fb4ef84 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -117,6 +117,16 @@ static int audit_iface(struct aa_profile *new, const char *name, audit_cb); } +void aa_loaddata_kref(struct kref *kref) +{ + struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); + + if (d) { + kzfree(d->hash); + kvfree(d); + } +} + /* test if read will be in packed data bounds */ static bool inbounds(struct aa_ext *e, size_t size) { @@ -749,7 +759,6 @@ struct aa_load_ent *aa_load_ent_alloc(void) /** * aa_unpack - unpack packed binary profile(s) data loaded from user space * @udata: user data copied to kmem (NOT NULL) - * @size: the size of the user data * @lh: list to place unpacked profiles in a aa_repl_ws * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) * @@ -759,15 +768,16 @@ struct aa_load_ent *aa_load_ent_alloc(void) * * Returns: profile(s) on @lh else error pointer if fails to unpack */ -int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) +int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, + const char **ns) { struct aa_load_ent *tmp, *ent; struct aa_profile *profile = NULL; int error; struct aa_ext e = { - .start = udata, - .end = udata + size, - .pos = udata, + .start = udata->data, + .end = udata->data + udata->size, + .pos = udata->data, }; *ns = NULL; @@ -802,7 +812,13 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) ent->new = profile; list_add_tail(&ent->list, lh); } - + udata->abi = e.version & K_ABI_MASK; + udata->hash = aa_calc_hash(udata->data, udata->size); + if (IS_ERR(udata->hash)) { + error = PTR_ERR(udata->hash); + udata->hash = NULL; + goto fail; + } return 0; fail_profile: -- cgit v1.1 From 04dc715e24d0820bf8740e1a1135ed61fe162bc8 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:56 -0800 Subject: apparmor: audit policy ns specified in policy load Verify that profiles in a load set specify the same policy ns and audit the name of the policy ns that policy is being loaded for. Signed-off-by: John Johansen --- security/apparmor/include/policy_unpack.h | 1 + security/apparmor/policy.c | 54 +++++++++++++++++++++++-------- security/apparmor/policy_unpack.c | 46 ++++++++++++++++++++------ 3 files changed, 77 insertions(+), 24 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index 7b675b6..4c1319e 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -23,6 +23,7 @@ struct aa_load_ent { struct aa_profile *new; struct aa_profile *old; struct aa_profile *rename; + const char *ns_name; }; void aa_load_ent_free(struct aa_load_ent *ent); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ff29b60..eb1ccd1 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; - ssize_t error; + ssize_t count, error; LIST_HEAD(lh); /* released below */ @@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, if (error) goto out; - /* released below */ - ns = aa_prepare_ns(view, ns_name); - if (!ns) { - error = audit_policy(__aa_current_profile(), op, GFP_KERNEL, - NULL, ns_name, - "failed to prepare namespace", -ENOMEM); - goto free; + /* ensure that profiles are all for the same ns + * TODO: update locking to remove this constaint. All profiles in + * the load set must succeed as a set or the load will + * fail. Sort ent list and take ns locks in hierarchy order + */ + count = 0; + list_for_each_entry(ent, &lh, list) { + if (ns_name) { + if (ent->ns_name && + strcmp(ent->ns_name, ns_name) != 0) { + info = "policy load has mixed namespaces"; + error = -EACCES; + goto fail; + } + } else if (ent->ns_name) { + if (count) { + info = "policy load has mixed namespaces"; + error = -EACCES; + goto fail; + } + ns_name = ent->ns_name; + } else + count++; } + if (ns_name) { + ns = aa_prepare_ns(view, ns_name); + if (IS_ERR(ns)) { + info = "failed to prepare namespace"; + error = PTR_ERR(ns); + ns = NULL; + goto fail; + } + } else + ns = aa_get_ns(view); mutex_lock(&ns->lock); /* setup parent and ns info */ @@ -964,7 +990,8 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, +fail: + audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -975,10 +1002,9 @@ fail_lock: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL, + audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, tmp->new->base.hname, info, error); } -free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); @@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; const char *name = fqname, *info = NULL; + char *ns_name = NULL; ssize_t error = 0; if (*fqname == 0) { @@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) root = view; if (fqname[0] == ':') { - char *ns_name; name = aa_split_fqname(fqname, &ns_name); /* released below */ ns = aa_find_ns(root, ns_name); @@ -1050,7 +1076,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) /* don't fail removal if audit fails */ (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - NULL, name, info, error); + ns_name, name, info, error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1061,6 +1087,6 @@ fail_ns_lock: fail: (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - NULL, name, info, error); + ns_name, name, info, error); return error; } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index fb4ef84..38c148f 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -91,6 +91,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_iface - do audit message for policy unpacking/load/replace/remove * @new: profile if it has been allocated (MAYBE NULL) + * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL) * @name: name of the profile being manipulated (MAYBE NULL) * @info: any extra info about the failure (MAYBE NULL) * @e: buffer position info @@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: %0 or error */ -static int audit_iface(struct aa_profile *new, const char *name, - const char *info, struct aa_ext *e, int error) +static int audit_iface(struct aa_profile *new, const char *ns_name, + const char *name, const char *info, struct aa_ext *e, + int error) { struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; + aad.iface.ns = ns_name; if (e) aad.iface.pos = e->pos - e->start; aad.iface.target = new; @@ -486,19 +489,32 @@ fail: * * NOTE: unpack profile sets audit struct if there is a failure */ -static struct aa_profile *unpack_profile(struct aa_ext *e) +static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) { struct aa_profile *profile = NULL; - const char *name = NULL; + const char *tmpname, *tmpns = NULL, *name = NULL; + size_t ns_len; int i, error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; + *ns_name = NULL; + /* check that we have the right struct being passed */ if (!unpack_nameX(e, AA_STRUCT, "profile")) goto fail; if (!unpack_str(e, &name, NULL)) goto fail; + if (*name == '\0') + goto fail; + + tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); + if (tmpns) { + *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); + if (!*ns_name) + goto fail; + name = tmpname; + } profile = aa_alloc_profile(name, GFP_KERNEL); if (!profile) @@ -646,7 +662,8 @@ fail: name = NULL; else if (!name) name = "unknown"; - audit_iface(profile, name, "failed to unpack profile", e, error); + audit_iface(profile, NULL, name, "failed to unpack profile", e, + error); aa_free_profile(profile); return ERR_PTR(error); @@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /* get the interface version */ if (!unpack_u32(e, &e->version, "version")) { if (required) { - audit_iface(NULL, NULL, "invalid profile format", + audit_iface(NULL, NULL, NULL, "invalid profile format", e, error); return error; } @@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) * Mask off everything that is not kernel abi version */ if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { - audit_iface(NULL, NULL, "unsupported interface version", + audit_iface(NULL, NULL, NULL, "unsupported interface version", e, error); return error; } /* read the namespace if present */ if (unpack_str(e, &name, "namespace")) { + if (*name == '\0') { + audit_iface(NULL, NULL, NULL, "invalid namespace name", + e, error); + return error; + } if (*ns && strcmp(*ns, name)) - audit_iface(NULL, NULL, "invalid ns change", e, error); + audit_iface(NULL, NULL, NULL, "invalid ns change", e, + error); else if (!*ns) *ns = name; } @@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile) if (profile->file.dfa && !verify_dfa_xindex(profile->file.dfa, profile->file.trans.size)) { - audit_iface(profile, NULL, "Invalid named transition", + audit_iface(profile, NULL, NULL, "Invalid named transition", NULL, -EPROTO); return -EPROTO; } @@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent) aa_put_profile(ent->rename); aa_put_profile(ent->old); aa_put_profile(ent->new); + kfree(ent->ns_name); kzfree(ent); } } @@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, *ns = NULL; while (e.pos < e.end) { + char *ns_name = NULL; void *start; error = verify_header(&e, e.pos == e.start, ns); if (error) goto fail; start = e.pos; - profile = unpack_profile(&e); + profile = unpack_profile(&e, &ns_name); if (IS_ERR(profile)) { error = PTR_ERR(profile); goto fail; @@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, } ent->new = profile; + ent->ns_name = ns_name; list_add_tail(&ent->list, lh); } udata->abi = e.version & K_ABI_MASK; -- cgit v1.1 From 12dd7171d645a6658326ba234e6d4fc57a73bf98 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:57 -0800 Subject: apparmor: pass the subject profile into profile replace/remove This is just setup for new ns specific .load, .replace, .remove interface files. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 5 +++-- security/apparmor/include/policy.h | 7 ++++--- security/apparmor/policy.c | 25 ++++++++++++++----------- 3 files changed, 21 insertions(+), 16 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2e6790c..d654aac 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -133,7 +133,7 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, data = aa_simple_write_to_buffer(op, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(profile->ns, binop, data); + error = aa_replace_profiles(profile->ns, profile, binop, data); aa_put_loaddata(data); } @@ -192,7 +192,8 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, error = PTR_ERR(data); if (!IS_ERR(data)) { data->data[size] = 0; - error = aa_remove_profiles(profile->ns, data->data, size); + error = aa_remove_profiles(profile->ns, profile, data->data, + size); aa_put_loaddata(data); } out: diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index fbbc867..b315ad5 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -188,9 +188,10 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, - struct aa_loaddata *udata); -ssize_t aa_remove_profiles(struct aa_ns *view, char *name, size_t size); +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, + bool noreplace, struct aa_loaddata *udata); +ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile, + char *name, size_t size); void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index eb1ccd1..912cdbe 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -803,6 +803,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, /** * aa_replace_profiles - replace profile(s) on the profile list * @view: namespace load is viewed from + * @label: label that is attempting to load/replace policy * @noreplace: true if only doing addition, no replacement allowed * @udata: serialized data stream (NOT NULL) * @@ -812,8 +813,8 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * * Returns: size of data consumed else error code on failure. */ -ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, - struct aa_loaddata *udata) +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, + bool noreplace, struct aa_loaddata *udata) { const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; @@ -935,7 +936,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_ATOMIC, NULL, + audit_policy(profile, op, GFP_ATOMIC, NULL, ent->new->base.hname, NULL, error); if (ent->old) { @@ -991,8 +992,8 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; fail: - audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, - ent->new->base.hname, info, error); + audit_policy(profile, op, GFP_KERNEL, ns_name, ent->new->base.hname, + info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; list_for_each_entry(tmp, &lh, list) { @@ -1002,7 +1003,7 @@ fail: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name, + audit_policy(profile, op, GFP_KERNEL, ns_name, tmp->new->base.hname, info, error); } list_for_each_entry_safe(ent, tmp, &lh, list) { @@ -1016,6 +1017,7 @@ fail: /** * aa_remove_profiles - remove profile(s) from the system * @view: namespace the remove is being done from + * @subj: profile attempting to remove policy * @fqname: name of the profile or namespace to remove (NOT NULL) * @size: size of the name * @@ -1026,7 +1028,8 @@ fail: * * Returns: size of data consume else error code if fails */ -ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) +ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj, + char *fqname, size_t size) { struct aa_ns *root = NULL, *ns = NULL; struct aa_profile *profile = NULL; @@ -1075,8 +1078,8 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size) } /* don't fail removal if audit fails */ - (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - ns_name, name, info, error); + (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + error); aa_put_ns(ns); aa_put_profile(profile); return size; @@ -1086,7 +1089,7 @@ fail_ns_lock: aa_put_ns(ns); fail: - (void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL, - ns_name, name, info, error); + (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + error); return error; } -- cgit v1.1 From b7fd2c0340eacbee892425e9007647568b7f2a3c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:58 -0800 Subject: apparmor: add per policy ns .load, .replace, .remove interface files Having per policy ns interface files helps with containers restoring policy. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 146 ++++++++++++++++++++++++++++----- security/apparmor/include/apparmorfs.h | 6 ++ 2 files changed, 130 insertions(+), 22 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d654aac..af36af8 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -117,7 +117,7 @@ static struct aa_loaddata *aa_simple_write_to_buffer(int op, } static ssize_t policy_update(int binop, const char __user *buf, size_t size, - loff_t *pos) + loff_t *pos, struct aa_ns *ns) { ssize_t error; struct aa_loaddata *data; @@ -126,24 +126,29 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, /* high level check about policy management - fine grained in * below after unpack */ - error = aa_may_manage_policy(profile, profile->ns, op); + error = aa_may_manage_policy(profile, ns, op); if (error) return error; data = aa_simple_write_to_buffer(op, buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { - error = aa_replace_profiles(profile->ns, profile, binop, data); + error = aa_replace_profiles(ns ? ns : profile->ns, profile, + binop, data); aa_put_loaddata(data); } return error; } +/* .load file hook fn to load policy */ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - int error = policy_update(PROF_ADD, buf, size, pos); + struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); + int error = policy_update(PROF_ADD, buf, size, pos, ns); + + aa_put_ns(ns); return error; } @@ -157,7 +162,10 @@ static const struct file_operations aa_fs_profile_load = { static ssize_t profile_replace(struct file *f, const char __user *buf, size_t size, loff_t *pos) { - int error = policy_update(PROF_REPLACE, buf, size, pos); + struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); + int error = policy_update(PROF_REPLACE, buf, size, pos, ns); + + aa_put_ns(ns); return error; } @@ -167,18 +175,20 @@ static const struct file_operations aa_fs_profile_replace = { .llseek = default_llseek, }; +/* .remove file hook fn to remove loaded policy */ static ssize_t profile_remove(struct file *f, const char __user *buf, size_t size, loff_t *pos) { struct aa_loaddata *data; struct aa_profile *profile; ssize_t error; + struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); profile = aa_current_profile(); /* high level check about policy management - fine grained in * below after unpack */ - error = aa_may_manage_policy(profile, profile->ns, OP_PROF_RM); + error = aa_may_manage_policy(profile, ns, OP_PROF_RM); if (error) goto out; @@ -192,11 +202,12 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, error = PTR_ERR(data); if (!IS_ERR(data)) { data->data[size] = 0; - error = aa_remove_profiles(profile->ns, profile, data->data, - size); + error = aa_remove_profiles(ns ? ns : profile->ns, profile, + data->data, size); aa_put_loaddata(data); } out: + aa_put_ns(ns); return error; } @@ -676,12 +687,77 @@ void __aa_fs_ns_rmdir(struct aa_ns *ns) mutex_unlock(&sub->lock); } + if (ns_subns_dir(ns)) { + sub = d_inode(ns_subns_dir(ns))->i_private; + aa_put_ns(sub); + } + if (ns_subload(ns)) { + sub = d_inode(ns_subload(ns))->i_private; + aa_put_ns(sub); + } + if (ns_subreplace(ns)) { + sub = d_inode(ns_subreplace(ns))->i_private; + aa_put_ns(sub); + } + if (ns_subremove(ns)) { + sub = d_inode(ns_subremove(ns))->i_private; + aa_put_ns(sub); + } + for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { securityfs_remove(ns->dents[i]); ns->dents[i] = NULL; } } +/* assumes cleanup in caller */ +static int __aa_fs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir) +{ + struct dentry *dent; + + AA_BUG(!ns); + AA_BUG(!dir); + + dent = securityfs_create_dir("profiles", dir); + if (IS_ERR(dent)) + return PTR_ERR(dent); + ns_subprofs_dir(ns) = dent; + + dent = securityfs_create_dir("raw_data", dir); + if (IS_ERR(dent)) + return PTR_ERR(dent); + ns_subdata_dir(ns) = dent; + + dent = securityfs_create_file(".load", 0640, dir, ns, + &aa_fs_profile_load); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subload(ns) = dent; + + dent = securityfs_create_file(".replace", 0640, dir, ns, + &aa_fs_profile_replace); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subreplace(ns) = dent; + + dent = securityfs_create_file(".remove", 0640, dir, ns, + &aa_fs_profile_remove); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subremove(ns) = dent; + + dent = securityfs_create_dir("namespaces", dir); + if (IS_ERR(dent)) + return PTR_ERR(dent); + aa_get_ns(ns); + ns_subns_dir(ns) = dent; + + return 0; +} + int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) { struct aa_ns *sub; @@ -689,30 +765,31 @@ int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name) struct dentry *dent, *dir; int error; + AA_BUG(!ns); + AA_BUG(!parent); + AA_BUG(!mutex_is_locked(&ns->lock)); + if (!name) name = ns->base.name; + /* create ns dir if it doesn't already exist */ dent = securityfs_create_dir(name, parent); if (IS_ERR(dent)) goto fail; - ns_dir(ns) = dir = dent; - - dent = securityfs_create_dir("profiles", dir); - if (IS_ERR(dent)) - goto fail; - ns_subprofs_dir(ns) = dent; - dent = securityfs_create_dir("namespaces", dir); - if (IS_ERR(dent)) - goto fail; - ns_subns_dir(ns) = dent; + ns_dir(ns) = dir = dent; + error = __aa_fs_ns_mkdir_entries(ns, dir); + if (error) + goto fail2; + /* profiles */ list_for_each_entry(child, &ns->base.profiles, base.list) { error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); if (error) goto fail2; } + /* subnamespaces */ list_for_each_entry(sub, &ns->sub_ns, base.list) { mutex_lock(&sub->lock); error = __aa_fs_ns_mkdir(sub, ns_subns_dir(ns), NULL); @@ -1003,12 +1080,9 @@ static struct aa_fs_entry aa_fs_entry_features[] = { }; static struct aa_fs_entry aa_fs_entry_apparmor[] = { - AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load), - AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace), - AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove), AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level), AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name), - AA_FS_FILE_FOPS("profiles", 0640, &aa_fs_profiles_fops), + AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops), AA_FS_DIR("features", aa_fs_entry_features), { } }; @@ -1172,6 +1246,7 @@ out: */ static int __init aa_create_aafs(void) { + struct dentry *dent; int error; if (!apparmor_initialized) @@ -1187,7 +1262,34 @@ static int __init aa_create_aafs(void) if (error) goto error; + dent = securityfs_create_file(".load", 0666, aa_fs_entry.dentry, + NULL, &aa_fs_profile_load); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subload(root_ns) = dent; + + dent = securityfs_create_file(".replace", 0666, aa_fs_entry.dentry, + NULL, &aa_fs_profile_replace); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subreplace(root_ns) = dent; + + dent = securityfs_create_file(".remove", 0666, aa_fs_entry.dentry, + NULL, &aa_fs_profile_remove); + if (IS_ERR(dent)) { + error = PTR_ERR(dent); + goto error; + } + ns_subremove(root_ns) = dent; + + mutex_lock(&root_ns->lock); error = __aa_fs_ns_mkdir(root_ns, aa_fs_entry.dentry, "policy"); + mutex_unlock(&root_ns->lock); + if (error) goto error; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index a593e75..120a798 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -71,6 +71,9 @@ enum aafs_ns_type { AAFS_NS_PROFS, AAFS_NS_NS, AAFS_NS_RAW_DATA, + AAFS_NS_LOAD, + AAFS_NS_REPLACE, + AAFS_NS_REMOVE, AAFS_NS_COUNT, AAFS_NS_MAX_COUNT, AAFS_NS_SIZE, @@ -96,6 +99,9 @@ enum aafs_prof_type { #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) #define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) +#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) +#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) +#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) -- cgit v1.1 From a20aa95fbe1abb4c6f333a1f55e9fd15b01c7f12 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:42:59 -0800 Subject: apparmor: fail task profile update if current_cred isn't real_cred Trying to update the task cred while the task current cred is not the real cred will result in an error at the cred layer. Avoid this by failing early and delaying the update. Signed-off-by: John Johansen --- security/apparmor/context.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'security/apparmor') diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3c4f534..3f32f594 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -100,6 +100,9 @@ int aa_replace_current_profile(struct aa_profile *profile) if (cxt->profile == profile) return 0; + if (current_cred() != current_real_cred()) + return -EBUSY; + new = prepare_creds(); if (!new) return -ENOMEM; -- cgit v1.1 From 55a26ebf630b6bf1cb7ddf8882fdc81d58afeaa2 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:00 -0800 Subject: apparmor: rename context abreviation cxt to the more standard ctx Signed-off-by: John Johansen --- security/apparmor/context.c | 100 ++++++++++++++++++------------------ security/apparmor/domain.c | 42 +++++++-------- security/apparmor/include/context.h | 78 ++++++++++++++-------------- security/apparmor/lsm.c | 72 +++++++++++++------------- security/apparmor/policy.c | 2 +- 5 files changed, 150 insertions(+), 144 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3f32f594..71e9910 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -13,11 +13,11 @@ * License. * * - * AppArmor sets confinement on every task, via the the aa_task_cxt and - * the aa_task_cxt.profile, both of which are required and are not allowed - * to be NULL. The aa_task_cxt is not reference counted and is unique + * AppArmor sets confinement on every task, via the the aa_task_ctx and + * the aa_task_ctx.profile, both of which are required and are not allowed + * to be NULL. The aa_task_ctx is not reference counted and is unique * to each cred (which is reference count). The profile pointed to by - * the task_cxt is reference counted. + * the task_ctx is reference counted. * * TODO * If a task uses change_hat it currently does not return to the old @@ -30,28 +30,28 @@ #include "include/policy.h" /** - * aa_alloc_task_context - allocate a new task_cxt + * aa_alloc_task_context - allocate a new task_ctx * @flags: gfp flags for allocation * * Returns: allocated buffer or NULL on failure */ -struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) +struct aa_task_ctx *aa_alloc_task_context(gfp_t flags) { - return kzalloc(sizeof(struct aa_task_cxt), flags); + return kzalloc(sizeof(struct aa_task_ctx), flags); } /** - * aa_free_task_context - free a task_cxt - * @cxt: task_cxt to free (MAYBE NULL) + * aa_free_task_context - free a task_ctx + * @ctx: task_ctx to free (MAYBE NULL) */ -void aa_free_task_context(struct aa_task_cxt *cxt) +void aa_free_task_context(struct aa_task_ctx *ctx) { - if (cxt) { - aa_put_profile(cxt->profile); - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); + if (ctx) { + aa_put_profile(ctx->profile); + aa_put_profile(ctx->previous); + aa_put_profile(ctx->onexec); - kzfree(cxt); + kzfree(ctx); } } @@ -60,7 +60,7 @@ void aa_free_task_context(struct aa_task_cxt *cxt) * @new: a blank task context (NOT NULL) * @old: the task context to copy (NOT NULL) */ -void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) +void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old) { *new = *old; aa_get_profile(new->profile); @@ -93,11 +93,11 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task) */ int aa_replace_current_profile(struct aa_profile *profile) { - struct aa_task_cxt *cxt = current_cxt(); + struct aa_task_ctx *ctx = current_ctx(); struct cred *new; BUG_ON(!profile); - if (cxt->profile == profile) + if (ctx->profile == profile) return 0; if (current_cred() != current_real_cred()) @@ -107,20 +107,22 @@ int aa_replace_current_profile(struct aa_profile *profile) if (!new) return -ENOMEM; - cxt = cred_cxt(new); - if (unconfined(profile) || (cxt->profile->ns != profile->ns)) + ctx = cred_ctx(new); + if (unconfined(profile) || (ctx->profile->ns != profile->ns)) /* if switching to unconfined or a different profile namespace * clear out context state */ - aa_clear_task_cxt_trans(cxt); + aa_clear_task_ctx_trans(ctx); - /* be careful switching cxt->profile, when racing replacement it - * is possible that cxt->profile->proxy->profile is the reference + /* + * be careful switching ctx->profile, when racing replacement it + * is possible that ctx->profile->proxy->profile is the reference * keeping @profile valid, so make sure to get its reference before - * dropping the reference on cxt->profile */ + * dropping the reference on ctx->profile + */ aa_get_profile(profile); - aa_put_profile(cxt->profile); - cxt->profile = profile; + aa_put_profile(ctx->profile); + ctx->profile = profile; commit_creds(new); return 0; @@ -134,15 +136,15 @@ int aa_replace_current_profile(struct aa_profile *profile) */ int aa_set_current_onexec(struct aa_profile *profile) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; - cxt = cred_cxt(new); + ctx = cred_ctx(new); aa_get_profile(profile); - aa_put_profile(cxt->onexec); - cxt->onexec = profile; + aa_put_profile(ctx->onexec); + ctx->onexec = profile; commit_creds(new); return 0; @@ -160,28 +162,28 @@ int aa_set_current_onexec(struct aa_profile *profile) */ int aa_set_current_hat(struct aa_profile *profile, u64 token) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; BUG_ON(!profile); - cxt = cred_cxt(new); - if (!cxt->previous) { + ctx = cred_ctx(new); + if (!ctx->previous) { /* transfer refcount */ - cxt->previous = cxt->profile; - cxt->token = token; - } else if (cxt->token == token) { - aa_put_profile(cxt->profile); + ctx->previous = ctx->profile; + ctx->token = token; + } else if (ctx->token == token) { + aa_put_profile(ctx->profile); } else { - /* previous_profile && cxt->token != token */ + /* previous_profile && ctx->token != token */ abort_creds(new); return -EACCES; } - cxt->profile = aa_get_newest_profile(profile); + ctx->profile = aa_get_newest_profile(profile); /* clear exec on switching context */ - aa_put_profile(cxt->onexec); - cxt->onexec = NULL; + aa_put_profile(ctx->onexec); + ctx->onexec = NULL; commit_creds(new); return 0; @@ -198,27 +200,27 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) */ int aa_restore_previous_profile(u64 token) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct cred *new = prepare_creds(); if (!new) return -ENOMEM; - cxt = cred_cxt(new); - if (cxt->token != token) { + ctx = cred_ctx(new); + if (ctx->token != token) { abort_creds(new); return -EACCES; } /* ignore restores when there is no saved profile */ - if (!cxt->previous) { + if (!ctx->previous) { abort_creds(new); return 0; } - aa_put_profile(cxt->profile); - cxt->profile = aa_get_newest_profile(cxt->previous); - BUG_ON(!cxt->profile); + aa_put_profile(ctx->profile); + ctx->profile = aa_get_newest_profile(ctx->previous); + AA_BUG(!ctx->profile); /* clear exec && prev information when restoring to previous context */ - aa_clear_task_cxt_trans(cxt); + aa_clear_task_ctx_trans(ctx); commit_creds(new); return 0; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 1a8ffc5..84856b7 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -337,7 +337,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile, */ int apparmor_bprm_set_creds(struct linux_binprm *bprm) { - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct aa_profile *profile, *new_profile = NULL; struct aa_ns *ns; char *buffer = NULL; @@ -353,10 +353,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; - cxt = cred_cxt(bprm->cred); - BUG_ON(!cxt); + ctx = cred_ctx(bprm->cred); + AA_BUG(!ctx); - profile = aa_get_newest_profile(cxt->profile); + profile = aa_get_newest_profile(ctx->profile); /* * get the namespace from the replacement profile as replacement * can change the namespace @@ -380,9 +380,9 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) */ if (unconfined(profile)) { /* unconfined task */ - if (cxt->onexec) + if (ctx->onexec) /* change_profile on exec already been granted */ - new_profile = aa_get_profile(cxt->onexec); + new_profile = aa_get_profile(ctx->onexec); else new_profile = find_attach(ns, &ns->base.profiles, name); if (!new_profile) @@ -397,10 +397,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) /* find exec permissions for name */ state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); - if (cxt->onexec) { + if (ctx->onexec) { struct file_perms cp; info = "change_profile onexec"; - new_profile = aa_get_newest_profile(cxt->onexec); + new_profile = aa_get_newest_profile(ctx->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -409,8 +409,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * exec\0change_profile */ state = aa_dfa_null_transition(profile->file.dfa, state); - cp = change_profile_perms(profile, cxt->onexec->ns, - cxt->onexec->base.name, + cp = change_profile_perms(profile, ctx->onexec->ns, + ctx->onexec->base.name, AA_MAY_ONEXEC, state); if (!(cp.allow & AA_MAY_ONEXEC)) @@ -499,13 +499,13 @@ apply: bprm->per_clear |= PER_CLEAR_ON_SETID; x_clear: - aa_put_profile(cxt->profile); - /* transfer new profile reference will be released when cxt is freed */ - cxt->profile = new_profile; + aa_put_profile(ctx->profile); + /* transfer new profile reference will be released when ctx is freed */ + ctx->profile = new_profile; new_profile = NULL; /* clear out all temporary/transitional state from the context */ - aa_clear_task_cxt_trans(cxt); + aa_clear_task_ctx_trans(ctx); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, @@ -545,17 +545,17 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm) void apparmor_bprm_committing_creds(struct linux_binprm *bprm) { struct aa_profile *profile = __aa_current_profile(); - struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred); + struct aa_task_ctx *new_ctx = cred_ctx(bprm->cred); /* bail out if unconfined or not changing profile */ - if ((new_cxt->profile == profile) || - (unconfined(new_cxt->profile))) + if ((new_ctx->profile == profile) || + (unconfined(new_ctx->profile))) return; current->pdeath_signal = 0; /* reset soft limits and set hard limits for the new profile */ - __aa_transition_rlimits(profile, new_cxt->profile); + __aa_transition_rlimits(profile, new_ctx->profile); } /** @@ -604,7 +604,7 @@ static char *new_compound_name(const char *n1, const char *n2) int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) { const struct cred *cred; - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; struct aa_profile *profile, *previous_profile, *hat = NULL; char *name = NULL; int i; @@ -622,9 +622,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) /* released below */ cred = get_current_cred(); - cxt = cred_cxt(cred); + ctx = cred_ctx(cred); profile = aa_get_newest_profile(aa_cred_profile(cred)); - previous_profile = aa_get_newest_profile(cxt->previous); + previous_profile = aa_get_newest_profile(ctx->previous); if (unconfined(profile)) { info = "unconfined"; diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index d378bff..5b18fed 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -22,43 +22,43 @@ #include "policy.h" #include "policy_ns.h" -#define cred_cxt(X) (X)->security -#define current_cxt() cred_cxt(current_cred()) +#define cred_ctx(X) ((X)->security) +#define current_ctx() cred_ctx(current_cred()) -/* struct aa_file_cxt - the AppArmor context the file was opened in +/* struct aa_file_ctx - the AppArmor context the file was opened in * @perms: the permission the file was opened with * - * The file_cxt could currently be directly stored in file->f_security + * The file_ctx could currently be directly stored in file->f_security * as the profile reference is now stored in the f_cred. However the - * cxt struct will expand in the future so we keep the struct. + * ctx struct will expand in the future so we keep the struct. */ -struct aa_file_cxt { +struct aa_file_ctx { u16 allow; }; /** - * aa_alloc_file_context - allocate file_cxt + * aa_alloc_file_context - allocate file_ctx * @gfp: gfp flags for allocation * - * Returns: file_cxt or NULL on failure + * Returns: file_ctx or NULL on failure */ -static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) +static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp) { - return kzalloc(sizeof(struct aa_file_cxt), gfp); + return kzalloc(sizeof(struct aa_file_ctx), gfp); } /** - * aa_free_file_context - free a file_cxt - * @cxt: file_cxt to free (MAYBE_NULL) + * aa_free_file_context - free a file_ctx + * @ctx: file_ctx to free (MAYBE_NULL) */ -static inline void aa_free_file_context(struct aa_file_cxt *cxt) +static inline void aa_free_file_context(struct aa_file_ctx *ctx) { - if (cxt) - kzfree(cxt); + if (ctx) + kzfree(ctx); } /** - * struct aa_task_cxt - primary label for confined tasks + * struct aa_task_ctx - primary label for confined tasks * @profile: the current profile (NOT NULL) * @exec: profile to transition to on next exec (MAYBE NULL) * @previous: profile the task may return to (MAYBE NULL) @@ -69,17 +69,17 @@ static inline void aa_free_file_context(struct aa_file_cxt *cxt) * * TODO: make so a task can be confined by a stack of contexts */ -struct aa_task_cxt { +struct aa_task_ctx { struct aa_profile *profile; struct aa_profile *onexec; struct aa_profile *previous; u64 token; }; -struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); -void aa_free_task_context(struct aa_task_cxt *cxt); -void aa_dup_task_context(struct aa_task_cxt *new, - const struct aa_task_cxt *old); +struct aa_task_ctx *aa_alloc_task_context(gfp_t flags); +void aa_free_task_context(struct aa_task_ctx *ctx); +void aa_dup_task_context(struct aa_task_ctx *new, + const struct aa_task_ctx *old); int aa_replace_current_profile(struct aa_profile *profile); int aa_set_current_onexec(struct aa_profile *profile); int aa_set_current_hat(struct aa_profile *profile, u64 token); @@ -97,9 +97,10 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task); */ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) { - struct aa_task_cxt *cxt = cred_cxt(cred); - BUG_ON(!cxt || !cxt->profile); - return cxt->profile; + struct aa_task_ctx *ctx = cred_ctx(cred); + + AA_BUG(!ctx || !ctx->profile); + return ctx->profile; } /** @@ -149,18 +150,19 @@ static inline struct aa_profile *__aa_current_profile(void) */ static inline struct aa_profile *aa_current_profile(void) { - const struct aa_task_cxt *cxt = current_cxt(); + const struct aa_task_ctx *ctx = current_ctx(); struct aa_profile *profile; - BUG_ON(!cxt || !cxt->profile); - if (profile_is_stale(cxt->profile)) { - profile = aa_get_newest_profile(cxt->profile); + AA_BUG(!ctx || !ctx->profile); + + if (profile_is_stale(ctx->profile)) { + profile = aa_get_newest_profile(ctx->profile); aa_replace_current_profile(profile); aa_put_profile(profile); - cxt = current_cxt(); + ctx = current_ctx(); } - return cxt->profile; + return ctx->profile; } static inline struct aa_ns *aa_get_current_ns(void) @@ -169,16 +171,16 @@ static inline struct aa_ns *aa_get_current_ns(void) } /** - * aa_clear_task_cxt_trans - clear transition tracking info from the cxt - * @cxt: task context to clear (NOT NULL) + * aa_clear_task_ctx_trans - clear transition tracking info from the ctx + * @ctx: task context to clear (NOT NULL) */ -static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt) +static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) { - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); - cxt->previous = NULL; - cxt->onexec = NULL; - cxt->token = 0; + aa_put_profile(ctx->previous); + aa_put_profile(ctx->onexec); + ctx->previous = NULL; + ctx->onexec = NULL; + ctx->token = 0; } #endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 09f1c40..8466611 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -45,12 +45,12 @@ int apparmor_initialized __initdata; */ /* - * free the associated aa_task_cxt and put its profiles + * free the associated aa_task_ctx and put its profiles */ static void apparmor_cred_free(struct cred *cred) { - aa_free_task_context(cred_cxt(cred)); - cred_cxt(cred) = NULL; + aa_free_task_context(cred_ctx(cred)); + cred_ctx(cred) = NULL; } /* @@ -59,27 +59,29 @@ static void apparmor_cred_free(struct cred *cred) static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) { /* freed by apparmor_cred_free */ - struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); - if (!cxt) + struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); + + if (!ctx) return -ENOMEM; - cred_cxt(cred) = cxt; + cred_ctx(cred) = ctx; return 0; } /* - * prepare new aa_task_cxt for modification by prepare_cred block + * prepare new aa_task_ctx for modification by prepare_cred block */ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { /* freed by apparmor_cred_free */ - struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); - if (!cxt) + struct aa_task_ctx *ctx = aa_alloc_task_context(gfp); + + if (!ctx) return -ENOMEM; - aa_dup_task_context(cxt, cred_cxt(old)); - cred_cxt(new) = cxt; + aa_dup_task_context(ctx, cred_ctx(old)); + cred_ctx(new) = ctx; return 0; } @@ -88,10 +90,10 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - const struct aa_task_cxt *old_cxt = cred_cxt(old); - struct aa_task_cxt *new_cxt = cred_cxt(new); + const struct aa_task_ctx *old_ctx = cred_ctx(old); + struct aa_task_ctx *new_ctx = cred_ctx(new); - aa_dup_task_context(new_cxt, old_cxt); + aa_dup_task_context(new_ctx, old_ctx); } static int apparmor_ptrace_access_check(struct task_struct *child, @@ -345,7 +347,7 @@ static int apparmor_inode_getattr(const struct path *path) static int apparmor_file_open(struct file *file, const struct cred *cred) { - struct aa_file_cxt *fcxt = file->f_security; + struct aa_file_ctx *fctx = file->f_security; struct aa_profile *profile; int error = 0; @@ -358,7 +360,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) * actually execute the image. */ if (current->in_execve) { - fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; + fctx->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; return 0; } @@ -370,7 +372,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred) error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, aa_map_file_to_perms(file), &cond); /* todo cache full allowed permissions set and state */ - fcxt->allow = aa_map_file_to_perms(file); + fctx->allow = aa_map_file_to_perms(file); } return error; @@ -388,14 +390,14 @@ static int apparmor_file_alloc_security(struct file *file) static void apparmor_file_free_security(struct file *file) { - struct aa_file_cxt *cxt = file->f_security; + struct aa_file_ctx *ctx = file->f_security; - aa_free_file_context(cxt); + aa_free_file_context(ctx); } static int common_file_perm(int op, struct file *file, u32 mask) { - struct aa_file_cxt *fcxt = file->f_security; + struct aa_file_ctx *fctx = file->f_security; struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); int error = 0; @@ -415,7 +417,7 @@ static int common_file_perm(int op, struct file *file, u32 mask) * delegation from unconfined tasks */ if (!unconfined(profile) && !unconfined(fprofile) && - ((fprofile != profile) || (mask & ~fcxt->allow))) + ((fprofile != profile) || (mask & ~fctx->allow))) error = aa_file_perm(op, profile, file, mask); return error; @@ -477,15 +479,15 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_cxt *cxt = cred_cxt(cred); + struct aa_task_ctx *ctx = cred_ctx(cred); struct aa_profile *profile = NULL; if (strcmp(name, "current") == 0) - profile = aa_get_newest_profile(cxt->profile); - else if (strcmp(name, "prev") == 0 && cxt->previous) - profile = aa_get_newest_profile(cxt->previous); - else if (strcmp(name, "exec") == 0 && cxt->onexec) - profile = aa_get_newest_profile(cxt->onexec); + profile = aa_get_newest_profile(ctx->profile); + else if (strcmp(name, "prev") == 0 && ctx->previous) + profile = aa_get_newest_profile(ctx->previous); + else if (strcmp(name, "exec") == 0 && ctx->onexec) + profile = aa_get_newest_profile(ctx->onexec); else error = -EINVAL; @@ -849,21 +851,21 @@ static int param_set_mode(const char *val, struct kernel_param *kp) */ /** - * set_init_cxt - set a task context and profile on the first task. + * set_init_ctx - set a task context and profile on the first task. * * TODO: allow setting an alternate profile than unconfined */ -static int __init set_init_cxt(void) +static int __init set_init_ctx(void) { struct cred *cred = (struct cred *)current->real_cred; - struct aa_task_cxt *cxt; + struct aa_task_ctx *ctx; - cxt = aa_alloc_task_context(GFP_KERNEL); - if (!cxt) + ctx = aa_alloc_task_context(GFP_KERNEL); + if (!ctx) return -ENOMEM; - cxt->profile = aa_get_profile(root_ns->unconfined); - cred_cxt(cred) = cxt; + ctx->profile = aa_get_profile(root_ns->unconfined); + cred_ctx(cred) = ctx; return 0; } @@ -890,7 +892,7 @@ static int __init apparmor_init(void) goto alloc_out; } - error = set_init_cxt(); + error = set_init_ctx(); if (error) { AA_ERROR("Failed to set context on init task\n"); aa_free_root_ns(); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 912cdbe..4ec2447 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -808,7 +808,7 @@ static int __lookup_replace(struct aa_ns *ns, const char *hname, * @udata: serialized data stream (NOT NULL) * * unpack and replace a profile on the profile list and uses of that profile - * by any aa_task_cxt. If the profile does not exist on the profile list + * by any aa_task_ctx. If the profile does not exist on the profile list * it is added. * * Returns: size of data consumed else error code on failure. -- cgit v1.1 From 47f6e5cc7355e4ff2fd7ace919aa9e291077c26b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:01 -0800 Subject: apparmor: change op from int to const char * Having ops be an integer that is an index into an op name table is awkward and brittle. Every op change requires an edit for both the op constant and a string in the table. Instead switch to using const strings directly, eliminating the need for the table that needs to be kept in sync. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 4 +- security/apparmor/audit.c | 55 +------------------ security/apparmor/domain.c | 4 +- security/apparmor/file.c | 9 ++-- security/apparmor/include/audit.h | 108 ++++++++++++++++++------------------- security/apparmor/include/file.h | 9 ++-- security/apparmor/include/policy.h | 3 +- security/apparmor/lsm.c | 15 +++--- security/apparmor/policy.c | 7 +-- security/apparmor/procattr.c | 4 +- 10 files changed, 84 insertions(+), 134 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index af36af8..999a43e 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -85,7 +85,7 @@ static int mangle_name(const char *name, char *target) * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. */ -static struct aa_loaddata *aa_simple_write_to_buffer(int op, +static struct aa_loaddata *aa_simple_write_to_buffer(const char *op, const char __user *userbuf, size_t alloc_size, size_t copy_size, @@ -122,7 +122,7 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, ssize_t error; struct aa_loaddata *data; struct aa_profile *profile = aa_current_profile(); - int op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; + const char *op = binop == PROF_ADD ? OP_PROF_LOAD : OP_PROF_REPL; /* high level check about policy management - fine grained in * below after unpack */ diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 42101c4..bcd28d8 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -20,59 +20,6 @@ #include "include/policy.h" #include "include/policy_ns.h" -const char *const op_table[] = { - "null", - - "sysctl", - "capable", - - "unlink", - "mkdir", - "rmdir", - "mknod", - "truncate", - "link", - "symlink", - "rename_src", - "rename_dest", - "chmod", - "chown", - "getattr", - "open", - - "file_perm", - "file_lock", - "file_mmap", - "file_mprotect", - - "create", - "post_create", - "bind", - "connect", - "listen", - "accept", - "sendmsg", - "recvmsg", - "getsockname", - "getpeername", - "getsockopt", - "setsockopt", - "socket_shutdown", - - "ptrace", - - "exec", - "change_hat", - "change_profile", - "change_onexec", - - "setprocattr", - "setrlimit", - - "profile_replace", - "profile_load", - "profile_remove" -}; const char *const audit_mode_names[] = { "normal", @@ -120,7 +67,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca) if (sa->aad->op) { audit_log_format(ab, " operation="); - audit_log_string(ab, op_table[sa->aad->op]); + audit_log_string(ab, sa->aad->op); } if (sa->aad->info) { diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 84856b7..c2f1d65 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -750,8 +750,8 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, struct aa_profile *profile, *target = NULL; struct aa_ns *ns = NULL; struct file_perms perms = {}; - const char *name = NULL, *info = NULL; - int op, error = 0; + const char *name = NULL, *info = NULL, *op; + int error = 0; u32 request; if (!hname && !ns_name) diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 608971a..e04f044 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -104,7 +104,7 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) * Returns: %0 or error on failure */ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, int op, u32 request, const char *name, + gfp_t gfp, const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error) { int type = AUDIT_APPARMOR_AUTO; @@ -276,8 +276,9 @@ static inline bool is_deleted(struct dentry *dentry) * * Returns: %0 else error if access denied or other error */ -int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, - int flags, u32 request, struct path_cond *cond) +int aa_path_perm(const char *op, struct aa_profile *profile, + const struct path *path, int flags, u32 request, + struct path_cond *cond) { char *buffer = NULL; struct file_perms perms = {}; @@ -446,7 +447,7 @@ audit: * * Returns: %0 if access allowed else error */ -int aa_file_perm(int op, struct aa_profile *profile, struct file *file, +int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, u32 request) { struct path_cond cond = { diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index dbfb4a6..956c0b1 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -46,65 +46,63 @@ enum audit_type { AUDIT_APPARMOR_AUTO }; -extern const char *const op_table[]; -enum aa_ops { - OP_NULL, - - OP_SYSCTL, - OP_CAPABLE, - - OP_UNLINK, - OP_MKDIR, - OP_RMDIR, - OP_MKNOD, - OP_TRUNC, - OP_LINK, - OP_SYMLINK, - OP_RENAME_SRC, - OP_RENAME_DEST, - OP_CHMOD, - OP_CHOWN, - OP_GETATTR, - OP_OPEN, - - OP_FPERM, - OP_FLOCK, - OP_FMMAP, - OP_FMPROT, - - OP_CREATE, - OP_POST_CREATE, - OP_BIND, - OP_CONNECT, - OP_LISTEN, - OP_ACCEPT, - OP_SENDMSG, - OP_RECVMSG, - OP_GETSOCKNAME, - OP_GETPEERNAME, - OP_GETSOCKOPT, - OP_SETSOCKOPT, - OP_SOCK_SHUTDOWN, - - OP_PTRACE, - - OP_EXEC, - OP_CHANGE_HAT, - OP_CHANGE_PROFILE, - OP_CHANGE_ONEXEC, - - OP_SETPROCATTR, - OP_SETRLIMIT, - - OP_PROF_REPL, - OP_PROF_LOAD, - OP_PROF_RM, -}; +#define OP_NULL NULL + +#define OP_SYSCTL "sysctl" +#define OP_CAPABLE "capable" + +#define OP_UNLINK "unlink" +#define OP_MKDIR "mkdir" +#define OP_RMDIR "rmdir" +#define OP_MKNOD "mknod" +#define OP_TRUNC "truncate" +#define OP_LINK "link" +#define OP_SYMLINK "symlink" +#define OP_RENAME_SRC "rename_src" +#define OP_RENAME_DEST "rename_dest" +#define OP_CHMOD "chmod" +#define OP_CHOWN "chown" +#define OP_GETATTR "getattr" +#define OP_OPEN "open" + +#define OP_FPERM "file_perm" +#define OP_FLOCK "file_lock" +#define OP_FMMAP "file_mmap" +#define OP_FMPROT "file_mprotect" + +#define OP_CREATE "create" +#define OP_POST_CREATE "post_create" +#define OP_BIND "bind" +#define OP_CONNECT "connect" +#define OP_LISTEN "listen" +#define OP_ACCEPT "accept" +#define OP_SENDMSG "sendmsg" +#define OP_RECVMSG "recvmsg" +#define OP_GETSOCKNAME "getsockname" +#define OP_GETPEERNAME "getpeername" +#define OP_GETSOCKOPT "getsockopt" +#define OP_SETSOCKOPT "setsockopt" +#define OP_SHUTDOWN "socket_shutdown" + +#define OP_PTRACE "ptrace" + +#define OP_EXEC "exec" + +#define OP_CHANGE_HAT "change_hat" +#define OP_CHANGE_PROFILE "change_profile" +#define OP_CHANGE_ONEXEC "change_onexec" + +#define OP_SETPROCATTR "setprocattr" +#define OP_SETRLIMIT "setrlimit" + +#define OP_PROF_REPL "profile_replace" +#define OP_PROF_LOAD "profile_load" +#define OP_PROF_RM "profile_remove" struct apparmor_audit_data { int error; - int op; + const char *op; int type; void *profile; const char *name; diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 4803c97..0eb5436 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask) dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, int op, u32 request, const char *name, + gfp_t gfp, const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error); /** @@ -171,13 +171,14 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, const char *name, struct path_cond *cond, struct file_perms *perms); -int aa_path_perm(int op, struct aa_profile *profile, const struct path *path, - int flags, u32 request, struct path_cond *cond); +int aa_path_perm(const char *op, struct aa_profile *profile, + const struct path *path, int flags, u32 request, + struct path_cond *cond); int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry); -int aa_file_perm(int op, struct aa_profile *profile, struct file *file, +int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, u32 request); static inline void aa_free_file_rules(struct aa_file_rules *rules) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b315ad5..a5a9978 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -303,6 +303,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(struct aa_ns *ns); -int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op); +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, + const char *op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8466611..c751b03 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -152,7 +152,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, * * Returns: %0 else error code if error or permission denied */ -static int common_perm(int op, const struct path *path, u32 mask, +static int common_perm(const char *op, const struct path *path, u32 mask, struct path_cond *cond) { struct aa_profile *profile; @@ -175,7 +175,7 @@ static int common_perm(int op, const struct path *path, u32 mask, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_dir_dentry(int op, const struct path *dir, +static int common_perm_dir_dentry(const char *op, const struct path *dir, struct dentry *dentry, u32 mask, struct path_cond *cond) { @@ -192,7 +192,8 @@ static int common_perm_dir_dentry(int op, const struct path *dir, * * Returns: %0 else error code if error or permission denied */ -static inline int common_perm_path(int op, const struct path *path, u32 mask) +static inline int common_perm_path(const char *op, const struct path *path, + u32 mask) { struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, d_backing_inode(path->dentry)->i_mode @@ -212,7 +213,7 @@ static inline int common_perm_path(int op, const struct path *path, u32 mask) * * Returns: %0 else error code if error or permission denied */ -static int common_perm_rm(int op, const struct path *dir, +static int common_perm_rm(const char *op, const struct path *dir, struct dentry *dentry, u32 mask) { struct inode *inode = d_backing_inode(dentry); @@ -237,7 +238,7 @@ static int common_perm_rm(int op, const struct path *dir, * * Returns: %0 else error code if error or permission denied */ -static int common_perm_create(int op, const struct path *dir, +static int common_perm_create(const char *op, const struct path *dir, struct dentry *dentry, u32 mask, umode_t mode) { struct path_cond cond = { current_fsuid(), mode }; @@ -395,7 +396,7 @@ static void apparmor_file_free_security(struct file *file) aa_free_file_context(ctx); } -static int common_file_perm(int op, struct file *file, u32 mask) +static int common_file_perm(const char *op, struct file *file, u32 mask) { struct aa_file_ctx *fctx = file->f_security; struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); @@ -438,7 +439,7 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd) return common_file_perm(OP_FLOCK, file, mask); } -static int common_mmap(int op, struct file *file, unsigned long prot, +static int common_mmap(const char *op, struct file *file, unsigned long prot, unsigned long flags) { int mask = 0; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4ec2447..17754ee 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -606,7 +606,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: the error to be returned after audit is done */ -static int audit_policy(struct aa_profile *profile, int op, gfp_t gfp, +static int audit_policy(struct aa_profile *profile, const char *op, gfp_t gfp, const char *nsname, const char *name, const char *info, int error) { @@ -670,7 +670,8 @@ bool policy_admin_capable(struct aa_ns *ns) * * Returns: 0 if the task is allowed to manipulate policy else error */ -int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, int op) +int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, + const char *op) { /* check if loading policy is locked out */ if (aa_g_lock_policy) @@ -819,7 +820,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, const char *ns_name, *info = NULL; struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; - int op = OP_PROF_REPL; + const char *op = OP_PROF_REPL; ssize_t count, error; LIST_HEAD(lh); diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 1babd36..4cb5f3d 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -88,13 +88,13 @@ int aa_getprocattr(struct aa_profile *profile, char **string) * * Returns: start position of name after token else NULL on failure */ -static char *split_token_from_name(int op, char *args, u64 * token) +static char *split_token_from_name(const char *op, char *args, u64 *token) { char *name; *token = simple_strtoull(args, &name, 16); if ((name == args) || *name != '^') { - AA_ERROR("%s: Invalid input '%s'", op_table[op], args); + AA_ERROR("%s: Invalid input '%s'", op, args); return ERR_PTR(-EINVAL); } -- cgit v1.1 From ef88a7ac55fdd3bf6ac3942b83aa29311b45339b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:02 -0800 Subject: apparmor: change aad apparmor_audit_data macro to a fn macro The aad macro can replace aad strings when it is not intended to. Switch to a fn macro so it is only applied when intended. Also at the same time cleanup audit_data initialization by putting common boiler plate behind a macro, and dropping the gfp_t parameter which will become useless. Signed-off-by: John Johansen --- security/apparmor/audit.c | 42 ++++++++++++------------ security/apparmor/capability.c | 10 ++---- security/apparmor/domain.c | 13 ++++---- security/apparmor/file.c | 69 +++++++++++++++++++-------------------- security/apparmor/include/audit.h | 43 +++++++++++++++++------- security/apparmor/include/file.h | 2 +- security/apparmor/ipc.c | 18 ++++------ security/apparmor/lib.c | 8 ++--- security/apparmor/lsm.c | 12 +++---- security/apparmor/policy.c | 42 +++++++++++------------- security/apparmor/policy_unpack.c | 38 +++++++++++---------- security/apparmor/resource.c | 19 ++++------- 12 files changed, 155 insertions(+), 161 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index bcd28d8..0c81ff64 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -62,23 +62,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca) if (aa_g_audit_header) { audit_log_format(ab, "apparmor="); - audit_log_string(ab, aa_audit_type[sa->aad->type]); + audit_log_string(ab, aa_audit_type[aad(sa)->type]); } - if (sa->aad->op) { + if (aad(sa)->op) { audit_log_format(ab, " operation="); - audit_log_string(ab, sa->aad->op); + audit_log_string(ab, aad(sa)->op); } - if (sa->aad->info) { + if (aad(sa)->info) { audit_log_format(ab, " info="); - audit_log_string(ab, sa->aad->info); - if (sa->aad->error) - audit_log_format(ab, " error=%d", sa->aad->error); + audit_log_string(ab, aad(sa)->info); + if (aad(sa)->error) + audit_log_format(ab, " error=%d", aad(sa)->error); } - if (sa->aad->profile) { - struct aa_profile *profile = sa->aad->profile; + if (aad(sa)->profile) { + struct aa_profile *profile = aad(sa)->profile; if (profile->ns != root_ns) { audit_log_format(ab, " namespace="); audit_log_untrustedstring(ab, profile->ns->base.hname); @@ -87,9 +87,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca) audit_log_untrustedstring(ab, profile->base.hname); } - if (sa->aad->name) { + if (aad(sa)->name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, sa->aad->name); + audit_log_untrustedstring(ab, aad(sa)->name); } } @@ -101,7 +101,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca) void aa_audit_msg(int type, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)) { - sa->aad->type = type; + aad(sa)->type = type; common_lsm_audit(sa, audit_pre, cb); } @@ -109,7 +109,6 @@ void aa_audit_msg(int type, struct common_audit_data *sa, * aa_audit - Log a profile based audit event to the audit subsystem * @type: audit type for the message * @profile: profile to check against (NOT NULL) - * @gfp: allocation flags to use * @sa: audit event (NOT NULL) * @cb: optional callback fn for type specific fields (MAYBE NULL) * @@ -117,14 +116,13 @@ void aa_audit_msg(int type, struct common_audit_data *sa, * * Returns: error on failure */ -int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, - struct common_audit_data *sa, +int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)) { BUG_ON(!profile); if (type == AUDIT_APPARMOR_AUTO) { - if (likely(!sa->aad->error)) { + if (likely(!aad(sa)->error)) { if (AUDIT_MODE(profile) != AUDIT_ALL) return 0; type = AUDIT_APPARMOR_AUDIT; @@ -136,23 +134,23 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (AUDIT_MODE(profile) == AUDIT_QUIET || (type == AUDIT_APPARMOR_DENIED && AUDIT_MODE(profile) == AUDIT_QUIET)) - return sa->aad->error; + return aad(sa)->error; if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) type = AUDIT_APPARMOR_KILL; if (!unconfined(profile)) - sa->aad->profile = profile; + aad(sa)->profile = profile; aa_audit_msg(type, sa, cb); - if (sa->aad->type == AUDIT_APPARMOR_KILL) + if (aad(sa)->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? sa->u.tsk : current); - if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) - return complain_error(sa->aad->error); + if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED) + return complain_error(aad(sa)->error); - return sa->aad->error; + return aad(sa)->error; } diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 1101c6f..1d2e2de 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -66,13 +66,9 @@ static int audit_caps(struct aa_profile *profile, int cap, int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_CAP; - sa.aad = &aad; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); sa.u.cap = cap; - sa.aad->op = OP_CAPABLE; - sa.aad->error = error; + aad(&sa)->error = error; if (likely(!error)) { /* test if auditing is being forced */ @@ -104,7 +100,7 @@ static int audit_caps(struct aa_profile *profile, int cap, int error) } put_cpu_var(audit_cache); - return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); + return aa_audit(type, profile, &sa, audit_cb); } /** diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index c2f1d65..d18b3f0 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -508,8 +508,7 @@ x_clear: aa_clear_task_ctx_trans(ctx); audit: - error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, + error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, new_profile ? new_profile->base.hname : NULL, cond.uid, info, error); @@ -714,9 +713,9 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) audit: if (!permtest) - error = aa_audit_file(profile, &perms, GFP_KERNEL, - OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, - target, GLOBAL_ROOT_UID, info, error); + error = aa_audit_file(profile, &perms, OP_CHANGE_HAT, + AA_MAY_CHANGEHAT, NULL, target, + GLOBAL_ROOT_UID, info, error); out: aa_put_profile(hat); @@ -842,8 +841,8 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, audit: if (!permtest) - error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, - name, hname, GLOBAL_ROOT_UID, info, error); + error = aa_audit_file(profile, &perms, op, request, name, + hname, GLOBAL_ROOT_UID, info, error); aa_put_ns(ns); aa_put_profile(target); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index e04f044..750564c 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -67,24 +67,24 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; kuid_t fsuid = current_fsuid(); - if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { + if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " requested_mask="); - audit_file_mask(ab, sa->aad->fs.request); + audit_file_mask(ab, aad(sa)->fs.request); } - if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) { + if (aad(sa)->fs.denied & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " denied_mask="); - audit_file_mask(ab, sa->aad->fs.denied); + audit_file_mask(ab, aad(sa)->fs.denied); } - if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) { + if (aad(sa)->fs.request & AA_AUDIT_FILE_MASK) { audit_log_format(ab, " fsuid=%d", from_kuid(&init_user_ns, fsuid)); audit_log_format(ab, " ouid=%d", - from_kuid(&init_user_ns, sa->aad->fs.ouid)); + from_kuid(&init_user_ns, aad(sa)->fs.ouid)); } - if (sa->aad->fs.target) { + if (aad(sa)->fs.target) { audit_log_format(ab, " target="); - audit_log_untrustedstring(ab, sa->aad->fs.target); + audit_log_untrustedstring(ab, aad(sa)->fs.target); } } @@ -104,54 +104,53 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) * Returns: %0 or error on failure */ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, const char *op, u32 request, const char *name, + const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error) { int type = AUDIT_APPARMOR_AUTO; - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_TASK; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op); + + sa.u.tsk = NULL; + aad(&sa)->fs.request = request; + aad(&sa)->name = name; + aad(&sa)->fs.target = target; + aad(&sa)->fs.ouid = ouid; + aad(&sa)->info = info; + aad(&sa)->error = error; sa.u.tsk = NULL; - sa.aad = &aad; - aad.op = op, - aad.fs.request = request; - aad.name = name; - aad.fs.target = target; - aad.fs.ouid = ouid; - aad.info = info; - aad.error = error; - - if (likely(!sa.aad->error)) { + + if (likely(!aad(&sa)->error)) { u32 mask = perms->audit; if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) mask = 0xffff; /* mask off perms that are not being force audited */ - sa.aad->fs.request &= mask; + aad(&sa)->fs.request &= mask; - if (likely(!sa.aad->fs.request)) + if (likely(!aad(&sa)->fs.request)) return 0; type = AUDIT_APPARMOR_AUDIT; } else { /* only report permissions that were denied */ - sa.aad->fs.request = sa.aad->fs.request & ~perms->allow; + aad(&sa)->fs.request = aad(&sa)->fs.request & ~perms->allow; + AA_BUG(!aad(&sa)->fs.request); - if (sa.aad->fs.request & perms->kill) + if (aad(&sa)->fs.request & perms->kill) type = AUDIT_APPARMOR_KILL; /* quiet known rejects, assumes quiet and kill do not overlap */ - if ((sa.aad->fs.request & perms->quiet) && + if ((aad(&sa)->fs.request & perms->quiet) && AUDIT_MODE(profile) != AUDIT_NOQUIET && AUDIT_MODE(profile) != AUDIT_ALL) - sa.aad->fs.request &= ~perms->quiet; + aad(&sa)->fs.request &= ~perms->quiet; - if (!sa.aad->fs.request) - return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; + if (!aad(&sa)->fs.request) + return COMPLAIN_MODE(profile) ? 0 : aad(&sa)->error; } - sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow; - return aa_audit(type, profile, gfp, &sa, file_audit_cb); + aad(&sa)->fs.denied = aad(&sa)->fs.request & ~perms->allow; + return aa_audit(type, profile, &sa, file_audit_cb); } /** @@ -302,8 +301,8 @@ int aa_path_perm(const char *op, struct aa_profile *profile, if (request & ~perms.allow) error = -EACCES; } - error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, - NULL, cond->uid, info, error); + error = aa_audit_file(profile, &perms, op, request, name, NULL, + cond->uid, info, error); kfree(buffer); return error; @@ -430,7 +429,7 @@ done_tests: error = 0; audit: - error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, + error = aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname, cond.uid, info, error); kfree(buffer); kfree(buffer2); diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 956c0b1..fdc4774 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -108,34 +108,53 @@ struct apparmor_audit_data { const char *name; const char *info; union { - void *target; + /* these entries require a custom callback fn */ struct { + struct aa_profile *peer; + struct { + const char *target; + u32 request; + u32 denied; + kuid_t ouid; + } fs; + }; + struct { + const char *name; long pos; const char *ns; - void *target; } iface; struct { int rlim; unsigned long max; } rlim; - struct { - const char *target; - u32 request; - u32 denied; - kuid_t ouid; - } fs; }; }; -/* define a short hand for apparmor_audit_data structure */ -#define aad apparmor_audit_data +/* macros for dealing with apparmor_audit_data structure */ +#define aad(SA) ((SA)->apparmor_audit_data) +#define DEFINE_AUDIT_DATA(NAME, T, X) \ + /* TODO: cleanup audit init so we don't need _aad = {0,} */ \ + struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \ + struct common_audit_data NAME = \ + { \ + .type = (T), \ + .u.tsk = NULL, \ + }; \ + NAME.apparmor_audit_data = &(NAME ## _aad) void aa_audit_msg(int type, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)); -int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, - struct common_audit_data *sa, +int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)); +#define aa_audit_error(ERROR, SA, CB) \ +({ \ + aad((SA))->error = (ERROR); \ + aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB)); \ + aad((SA))->error; \ +}) + + static inline int complain_error(int error) { if (error == -EPERM || error == -EACCES) diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 0eb5436..38f821b 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -145,7 +145,7 @@ static inline u16 dfa_map_xindex(u16 mask) dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, const char *op, u32 request, const char *name, + const char *op, u32 request, const char *name, const char *target, kuid_t ouid, const char *info, int error); /** diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 777ac1c..edac790 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -25,8 +25,8 @@ static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - audit_log_format(ab, " target="); - audit_log_untrustedstring(ab, sa->aad->target); + audit_log_format(ab, " peer="); + audit_log_untrustedstring(ab, aad(sa)->peer->base.hname); } /** @@ -40,16 +40,12 @@ static void audit_cb(struct audit_buffer *ab, void *va) static int aa_audit_ptrace(struct aa_profile *profile, struct aa_profile *target, int error) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = OP_PTRACE; - aad.target = target; - aad.error = error; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); - return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, - audit_cb); + aad(&sa)->peer = target; + aad(&sa)->error = error; + + return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); } /** diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 5d8ef31..66475bd 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -120,11 +120,9 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, void aa_info_message(const char *str) { if (audit_enabled) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.info = str; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); + + aad(&sa)->info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); } printk(KERN_INFO "AppArmor: %s\n", str); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c751b03..c4bae8a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -504,11 +504,10 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(struct task_struct *task, char *name, void *value, size_t size) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; char *command, *largs = NULL, *args = value; size_t arg_size; int error; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR); if (size == 0) return -EINVAL; @@ -568,12 +567,9 @@ out: return error; fail: - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.profile = aa_current_profile(); - aad.op = OP_SETPROCATTR; - aad.info = name; - aad.error = error = -EINVAL; + aad(&sa)->profile = aa_current_profile(); + aad(&sa)->info = name; + aad(&sa)->error = error = -EINVAL; aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); goto out; } diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 17754ee..bc63cf7 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -588,9 +588,9 @@ static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - if (sa->aad->iface.ns) { + if (aad(sa)->iface.ns) { audit_log_format(ab, " ns="); - audit_log_untrustedstring(ab, sa->aad->iface.ns); + audit_log_untrustedstring(ab, aad(sa)->iface.ns); } } @@ -606,22 +606,18 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: the error to be returned after audit is done */ -static int audit_policy(struct aa_profile *profile, const char *op, gfp_t gfp, +static int audit_policy(struct aa_profile *profile, const char *op, const char *nsname, const char *name, const char *info, int error) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = op; - aad.iface.ns = nsname; - aad.name = name; - aad.info = info; - aad.error = error; - - return aa_audit(AUDIT_APPARMOR_STATUS, profile, gfp, - &sa, audit_cb); + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); + + aad(&sa)->iface.ns = nsname; + aad(&sa)->name = name; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); } /** @@ -675,11 +671,11 @@ int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, { /* check if loading policy is locked out */ if (aa_g_lock_policy) - return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, + return audit_policy(profile, op, NULL, NULL, "policy_locked", -EACCES); if (!policy_admin_capable(ns)) - return audit_policy(profile, op, GFP_KERNEL, NULL, NULL, + return audit_policy(profile, op, NULL, NULL, "not policy admin", -EACCES); /* TODO: add fine grained mediation of policy loads */ @@ -937,8 +933,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(profile, op, GFP_ATOMIC, NULL, - ent->new->base.hname, NULL, error); + audit_policy(profile, op, NULL, ent->new->base.hname, + NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); @@ -993,7 +989,7 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; fail: - audit_policy(profile, op, GFP_KERNEL, ns_name, ent->new->base.hname, + audit_policy(profile, op, ns_name, ent->new->base.hname, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -1004,7 +1000,7 @@ fail: continue; } op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(profile, op, GFP_KERNEL, ns_name, + audit_policy(profile, op, ns_name, tmp->new->base.hname, info, error); } list_for_each_entry_safe(ent, tmp, &lh, list) { @@ -1079,7 +1075,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *subj, } /* don't fail removal if audit fails */ - (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info, error); aa_put_ns(ns); aa_put_profile(profile); @@ -1090,7 +1086,7 @@ fail_ns_lock: aa_put_ns(ns); fail: - (void) audit_policy(subj, OP_PROF_RM, GFP_KERNEL, ns_name, name, info, + (void) audit_policy(subj, OP_PROF_RM, ns_name, name, info, error); return error; } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 38c148f..441efc9 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -79,13 +79,17 @@ struct aa_ext { static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; - if (sa->aad->iface.target) { - struct aa_profile *name = sa->aad->iface.target; + + if (aad(sa)->iface.ns) { + audit_log_format(ab, " ns="); + audit_log_untrustedstring(ab, aad(sa)->iface.ns); + } + if (aad(sa)->iface.name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, name->base.hname); + audit_log_untrustedstring(ab, aad(sa)->iface.name); } - if (sa->aad->iface.pos) - audit_log_format(ab, " offset=%ld", sa->aad->iface.pos); + if (aad(sa)->iface.pos) + audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); } /** @@ -104,20 +108,18 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, int error) { struct aa_profile *profile = __aa_current_profile(); - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.iface.ns = ns_name; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); if (e) - aad.iface.pos = e->pos - e->start; - aad.iface.target = new; - aad.name = name; - aad.info = info; - aad.error = error; - - return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, - audit_cb); + aad(&sa)->iface.pos = e->pos - e->start; + aad(&sa)->iface.ns = ns_name; + if (new) + aad(&sa)->iface.name = new->base.hname; + else + aad(&sa)->iface.name = name; + aad(&sa)->info = info; + aad(&sa)->error = error; + + return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); } void aa_loaddata_kref(struct kref *kref) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 67a6072..86a941a 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -35,7 +35,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) struct common_audit_data *sa = va; audit_log_format(ab, " rlimit=%s value=%lu", - rlim_names[sa->aad->rlim.rlim], sa->aad->rlim.max); + rlim_names[aad(sa)->rlim.rlim], aad(sa)->rlim.max); } /** @@ -50,17 +50,12 @@ static void audit_cb(struct audit_buffer *ab, void *va) static int audit_resource(struct aa_profile *profile, unsigned int resource, unsigned long value, int error) { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = OP_SETRLIMIT, - aad.rlim.rlim = resource; - aad.rlim.max = value; - aad.error = error; - return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, - audit_cb); + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT); + + aad(&sa)->rlim.rlim = resource; + aad(&sa)->rlim.max = value; + aad(&sa)->error = error; + return aa_audit(AUDIT_APPARMOR_AUTO, profile, &sa, audit_cb); } /** -- cgit v1.1 From 5ef50d014c59240c2cac38594377583c4e9ea4fa Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:03 -0800 Subject: apparmor: remove unused op parameter from simple_write_to_buffer() Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 999a43e..fd0d9e3 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -76,7 +76,6 @@ static int mangle_name(const char *name, char *target) /** * aa_simple_write_to_buffer - common routine for getting policy from user - * @op: operation doing the user buffer copy * @userbuf: user buffer to copy data from (NOT NULL) * @alloc_size: size of user buffer (REQUIRES: @alloc_size >= @copy_size) * @copy_size: size of data to copy from user buffer @@ -85,8 +84,7 @@ static int mangle_name(const char *name, char *target) * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. */ -static struct aa_loaddata *aa_simple_write_to_buffer(const char *op, - const char __user *userbuf, +static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, size_t alloc_size, size_t copy_size, loff_t *pos) @@ -130,7 +128,7 @@ static ssize_t policy_update(int binop, const char __user *buf, size_t size, if (error) return error; - data = aa_simple_write_to_buffer(op, buf, size, size, pos); + data = aa_simple_write_to_buffer(buf, size, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { error = aa_replace_profiles(ns ? ns : profile->ns, profile, @@ -196,8 +194,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, * aa_remove_profile needs a null terminated string so 1 extra * byte is allocated and the copied data is null terminated. */ - data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, - pos); + data = aa_simple_write_to_buffer(buf, size + 1, size, pos); error = PTR_ERR(data); if (!IS_ERR(data)) { -- cgit v1.1 From c3e1e584ad3853b3f13ea4bd0aabd43e6c9b9fda Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:05 -0800 Subject: apparmor: fix change_hat debug output Signed-off-by: John Johansen --- security/apparmor/procattr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 4cb5f3d..a9a9ee6 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -139,12 +139,13 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) for (count = 0; (hat < end) && count < 16; ++count) { char *next = hat + strlen(hat) + 1; hats[count] = hat; + AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d hat '%s'\n" + , __func__, current->pid, token, count, hat); hat = next; } - } - - AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", - __func__, token, hat ? hat : NULL); + } else + AA_DEBUG("%s: (pid %d) Magic 0x%llx count %d Hat '%s'\n", + __func__, current->pid, token, count, ""); return aa_change_hat(hats, count, token, test); } -- cgit v1.1 From aa9a39ad8f60cc73e1bd2f18f0693bba6be8b067 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:06 -0800 Subject: apparmor: convert change_profile to use fqname later to give better control Moving the use of fqname to later allows learning profiles to be based on the fqname request instead of just the hname. It also allows cleaning up some of the name parsing and lookup by allowing the use of the fqlookupn_profile() lib fn. Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 1 + security/apparmor/domain.c | 61 ++++++++++++-------------------------- security/apparmor/include/domain.h | 4 +-- security/apparmor/lsm.c | 12 ++++---- security/apparmor/procattr.c | 16 ---------- 5 files changed, 28 insertions(+), 66 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index fd0d9e3..7613a28 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1052,6 +1052,7 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { AA_FS_FILE_BOOLEAN("change_onexec", 1), AA_FS_FILE_BOOLEAN("change_profile", 1), AA_FS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1), + AA_FS_FILE_STRING("version", "1.2"), { } }; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index d18b3f0..ef4beef 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -729,8 +729,7 @@ out: /** * aa_change_profile - perform a one-way profile transition - * @ns_name: name of the profile namespace to change to (MAYBE NULL) - * @hname: name of profile to change to (MAYBE NULL) + * @fqname: name of profile may include namespace (NOT NULL) * @onexec: whether this transition is to take place immediately or at exec * @permtest: true if this is just a permission test * @@ -742,19 +741,20 @@ out: * * Returns %0 on success, error otherwise. */ -int aa_change_profile(const char *ns_name, const char *hname, bool onexec, - bool permtest) +int aa_change_profile(const char *fqname, bool onexec, + bool permtest, bool stack) { const struct cred *cred; struct aa_profile *profile, *target = NULL; - struct aa_ns *ns = NULL; struct file_perms perms = {}; - const char *name = NULL, *info = NULL, *op; + const char *info = NULL, *op; int error = 0; u32 request; - if (!hname && !ns_name) + if (!fqname || !*fqname) { + AA_DEBUG("no profile name"); return -EINVAL; + } if (onexec) { request = AA_MAY_ONEXEC; @@ -779,44 +779,15 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, return -EPERM; } - if (ns_name) { - /* released below */ - ns = aa_find_ns(profile->ns, ns_name); - if (!ns) { - /* we don't create new namespace in complain mode */ - name = ns_name; - info = "namespace not found"; - error = -ENOENT; - goto audit; - } - } else - /* released below */ - ns = aa_get_ns(profile->ns); - - /* if the name was not specified, use the name of the current profile */ - if (!hname) { - if (unconfined(profile)) - hname = ns->unconfined->base.hname; - else - hname = profile->base.hname; - } - - perms = change_profile_perms(profile, ns, hname, request, - profile->file.start); - if (!(perms.allow & request)) { - error = -EACCES; - goto audit; - } - - /* released below */ - target = aa_lookup_profile(ns, hname); + target = aa_fqlookupn_profile(profile, fqname, strlen(fqname)); if (!target) { info = "profile not found"; error = -ENOENT; if (permtest || !COMPLAIN_MODE(profile)) goto audit; /* released below */ - target = aa_new_null_profile(profile, false, hname, GFP_KERNEL); + target = aa_new_null_profile(profile, false, fqname, + GFP_KERNEL); if (!target) { info = "failed null profile create"; error = -ENOMEM; @@ -824,6 +795,13 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } } + perms = change_profile_perms(profile, target->ns, target->base.hname, + request, profile->file.start); + if (!(perms.allow & request)) { + error = -EACCES; + goto audit; + } + /* check if tracing task is allowed to trace target domain */ error = may_change_ptraced_domain(target); if (error) { @@ -841,10 +819,9 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, audit: if (!permtest) - error = aa_audit_file(profile, &perms, op, request, name, - hname, GLOBAL_ROOT_UID, info, error); + error = aa_audit_file(profile, &perms, op, request, NULL, + fqname, GLOBAL_ROOT_UID, info, error); - aa_put_ns(ns); aa_put_profile(target); put_cred(cred); diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index de04464..3054472 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -30,7 +30,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm); void aa_free_domain_entries(struct aa_domain *domain); int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); -int aa_change_profile(const char *ns_name, const char *name, bool onexec, - bool permtest); +int aa_change_profile(const char *fqname, bool onexec, bool permtest, + bool stack); #endif /* __AA_DOMAIN_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c4bae8a..264aa19 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -543,17 +543,17 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, error = aa_setprocattr_changehat(args, arg_size, AA_DO_TEST); } else if (strcmp(command, "changeprofile") == 0) { - error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, - !AA_DO_TEST); + error = aa_change_profile(args, !AA_ONEXEC, + !AA_DO_TEST, false); } else if (strcmp(command, "permprofile") == 0) { - error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, - AA_DO_TEST); + error = aa_change_profile(args, !AA_ONEXEC, AA_DO_TEST, + false); } else goto fail; } else if (strcmp(name, "exec") == 0) { if (strcmp(command, "exec") == 0) - error = aa_setprocattr_changeprofile(args, AA_ONEXEC, - !AA_DO_TEST); + error = aa_change_profile(args, AA_ONEXEC, !AA_DO_TEST, + false); else goto fail; } else diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index a9a9ee6..3466a27 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -149,19 +149,3 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) return aa_change_hat(hats, count, token, test); } - -/** - * aa_setprocattr_changeprofile - handle procattr interface to changeprofile - * @fqname: args received from writting to /proc//attr/current (NOT NULL) - * @onexec: true if change_profile should be delayed until exec - * @test: true if this is a test of change_profile permissions - * - * Returns: %0 or error code if change_profile fails - */ -int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) -{ - char *name, *ns_name; - - name = aa_split_fqname(fqname, &ns_name); - return aa_change_profile(ns_name, name, onexec, test); -} -- cgit v1.1 From 31f75bfecd9cef7d485b1cda3c6c38cc0b4a5c6c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:07 -0800 Subject: apparmor: make computing policy hashes conditional on kernel parameter Allow turning off the computation of the policy hashes via the apparmor.hash_policy kernel parameter. Signed-off-by: John Johansen --- security/apparmor/lsm.c | 46 +++++++++++++++++++-------------------- security/apparmor/policy_unpack.c | 15 ++++++++----- 2 files changed, 32 insertions(+), 29 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 264aa19..6a5cf54 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -166,42 +166,42 @@ static int common_perm(const char *op, const struct path *path, u32 mask, } /** - * common_perm_dir_dentry - common permission wrapper when path is dir, dentry + * common_perm_cond - common permission wrapper around inode cond * @op: operation being checked - * @dir: directory of the dentry (NOT NULL) - * @dentry: dentry to check (NOT NULL) + * @path: location to check (NOT NULL) * @mask: requested permissions mask - * @cond: conditional info for the permission request (NOT NULL) * * Returns: %0 else error code if error or permission denied */ -static int common_perm_dir_dentry(const char *op, const struct path *dir, - struct dentry *dentry, u32 mask, - struct path_cond *cond) +static int common_perm_cond(const char *op, const struct path *path, u32 mask) { - struct path path = { .mnt = dir->mnt, .dentry = dentry }; + struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, + d_backing_inode(path->dentry)->i_mode + }; - return common_perm(op, &path, mask, cond); + if (!path_mediated_fs(path->dentry)) + return 0; + + return common_perm(op, path, mask, &cond); } /** - * common_perm_path - common permission wrapper when mnt, dentry + * common_perm_dir_dentry - common permission wrapper when path is dir, dentry * @op: operation being checked - * @path: location to check (NOT NULL) + * @dir: directory of the dentry (NOT NULL) + * @dentry: dentry to check (NOT NULL) * @mask: requested permissions mask + * @cond: conditional info for the permission request (NOT NULL) * * Returns: %0 else error code if error or permission denied */ -static inline int common_perm_path(const char *op, const struct path *path, - u32 mask) +static int common_perm_dir_dentry(const char *op, const struct path *dir, + struct dentry *dentry, u32 mask, + struct path_cond *cond) { - struct path_cond cond = { d_backing_inode(path->dentry)->i_uid, - d_backing_inode(path->dentry)->i_mode - }; - if (!path_mediated_fs(path->dentry)) - return 0; + struct path path = { .mnt = dir->mnt, .dentry = dentry }; - return common_perm(op, path, mask, &cond); + return common_perm(op, &path, mask, cond); } /** @@ -274,7 +274,7 @@ static int apparmor_path_mknod(const struct path *dir, struct dentry *dentry, static int apparmor_path_truncate(const struct path *path) { - return common_perm_path(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); + return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE); } static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry, @@ -333,17 +333,17 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d static int apparmor_path_chmod(const struct path *path, umode_t mode) { - return common_perm_path(OP_CHMOD, path, AA_MAY_CHMOD); + return common_perm_cond(OP_CHMOD, path, AA_MAY_CHMOD); } static int apparmor_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { - return common_perm_path(OP_CHOWN, path, AA_MAY_CHOWN); + return common_perm_cond(OP_CHOWN, path, AA_MAY_CHOWN); } static int apparmor_inode_getattr(const struct path *path) { - return common_perm_path(OP_GETATTR, path, AA_MAY_META_READ); + return common_perm_cond(OP_GETATTR, path, AA_MAY_META_READ); } static int apparmor_file_open(struct file *file, const struct cred *cred) diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 441efc9..59c891a 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -825,7 +825,8 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, if (error) goto fail_profile; - error = aa_calc_profile_hash(profile, e.version, start, + if (aa_g_hash_policy) + error = aa_calc_profile_hash(profile, e.version, start, e.pos - start); if (error) goto fail_profile; @@ -841,11 +842,13 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, list_add_tail(&ent->list, lh); } udata->abi = e.version & K_ABI_MASK; - udata->hash = aa_calc_hash(udata->data, udata->size); - if (IS_ERR(udata->hash)) { - error = PTR_ERR(udata->hash); - udata->hash = NULL; - goto fail; + if (aa_g_hash_policy) { + udata->hash = aa_calc_hash(udata->data, udata->size); + if (IS_ERR(udata->hash)) { + error = PTR_ERR(udata->hash); + udata->hash = NULL; + goto fail; + } } return 0; -- cgit v1.1 From 12eb87d50bfe234c3f964e9fb47bbd0135010c13 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 16 Jan 2017 00:43:08 -0800 Subject: apparmor: update cap audit to check SECURITY_CAP_NOAUDIT apparmor should be checking the SECURITY_CAP_NOAUDIT constant. Also in complain mode make it so apparmor can elect to log a message, informing of the check. Signed-off-by: John Johansen --- security/apparmor/capability.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'security/apparmor') diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 1d2e2de..ed0a3e6 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "include/apparmor.h" #include "include/capability.h" @@ -55,6 +56,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) * audit_caps - audit a capability * @profile: profile being tested for confinement (NOT NULL) * @cap: capability tested + @audit: whether an audit record should be generated * @error: error code returned by test * * Do auditing of capability and handle, audit/complain/kill modes switching @@ -62,13 +64,16 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: 0 or sa->error on success, error code on failure */ -static int audit_caps(struct aa_profile *profile, int cap, int error) +static int audit_caps(struct aa_profile *profile, int cap, int audit, + int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); sa.u.cap = cap; aad(&sa)->error = error; + if (audit == SECURITY_CAP_NOAUDIT) + aad(&sa)->info = "optional: no audit"; if (likely(!error)) { /* test if auditing is being forced */ @@ -129,11 +134,10 @@ int aa_capable(struct aa_profile *profile, int cap, int audit) { int error = profile_capable(profile, cap); - if (!audit) { - if (COMPLAIN_MODE(profile)) - return complain_error(error); - return error; + if (audit == SECURITY_CAP_NOAUDIT) { + if (!COMPLAIN_MODE(profile)) + return error; } - return audit_caps(profile, cap, error); + return audit_caps(profile, cap, audit, error); } -- cgit v1.1 From e025be0f26d5597b0a2bdfa65145a0171e77b614 Mon Sep 17 00:00:00 2001 From: William Hua Date: Sun, 15 Jan 2017 16:49:28 -0800 Subject: apparmor: support querying extended trusted helper extra data Allow a profile to carry extra data that can be queried via userspace. This provides a means to store extra data in a profile that a trusted helper can extract and use from live policy. Signed-off-by: William Hua Signed-off-by: John Johansen --- security/apparmor/apparmorfs.c | 139 +++++++++++++++++++++++++++++++++++++ security/apparmor/include/policy.h | 16 +++++ security/apparmor/lsm.c | 1 + security/apparmor/policy.c | 23 ++++++ security/apparmor/policy_unpack.c | 66 ++++++++++++++++++ 5 files changed, 245 insertions(+) (limited to 'security/apparmor') diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 7613a28..6834000 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -213,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = { .llseek = default_llseek, }; +/** + * query_data - queries a policy and writes its data to buf + * @buf: the resulting data is stored here (NOT NULL) + * @buf_len: size of buf + * @query: query string used to retrieve data + * @query_len: size of query including second NUL byte + * + * The buffers pointed to by buf and query may overlap. The query buffer is + * parsed before buf is written to. + * + * The query should look like "