diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-10 10:53:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-10 10:53:22 -0700 |
commit | 00aa9d0bbfec91dcf191dd7899a65ab28b6cc76d (patch) | |
tree | 467d829daf4991c4aa340d81d1207aca104b5bfa /security | |
parent | 0af5cb349a2c97fbabb3cede96efcde9d54b7940 (diff) | |
parent | c269fca7b37a08b7eec6f6b79a0abf1d0a245acb (diff) |
Merge tag 'apparmor-pr-2022-08-08' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull AppArmor updates from John Johansen:
"This is mostly cleanups and bug fixes with the one bigger change being
Mathew Wilcox's patch to use XArrays instead of the IDR from the
thread around the locking weirdness.
Features:
- Convert secid mapping to XArrays instead of IDR
- Add a kernel label to use on kernel objects
- Extend policydb permission set by making use of the xbits
- Make export of raw binary profile to userspace optional
- Enable tuning of policy paranoid load for embedded systems
- Don't create raw_sha1 symlink if sha1 hashing is disabled
- Allow labels to carry debug flags
Cleanups:
- Update MAINTAINERS file
- Use struct_size() helper in kmalloc()
- Move ptrace mediation to more logical task.{h,c}
- Resolve uninitialized symbol warnings
- Remove redundant ret variable
- Mark alloc_unconfined() as static
- Update help description of policy hash for introspection
- Remove some casts which are no-longer required
Bug Fixes:
- Fix aa_label_asxprint return check
- Fix reference count leak in aa_pivotroot()
- Fix memleak in aa_simple_write_to_buffer()
- Fix kernel doc comments
- Fix absroot causing audited secids to begin with =
- Fix quiet_denied for file rules
- Fix failed mount permission check error message
- Disable showing the mode as part of a secid to secctx
- Fix setting unconfined mode on a loaded profile
- Fix overlapping attachment computation
- Fix undefined reference to `zlib_deflate_workspacesize'"
* tag 'apparmor-pr-2022-08-08' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (34 commits)
apparmor: Update MAINTAINERS file with new email address
apparmor: correct config reference to intended one
apparmor: move ptrace mediation to more logical task.{h,c}
apparmor: extend policydb permission set by making use of the xbits
apparmor: allow label to carry debug flags
apparmor: fix overlapping attachment computation
apparmor: fix setting unconfined mode on a loaded profile
apparmor: Fix some kernel-doc comments
apparmor: Mark alloc_unconfined() as static
apparmor: disable showing the mode as part of a secid to secctx
apparmor: Convert secid mapping to XArrays instead of IDR
apparmor: add a kernel label to use on kernel objects
apparmor: test: Remove some casts which are no-longer required
apparmor: Fix memleak in aa_simple_write_to_buffer()
apparmor: fix reference count leak in aa_pivotroot()
apparmor: Fix some kernel-doc comments
apparmor: Fix undefined reference to `zlib_deflate_workspacesize'
apparmor: fix aa_label_asxprint return check
apparmor: Fix some kernel-doc comments
apparmor: Fix some kernel-doc comments
...
Diffstat (limited to 'security')
29 files changed, 486 insertions, 338 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 348ed6cfa08a..cb3496e00d8a 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -6,8 +6,6 @@ config SECURITY_APPARMOR select SECURITY_PATH select SECURITYFS select SECURITY_NETWORK - select ZLIB_INFLATE - select ZLIB_DEFLATE default n help This enables the AppArmor security module. @@ -17,29 +15,6 @@ config SECURITY_APPARMOR If you are unsure how to answer this question, answer N. -config SECURITY_APPARMOR_HASH - bool "Enable introspection of sha1 hashes for loaded profiles" - depends on SECURITY_APPARMOR - select CRYPTO - select CRYPTO_SHA1 - default y - help - This option selects whether introspection of loaded policy - is available to userspace via the apparmor filesystem. - -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 - loaded policy provide system administrators a quick way - to verify that policy in the kernel matches what is expected, - 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 @@ -69,6 +44,67 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES When enabled, various debug messages will be logged to the kernel message buffer. +config SECURITY_APPARMOR_INTROSPECT_POLICY + bool "Allow loaded policy to be introspected" + depends on SECURITY_APPARMOR + default y + help + This option selects whether introspection of loaded policy + is available to userspace via the apparmor filesystem. This + adds to kernel memory usage. It is required for introspection + of loaded policy, and check point and restore support. It + can be disabled for embedded systems where reducing memory and + cpu is paramount. + +config SECURITY_APPARMOR_HASH + bool "Enable introspection of sha1 hashes for loaded profiles" + depends on SECURITY_APPARMOR_INTROSPECT_POLICY + select CRYPTO + select CRYPTO_SHA1 + default y + help + This option selects whether introspection of loaded policy + hashes is available to userspace via the apparmor + filesystem. This option provides a light weight means of + checking loaded policy. This option adds to policy load + time and can be disabled for small embedded systems. + +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 + loaded policy provide system administrators a quick way + to verify that policy in the kernel matches what is expected, + 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_EXPORT_BINARY + bool "Allow exporting the raw binary policy" + depends on SECURITY_APPARMOR_INTROSPECT_POLICY + select ZLIB_INFLATE + select ZLIB_DEFLATE + default y + help + This option allows reading back binary policy as it was loaded. + It increases the amount of kernel memory needed by policy and + also increases policy load time. This option is required for + checkpoint and restore support, and debugging of loaded policy. + +config SECURITY_APPARMOR_PARANOID_LOAD + bool "Perform full verification of loaded policy" + depends on SECURITY_APPARMOR + default y + help + This options allows controlling whether apparmor does a full + verification of loaded policy. This should not be disabled + except for embedded systems where the image is read only, + includes policy, and has some form of integrity check. + Disabling the check will speed up policy loads. + config SECURITY_APPARMOR_KUNIT_TEST bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS depends on KUNIT=y && SECURITY_APPARMOR diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0797edb2fb3d..d066ccc219e2 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -36,6 +36,7 @@ #include "include/policy_ns.h" #include "include/resource.h" #include "include/policy_unpack.h" +#include "include/task.h" /* * The apparmor filesystem interface used for policy load and introspection @@ -70,6 +71,7 @@ struct rawdata_f_data { struct aa_loaddata *loaddata; }; +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY #define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) static void rawdata_f_data_free(struct rawdata_f_data *private) @@ -94,9 +96,10 @@ static struct rawdata_f_data *rawdata_f_data_alloc(size_t size) return ret; } +#endif /** - * aa_mangle_name - mangle a profile name to std profile layout form + * mangle_name - mangle a profile name to std profile layout form * @name: profile name to mangle (NOT NULL) * @target: buffer to store mangled name, same length as @name (MAYBE NULL) * @@ -401,7 +404,7 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, data->size = copy_size; if (copy_from_user(data->data, userbuf, copy_size)) { - kvfree(data); + aa_put_loaddata(data); return ERR_PTR(-EFAULT); } @@ -1201,7 +1204,7 @@ SEQ_NS_FOPS(name); /* policy/raw_data/ * file ops */ - +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY #define SEQ_RAWDATA_FOPS(NAME) \ static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\ { \ @@ -1294,44 +1297,47 @@ SEQ_RAWDATA_FOPS(compressed_size); static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen) { - int error; - struct z_stream_s strm; +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY + if (aa_g_rawdata_compression_level != 0) { + int error = 0; + struct z_stream_s strm; - if (aa_g_rawdata_compression_level == 0) { - if (dlen < slen) - return -EINVAL; - memcpy(dst, src, slen); - return 0; - } + memset(&strm, 0, sizeof(strm)); - memset(&strm, 0, sizeof(strm)); + strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); + if (!strm.workspace) + return -ENOMEM; - strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); - if (!strm.workspace) - return -ENOMEM; - - strm.next_in = src; - strm.avail_in = slen; + strm.next_in = src; + strm.avail_in = slen; - error = zlib_inflateInit(&strm); - if (error != Z_OK) { - error = -ENOMEM; - goto fail_inflate_init; - } + error = zlib_inflateInit(&strm); + if (error != Z_OK) { + error = -ENOMEM; + goto fail_inflate_init; + } - strm.next_out = dst; - strm.avail_out = dlen; + strm.next_out = dst; + strm.avail_out = dlen; - error = zlib_inflate(&strm, Z_FINISH); - if (error != Z_STREAM_END) - error = -EINVAL; - else - error = 0; + error = zlib_inflate(&strm, Z_FINISH); + if (error != Z_STREAM_END) + error = -EINVAL; + else + error = 0; - zlib_inflateEnd(&strm); + zlib_inflateEnd(&strm); fail_inflate_init: - kvfree(strm.workspace); - return error; + kvfree(strm.workspace); + + return error; + } +#endif + + if (dlen < slen) + return -EINVAL; + memcpy(dst, src, slen); + return 0; } static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, @@ -1492,10 +1498,12 @@ fail: return PTR_ERR(dent); } +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + /** fns to setup dynamic per profile/namespace files **/ -/** +/* * * Requires: @profile->ns->lock held */ @@ -1522,7 +1530,7 @@ void __aafs_profile_rmdir(struct aa_profile *profile) } } -/** +/* * * Requires: @old->ns->lock held */ @@ -1557,6 +1565,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, return dent; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY static int profile_depth(struct aa_profile *profile) { int depth = 0; @@ -1658,7 +1667,7 @@ static const struct inode_operations rawdata_link_abi_iops = { static const struct inode_operations rawdata_link_data_iops = { .get_link = rawdata_get_link_data, }; - +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ /* * Requires: @profile->ns->lock held @@ -1729,15 +1738,17 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) profile->dents[AAFS_PROF_HASH] = dent; } +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY if (profile->rawdata) { - dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, - profile->label.proxy, NULL, NULL, - &rawdata_link_sha1_iops); - if (IS_ERR(dent)) - goto fail; - aa_get_proxy(profile->label.proxy); - profile->dents[AAFS_PROF_RAW_HASH] = dent; - + if (aa_g_hash_policy) { + dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, + profile->label.proxy, NULL, NULL, + &rawdata_link_sha1_iops); + if (IS_ERR(dent)) + goto fail; + aa_get_proxy(profile->label.proxy); + profile->dents[AAFS_PROF_RAW_HASH] = dent; + } dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, profile->label.proxy, NULL, NULL, &rawdata_link_abi_iops); @@ -1754,6 +1765,7 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) aa_get_proxy(profile->label.proxy); profile->dents[AAFS_PROF_RAW_DATA] = dent; } +#endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ list_for_each_entry(child, &profile->base.profiles, base.list) { error = __aafs_profile_mkdir(child, prof_child_dir(profile)); @@ -1880,7 +1892,7 @@ static void __aa_fs_list_remove_rawdata(struct aa_ns *ns) __aa_fs_remove_rawdata(ent); } -/** +/* * * Requires: @ns->lock held */ @@ -2323,6 +2335,7 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = { AA_SFS_FILE_BOOLEAN("v6", 1), AA_SFS_FILE_BOOLEAN("v7", 1), AA_SFS_FILE_BOOLEAN("v8", 1), + AA_SFS_FILE_BOOLEAN("v9", 1), { } }; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index f7e97c7e80f3..704b0c895605 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -137,7 +137,7 @@ int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, } if (AUDIT_MODE(profile) == AUDIT_QUIET || (type == AUDIT_APPARMOR_DENIED && - AUDIT_MODE(profile) == AUDIT_QUIET)) + AUDIT_MODE(profile) == AUDIT_QUIET_DENIED)) return aad(sa)->error; if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index a29e69d2c300..91689d34d281 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -119,7 +119,7 @@ static inline unsigned int match_component(struct aa_profile *profile, * @profile: profile to find perms for * @label: label to check access permissions for * @stack: whether this is a stacking request - * @start: state to start match in + * @state: state to start match in * @subns: whether to do permission checks on components in a subns * @request: permissions to request * @perms: perms struct to set @@ -466,7 +466,7 @@ restart: * xattrs, or a longer match */ candidate = profile; - candidate_len = profile->xmatch_len; + candidate_len = max(count, profile->xmatch_len); candidate_xattrs = ret; conflict = false; } @@ -1279,7 +1279,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name, /** * aa_change_profile - perform a one-way profile transition * @fqname: name of profile may include namespace (NOT NULL) - * @onexec: whether this transition is to take place immediately or at exec * @flags: flags affecting change behavior * * Change to new profile @name. Unlike with hats, there is no way diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1fbabdb565a8..9c3fc36a0702 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -36,6 +36,7 @@ extern enum audit_mode aa_g_audit; extern bool aa_g_audit_header; extern bool aa_g_debug; extern bool aa_g_hash_policy; +extern bool aa_g_export_binary; extern int aa_g_rawdata_compression_level; extern bool aa_g_lock_policy; extern bool aa_g_logsyscall; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 6e14f6cecdb9..1e94904f68d9 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -114,7 +114,21 @@ int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name, struct dentry *dent); struct aa_loaddata; + +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); +#else +static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata) +{ + /* empty stub */ +} + +static inline int __aa_fs_create_rawdata(struct aa_ns *ns, + struct aa_loaddata *rawdata) +{ + return 0; +} +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 7517605a183d..029cb20e322d 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -142,6 +142,7 @@ static inline u16 dfa_map_xindex(u16 mask) */ #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f) #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) #define dfa_user_xindex(dfa, state) \ @@ -150,6 +151,8 @@ static inline u16 dfa_map_xindex(u16 mask) #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ 0x7f) | \ ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_other_xbits(dfa, state) \ + ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f) #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) #define dfa_other_quiet(dfa, state) \ ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 9cafd80f7731..a1ac6ffb95e9 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -13,24 +13,6 @@ #include <linux/sched.h> -struct aa_profile; - -#define AA_PTRACE_TRACE MAY_WRITE -#define AA_PTRACE_READ MAY_READ -#define AA_MAY_BE_TRACED AA_MAY_APPEND -#define AA_MAY_BE_READ AA_MAY_CREATE -#define PTRACE_PERM_SHIFT 2 - -#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ - AA_MAY_BE_READ | AA_MAY_BE_TRACED) -#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) - -#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ - "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ - "xcpu xfsz vtalrm prof winch io pwr sys emt lost" - -int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, - u32 request); int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); #endif /* __AA_IPC_H */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 9101c2c76d9e..860484c6f99a 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -92,6 +92,8 @@ enum label_flags { FLAG_STALE = 0x800, /* replaced/removed */ FLAG_RENAMED = 0x1000, /* label has renaming in it */ FLAG_REVOKED = 0x2000, /* label has revocation in it */ + FLAG_DEBUG1 = 0x4000, + FLAG_DEBUG2 = 0x8000, /* These flags must correspond with PATH_flags */ /* TODO: add new path flags */ diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index e2e8df0c6f1c..f42359f58eb5 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -22,6 +22,11 @@ */ #define DEBUG_ON (aa_g_debug) +/* + * split individual debug cases out in preparation for finer grained + * debug controls in the future. + */ +#define AA_DEBUG_LABEL DEBUG_ON #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) #define AA_DEBUG(fmt, args...) \ do { \ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 44a7945fbe3c..343189903dba 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -17,8 +17,8 @@ enum path_flags { PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ - PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ - PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ + PATH_DELEGATE_DELETED = 0x10000, /* delegate deleted files */ + PATH_MEDIATE_DELETED = 0x20000, /* mediate deleted paths */ }; int aa_path_name(const struct path *path, int flags, char *buffer, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index cb5ef21991b7..639b5b248e63 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -48,6 +48,10 @@ extern const char *const aa_profile_mode_names[]; #define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT) +#define CHECK_DEBUG1(_profile) ((_profile)->label.flags & FLAG_DEBUG1) + +#define CHECK_DEBUG2(_profile) ((_profile)->label.flags & FLAG_DEBUG2) + #define profile_is_stale(_profile) (label_is_stale(&(_profile)->label)) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) @@ -135,7 +139,7 @@ struct aa_profile { const char *attach; struct aa_dfa *xmatch; - int xmatch_len; + unsigned int xmatch_len; enum audit_mode audit; long mode; u32 path_flags; diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 3df6f804922d..33d665516fc1 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -74,6 +74,7 @@ struct aa_ns { struct dentry *dents[AAFS_NS_SIZEOF]; }; +extern struct aa_label *kernel_t; extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index e0e1ca7ebc38..eb5f7d7f132b 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -28,6 +28,8 @@ void aa_load_ent_free(struct aa_load_ent *ent); struct aa_load_ent *aa_load_ent_alloc(void); #define PACKED_FLAG_HAT 1 +#define PACKED_FLAG_DEBUG1 2 +#define PACKED_FLAG_DEBUG2 4 #define PACKED_MODE_ENFORCE 0 #define PACKED_MODE_COMPLAIN 1 diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h index 48ff1ddecad5..a912a5d5d04f 100644 --- a/security/apparmor/include/secid.h +++ b/security/apparmor/include/secid.h @@ -21,6 +21,9 @@ struct aa_label; /* secid value that matches any other secid */ #define AA_SECID_WILDCARD 1 +/* sysctl to enable displaying mode when converting secid to secctx */ +extern int apparmor_display_secid_mode; + struct aa_label *aa_secid_to_label(u32 secid); int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); @@ -31,6 +34,4 @@ int aa_alloc_secid(struct aa_label *label, gfp_t gfp); void aa_free_secid(u32 secid); void aa_secid_update(u32 secid, struct aa_label *label); -void aa_secids_init(void); - #endif /* __AA_SECID_H */ diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index f13d12373b25..13437d62c70f 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h @@ -77,4 +77,22 @@ static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) ctx->token = 0; } +#define AA_PTRACE_TRACE MAY_WRITE +#define AA_PTRACE_READ MAY_READ +#define AA_MAY_BE_TRACED AA_MAY_APPEND +#define AA_MAY_BE_READ AA_MAY_CREATE +#define PTRACE_PERM_SHIFT 2 + +#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ + AA_MAY_BE_READ | AA_MAY_BE_TRACED) +#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) + +#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ + "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ + "xcpu xfsz vtalrm prof winch io pwr sys emt lost" + +int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request); + + #endif /* __AA_TASK_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index fe36d112aad9..3dbbc59d440d 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -9,7 +9,6 @@ */ #include <linux/gfp.h> -#include <linux/ptrace.h> #include "include/audit.h" #include "include/capability.h" @@ -18,115 +17,6 @@ #include "include/ipc.h" #include "include/sig_names.h" -/** - * audit_ptrace_mask - convert mask to permission string - * @mask: permission mask to convert - * - * Returns: pointer to static string - */ -static const char *audit_ptrace_mask(u32 mask) -{ - switch (mask) { - case MAY_READ: - return "read"; - case MAY_WRITE: - return "trace"; - case AA_MAY_BE_READ: - return "readby"; - case AA_MAY_BE_TRACED: - return "tracedby"; - } - return ""; -} - -/* call back to audit ptrace fields */ -static void audit_ptrace_cb(struct audit_buffer *ab, void *va) -{ - struct common_audit_data *sa = va; - - if (aad(sa)->request & AA_PTRACE_PERM_MASK) { - audit_log_format(ab, " requested_mask=\"%s\"", - audit_ptrace_mask(aad(sa)->request)); - - if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { - audit_log_format(ab, " denied_mask=\"%s\"", - audit_ptrace_mask(aad(sa)->denied)); - } - } - audit_log_format(ab, " peer="); - aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, - FLAGS_NONE, GFP_ATOMIC); -} - -/* assumes check for PROFILE_MEDIATES is already done */ -/* TODO: conditionals */ -static int profile_ptrace_perm(struct aa_profile *profile, - struct aa_label *peer, u32 request, - struct common_audit_data *sa) -{ - struct aa_perms perms = { }; - - aad(sa)->peer = peer; - aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, - &perms); - aa_apply_modes_to_perms(profile, &perms); - return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); -} - -static int profile_tracee_perm(struct aa_profile *tracee, - struct aa_label *tracer, u32 request, - struct common_audit_data *sa) -{ - if (profile_unconfined(tracee) || unconfined(tracer) || - !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) - return 0; - - return profile_ptrace_perm(tracee, tracer, request, sa); -} - -static int profile_tracer_perm(struct aa_profile *tracer, - struct aa_label *tracee, u32 request, - struct common_audit_data *sa) -{ - if (profile_unconfined(tracer)) - return 0; - - if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) - return profile_ptrace_perm(tracer, tracee, request, sa); - - /* profile uses the old style capability check for ptrace */ - if (&tracer->label == tracee) - return 0; - - aad(sa)->label = &tracer->label; - aad(sa)->peer = tracee; - aad(sa)->request = 0; - aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, - CAP_OPT_NONE); - - return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); -} - -/** - * aa_may_ptrace - test if tracer task can trace the tracee - * @tracer: label of the task doing the tracing (NOT NULL) - * @tracee: task label to be traced - * @request: permission request - * - * Returns: %0 else error code if permission denied or error - */ -int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, - u32 request) -{ - struct aa_profile *profile; - u32 xrequest = request << PTRACE_PERM_SHIFT; - DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); - - return xcheck_labels(tracer, tracee, profile, - profile_tracer_perm(profile, tracee, request, &sa), - profile_tracee_perm(profile, tracer, xrequest, &sa)); -} - static inline int map_signal_num(int sig) { diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 0b0265da1926..0f36ee907438 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -197,18 +197,18 @@ static bool vec_is_stale(struct aa_profile **vec, int n) return false; } -static bool vec_unconfined(struct aa_profile **vec, int n) +static long union_vec_flags(struct aa_profile **vec, int n, long mask) { + long u = 0; int i; AA_BUG(!vec); for (i = 0; i < n; i++) { - if (!profile_unconfined(vec[i])) - return false; + u |= vec[i]->label.flags & mask; } - return true; + return u; } static int sort_cmp(const void *a, const void *b) @@ -485,7 +485,7 @@ int aa_label_next_confined(struct aa_label *label, int i) } /** - * aa_label_next_not_in_set - return the next profile of @sub not in @set + * __aa_label_next_not_in_set - return the next profile of @sub not in @set * @I: label iterator * @set: label to test against * @sub: label to if is subset of @set @@ -1097,8 +1097,8 @@ static struct aa_label *label_merge_insert(struct aa_label *new, else if (k == b->size) return aa_get_label(b); } - if (vec_unconfined(new->vec, new->size)) - new->flags |= FLAG_UNCONFINED; + new->flags |= union_vec_flags(new->vec, new->size, FLAG_UNCONFINED | + FLAG_DEBUG1 | FLAG_DEBUG2); ls = labels_set(new); write_lock_irqsave(&ls->lock, flags); label = __label_insert(labels_set(new), new, false); @@ -1631,9 +1631,9 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, AA_BUG(!str && size != 0); AA_BUG(!label); - if (flags & FLAG_ABS_ROOT) { + if (AA_DEBUG_LABEL && (flags & FLAG_ABS_ROOT)) { ns = root_ns; - len = snprintf(str, size, "="); + len = snprintf(str, size, "_"); update_for_len(total, len, size, str); } else if (!ns) { ns = labels_ns(label); @@ -1744,7 +1744,7 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, if (!use_label_hname(ns, label, flags) || display_mode(ns, label, flags)) { len = aa_label_asxprint(&name, ns, label, flags, gfp); - if (len == -1) { + if (len < 0) { AA_DEBUG("label print error"); return; } @@ -1772,7 +1772,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, int len; len = aa_label_asxprint(&str, ns, label, flags, gfp); - if (len == -1) { + if (len < 0) { AA_DEBUG("label print error"); return; } @@ -1795,7 +1795,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, int len; len = aa_label_asxprint(&str, ns, label, flags, gfp); - if (len == -1) { + if (len < 0) { AA_DEBUG("label print error"); return; } @@ -1895,7 +1895,8 @@ struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, AA_BUG(!str); str = skipn_spaces(str, n); - if (str == NULL || (*str == '=' && base != &root_ns->unconfined->label)) + if (str == NULL || (AA_DEBUG_LABEL && *str == '_' && + base != &root_ns->unconfined->label)) return ERR_PTR(-EINVAL); len = label_count_strn_entries(str, end - str); @@ -2136,7 +2137,7 @@ static void __labelset_update(struct aa_ns *ns) } /** - * __aa_labelset_udate_subtree - update all labels with a stale component + * __aa_labelset_update_subtree - update all labels with a stale component * @ns: ns to start update at (NOT NULL) * * Requires: @ns lock be held diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index fa49b81eb54c..1c72a61108d3 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -136,7 +136,7 @@ __counted char *aa_str_alloc(int size, gfp_t gfp) { struct counted_str *str; - str = kmalloc(sizeof(struct counted_str) + size, gfp); + str = kmalloc(struct_size(str, name, size), gfp); if (!str) return NULL; @@ -322,22 +322,39 @@ static u32 map_other(u32 x) ((x & 0x60) << 19); /* SETOPT/GETOPT */ } +static u32 map_xbits(u32 x) +{ + return ((x & 0x1) << 7) | + ((x & 0x7e) << 9); +} + void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, struct aa_perms *perms) { + /* This mapping is convulated due to history. + * v1-v4: only file perms + * v5: added policydb which dropped in perm user conditional to + * gain new perm bits, but had to map around the xbits because + * the userspace compiler was still munging them. + * v9: adds using the xbits in policydb because the compiler now + * supports treating policydb permission bits different. + * Unfortunately there is not way to force auditing on the + * perms represented by the xbits + */ *perms = (struct aa_perms) { - .allow = dfa_user_allow(dfa, state), + .allow = dfa_user_allow(dfa, state) | + map_xbits(dfa_user_xbits(dfa, state)), .audit = dfa_user_audit(dfa, state), - .quiet = dfa_user_quiet(dfa, state), + .quiet = dfa_user_quiet(dfa, state) | + map_xbits(dfa_other_xbits(dfa, state)), }; - /* for v5 perm mapping in the policydb, the other set is used + /* for v5-v9 perm mapping in the policydb, the other set is used * to extend the general perm set */ perms->allow |= map_other(dfa_other_allow(dfa, state)); perms->audit |= map_other(dfa_other_audit(dfa, state)); perms->quiet |= map_other(dfa_other_quiet(dfa, state)); -// perms->xindex = dfa_user_xindex(dfa, state); } /** diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 900bc540656a..e29cade7b662 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -832,7 +832,7 @@ static void apparmor_sk_free_security(struct sock *sk) } /** - * apparmor_clone_security - clone the sk_security field + * apparmor_sk_clone_security - clone the sk_security field */ static void apparmor_sk_clone_security(const struct sock *sk, struct sock *newsk) @@ -886,10 +886,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, struct aa_label *label; if (kern) { - struct aa_ns *ns = aa_get_current_ns(); - - label = aa_get_label(ns_unconfined(ns)); - aa_put_ns(ns); + label = aa_get_label(kernel_t); } else label = aa_get_current_label(); @@ -937,7 +934,7 @@ static int apparmor_socket_connect(struct socket *sock, } /** - * apparmor_socket_list - check perms before allowing listen + * apparmor_socket_listen - check perms before allowing listen */ static int apparmor_socket_listen(struct socket *sock, int backlog) { @@ -1041,7 +1038,7 @@ static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, } /** - * apparmor_getsockopt - check perms before getting socket options + * apparmor_socket_getsockopt - check perms before getting socket options */ static int apparmor_socket_getsockopt(struct socket *sock, int level, int optname) @@ -1051,7 +1048,7 @@ static int apparmor_socket_getsockopt(struct socket *sock, int level, } /** - * apparmor_setsockopt - check perms before setting socket options + * apparmor_socket_setsockopt - check perms before setting socket options */ static int apparmor_socket_setsockopt(struct socket *sock, int level, int optname) @@ -1070,7 +1067,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how) #ifdef CONFIG_NETWORK_SECMARK /** - * apparmor_socket_sock_recv_skb - check perms before associating skb to sk + * apparmor_socket_sock_rcv_skb - check perms before associating skb to sk * * Note: can not sleep may be called with locks held * @@ -1357,6 +1354,12 @@ bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); #endif +/* whether policy exactly as loaded is retained for debug and checkpointing */ +bool aa_g_export_binary = IS_ENABLED(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY); +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY +module_param_named(export_binary, aa_g_export_binary, aabool, 0600); +#endif + /* policy loaddata compression level */ int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION; module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level, @@ -1399,7 +1402,7 @@ module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR); * 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 = true; +bool aa_g_paranoid_load = IS_ENABLED(CONFIG_SECURITY_APPARMOR_PARANOID_LOAD); module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO); static int param_get_aaintbool(char *buffer, const struct kernel_param *kp); @@ -1761,6 +1764,14 @@ static struct ctl_table apparmor_sysctl_table[] = { .mode = 0600, .proc_handler = apparmor_dointvec, }, + { + .procname = "apparmor_display_secid_mode", + .data = &apparmor_display_secid_mode, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = apparmor_dointvec, + }, + { } }; @@ -1819,11 +1830,8 @@ static const struct nf_hook_ops apparmor_nf_ops[] = { static int __net_init apparmor_nf_register(struct net *net) { - int ret; - - ret = nf_register_net_hooks(net, apparmor_nf_ops, + return nf_register_net_hooks(net, apparmor_nf_ops, ARRAY_SIZE(apparmor_nf_ops)); - return ret; } static void __net_exit apparmor_nf_unregister(struct net *net) @@ -1857,8 +1865,6 @@ static int __init apparmor_init(void) { int error; - aa_secids_init(); - error = aa_setup_dfa_engine(); if (error) { AA_ERROR("Unable to setup dfa engine\n"); diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index aa6fcfde3051..f61247241803 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -217,7 +217,6 @@ static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa, .allow = dfa_user_allow(dfa, state), .audit = dfa_user_audit(dfa, state), .quiet = dfa_user_quiet(dfa, state), - .xindex = dfa_user_xindex(dfa, state), }; return perms; @@ -229,7 +228,8 @@ static const char * const mnt_info_table[] = { "failed srcname match", "failed type match", "failed flags match", - "failed data match" + "failed data match", + "failed perms check" }; /* @@ -284,8 +284,8 @@ static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, return 0; } - /* failed at end of flags match */ - return 4; + /* failed at perms check, don't confuse with flags match */ + return 6; } @@ -303,7 +303,7 @@ static int path_flags(struct aa_profile *profile, const struct path *path) * @profile: the confining profile * @mntpath: for the mntpnt (NOT NULL) * @buffer: buffer to be used to lookup mntpath - * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) + * @devname: string for the devname/src_name (MAY BE NULL OR ERRPTR) * @type: string for the dev type (MAYBE NULL) * @flags: mount flags to match * @data: fs mount data (MAYBE NULL) @@ -358,7 +358,7 @@ audit: /** * match_mnt - handle path matching for mount * @profile: the confining profile - * @mntpath: for the mntpnt (NOT NULL) + * @path: for the mntpnt (NOT NULL) * @buffer: buffer to be used to lookup mntpath * @devpath: path devname/src_name (MAYBE NULL) * @devbuffer: buffer to be used to lookup devname/src_name @@ -718,6 +718,7 @@ int aa_pivotroot(struct aa_label *label, const struct path *old_path, aa_put_label(target); goto out; } + aa_put_label(target); } else /* already audited error */ error = PTR_ERR(target); diff --git a/security/apparmor/net.c b/security/apparmor/net.c index e0c1b50d6edd..7efe4d17273d 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -145,12 +145,13 @@ int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, struct sock *sk) { + struct aa_sk_ctx *ctx = SK_CTX(sk); int error = 0; AA_BUG(!label); AA_BUG(!sk); - if (!unconfined(label)) { + if (ctx->label != kernel_t && !unconfined(label)) { struct aa_profile *profile; DEFINE_AUDIT_SK(sa, op, sk); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index b0cbc4906cb3..499c0209b6a4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -422,7 +422,7 @@ static struct aa_profile *__lookup_profile(struct aa_policy *base, } /** - * aa_lookup_profile - find a profile by its full or partial name + * aa_lookupn_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 @@ -952,16 +952,18 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, mutex_lock_nested(&ns->lock, ns->level); /* check for duplicate rawdata blobs: space and file dedup */ - list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { - if (aa_rawdata_eq(rawdata_ent, udata)) { - struct aa_loaddata *tmp; - - tmp = __aa_get_loaddata(rawdata_ent); - /* check we didn't fail the race */ - if (tmp) { - aa_put_loaddata(udata); - udata = tmp; - break; + if (!list_empty(&ns->rawdata_list)) { + list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { + if (aa_rawdata_eq(rawdata_ent, udata)) { + struct aa_loaddata *tmp; + + tmp = __aa_get_loaddata(rawdata_ent); + /* check we didn't fail the race */ + if (tmp) { + aa_put_loaddata(udata); + udata = tmp; + break; + } } } } @@ -969,7 +971,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - ent->new->rawdata = aa_get_loaddata(udata); + if (aa_g_export_binary) + ent->new->rawdata = aa_get_loaddata(udata); error = __lookup_replace(ns, ent->new->base.hname, !(mask & AA_MAY_REPLACE_POLICY), &ent->old, &info); @@ -1009,7 +1012,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, } /* create new fs entries for introspection if needed */ - if (!udata->dents[AAFS_LOADDATA_DIR]) { + if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) { error = __aa_fs_create_rawdata(ns, udata); if (error) { info = "failed to create raw_data dir and files"; @@ -1037,12 +1040,14 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, /* Done with checks that may fail - do actual replacement */ __aa_bump_ns_revision(ns); - __aa_loaddata_update(udata, ns->revision); + if (aa_g_export_binary) + __aa_loaddata_update(udata, ns->revision); list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - if (ent->old && ent->old->rawdata == ent->new->rawdata) { + if (ent->old && ent->old->rawdata == ent->new->rawdata && + ent->new->rawdata) { /* dedup actual profile replacement */ audit_policy(label, op, ns_name, ent->new->base.hname, "same as current profile, skipping", diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index 70921d95fb40..43beaad083fe 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -22,6 +22,9 @@ #include "include/label.h" #include "include/policy.h" +/* kernel label */ +struct aa_label *kernel_t; + /* root profile namespace */ struct aa_ns *root_ns; const char *aa_hidden_ns_name = "---"; @@ -51,10 +54,10 @@ bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns) } /** - * 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 + * aa_ns_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 */ @@ -77,6 +80,23 @@ const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view, bool subns) return aa_hidden_ns_name; } +static struct aa_profile *alloc_unconfined(const char *name) +{ + struct aa_profile *profile; + + profile = aa_alloc_profile(name, NULL, GFP_KERNEL); + if (!profile) + return NULL; + + profile->label.flags |= FLAG_IX_ON_NAME_ERROR | + FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; + profile->mode = APPARMOR_UNCONFINED; + profile->file.dfa = aa_get_dfa(nulldfa); + profile->policy.dfa = aa_get_dfa(nulldfa); + + return profile; +} + /** * alloc_ns - allocate, initialize and return a new namespace * @prefix: parent namespace name (MAYBE NULL) @@ -101,16 +121,9 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) init_waitqueue_head(&ns->wait); /* released by aa_free_ns() */ - ns->unconfined = aa_alloc_profile("unconfined", NULL, GFP_KERNEL); + ns->unconfined = alloc_unconfined("unconfined"); if (!ns->unconfined) goto fail_unconfined; - - ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR | - FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; - ns->unconfined->mode = APPARMOR_UNCONFINED; - ns->unconfined->file.dfa = aa_get_dfa(nulldfa); - ns->unconfined->policy.dfa = aa_get_dfa(nulldfa); - /* ns and ns->unconfined share ns->unconfined refcount */ ns->unconfined->ns = ns; @@ -187,7 +200,7 @@ struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name) /** * __aa_lookupn_ns - lookup the namespace matching @hname - * @base: base list to start looking up profile name from (NOT NULL) + * @view: namespace to search in (NOT NULL) * @hname: hierarchical ns name (NOT NULL) * @n: length of @hname * @@ -272,7 +285,7 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, } /** - * aa_create_ns - create an ns, fail if it already exists + * __aa_find_or_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 @@ -388,11 +401,22 @@ static void __ns_list_release(struct list_head *head) */ int __init aa_alloc_root_ns(void) { + struct aa_profile *kernel_p; + /* released by aa_free_root_ns - used as list ref*/ root_ns = alloc_ns(NULL, "root"); if (!root_ns) return -ENOMEM; + kernel_p = alloc_unconfined("kernel_t"); + if (!kernel_p) { + destroy_ns(root_ns); + aa_free_ns(root_ns); + return -ENOMEM; + } + kernel_t = &kernel_p->label; + root_ns->unconfined->ns = aa_get_ns(root_ns); + return 0; } @@ -405,6 +429,7 @@ void __init aa_free_root_ns(void) root_ns = NULL; + aa_label_free(kernel_t); destroy_ns(ns); aa_put_ns(ns); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 0acca6f2a93f..55d31bac4f35 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -125,15 +125,16 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision) { AA_BUG(!data); AA_BUG(!data->ns); - AA_BUG(!data->dents[AAFS_LOADDATA_REVISION]); AA_BUG(!mutex_is_locked(&data->ns->lock)); AA_BUG(data->revision > revision); data->revision = revision; - d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime = - current_time(d_inode(data->dents[AAFS_LOADDATA_DIR])); - d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime = - current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION])); + if ((data->dents[AAFS_LOADDATA_REVISION])) { + d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime = + current_time(d_inode(data->dents[AAFS_LOADDATA_DIR])); + d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime = + current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION])); + } } bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r) @@ -213,7 +214,7 @@ static void *kvmemdup(const void *src, size_t len) } /** - * aa_u16_chunck - test and do bounds checking for a u16 size based chunk + * unpack_u16_chunk - test and do bounds checking for a u16 size based chunk * @e: serialized data read head (NOT NULL) * @chunk: start address for chunk of data (NOT NULL) * @@ -456,7 +457,9 @@ 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) | DFA_FLAG_VERIFY_STATES; + TO_ACCEPT2_FLAG(YYTD_DATA32); + if (aa_g_paranoid_load) + flags |= DFA_FLAG_VERIFY_STATES; dfa = aa_dfa_unpack(blob + pad, size - pad, flags); if (IS_ERR(dfa)) @@ -668,6 +671,7 @@ static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) /** * unpack_profile - unpack a serialized profile * @e: serialized data extent information (NOT NULL) + * @ns_name: pointer of newly allocated copy of %NULL in case of error * * NOTE: unpack profile sets audit struct if there is a failure */ @@ -744,18 +748,24 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; if (tmp & PACKED_FLAG_HAT) profile->label.flags |= FLAG_HAT; + if (tmp & PACKED_FLAG_DEBUG1) + profile->label.flags |= FLAG_DEBUG1; + if (tmp & PACKED_FLAG_DEBUG2) + profile->label.flags |= FLAG_DEBUG2; if (!unpack_u32(e, &tmp, NULL)) goto fail; - if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) + if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) { profile->mode = APPARMOR_COMPLAIN; - else if (tmp == PACKED_MODE_ENFORCE) + } else if (tmp == PACKED_MODE_ENFORCE) { profile->mode = APPARMOR_ENFORCE; - else if (tmp == PACKED_MODE_KILL) + } else if (tmp == PACKED_MODE_KILL) { profile->mode = APPARMOR_KILL; - else if (tmp == PACKED_MODE_UNCONFINED) + } else if (tmp == PACKED_MODE_UNCONFINED) { profile->mode = APPARMOR_UNCONFINED; - else + profile->label.flags |= FLAG_UNCONFINED; + } else { goto fail; + } if (!unpack_u32(e, &tmp, NULL)) goto fail; if (tmp) @@ -936,7 +946,7 @@ fail: } /** - * verify_head - unpack serialized stream header + * verify_header - unpack serialized stream header * @e: serialized data read head (NOT NULL) * @required: whether the header is required or optional * @ns: Returns - namespace if one is specified else NULL (NOT NULL) @@ -1052,6 +1062,7 @@ struct aa_load_ent *aa_load_ent_alloc(void) static int deflate_compress(const char *src, size_t slen, char **dst, size_t *dlen) { +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY int error; struct z_stream_s strm; void *stgbuf, *dstbuf; @@ -1123,6 +1134,10 @@ fail_deflate_init: fail_deflate: kvfree(stgbuf); goto fail_stg_alloc; +#else + *dlen = slen; + return 0; +#endif } static int compress_loaddata(struct aa_loaddata *data) @@ -1141,7 +1156,8 @@ static int compress_loaddata(struct aa_loaddata *data) if (error) return error; - kvfree(udata); + if (udata != data->data) + kvfree(udata); } else data->compressed_size = data->size; @@ -1216,9 +1232,12 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, goto fail; } } - error = compress_loaddata(udata); - if (error) - goto fail; + + if (aa_g_export_binary) { + error = compress_loaddata(udata); + if (error) + goto fail; + } return 0; fail_profile: diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index 7954cb23d5f2..0a969b2e03db 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -48,8 +48,8 @@ struct policy_unpack_fixture { size_t e_size; }; -struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, - struct kunit *test, size_t buf_size) +static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, + struct kunit *test, size_t buf_size) { char *buf; struct aa_ext *e; @@ -439,7 +439,7 @@ static void policy_unpack_test_unpack_u32_with_null_name(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; bool success; - u32 data; + u32 data = 0; puf->e->pos += TEST_U32_BUF_OFFSET; @@ -456,7 +456,7 @@ static void policy_unpack_test_unpack_u32_with_name(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U32_NAME; bool success; - u32 data; + u32 data = 0; puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; @@ -473,7 +473,7 @@ static void policy_unpack_test_unpack_u32_out_of_bounds(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U32_NAME; bool success; - u32 data; + u32 data = 0; puf->e->pos += TEST_NAMED_U32_BUF_OFFSET; puf->e->end = puf->e->start + TEST_U32_BUF_OFFSET + sizeof(u32); @@ -489,7 +489,7 @@ static void policy_unpack_test_unpack_u64_with_null_name(struct kunit *test) { struct policy_unpack_fixture *puf = test->priv; bool success; - u64 data; + u64 data = 0; puf->e->pos += TEST_U64_BUF_OFFSET; @@ -506,7 +506,7 @@ static void policy_unpack_test_unpack_u64_with_name(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U64_NAME; bool success; - u64 data; + u64 data = 0; puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; @@ -523,7 +523,7 @@ static void policy_unpack_test_unpack_u64_out_of_bounds(struct kunit *test) struct policy_unpack_fixture *puf = test->priv; const char name[] = TEST_U64_NAME; bool success; - u64 data; + u64 data = 0; puf->e->pos += TEST_NAMED_U64_BUF_OFFSET; puf->e->end = puf->e->start + TEST_U64_BUF_OFFSET + sizeof(u64); diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index fde332e0ea7d..86ad26ef72ed 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -90,7 +90,7 @@ static char *split_token_from_name(const char *op, char *args, u64 *token) } /** - * aa_setprocattr_chagnehat - handle procattr interface to change_hat + * aa_setprocattr_changehat - handle procattr interface to change_hat * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) * @size: size of the args * @flags: set of flags governing behavior diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c index ce545f99259e..24a0e23f1b2b 100644 --- a/security/apparmor/secid.c +++ b/security/apparmor/secid.c @@ -13,9 +13,9 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/gfp.h> -#include <linux/idr.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/xarray.h> #include "include/cred.h" #include "include/lib.h" @@ -29,8 +29,9 @@ */ #define AA_FIRST_SECID 2 -static DEFINE_IDR(aa_secids); -static DEFINE_SPINLOCK(secid_lock); +static DEFINE_XARRAY_FLAGS(aa_secids, XA_FLAGS_LOCK_IRQ | XA_FLAGS_TRACK_FREE); + +int apparmor_display_secid_mode; /* * TODO: allow policy to reserve a secid range? @@ -47,9 +48,9 @@ void aa_secid_update(u32 secid, struct aa_label *label) { unsigned long flags; - spin_lock_irqsave(&secid_lock, flags); - idr_replace(&aa_secids, label, secid); - spin_unlock_irqrestore(&secid_lock, flags); + xa_lock_irqsave(&aa_secids, flags); + __xa_store(&aa_secids, secid, label, 0); + xa_unlock_irqrestore(&aa_secids, flags); } /** @@ -58,19 +59,14 @@ void aa_secid_update(u32 secid, struct aa_label *label) */ struct aa_label *aa_secid_to_label(u32 secid) { - struct aa_label *label; - - rcu_read_lock(); - label = idr_find(&aa_secids, secid); - rcu_read_unlock(); - - return label; + return xa_load(&aa_secids, secid); } int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { /* TODO: cache secctx and ref count so we don't have to recreate */ struct aa_label *label = aa_secid_to_label(secid); + int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT; int len; AA_BUG(!seclen); @@ -78,15 +74,15 @@ int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) if (!label) return -EINVAL; + if (apparmor_display_secid_mode) + flags |= FLAG_SHOW_MODE; + if (secdata) len = aa_label_asxprint(secdata, root_ns, label, - FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | - FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT, - GFP_ATOMIC); + flags, GFP_ATOMIC); else - len = aa_label_snxprint(NULL, 0, root_ns, label, - FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | - FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT); + len = aa_label_snxprint(NULL, 0, root_ns, label, flags); + if (len < 0) return -ENOMEM; @@ -126,19 +122,16 @@ int aa_alloc_secid(struct aa_label *label, gfp_t gfp) unsigned long flags; int ret; - idr_preload(gfp); - spin_lock_irqsave(&secid_lock, flags); - ret = idr_alloc(&aa_secids, label, AA_FIRST_SECID, 0, GFP_ATOMIC); - spin_unlock_irqrestore(&secid_lock, flags); - idr_preload_end(); + xa_lock_irqsave(&aa_secids, flags); + ret = __xa_alloc(&aa_secids, &label->secid, label, + XA_LIMIT(AA_FIRST_SECID, INT_MAX), gfp); + xa_unlock_irqrestore(&aa_secids, flags); if (ret < 0) { label->secid = AA_SECID_INVALID; return ret; } - AA_BUG(ret == AA_SECID_INVALID); - label->secid = ret; return 0; } @@ -150,12 +143,7 @@ void aa_free_secid(u32 secid) { unsigned long flags; - spin_lock_irqsave(&secid_lock, flags); - idr_remove(&aa_secids, secid); - spin_unlock_irqrestore(&secid_lock, flags); -} - -void aa_secids_init(void) -{ - idr_init_base(&aa_secids, AA_FIRST_SECID); + xa_lock_irqsave(&aa_secids, flags); + __xa_erase(&aa_secids, secid); + xa_unlock_irqrestore(&aa_secids, flags); } diff --git a/security/apparmor/task.c b/security/apparmor/task.c index d17130ee6795..503dc0877fb1 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -12,7 +12,12 @@ * should return to the previous cred if it has not been modified. */ +#include <linux/gfp.h> +#include <linux/ptrace.h> + +#include "include/audit.h" #include "include/cred.h" +#include "include/policy.h" #include "include/task.h" /** @@ -177,3 +182,112 @@ int aa_restore_previous_label(u64 token) return 0; } + +/** + * audit_ptrace_mask - convert mask to permission string + * @mask: permission mask to convert + * + * Returns: pointer to static string + */ +static const char *audit_ptrace_mask(u32 mask) +{ + switch (mask) { + case MAY_READ: + return "read"; + case MAY_WRITE: + return "trace"; + case AA_MAY_BE_READ: + return "readby"; + case AA_MAY_BE_TRACED: + return "tracedby"; + } + return ""; +} + +/* call back to audit ptrace fields */ +static void audit_ptrace_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + if (aad(sa)->request & AA_PTRACE_PERM_MASK) { + audit_log_format(ab, " requested_mask=\"%s\"", + audit_ptrace_mask(aad(sa)->request)); + + if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { + audit_log_format(ab, " denied_mask=\"%s\"", + audit_ptrace_mask(aad(sa)->denied)); + } + } + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, + FLAGS_NONE, GFP_ATOMIC); +} + +/* assumes check for PROFILE_MEDIATES is already done */ +/* TODO: conditionals */ +static int profile_ptrace_perm(struct aa_profile *profile, + struct aa_label *peer, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms = { }; + + aad(sa)->peer = peer; + aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, + &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); +} + +static int profile_tracee_perm(struct aa_profile *tracee, + struct aa_label *tracer, u32 request, + struct common_audit_data *sa) +{ + if (profile_unconfined(tracee) || unconfined(tracer) || + !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) + return 0; + + return profile_ptrace_perm(tracee, tracer, request, sa); +} + +static int profile_tracer_perm(struct aa_profile *tracer, + struct aa_label *tracee, u32 request, + struct common_audit_data *sa) +{ + if (profile_unconfined(tracer)) + return 0; + + if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) + return profile_ptrace_perm(tracer, tracee, request, sa); + + /* profile uses the old style capability check for ptrace */ + if (&tracer->label == tracee) + return 0; + + aad(sa)->label = &tracer->label; + aad(sa)->peer = tracee; + aad(sa)->request = 0; + aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, + CAP_OPT_NONE); + + return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); +} + +/** + * aa_may_ptrace - test if tracer task can trace the tracee + * @tracer: label of the task doing the tracing (NOT NULL) + * @tracee: task label to be traced + * @request: permission request + * + * Returns: %0 else error code if permission denied or error + */ +int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request) +{ + struct aa_profile *profile; + u32 xrequest = request << PTRACE_PERM_SHIFT; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); + + return xcheck_labels(tracer, tracee, profile, + profile_tracer_perm(profile, tracee, request, &sa), + profile_tracee_perm(profile, tracer, xrequest, &sa)); +} |