summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-12-14 13:42:09 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2022-12-14 13:42:09 -0800
commit93761c93e9da28d8a020777cee2a84133082b477 (patch)
treeb84216293efb4b85724dcf83b27e099436e1509a
parent64e7003c6b85626a533a67c1ba938b75a3db24e6 (diff)
parent4295c60bbe9e63e35d330546eeaa1d2b62dae303 (diff)
downloadlinux-93761c93e9da28d8a020777cee2a84133082b477.tar.gz
linux-93761c93e9da28d8a020777cee2a84133082b477.tar.bz2
linux-93761c93e9da28d8a020777cee2a84133082b477.zip
Merge tag 'apparmor-pr-2022-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull apparmor updates from John Johansen: "Features: - switch to zstd compression for profile raw data Cleanups: - simplify obtaining the newest label on a cred - remove useless static inline functions - compute permission conversion on policy unpack - refactor code to share common permissins - refactor unpack to group policy backwards compatiblity code - add __init annotation to aa_{setup/teardown}_dfa_engine() Bug Fixes: - fix a memleak in - multi_transaction_new() - free_ruleset() - unpack_profile() - alloc_ns() - fix lockdep warning when removing a namespace - fix regression in stacking due to label flags - fix loading of child before parent - fix kernel-doc comments that differ from fns - fix spelling errors in comments - store return value of unpack_perms_table() to signed variable" * tag 'apparmor-pr-2022-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (64 commits) apparmor: Fix uninitialized symbol 'array_size' in policy_unpack_test.c apparmor: Add __init annotation to aa_{setup/teardown}_dfa_engine() apparmor: Fix memleak in alloc_ns() apparmor: Fix memleak issue in unpack_profile() apparmor: fix a memleak in free_ruleset() apparmor: Fix spelling of function name in comment block apparmor: Use pointer to struct aa_label for lbs_cred AppArmor: Fix kernel-doc LSM: Fix kernel-doc AppArmor: Fix kernel-doc apparmor: Fix loading of child before parent apparmor: refactor code that alloc null profiles apparmor: fix obsoleted comments for aa_getprocattr() and audit_resource() apparmor: remove useless static inline functions apparmor: Fix unpack_profile() warn: passing zero to 'ERR_PTR' apparmor: fix uninitialize table variable in error in unpack_trans_table apparmor: store return value of unpack_perms_table() to signed variable apparmor: Fix kunit test for out of bounds array apparmor: Fix decompression of rawdata for read back to userspace apparmor: Fix undefined references to zstd_ symbols ...
-rw-r--r--security/apparmor/Kconfig4
-rw-r--r--security/apparmor/Makefile3
-rw-r--r--security/apparmor/apparmorfs.c115
-rw-r--r--security/apparmor/audit.c45
-rw-r--r--security/apparmor/capability.c16
-rw-r--r--security/apparmor/domain.c149
-rw-r--r--security/apparmor/file.c125
-rw-r--r--security/apparmor/include/apparmor.h23
-rw-r--r--security/apparmor/include/audit.h8
-rw-r--r--security/apparmor/include/cred.h13
-rw-r--r--security/apparmor/include/domain.h6
-rw-r--r--security/apparmor/include/file.h108
-rw-r--r--security/apparmor/include/label.h13
-rw-r--r--security/apparmor/include/lib.h10
-rw-r--r--security/apparmor/include/match.h28
-rw-r--r--security/apparmor/include/net.h1
-rw-r--r--security/apparmor/include/perms.h91
-rw-r--r--security/apparmor/include/policy.h141
-rw-r--r--security/apparmor/include/policy_compat.h33
-rw-r--r--security/apparmor/include/policy_unpack.h4
-rw-r--r--security/apparmor/ipc.c16
-rw-r--r--security/apparmor/label.c75
-rw-r--r--security/apparmor/lib.c125
-rw-r--r--security/apparmor/lsm.c25
-rw-r--r--security/apparmor/match.c62
-rw-r--r--security/apparmor/mount.c93
-rw-r--r--security/apparmor/net.c28
-rw-r--r--security/apparmor/policy.c204
-rw-r--r--security/apparmor/policy_compat.c319
-rw-r--r--security/apparmor/policy_ns.c6
-rw-r--r--security/apparmor/policy_unpack.c589
-rw-r--r--security/apparmor/policy_unpack_test.c14
-rw-r--r--security/apparmor/procattr.c11
-rw-r--r--security/apparmor/resource.c29
-rw-r--r--security/apparmor/task.c14
35 files changed, 1632 insertions, 914 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index f334e7cccf2d..e0d1dd0a192a 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -85,8 +85,8 @@ config SECURITY_APPARMOR_HASH_DEFAULT
config SECURITY_APPARMOR_EXPORT_BINARY
bool "Allow exporting the raw binary policy"
depends on SECURITY_APPARMOR_INTROSPECT_POLICY
- select ZLIB_INFLATE
- select ZLIB_DEFLATE
+ select ZSTD_COMPRESS
+ select ZSTD_DECOMPRESS
default y
help
This option allows reading back binary policy as it was loaded.
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 065f4e346553..b9c5879dd599 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- resource.o secid.o file.o policy_ns.o label.o mount.o net.o
+ resource.o secid.o file.o policy_ns.o label.o mount.o net.o \
+ policy_compat.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index d066ccc219e2..424b2c1e586d 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -21,7 +21,7 @@
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/poll.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>
@@ -611,29 +611,30 @@ static const struct file_operations aa_fs_ns_revision_fops = {
static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
const char *match_str, size_t match_len)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct aa_perms tmp = { };
- struct aa_dfa *dfa;
- unsigned int state = 0;
+ aa_state_t state = DFA_NOMATCH;
if (profile_unconfined(profile))
return;
- if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
- dfa = profile->file.dfa;
- state = aa_dfa_match_len(dfa, profile->file.start,
+ if (rules->file.dfa && *match_str == AA_CLASS_FILE) {
+ state = aa_dfa_match_len(rules->file.dfa,
+ rules->file.start[AA_CLASS_FILE],
match_str + 1, match_len - 1);
if (state) {
struct path_cond cond = { };
- tmp = aa_compute_fperms(dfa, state, &cond);
+ tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
}
- } else if (profile->policy.dfa) {
- if (!PROFILE_MEDIATES(profile, *match_str))
+ } else if (rules->policy.dfa) {
+ if (!RULE_MEDIATES(rules, *match_str))
return; /* no change to current perms */
- dfa = profile->policy.dfa;
- state = aa_dfa_match_len(dfa, profile->policy.start[0],
+ state = aa_dfa_match_len(rules->policy.dfa,
+ rules->policy.start[0],
match_str, match_len);
if (state)
- aa_compute_perms(dfa, state, &tmp);
+ tmp = *aa_lookup_perms(&rules->policy, state);
}
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum_raw(perms, &tmp);
@@ -868,8 +869,10 @@ static struct multi_transaction *multi_transaction_new(struct file *file,
if (!t)
return ERR_PTR(-ENOMEM);
kref_init(&t->count);
- if (copy_from_user(t->data, buf, size))
+ if (copy_from_user(t->data, buf, size)) {
+ put_multi_transaction(t);
return ERR_PTR(-EFAULT);
+ }
return t;
}
@@ -1090,9 +1093,9 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v)
struct aa_proxy *proxy = seq->private;
struct aa_label *label = aa_get_label_rcu(&proxy->label);
struct aa_profile *profile = labels_profile(label);
- if (profile->attach)
- seq_printf(seq, "%s\n", profile->attach);
- else if (profile->xmatch)
+ if (profile->attach.xmatch_str)
+ seq_printf(seq, "%s\n", profile->attach.xmatch_str);
+ else if (profile->attach.xmatch.dfa)
seq_puts(seq, "<unknown>\n");
else
seq_printf(seq, "%s\n", profile->base.name);
@@ -1197,10 +1200,24 @@ static int seq_ns_name_show(struct seq_file *seq, void *v)
return 0;
}
+static int seq_ns_compress_min_show(struct seq_file *seq, void *v)
+{
+ seq_printf(seq, "%d\n", AA_MIN_CLEVEL);
+ return 0;
+}
+
+static int seq_ns_compress_max_show(struct seq_file *seq, void *v)
+{
+ seq_printf(seq, "%d\n", AA_MAX_CLEVEL);
+ return 0;
+}
+
SEQ_NS_FOPS(stacked);
SEQ_NS_FOPS(nsstacked);
SEQ_NS_FOPS(level);
SEQ_NS_FOPS(name);
+SEQ_NS_FOPS(compress_min);
+SEQ_NS_FOPS(compress_max);
/* policy/raw_data/ * file ops */
@@ -1295,42 +1312,34 @@ SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size);
-static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
+static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
{
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
- if (aa_g_rawdata_compression_level != 0) {
- int error = 0;
- struct z_stream_s strm;
-
- memset(&strm, 0, sizeof(strm));
-
- strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
- if (!strm.workspace)
- return -ENOMEM;
-
- strm.next_in = src;
- strm.avail_in = slen;
-
- error = zlib_inflateInit(&strm);
- if (error != Z_OK) {
- error = -ENOMEM;
- goto fail_inflate_init;
+ if (slen < dlen) {
+ const size_t wksp_len = zstd_dctx_workspace_bound();
+ zstd_dctx *ctx;
+ void *wksp;
+ size_t out_len;
+ int ret = 0;
+
+ wksp = kvzalloc(wksp_len, GFP_KERNEL);
+ if (!wksp) {
+ ret = -ENOMEM;
+ goto cleanup;
}
-
- strm.next_out = dst;
- strm.avail_out = dlen;
-
- error = zlib_inflate(&strm, Z_FINISH);
- if (error != Z_STREAM_END)
- error = -EINVAL;
- else
- error = 0;
-
- zlib_inflateEnd(&strm);
-fail_inflate_init:
- kvfree(strm.workspace);
-
- return error;
+ ctx = zstd_init_dctx(wksp, wksp_len);
+ if (ctx == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
+ if (zstd_is_error(out_len)) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+cleanup:
+ kvfree(wksp);
+ return ret;
}
#endif
@@ -1379,9 +1388,9 @@ static int rawdata_open(struct inode *inode, struct file *file)
private->loaddata = loaddata;
- error = deflate_decompress(loaddata->data, loaddata->compressed_size,
- RAWDATA_F_DATA_BUF(private),
- loaddata->size);
+ error = decompress_zstd(loaddata->data, loaddata->compressed_size,
+ RAWDATA_F_DATA_BUF(private),
+ loaddata->size);
if (error)
goto fail_decompress;
@@ -2392,6 +2401,8 @@ static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
+ AA_SFS_FILE_FOPS("raw_data_compression_level_min", 0444, &seq_ns_compress_min_fops),
+ AA_SFS_FILE_FOPS("raw_data_compression_level_max", 0444, &seq_ns_compress_max_fops),
AA_SFS_DIR("features", aa_sfs_entry_features),
{ }
};
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 704b0c895605..5a7978aa4b19 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -36,6 +36,43 @@ static const char *const aa_audit_type[] = {
"AUTO"
};
+static const char *const aa_class_names[] = {
+ "none",
+ "unknown",
+ "file",
+ "cap",
+ "net",
+ "rlimits",
+ "domain",
+ "mount",
+ "unknown",
+ "ptrace",
+ "signal",
+ "xmatch",
+ "unknown",
+ "unknown",
+ "net",
+ "unknown",
+ "label",
+ "posix_mqueue",
+ "io_uring",
+ "module",
+ "lsm",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "unknown",
+ "X",
+ "dbus",
+};
+
+
/*
* Currently AppArmor auditing is fed straight into the audit framework.
*
@@ -46,7 +83,7 @@ static const char *const aa_audit_type[] = {
*/
/**
- * audit_base - core AppArmor function.
+ * audit_pre() - core AppArmor function.
* @ab: audit buffer to fill (NOT NULL)
* @ca: audit structure containing data to audit (NOT NULL)
*
@@ -65,6 +102,12 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
audit_log_format(ab, " operation=\"%s\"", aad(sa)->op);
}
+ if (aad(sa)->class)
+ audit_log_format(ab, " class=\"%s\"",
+ aad(sa)->class <= AA_CLASS_LAST ?
+ aa_class_names[aad(sa)->class] :
+ "unknown");
+
if (aad(sa)->info) {
audit_log_format(ab, " info=\"%s\"", aad(sa)->info);
if (aad(sa)->error)
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index deccea8654ad..326a51838ef2 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -64,6 +64,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
int cap, int error)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO;
@@ -72,13 +74,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
if (likely(!error)) {
/* test if auditing is being forced */
if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
- !cap_raised(profile->caps.audit, cap)))
+ !cap_raised(rules->caps.audit, cap)))
return 0;
type = AUDIT_APPARMOR_AUDIT;
} else if (KILL_MODE(profile) ||
- cap_raised(profile->caps.kill, cap)) {
+ cap_raised(rules->caps.kill, cap)) {
type = AUDIT_APPARMOR_KILL;
- } else if (cap_raised(profile->caps.quiet, cap) &&
+ } else if (cap_raised(rules->caps.quiet, cap) &&
AUDIT_MODE(profile) != AUDIT_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL) {
/* quiet auditing */
@@ -114,10 +116,12 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
static int profile_capable(struct aa_profile *profile, int cap,
unsigned int opts, struct common_audit_data *sa)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
int error;
- if (cap_raised(profile->caps.allow, cap) &&
- !cap_raised(profile->caps.denied, cap))
+ if (cap_raised(rules->caps.allow, cap) &&
+ !cap_raised(rules->caps.denied, cap))
error = 0;
else
error = -EPERM;
@@ -148,7 +152,7 @@ int aa_capable(struct aa_label *label, int cap, unsigned int opts)
{
struct aa_profile *profile;
int error = 0;
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, AA_CLASS_CAP, OP_CAPABLE);
sa.u.cap = cap;
error = fn_for_each_confined(label, profile,
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 00dc0ec066de..6dd3cc5309bf 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -30,24 +30,6 @@
#include "include/policy_ns.h"
/**
- * aa_free_domain_entries - free entries in a domain table
- * @domain: the domain table to free (MAYBE NULL)
- */
-void aa_free_domain_entries(struct aa_domain *domain)
-{
- int i;
- if (domain) {
- if (!domain->table)
- return;
-
- for (i = 0; i < domain->size; i++)
- kfree_sensitive(domain->table[i]);
- kfree_sensitive(domain->table);
- domain->table = NULL;
- }
-}
-
-/**
* may_change_ptraced_domain - check if can change profile on ptraced task
* @to_label: profile to change to (NOT NULL)
* @info: message if there is an error
@@ -95,23 +77,25 @@ out:
* If a subns profile is not to be matched should be prescreened with
* visibility test.
*/
-static inline unsigned int match_component(struct aa_profile *profile,
- struct aa_profile *tp,
- bool stack, unsigned int state)
+static inline aa_state_t match_component(struct aa_profile *profile,
+ struct aa_profile *tp,
+ bool stack, aa_state_t state)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
const char *ns_name;
if (stack)
- state = aa_dfa_match(profile->file.dfa, state, "&");
+ state = aa_dfa_match(rules->file.dfa, state, "&");
if (profile->ns == tp->ns)
- return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
+ return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
/* try matching with namespace name and then profile */
ns_name = aa_ns_name(profile->ns, tp->ns, true);
- state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
- state = aa_dfa_match(profile->file.dfa, state, ns_name);
- state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
- return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
+ state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
+ state = aa_dfa_match(rules->file.dfa, state, ns_name);
+ state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
+ return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
}
/**
@@ -132,9 +116,11 @@ static inline unsigned int match_component(struct aa_profile *profile,
*/
static int label_compound_match(struct aa_profile *profile,
struct aa_label *label, bool stack,
- unsigned int state, bool subns, u32 request,
+ aa_state_t state, bool subns, u32 request,
struct aa_perms *perms)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct aa_profile *tp;
struct label_it i;
struct path_cond cond = { };
@@ -157,12 +143,12 @@ next:
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue;
- state = aa_dfa_match(profile->file.dfa, state, "//&");
+ state = aa_dfa_match(rules->file.dfa, state, "//&");
state = match_component(profile, tp, false, state);
if (!state)
goto fail;
}
- *perms = aa_compute_fperms(profile->file.dfa, state, &cond);
+ *perms = *(aa_lookup_fperms(&(rules->file), state, &cond));
aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request)
return -EACCES;
@@ -192,14 +178,16 @@ fail:
*/
static int label_components_match(struct aa_profile *profile,
struct aa_label *label, bool stack,
- unsigned int start, bool subns, u32 request,
+ aa_state_t start, bool subns, u32 request,
struct aa_perms *perms)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct aa_profile *tp;
struct label_it i;
struct aa_perms tmp;
struct path_cond cond = { };
- unsigned int state = 0;
+ aa_state_t state = 0;
/* find first subcomponent to test */
label_for_each(i, label, tp) {
@@ -215,7 +203,7 @@ static int label_components_match(struct aa_profile *profile,
return 0;
next:
- tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
+ tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) {
@@ -224,7 +212,7 @@ next:
state = match_component(profile, tp, stack, start);
if (!state)
goto fail;
- tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
+ tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
}
@@ -252,7 +240,7 @@ fail:
* Returns: the state the match finished in, may be the none matching state
*/
static int label_match(struct aa_profile *profile, struct aa_label *label,
- bool stack, unsigned int state, bool subns, u32 request,
+ bool stack, aa_state_t state, bool subns, u32 request,
struct aa_perms *perms)
{
int error;
@@ -286,7 +274,7 @@ static int label_match(struct aa_profile *profile, struct aa_label *label,
*/
static int change_profile_perms(struct aa_profile *profile,
struct aa_label *target, bool stack,
- u32 request, unsigned int start,
+ u32 request, aa_state_t start,
struct aa_perms *perms)
{
if (profile_unconfined(profile)) {
@@ -308,44 +296,47 @@ static int change_profile_perms(struct aa_profile *profile,
* Returns: number of extended attributes that matched, or < 0 on error
*/
static int aa_xattrs_match(const struct linux_binprm *bprm,
- struct aa_profile *profile, unsigned int state)
+ struct aa_profile *profile, aa_state_t state)
{
int i;
struct dentry *d;
char *value = NULL;
- int size, value_size = 0, ret = profile->xattr_count;
+ struct aa_attachment *attach = &profile->attach;
+ int size, value_size = 0, ret = attach->xattr_count;
- if (!bprm || !profile->xattr_count)
+ if (!bprm || !attach->xattr_count)
return 0;
might_sleep();
/* transition from exec match to xattr set */
- state = aa_dfa_outofband_transition(profile->xmatch, state);
+ state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
d = bprm->file->f_path.dentry;
- for (i = 0; i < profile->xattr_count; i++) {
- size = vfs_getxattr_alloc(&init_user_ns, d, profile->xattrs[i],
+ for (i = 0; i < attach->xattr_count; i++) {
+ size = vfs_getxattr_alloc(&init_user_ns, d, attach->xattrs[i],
&value, value_size, GFP_KERNEL);
if (size >= 0) {
- u32 perm;
+ u32 index, perm;
/*
* Check the xattr presence before value. This ensure
* that not present xattr can be distinguished from a 0
* length value or rule that matches any value
*/
- state = aa_dfa_null_transition(profile->xmatch, state);
+ state = aa_dfa_null_transition(attach->xmatch.dfa,
+ state);
/* Check xattr value */
- state = aa_dfa_match_len(profile->xmatch, state, value,
- size);
- perm = dfa_user_allow(profile->xmatch, state);
+ state = aa_dfa_match_len(attach->xmatch.dfa, state,
+ value, size);
+ index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
+ perm = attach->xmatch.perms[index].allow;
if (!(perm & MAY_EXEC)) {
ret = -EINVAL;
goto out;
}
}
/* transition to next element */
- state = aa_dfa_outofband_transition(profile->xmatch, state);
+ state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
if (size < 0) {
/*
* No xattr match, so verify if transition to
@@ -397,6 +388,8 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
rcu_read_lock();
restart:
list_for_each_entry_rcu(profile, head, base.list) {
+ struct aa_attachment *attach = &profile->attach;
+
if (profile->label.flags & FLAG_NULL &&
&profile->label == ns_unconfined(profile->ns))
continue;
@@ -412,13 +405,16 @@ restart:
* as another profile, signal a conflict and refuse to
* match.
*/
- if (profile->xmatch) {
- unsigned int state, count;
- u32 perm;
-
- state = aa_dfa_leftmatch(profile->xmatch, DFA_START,
- name, &count);
- perm = dfa_user_allow(profile->xmatch, state);
+ if (attach->xmatch.dfa) {
+ unsigned int count;
+ aa_state_t state;
+ u32 index, perm;
+
+ state = aa_dfa_leftmatch(attach->xmatch.dfa,
+ attach->xmatch.start[AA_CLASS_XMATCH],
+ name, &count);
+ index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
+ perm = attach->xmatch.perms[index].allow;
/* any accepting state means a valid match. */
if (perm & MAY_EXEC) {
int ret = 0;
@@ -426,7 +422,7 @@ restart:
if (count < candidate_len)
continue;
- if (bprm && profile->xattr_count) {
+ if (bprm && attach->xattr_count) {
long rev = READ_ONCE(ns->revision);
if (!aa_get_profile_not0(profile))
@@ -465,7 +461,7 @@ restart:
* xattrs, or a longer match
*/
candidate = profile;
- candidate_len = max(count, profile->xmatch_len);
+ candidate_len = max(count, attach->xmatch_len);
candidate_xattrs = ret;
conflict = false;
}
@@ -509,6 +505,8 @@ static const char *next_name(int xtype, const char *name)
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct aa_label *label = NULL;
u32 xtype = xindex & AA_X_TYPE_MASK;
int index = xindex & AA_X_INDEX_MASK;
@@ -519,7 +517,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
/* TODO: move lookup parsing to unpack time so this is a straight
* index into the resultant label
*/
- for (*name = profile->file.trans.table[index]; !label && *name;
+ for (*name = rules->file.trans.table[index]; !label && *name;
*name = next_name(xtype, *name)) {
if (xindex & AA_X_CHILD) {
struct aa_profile *new_profile;
@@ -558,6 +556,8 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
const char **lookupname,
const char **info)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct aa_label *new = NULL;
struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK;
@@ -570,7 +570,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
break;
case AA_X_TABLE:
/* TODO: fix when perm mapping done at unload */
- stack = profile->file.trans.table[xindex & AA_X_INDEX_MASK];
+ stack = rules->file.trans.table[xindex & AA_X_INDEX_MASK];
if (*stack != '&') {
/* released by caller */
new = x_table_lookup(profile, xindex, lookupname);
@@ -624,9 +624,11 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
char *buffer, struct path_cond *cond,
bool *secure_exec)
{
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
struct aa_label *new = NULL;
const char *info = NULL, *name = NULL, *target = NULL;
- unsigned int state = profile->file.start;
+ aa_state_t state = rules->file.start[AA_CLASS_FILE];
struct aa_perms perms = {};
bool nonewprivs = false;
int error = 0;
@@ -660,7 +662,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
}
/* find exec permissions for name */
- state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
+ state = aa_str_perms(&(rules->file), state, name, cond, &perms);
if (perms.allow & MAY_EXEC) {
/* exec permission determine how to transition */
new = x_to_label(profile, bprm, name, perms.xindex, &target,
@@ -678,8 +680,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
/* no exec permission - learning mode */
struct aa_profile *new_profile = NULL;
- new_profile = aa_new_null_profile(profile, false, name,
- GFP_KERNEL);
+ new_profile = aa_new_learning_profile(profile, false, name,
+ GFP_KERNEL);
if (!new_profile) {
error = -ENOMEM;
info = "could not create null profile";
@@ -722,7 +724,9 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
char *buffer, struct path_cond *cond,
bool *secure_exec)
{
- unsigned int state = profile->file.start;
+ struct aa_ruleset *rules = list_first_entry(&profile->rules,
+ typeof(*rules), list);
+ aa_state_t state = rules->file.start[AA_CLASS_FILE];
struct aa_perms perms = {};
const char *xname = NULL, *info = "change_profile onexec";
int error = -EACCES;
@@ -755,7 +759,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
}
/* find exec permissions for name */
- state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms);
+ state = aa_str_perms(&(rules->file), state, xname, cond, &perms);
if (!(perms.allow & AA_MAY_ONEXEC)) {
info = "no change_onexec valid for executable";
goto audit;
@@ -764,7 +768,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
* onexec permission is linked to exec with a standard pairing
* exec\0change_profile
*/
- state = aa_dfa_null_transition(profile->file.dfa, state);
+ state = aa_dfa_null_transition(rules->file.dfa, state);
error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
state, &perms);
if (error) {
@@ -1004,8 +1008,8 @@ static struct aa_label *build_change_hat(struct aa_profile *profile,
if (!hat) {
error = -ENOE