+ 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'
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE7cSDD705q2rFEEf7BS82cBjVw9gFAmLxtlkACgkQBS82cBjV
 w9iomw/8DPUO6qqXcOmP1TRnPUzwfHdkxRzNYWhBjfMAQmIWd4SZN/c2L2FbVbxs
 aund3yEmHdMDH8udzN4ZDygmS61tsImpNjZHXfkX+0CULeZiJeVEPTIrFlu5ce/H
 pNfxhwrbdJhsq0Gxj8k24qAsTm9590w9xRoWLTL3L023/6C9aFOjZrWhH1/qvgtY
 vQ1/Rp64E9u931NY+IF5+sHOud0yzOc0VItzf7gOPZLMN6Vmntvzh/HscoZUSAA/
 6Pyj01wNjIjoq+fJ8Gwe5vnrfIRHOfddVDnqVI6AxQ8PwQi3ooaeH0c2+6QZKKd1
 6iGDTkI3f5TVVLXwOw/8/sht/kB7vbOvnhssP8fdPHEormiqsBqeyFNEn6Lz2qoW
 rdoq314RzXx+5SI50Ta+YqaTs+TMdSqggsq01oV2lkBZYI7/dot//HgOgUUvtD7Z
 l3g/3ldKGjlwoCp55JBggjCrvMIK/NE18w3+TToeGL8Jl4ubJ7v+TgCD+77Chw9X
 57sVgfe/RukqwoYuABJHdkcaWY3AxKfksMG2qYbjkUxZXncA7pyhG4oF3RdI/fsl
 HvvlGd5mG+jPxVLrs1qRgIK4T98EIpMzV/qxe+ykpgN6WdW7Mv+NGnc2ybBbia8C
 5kQdoUyG11mff1YSfnKXKgRKrHBHWD/oW/GyFYh95VJJvSwhvAM=
 =Wt4E
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2022-08-10 10:53:22 -07:00
commit 00aa9d0bbf
30 changed files with 491 additions and 339 deletions

View File

@ -1390,10 +1390,14 @@ F: include/uapi/linux/apm_bios.h
APPARMOR SECURITY MODULE
M: John Johansen <john.johansen@canonical.com>
L: apparmor@lists.ubuntu.com (subscribers-only, general discussion)
M: John Johansen <john@apparmor.net>
L: apparmor@lists.ubuntu.com (moderated for non-subscribers)
S: Supported
W: wiki.apparmor.net
W: apparmor.net
B: https://gitlab.com/apparmor/apparmor-kernel
C: irc://irc.oftc.net/apparmor
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
T: https://gitlab.com/apparmor/apparmor-kernel.git
F: Documentation/admin-guide/LSM/apparmor.rst
F: security/apparmor/

View File

@ -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

View File

@ -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),
{ }
};

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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)

View File

@ -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 */

View File

@ -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 */

View File

@ -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 { \

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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)
{

View File

@ -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

View File

@ -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);
}
/**

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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;
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;
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",

View File

@ -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);
}

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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));
}