Commit 93761c93 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'apparmor-pr-2022-12-14' of...

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
  ...
parents 64e7003c 4295c60b
...@@ -85,8 +85,8 @@ config SECURITY_APPARMOR_HASH_DEFAULT ...@@ -85,8 +85,8 @@ config SECURITY_APPARMOR_HASH_DEFAULT
config SECURITY_APPARMOR_EXPORT_BINARY config SECURITY_APPARMOR_EXPORT_BINARY
bool "Allow exporting the raw binary policy" bool "Allow exporting the raw binary policy"
depends on SECURITY_APPARMOR_INTROSPECT_POLICY depends on SECURITY_APPARMOR_INTROSPECT_POLICY
select ZLIB_INFLATE select ZSTD_COMPRESS
select ZLIB_DEFLATE select ZSTD_DECOMPRESS
default y default y
help help
This option allows reading back binary policy as it was loaded. This option allows reading back binary policy as it was loaded.
......
...@@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o ...@@ -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 \ 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 \ 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 apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/fs_context.h> #include <linux/fs_context.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/zlib.h> #include <linux/zstd.h>
#include <uapi/linux/major.h> #include <uapi/linux/major.h>
#include <uapi/linux/magic.h> #include <uapi/linux/magic.h>
...@@ -611,29 +611,30 @@ static const struct file_operations aa_fs_ns_revision_fops = { ...@@ -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, static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
const char *match_str, size_t match_len) 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_perms tmp = { };
struct aa_dfa *dfa; aa_state_t state = DFA_NOMATCH;
unsigned int state = 0;
if (profile_unconfined(profile)) if (profile_unconfined(profile))
return; return;
if (profile->file.dfa && *match_str == AA_CLASS_FILE) { if (rules->file.dfa && *match_str == AA_CLASS_FILE) {
dfa = profile->file.dfa; state = aa_dfa_match_len(rules->file.dfa,
state = aa_dfa_match_len(dfa, profile->file.start, rules->file.start[AA_CLASS_FILE],
match_str + 1, match_len - 1); match_str + 1, match_len - 1);
if (state) { if (state) {
struct path_cond cond = { }; struct path_cond cond = { };
tmp = aa_compute_fperms(dfa, state, &cond); tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
} }
} else if (profile->policy.dfa) { } else if (rules->policy.dfa) {
if (!PROFILE_MEDIATES(profile, *match_str)) if (!RULE_MEDIATES(rules, *match_str))
return; /* no change to current perms */ return; /* no change to current perms */
dfa = profile->policy.dfa; state = aa_dfa_match_len(rules->policy.dfa,
state = aa_dfa_match_len(dfa, profile->policy.start[0], rules->policy.start[0],
match_str, match_len); match_str, match_len);
if (state) if (state)
aa_compute_perms(dfa, state, &tmp); tmp = *aa_lookup_perms(&rules->policy, state);
} }
aa_apply_modes_to_perms(profile, &tmp); aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum_raw(perms, &tmp); aa_perms_accum_raw(perms, &tmp);
...@@ -868,8 +869,10 @@ static struct multi_transaction *multi_transaction_new(struct file *file, ...@@ -868,8 +869,10 @@ static struct multi_transaction *multi_transaction_new(struct file *file,
if (!t) if (!t)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
kref_init(&t->count); 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 ERR_PTR(-EFAULT);
}
return t; return t;
} }
...@@ -1090,9 +1093,9 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v) ...@@ -1090,9 +1093,9 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v)
struct aa_proxy *proxy = seq->private; struct aa_proxy *proxy = seq->private;
struct aa_label *label = aa_get_label_rcu(&proxy->label); struct aa_label *label = aa_get_label_rcu(&proxy->label);
struct aa_profile *profile = labels_profile(label); struct aa_profile *profile = labels_profile(label);
if (profile->attach) if (profile->attach.xmatch_str)
seq_printf(seq, "%s\n", profile->attach); seq_printf(seq, "%s\n", profile->attach.xmatch_str);
else if (profile->xmatch) else if (profile->attach.xmatch.dfa)
seq_puts(seq, "<unknown>\n"); seq_puts(seq, "<unknown>\n");
else else
seq_printf(seq, "%s\n", profile->base.name); seq_printf(seq, "%s\n", profile->base.name);
...@@ -1197,10 +1200,24 @@ static int seq_ns_name_show(struct seq_file *seq, void *v) ...@@ -1197,10 +1200,24 @@ static int seq_ns_name_show(struct seq_file *seq, void *v)
return 0; 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(stacked);
SEQ_NS_FOPS(nsstacked); SEQ_NS_FOPS(nsstacked);
SEQ_NS_FOPS(level); SEQ_NS_FOPS(level);
SEQ_NS_FOPS(name); SEQ_NS_FOPS(name);
SEQ_NS_FOPS(compress_min);
SEQ_NS_FOPS(compress_max);
/* policy/raw_data/ * file ops */ /* policy/raw_data/ * file ops */
...@@ -1295,42 +1312,34 @@ SEQ_RAWDATA_FOPS(revision); ...@@ -1295,42 +1312,34 @@ SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash); SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size); 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 #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
if (aa_g_rawdata_compression_level != 0) { if (slen < dlen) {
int error = 0; const size_t wksp_len = zstd_dctx_workspace_bound();
struct z_stream_s strm; zstd_dctx *ctx;
void *wksp;
memset(&strm, 0, sizeof(strm)); size_t out_len;
int ret = 0;
strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
if (!strm.workspace) wksp = kvzalloc(wksp_len, GFP_KERNEL);
return -ENOMEM; if (!wksp) {
ret = -ENOMEM;
strm.next_in = src; goto cleanup;
strm.avail_in = slen; }
ctx = zstd_init_dctx(wksp, wksp_len);
error = zlib_inflateInit(&strm); if (ctx == NULL) {
if (error != Z_OK) { ret = -ENOMEM;
error = -ENOMEM; goto cleanup;
goto fail_inflate_init; }
} out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
if (zstd_is_error(out_len)) {
strm.next_out = dst; ret = -EINVAL;
strm.avail_out = dlen; goto cleanup;
}
error = zlib_inflate(&strm, Z_FINISH); cleanup:
if (error != Z_STREAM_END) kvfree(wksp);
error = -EINVAL; return ret;
else
error = 0;
zlib_inflateEnd(&strm);
fail_inflate_init:
kvfree(strm.workspace);
return error;
} }
#endif #endif
...@@ -1379,7 +1388,7 @@ static int rawdata_open(struct inode *inode, struct file *file) ...@@ -1379,7 +1388,7 @@ static int rawdata_open(struct inode *inode, struct file *file)
private->loaddata = loaddata; private->loaddata = loaddata;
error = deflate_decompress(loaddata->data, loaddata->compressed_size, error = decompress_zstd(loaddata->data, loaddata->compressed_size,
RAWDATA_F_DATA_BUF(private), RAWDATA_F_DATA_BUF(private),
loaddata->size); loaddata->size);
if (error) if (error)
...@@ -2392,6 +2401,8 @@ static struct aa_sfs_entry aa_sfs_entry_apparmor[] = { ...@@ -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_level", 0444, &seq_ns_level_fops),
AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_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("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), AA_SFS_DIR("features", aa_sfs_entry_features),
{ } { }
}; };
......
...@@ -36,6 +36,43 @@ static const char *const aa_audit_type[] = { ...@@ -36,6 +36,43 @@ static const char *const aa_audit_type[] = {
"AUTO" "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. * Currently AppArmor auditing is fed straight into the audit framework.
* *
...@@ -46,7 +83,7 @@ static const char *const aa_audit_type[] = { ...@@ -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) * @ab: audit buffer to fill (NOT NULL)
* @ca: audit structure containing data to audit (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) ...@@ -65,6 +102,12 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
audit_log_format(ab, " operation=\"%s\"", aad(sa)->op); 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) { if (aad(sa)->info) {
audit_log_format(ab, " info=\"%s\"", aad(sa)->info); audit_log_format(ab, " info=\"%s\"", aad(sa)->info);
if (aad(sa)->error) if (aad(sa)->error)
......
...@@ -64,6 +64,8 @@ static void audit_cb(struct audit_buffer *ab, void *va) ...@@ -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, static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
int cap, int error) int cap, int error)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct audit_cache *ent; struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO; int type = AUDIT_APPARMOR_AUTO;
...@@ -72,13 +74,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile, ...@@ -72,13 +74,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
if (likely(!error)) { if (likely(!error)) {
/* test if auditing is being forced */ /* test if auditing is being forced */
if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
!cap_raised(profile->caps.audit, cap))) !cap_raised(rules->caps.audit, cap)))
return 0; return 0;
type = AUDIT_APPARMOR_AUDIT; type = AUDIT_APPARMOR_AUDIT;
} else if (KILL_MODE(profile) || } else if (KILL_MODE(profile) ||
cap_raised(profile->caps.kill, cap)) { cap_raised(rules->caps.kill, cap)) {
type = AUDIT_APPARMOR_KILL; 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_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL) { AUDIT_MODE(profile) != AUDIT_ALL) {
/* quiet auditing */ /* quiet auditing */
...@@ -114,10 +116,12 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile, ...@@ -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, static int profile_capable(struct aa_profile *profile, int cap,
unsigned int opts, struct common_audit_data *sa) unsigned int opts, struct common_audit_data *sa)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
int error; int error;
if (cap_raised(profile->caps.allow, cap) && if (cap_raised(rules->caps.allow, cap) &&
!cap_raised(profile->caps.denied, cap)) !cap_raised(rules->caps.denied, cap))
error = 0; error = 0;
else else
error = -EPERM; error = -EPERM;
...@@ -148,7 +152,7 @@ int aa_capable(struct aa_label *label, int cap, unsigned int opts) ...@@ -148,7 +152,7 @@ int aa_capable(struct aa_label *label, int cap, unsigned int opts)
{ {
struct aa_profile *profile; struct aa_profile *profile;
int error = 0; 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; sa.u.cap = cap;
error = fn_for_each_confined(label, profile, error = fn_for_each_confined(label, profile,
......
...@@ -29,24 +29,6 @@ ...@@ -29,24 +29,6 @@
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_ns.h" #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 * may_change_ptraced_domain - check if can change profile on ptraced task
* @to_label: profile to change to (NOT NULL) * @to_label: profile to change to (NOT NULL)
...@@ -95,23 +77,25 @@ static int may_change_ptraced_domain(struct aa_label *to_label, ...@@ -95,23 +77,25 @@ static int may_change_ptraced_domain(struct aa_label *to_label,
* If a subns profile is not to be matched should be prescreened with * If a subns profile is not to be matched should be prescreened with
* visibility test. * visibility test.
*/ */
static inline unsigned int match_component(struct aa_profile *profile, static inline aa_state_t match_component(struct aa_profile *profile,
struct aa_profile *tp, struct aa_profile *tp,
bool stack, unsigned int state) bool stack, aa_state_t state)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
const char *ns_name; const char *ns_name;
if (stack) if (stack)
state = aa_dfa_match(profile->file.dfa, state, "&"); state = aa_dfa_match(rules->file.dfa, state, "&");
if (profile->ns == tp->ns) 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 */ /* try matching with namespace name and then profile */
ns_name = aa_ns_name(profile->ns, tp->ns, true); ns_name = aa_ns_name(profile->ns, tp->ns, true);
state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
state = aa_dfa_match(profile->file.dfa, state, ns_name); state = aa_dfa_match(rules->file.dfa, state, ns_name);
state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
return aa_dfa_match(profile->file.dfa, state, tp->base.hname); 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, ...@@ -132,9 +116,11 @@ static inline unsigned int match_component(struct aa_profile *profile,
*/ */
static int label_compound_match(struct aa_profile *profile, static int label_compound_match(struct aa_profile *profile,
struct aa_label *label, bool stack, 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_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_profile *tp; struct aa_profile *tp;
struct label_it i; struct label_it i;
struct path_cond cond = { }; struct path_cond cond = { };
...@@ -157,12 +143,12 @@ static int label_compound_match(struct aa_profile *profile, ...@@ -157,12 +143,12 @@ static int label_compound_match(struct aa_profile *profile,
label_for_each_cont(i, label, tp) { label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns)) if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue; continue;
state = aa_dfa_match(profile->file.dfa, state, "//&"); state = aa_dfa_match(rules->file.dfa, state, "//&");
state = match_component(profile, tp, false, state); state = match_component(profile, tp, false, state);
if (!state) if (!state)
goto fail; 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); aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request) if ((perms->allow & request) != request)
return -EACCES; return -EACCES;
...@@ -192,14 +178,16 @@ static int label_compound_match(struct aa_profile *profile, ...@@ -192,14 +178,16 @@ static int label_compound_match(struct aa_profile *profile,
*/ */
static int label_components_match(struct aa_profile *profile, static int label_components_match(struct aa_profile *profile,
struct aa_label *label, bool stack, 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_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_profile *tp; struct aa_profile *tp;
struct label_it i; struct label_it i;
struct aa_perms tmp; struct aa_perms tmp;
struct path_cond cond = { }; struct path_cond cond = { };
unsigned int state = 0; aa_state_t state = 0;
/* find first subcomponent to test */ /* find first subcomponent to test */
label_for_each(i, label, tp) { label_for_each(i, label, tp) {
...@@ -215,7 +203,7 @@ static int label_components_match(struct aa_profile *profile, ...@@ -215,7 +203,7 @@ static int label_components_match(struct aa_profile *profile,
return 0; return 0;
next: 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_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp); aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) { label_for_each_cont(i, label, tp) {
...@@ -224,7 +212,7 @@ static int label_components_match(struct aa_profile *profile, ...@@ -224,7 +212,7 @@ static int label_components_match(struct aa_profile *profile,
state = match_component(profile, tp, stack, start); state = match_component(profile, tp, stack, start);
if (!state) if (!state)
goto fail; 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_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp); aa_perms_accum(perms, &tmp);
} }
...@@ -252,7 +240,7 @@ static int label_components_match(struct aa_profile *profile, ...@@ -252,7 +240,7 @@ static int label_components_match(struct aa_profile *profile,
* Returns: the state the match finished in, may be the none matching state * 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, 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) struct aa_perms *perms)
{ {
int error; int error;
...@@ -286,7 +274,7 @@ static int label_match(struct aa_profile *profile, struct aa_label *label, ...@@ -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, static int change_profile_perms(struct aa_profile *profile,
struct aa_label *target, bool stack, struct aa_label *target, bool stack,
u32 request, unsigned int start, u32 request, aa_state_t start,
struct aa_perms *perms) struct aa_perms *perms)
{ {
if (profile_unconfined(profile)) { if (profile_unconfined(profile)) {
...@@ -308,44 +296,47 @@ static int change_profile_perms(struct aa_profile *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 * Returns: number of extended attributes that matched, or < 0 on error
*/ */
static int aa_xattrs_match(const struct linux_binprm *bprm, 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; int i;
struct dentry *d; struct dentry *d;
char *value = NULL; 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; return 0;
might_sleep(); might_sleep();
/* transition from exec match to xattr set */ /* 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; d = bprm->file->f_path.dentry;
for (i = 0; i < profile->xattr_count; i++) { for (i = 0; i < attach->xattr_count; i++) {
size = vfs_getxattr_alloc(&init_user_ns, d, profile->xattrs[i], size = vfs_getxattr_alloc(&init_user_ns, d, attach->xattrs[i],
&value, value_size, GFP_KERNEL); &value, value_size, GFP_KERNEL);
if (size >= 0) { if (size >= 0) {
u32 perm; u32 index, perm;
/* /*
* Check the xattr presence before value. This ensure * Check the xattr presence before value. This ensure
* that not present xattr can be distinguished from a 0 * that not present xattr can be distinguished from a 0
* length value or rule that matches any value * 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 */ /* Check xattr value */
state = aa_dfa_match_len(profile->xmatch, state, value, state = aa_dfa_match_len(attach->xmatch.dfa, state,
size); value, size);
perm = dfa_user_allow(profile->xmatch, state); index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
perm = attach->xmatch.perms[index].allow;
if (!(perm & MAY_EXEC)) { if (!(perm & MAY_EXEC)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
} }
/* transition to next element */ /* transition to next element */
state = aa_dfa_outofband_transition(profile->xmatch, state); state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
if (size < 0) { if (size < 0) {
/* /*
* No xattr match, so verify if transition to * No xattr match, so verify if transition to
...@@ -397,6 +388,8 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm, ...@@ -397,6 +388,8 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
rcu_read_lock(); rcu_read_lock();
restart: restart:
list_for_each_entry_rcu(profile, head, base.list) { list_for_each_entry_rcu(profile, head, base.list) {
struct aa_attachment *attach = &profile->attach;
if (profile->label.flags & FLAG_NULL && if (profile->label.flags & FLAG_NULL &&
&profile->label == ns_unconfined(profile->ns)) &profile->label == ns_unconfined(profile->ns))
continue; continue;
...@@ -412,13 +405,16 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm, ...@@ -412,13 +405,16 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
* as another profile, signal a conflict and refuse to * as another profile, signal a conflict and refuse to
* match. * match.
*/ */
if (profile->xmatch) { if (attach->xmatch.dfa) {
unsigned int state, count; unsigned int count;
u32 perm; aa_state_t state;
u32 index, perm;
state = aa_dfa_leftmatch(profile->xmatch, DFA_START, state = aa_dfa_leftmatch(attach->xmatch.dfa,
attach->xmatch.start[AA_CLASS_XMATCH],
name, &count); name, &count);
perm = dfa_user_allow(profile->xmatch, state); index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
perm = attach->xmatch.perms[index].allow;
/* any accepting state means a valid match. */ /* any accepting state means a valid match. */
if (perm & MAY_EXEC) { if (perm & MAY_EXEC) {
int ret = 0; int ret = 0;
...@@ -426,7 +422,7 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm, ...@@ -426,7 +422,7 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
if (count < candidate_len) if (count < candidate_len)
continue; continue;
if (bprm && profile->xattr_count) { if (bprm && attach->xattr_count) {
long rev = READ_ONCE(ns->revision); long rev = READ_ONCE(ns->revision);
if (!aa_get_profile_not0(profile)) if (!aa_get_profile_not0(profile))
...@@ -465,7 +461,7 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm, ...@@ -465,7 +461,7 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
* xattrs, or a longer match * xattrs, or a longer match
*/ */
candidate = profile; candidate = profile;
candidate_len = max(count, profile->xmatch_len); candidate_len = max(count, attach->xmatch_len);
candidate_xattrs = ret; candidate_xattrs = ret;
conflict = false; conflict = false;
} }
...@@ -509,6 +505,8 @@ static const char *next_name(int xtype, const char *name) ...@@ -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, struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
const char **name) const char **name)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *label = NULL; struct aa_label *label = NULL;
u32 xtype = xindex & AA_X_TYPE_MASK; u32 xtype = xindex & AA_X_TYPE_MASK;
int index = xindex & AA_X_INDEX_MASK; int index = xindex & AA_X_INDEX_MASK;
...@@ -519,7 +517,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, ...@@ -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 /* TODO: move lookup parsing to unpack time so this is a straight
* index into the resultant label * 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)) { *name = next_name(xtype, *name)) {
if (xindex & AA_X_CHILD) { if (xindex & AA_X_CHILD) {
struct aa_profile *new_profile; struct aa_profile *new_profile;
...@@ -558,6 +556,8 @@ static struct aa_label *x_to_label(struct aa_profile *profile, ...@@ -558,6 +556,8 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
const char **lookupname, const char **lookupname,
const char **info) const char **info)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new = NULL; struct aa_label *new = NULL;
struct aa_ns *ns = profile->ns; struct aa_ns *ns = profile->ns;
u32 xtype = xindex & AA_X_TYPE_MASK; u32 xtype = xindex & AA_X_TYPE_MASK;
...@@ -570,7 +570,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile, ...@@ -570,7 +570,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
break; break;
case AA_X_TABLE: case AA_X_TABLE:
/* TODO: fix when perm mapping done at unload */ /* 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 != '&') { if (*stack != '&') {
/* released by caller */ /* released by caller */
new = x_table_lookup(profile, xindex, lookupname); new = x_table_lookup(profile, xindex, lookupname);
...@@ -624,9 +624,11 @@ static struct aa_label *profile_transition(struct aa_profile *profile, ...@@ -624,9 +624,11 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
char *buffer, struct path_cond *cond, char *buffer, struct path_cond *cond,
bool *secure_exec) bool *secure_exec)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new = NULL; struct aa_label *new = NULL;
const char *info = NULL, *name = NULL, *target = 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 = {}; struct aa_perms perms = {};
bool nonewprivs = false; bool nonewprivs = false;
int error = 0; int error = 0;
...@@ -660,7 +662,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile, ...@@ -660,7 +662,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
} }
/* find exec permissions for name */ /* 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) { if (perms.allow & MAY_EXEC) {
/* exec permission determine how to transition */ /* exec permission determine how to transition */
new = x_to_label(profile, bprm, name, perms.xindex, &target, new = x_to_label(profile, bprm, name, perms.xindex, &target,
...@@ -678,7 +680,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile, ...@@ -678,7 +680,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
/* no exec permission - learning mode */ /* no exec permission - learning mode */
struct aa_profile *new_profile = NULL; struct aa_profile *new_profile = NULL;
new_profile = aa_new_null_profile(profile, false, name, new_profile = aa_new_learning_profile(profile, false, name,
GFP_KERNEL); GFP_KERNEL);
if (!new_profile) { if (!new_profile) {
error = -ENOMEM; error = -ENOMEM;
...@@ -722,7 +724,9 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, ...@@ -722,7 +724,9 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
char *buffer, struct path_cond *cond, char *buffer, struct path_cond *cond,
bool *secure_exec) 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 = {}; struct aa_perms perms = {};
const char *xname = NULL, *info = "change_profile onexec"; const char *xname = NULL, *info = "change_profile onexec";
int error = -EACCES; int error = -EACCES;
...@@ -755,7 +759,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, ...@@ -755,7 +759,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
} }
/* find exec permissions for name */ /* 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)) { if (!(perms.allow & AA_MAY_ONEXEC)) {
info = "no change_onexec valid for executable"; info = "no change_onexec valid for executable";
goto audit; goto audit;
...@@ -764,7 +768,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec, ...@@ -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 * onexec permission is linked to exec with a standard pairing
* exec\0change_profile * 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, error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
state, &perms); state, &perms);
if (error) { if (error) {
...@@ -1004,7 +1008,7 @@ static struct aa_label *build_change_hat(struct aa_profile *profile, ...@@ -1004,7 +1008,7 @@ static struct aa_label *build_change_hat(struct aa_profile *profile,
if (!hat) { if (!hat) {
error = -ENOENT; error = -ENOENT;
if (COMPLAIN_MODE(profile)) { if (COMPLAIN_MODE(profile)) {
hat = aa_new_null_profile(profile, true, name, hat = aa_new_learning_profile(profile, true, name,
GFP_KERNEL); GFP_KERNEL);
if (!hat) { if (!hat) {
info = "failed null profile create"; info = "failed null profile create";
...@@ -1261,12 +1265,15 @@ static int change_profile_perms_wrapper(const char *op, const char *name, ...@@ -1261,12 +1265,15 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
struct aa_label *target, bool stack, struct aa_label *target, bool stack,
u32 request, struct aa_perms *perms) u32 request, struct aa_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
const char *info = NULL; const char *info = NULL;
int error = 0; int error = 0;
if (!error) if (!error)
error = change_profile_perms(profile, target, stack, request, error = change_profile_perms(profile, target, stack, request,
profile->file.start, perms); rules->file.start[AA_CLASS_FILE],
perms);
if (error) if (error)
error = aa_audit_file(profile, perms, op, request, name, error = aa_audit_file(profile, perms, op, request, name,
NULL, target, GLOBAL_ROOT_UID, info, NULL, target, GLOBAL_ROOT_UID, info,
...@@ -1353,7 +1360,7 @@ int aa_change_profile(const char *fqname, int flags) ...@@ -1353,7 +1360,7 @@ int aa_change_profile(const char *fqname, int flags)
!COMPLAIN_MODE(labels_profile(label))) !COMPLAIN_MODE(labels_profile(label)))
goto audit; goto audit;
/* released below */ /* released below */
tprofile = aa_new_null_profile(labels_profile(label), false, tprofile = aa_new_learning_profile(labels_profile(label), false,
fqname, GFP_KERNEL); fqname, GFP_KERNEL);
if (!tprofile) { if (!tprofile) {
info = "failed null profile create"; info = "failed null profile create";
......
...@@ -95,7 +95,7 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, ...@@ -95,7 +95,7 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
kuid_t ouid, const char *info, int error) kuid_t ouid, const char *info, int error)
{ {
int type = AUDIT_APPARMOR_AUTO; int type = AUDIT_APPARMOR_AUTO;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, AA_CLASS_FILE, op);
sa.u.tsk = NULL; sa.u.tsk = NULL;
aad(&sa)->request = request; aad(&sa)->request = request;
...@@ -141,19 +141,6 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, ...@@ -141,19 +141,6 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
return aa_audit(type, profile, &sa, file_audit_cb); return aa_audit(type, profile, &sa, file_audit_cb);
} }
/**
* is_deleted - test if a file has been completely unlinked
* @dentry: dentry of file to test for deletion (NOT NULL)
*
* Returns: true if deleted else false
*/
static inline bool is_deleted(struct dentry *dentry)
{
if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
return true;
return false;
}
static int path_name(const char *op, struct aa_label *label, static int path_name(const char *op, struct aa_label *label,
const struct path *path, int flags, char *buffer, const struct path *path, int flags, char *buffer,
const char **name, struct path_cond *cond, u32 request) const char **name, struct path_cond *cond, u32 request)
...@@ -175,73 +162,28 @@ static int path_name(const char *op, struct aa_label *label, ...@@ -175,73 +162,28 @@ static int path_name(const char *op, struct aa_label *label,
} }
/** /**
* map_old_perms - map old file perms layout to the new layout * aa_lookup_fperms - convert dfa compressed perms to internal perms
* @old: permission set in old mapping * @dfa: dfa to lookup perms for (NOT NULL)
*
* Returns: new permission mapping
*/
static u32 map_old_perms(u32 old)
{
u32 new = old & 0xf;
if (old & MAY_READ)
new |= AA_MAY_GETATTR | AA_MAY_OPEN;
if (old & MAY_WRITE)
new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
if (old & 0x10)
new |= AA_MAY_LINK;
/* the old mapping lock and link_subset flags where overlaid
* and use was determined by part of a pair that they were in
*/
if (old & 0x20)
new |= AA_MAY_LOCK | AA_LINK_SUBSET;
if (old & 0x40) /* AA_EXEC_MMAP */
new |= AA_EXEC_MMAP;
return new;
}
/**
* aa_compute_fperms - convert dfa compressed perms to internal perms
* @dfa: dfa to compute perms for (NOT NULL)
* @state: state in dfa * @state: state in dfa
* @cond: conditions to consider (NOT NULL) * @cond: conditions to consider (NOT NULL)
* *
* TODO: convert from dfa + state to permission entry, do computation conversion * TODO: convert from dfa + state to permission entry
* at load time.
* *
* Returns: computed permission set * Returns: a pointer to a file permission set
*/ */
struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state, struct aa_perms default_perms = {};
struct path_cond *cond) struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules,
aa_state_t state, struct path_cond *cond)
{ {
/* FIXME: change over to new dfa format unsigned int index = ACCEPT_TABLE(file_rules->dfa)[state];
* currently file perms are encoded in the dfa, new format
* splits the permissions from the dfa. This mapping can be
* done at profile load
*/
struct aa_perms perms = { };
if (uid_eq(current_fsuid(), cond->uid)) { if (!(file_rules->perms))
perms.allow = map_old_perms(dfa_user_allow(dfa, state)); return &default_perms;
perms.audit = map_old_perms(dfa_user_audit(dfa, state));
perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
perms.xindex = dfa_user_xindex(dfa, state);
} else {
perms.allow = map_old_perms(dfa_other_allow(dfa, state));
perms.audit = map_old_perms(dfa_other_audit(dfa, state));
perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
perms.xindex = dfa_other_xindex(dfa, state);
}
perms.allow |= AA_MAY_GETATTR;
/* change_profile wasn't determined by ownership in old mapping */ if (uid_eq(current_fsuid(), cond->uid))
if (ACCEPT_TABLE(dfa)[state] & 0x80000000) return &(file_rules->perms[index]);
perms.allow |= AA_MAY_CHANGE_PROFILE;
if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
perms.allow |= AA_MAY_ONEXEC;
return perms; return &(file_rules->perms[index + 1]);
} }
/** /**
...@@ -254,26 +196,30 @@ struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state, ...@@ -254,26 +196,30 @@ struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
* *
* Returns: the final state in @dfa when beginning @start and walking @name * Returns: the final state in @dfa when beginning @start and walking @name
*/ */
unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
const char *name, struct path_cond *cond, const char *name, struct path_cond *cond,
struct aa_perms *perms) struct aa_perms *perms)
{ {
unsigned int state; aa_state_t state;
state = aa_dfa_match(dfa, start, name); state = aa_dfa_match(file_rules->dfa, start, name);
*perms = aa_compute_fperms(dfa, state, cond); *perms = *(aa_lookup_fperms(file_rules, state, cond));
return state; return state;
} }
int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, static int __aa_path_perm(const char *op, struct aa_profile *profile,
u32 request, struct path_cond *cond, int flags, const char *name, u32 request,
struct path_cond *cond, int flags,
struct aa_perms *perms) struct aa_perms *perms)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
int e = 0; int e = 0;
if (profile_unconfined(profile)) if (profile_unconfined(profile))
return 0; return 0;
aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
name, cond, perms);
if (request & ~perms->allow) if (request & ~perms->allow)
e = -EACCES; e = -EACCES;
return aa_audit_file(profile, perms, op, request, name, NULL, NULL, return aa_audit_file(profile, perms, op, request, name, NULL, NULL,
...@@ -360,11 +306,13 @@ static int profile_path_link(struct aa_profile *profile, ...@@ -360,11 +306,13 @@ static int profile_path_link(struct aa_profile *profile,
const struct path *target, char *buffer2, const struct path *target, char *buffer2,
struct path_cond *cond) struct path_cond *cond)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
const char *lname, *tname = NULL; const char *lname, *tname = NULL;
struct aa_perms lperms = {}, perms; struct aa_perms lperms = {}, perms;
const char *info = NULL; const char *info = NULL;
u32 request = AA_MAY_LINK; u32 request = AA_MAY_LINK;
unsigned int state; aa_state_t state;
int error; int error;
error = path_name(OP_LINK, &profile->label, link, profile->path_flags, error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
...@@ -380,15 +328,16 @@ static int profile_path_link(struct aa_profile *profile, ...@@ -380,15 +328,16 @@ static int profile_path_link(struct aa_profile *profile,
error = -EACCES; error = -EACCES;
/* aa_str_perms - handles the case of the dfa being NULL */ /* aa_str_perms - handles the case of the dfa being NULL */
state = aa_str_perms(profile->file.dfa, profile->file.start, lname, state = aa_str_perms(&(rules->file),
rules->file.start[AA_CLASS_FILE], lname,
cond, &lperms); cond, &lperms);
if (!(lperms.allow & AA_MAY_LINK)) if (!(lperms.allow & AA_MAY_LINK))
goto audit; goto audit;
/* test to see if target can be paired with link */ /* test to see if target can be paired with link */
state = aa_dfa_null_transition(profile->file.dfa, state); state = aa_dfa_null_transition(rules->file.dfa, state);
aa_str_perms(profile->file.dfa, state, tname, cond, &perms); aa_str_perms(&(rules->file), state, tname, cond, &perms);
/* force audit/quiet masks for link are stored in the second entry /* force audit/quiet masks for link are stored in the second entry
* in the link pair. * in the link pair.
...@@ -410,8 +359,8 @@ static int profile_path_link(struct aa_profile *profile, ...@@ -410,8 +359,8 @@ static int profile_path_link(struct aa_profile *profile,
/* Do link perm subset test requiring allowed permission on link are /* Do link perm subset test requiring allowed permission on link are
* a subset of the allowed permissions on target. * a subset of the allowed permissions on target.
*/ */
aa_str_perms(profile->file.dfa, profile->file.start, tname, cond, aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
&perms); tname, cond, &perms);
/* AA_MAY_LINK is not considered in the subset test */ /* AA_MAY_LINK is not considered in the subset test */
request = lperms.allow & ~AA_MAY_LINK; request = lperms.allow & ~AA_MAY_LINK;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
/* /*
* Class of mediation types in the AppArmor policy db * Class of mediation types in the AppArmor policy db
*/ */
#define AA_CLASS_ENTRY 0 #define AA_CLASS_NONE 0
#define AA_CLASS_UNKNOWN 1 #define AA_CLASS_UNKNOWN 1
#define AA_CLASS_FILE 2 #define AA_CLASS_FILE 2
#define AA_CLASS_CAP 3 #define AA_CLASS_CAP 3
...@@ -26,10 +26,18 @@ ...@@ -26,10 +26,18 @@
#define AA_CLASS_MOUNT 7 #define AA_CLASS_MOUNT 7
#define AA_CLASS_PTRACE 9 #define AA_CLASS_PTRACE 9
#define AA_CLASS_SIGNAL 10 #define AA_CLASS_SIGNAL 10
#define AA_CLASS_XMATCH 11
#define AA_CLASS_NET 14 #define AA_CLASS_NET 14
#define AA_CLASS_LABEL 16 #define AA_CLASS_LABEL 16
#define AA_CLASS_POSIX_MQUEUE 17
#define AA_CLASS_IO_URING 18
#define AA_CLASS_MODULE 19
#define AA_CLASS_DISPLAY_LSM 20
#define AA_CLASS_LAST AA_CLASS_LABEL #define AA_CLASS_X 31
#define AA_CLASS_DBUS 32
#define AA_CLASS_LAST AA_CLASS_DBUS
/* Control parameters settable through module/boot flags */ /* Control parameters settable through module/boot flags */
extern enum audit_mode aa_g_audit; extern enum audit_mode aa_g_audit;
...@@ -43,4 +51,15 @@ extern bool aa_g_logsyscall; ...@@ -43,4 +51,15 @@ extern bool aa_g_logsyscall;
extern bool aa_g_paranoid_load; extern bool aa_g_paranoid_load;
extern unsigned int aa_g_path_max; extern unsigned int aa_g_path_max;
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define AA_MIN_CLEVEL zstd_min_clevel()
#define AA_MAX_CLEVEL zstd_max_clevel()
#define AA_DEFAULT_CLEVEL ZSTD_CLEVEL_DEFAULT
#else
#define AA_MIN_CLEVEL 0
#define AA_MAX_CLEVEL 0
#define AA_DEFAULT_CLEVEL 0
#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */
#endif /* __APPARMOR_H */ #endif /* __APPARMOR_H */
...@@ -107,6 +107,7 @@ enum audit_type { ...@@ -107,6 +107,7 @@ enum audit_type {
struct apparmor_audit_data { struct apparmor_audit_data {
int error; int error;
int type; int type;
u16 class;
const char *op; const char *op;
struct aa_label *label; struct aa_label *label;
const char *name; const char *name;
...@@ -155,9 +156,12 @@ struct apparmor_audit_data { ...@@ -155,9 +156,12 @@ struct apparmor_audit_data {
/* macros for dealing with apparmor_audit_data structure */ /* macros for dealing with apparmor_audit_data structure */
#define aad(SA) ((SA)->apparmor_audit_data) #define aad(SA) ((SA)->apparmor_audit_data)
#define DEFINE_AUDIT_DATA(NAME, T, X) \ #define DEFINE_AUDIT_DATA(NAME, T, C, X) \
/* TODO: cleanup audit init so we don't need _aad = {0,} */ \ /* TODO: cleanup audit init so we don't need _aad = {0,} */ \
struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \ struct apparmor_audit_data NAME ## _aad = { \
.class = (C), \
.op = (X), \
}; \
struct common_audit_data NAME = \ struct common_audit_data NAME = \
{ \ { \
.type = (T), \ .type = (T), \
......
...@@ -63,19 +63,6 @@ static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) ...@@ -63,19 +63,6 @@ static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
return aa_get_newest_label(aa_cred_raw_label(cred)); return aa_get_newest_label(aa_cred_raw_label(cred));
} }
/**
* __aa_task_raw_label - retrieve another task's label
* @task: task to query (NOT NULL)
*
* Returns: @task's label without incrementing its ref count
*
* If @task != current needs to be called in RCU safe critical section
*/
static inline struct aa_label *__aa_task_raw_label(struct task_struct *task)
{
return aa_cred_raw_label(__task_cred(task));
}
/** /**
* aa_current_raw_label - find the current tasks confining label * aa_current_raw_label - find the current tasks confining label
* *
......
...@@ -16,11 +16,6 @@ ...@@ -16,11 +16,6 @@
#ifndef __AA_DOMAIN_H #ifndef __AA_DOMAIN_H
#define __AA_DOMAIN_H #define __AA_DOMAIN_H
struct aa_domain {
int size;
char **table;
};
#define AA_CHANGE_NOFLAGS 0 #define AA_CHANGE_NOFLAGS 0
#define AA_CHANGE_TEST 1 #define AA_CHANGE_TEST 1
#define AA_CHANGE_CHILD 2 #define AA_CHANGE_CHILD 2
...@@ -32,7 +27,6 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, ...@@ -32,7 +27,6 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm); int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm);
void aa_free_domain_entries(struct aa_domain *domain);
int aa_change_hat(const char *hats[], int count, u64 token, int flags); int aa_change_hat(const char *hats[], int count, u64 token, int flags);
int aa_change_profile(const char *fqname, int flags); int aa_change_profile(const char *fqname, int flags);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "match.h" #include "match.h"
#include "perms.h" #include "perms.h"
struct aa_policydb;
struct aa_profile; struct aa_profile;
struct path; struct path;
...@@ -87,18 +88,17 @@ static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx) ...@@ -87,18 +88,17 @@ static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx)
* - exec type - which determines how the executable name and index are used * - exec type - which determines how the executable name and index are used
* - flags - which modify how the destination name is applied * - flags - which modify how the destination name is applied
*/ */
#define AA_X_INDEX_MASK 0x03ff #define AA_X_INDEX_MASK AA_INDEX_MASK
#define AA_X_TYPE_MASK 0x0c00 #define AA_X_TYPE_MASK 0x0c000000
#define AA_X_TYPE_SHIFT 10 #define AA_X_NONE AA_INDEX_NONE
#define AA_X_NONE 0x0000 #define AA_X_NAME 0x04000000 /* use executable name px */
#define AA_X_NAME 0x0400 /* use executable name px */ #define AA_X_TABLE 0x08000000 /* use a specified name ->n# */
#define AA_X_TABLE 0x0800 /* use a specified name ->n# */
#define AA_X_UNSAFE 0x1000 #define AA_X_UNSAFE 0x10000000
#define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ #define AA_X_CHILD 0x20000000
#define AA_X_INHERIT 0x4000 #define AA_X_INHERIT 0x40000000
#define AA_X_UNCONFINED 0x8000 #define AA_X_UNCONFINED 0x80000000
/* need to make conditional which ones are being set */ /* need to make conditional which ones are being set */
struct path_cond { struct path_cond {
...@@ -108,90 +108,17 @@ struct path_cond { ...@@ -108,90 +108,17 @@ struct path_cond {
#define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill)
/* FIXME: split perms from dfa and match this to description
* also add delegation info.
*/
static inline u16 dfa_map_xindex(u16 mask)
{
u16 old_index = (mask >> 10) & 0xf;
u16 index = 0;
if (mask & 0x100)
index |= AA_X_UNSAFE;
if (mask & 0x200)
index |= AA_X_INHERIT;
if (mask & 0x80)
index |= AA_X_UNCONFINED;
if (old_index == 1) {
index |= AA_X_UNCONFINED;
} else if (old_index == 2) {
index |= AA_X_NAME;
} else if (old_index == 3) {
index |= AA_X_NAME | AA_X_CHILD;
} else if (old_index) {
index |= AA_X_TABLE;
index |= old_index - 4;
}
return index;
}
/*
* map old dfa inline permissions to new format
*/
#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) \
(dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff))
#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)
#define dfa_other_xindex(dfa, state) \
dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
const char *op, u32 request, const char *name, const char *op, u32 request, const char *name,
const char *target, struct aa_label *tlabel, kuid_t ouid, const char *target, struct aa_label *tlabel, kuid_t ouid,
const char *info, int error); const char *info, int error);
/** struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules,
* struct aa_file_rules - components used for file rule permissions aa_state_t state, struct path_cond *cond);
* @dfa: dfa to match path names and conditionals against aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
* @perms: permission table indexed by the matched state accept entry of @dfa
* @trans: transition table for indexed by named x transitions
*
* File permission are determined by matching a path against @dfa and
* then using the value of the accept entry for the matching state as
* an index into @perms. If a named exec transition is required it is
* looked up in the transition table.
*/
struct aa_file_rules {
unsigned int start;
struct aa_dfa *dfa;
/* struct perms perms; */
struct aa_domain trans;
/* TODO: add delegate table */
};
struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
struct path_cond *cond);
unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
const char *name, struct path_cond *cond, const char *name, struct path_cond *cond,
struct aa_perms *perms); struct aa_perms *perms);
int __aa_path_perm(const char *op, struct aa_profile *profile,
const char *name, u32 request, struct path_cond *cond,
int flags, struct aa_perms *perms);
int aa_path_perm(const char *op, struct aa_label *label, int aa_path_perm(const char *op, struct aa_label *label,
const struct path *path, int flags, u32 request, const struct path *path, int flags, u32 request,
struct path_cond *cond); struct path_cond *cond);
...@@ -204,11 +131,6 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, ...@@ -204,11 +131,6 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
void aa_inherit_files(const struct cred *cred, struct files_struct *files); void aa_inherit_files(const struct cred *cred, struct files_struct *files);
static inline void aa_free_file_rules(struct aa_file_rules *rules)
{
aa_put_dfa(rules->dfa);
aa_free_domain_entries(&rules->trans);
}
/** /**
* aa_map_file_perms - map file flags to AppArmor permissions * aa_map_file_perms - map file flags to AppArmor permissions
......
...@@ -261,7 +261,7 @@ for ((I).i = (I).j = 0; \ ...@@ -261,7 +261,7 @@ for ((I).i = (I).j = 0; \
struct label_it i; \ struct label_it i; \
int ret = 0; \ int ret = 0; \
label_for_each(i, (L), profile) { \ label_for_each(i, (L), profile) { \
if (PROFILE_MEDIATES(profile, (C))) { \ if (RULE_MEDIATES(&profile->rules, (C))) { \
ret = 1; \ ret = 1; \
break; \ break; \
} \ } \
...@@ -333,7 +333,7 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, ...@@ -333,7 +333,7 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
static inline const char *aa_label_strn_split(const char *str, int n) static inline const char *aa_label_strn_split(const char *str, int n)
{ {
const char *pos; const char *pos;
unsigned int state; aa_state_t state;
state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos); state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos);
if (!ACCEPT_TABLE(stacksplitdfa)[state]) if (!ACCEPT_TABLE(stacksplitdfa)[state])
...@@ -345,7 +345,7 @@ static inline const char *aa_label_strn_split(const char *str, int n) ...@@ -345,7 +345,7 @@ static inline const char *aa_label_strn_split(const char *str, int n)
static inline const char *aa_label_str_split(const char *str) static inline const char *aa_label_str_split(const char *str)
{ {
const char *pos; const char *pos;
unsigned int state; aa_state_t state;
state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos); state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos);
if (!ACCEPT_TABLE(stacksplitdfa)[state]) if (!ACCEPT_TABLE(stacksplitdfa)[state])
...@@ -357,9 +357,10 @@ static inline const char *aa_label_str_split(const char *str) ...@@ -357,9 +357,10 @@ static inline const char *aa_label_str_split(const char *str)
struct aa_perms; struct aa_perms;
int aa_label_match(struct aa_profile *profile, struct aa_label *label, struct aa_ruleset;
unsigned int state, bool subns, u32 request, int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
struct aa_perms *perms); struct aa_label *label, aa_state_t state, bool subns,
u32 request, struct aa_perms *perms);
/** /**
......
...@@ -87,8 +87,8 @@ static inline bool aa_strneq(const char *str, const char *sub, int len) ...@@ -87,8 +87,8 @@ static inline bool aa_strneq(const char *str, const char *sub, int len)
* character which is not used in standard matching and is only * character which is not used in standard matching and is only
* used to separate pairs. * used to separate pairs.
*/ */
static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, static inline aa_state_t aa_dfa_null_transition(struct aa_dfa *dfa,
unsigned int start) aa_state_t start)
{ {
/* the null transition only needs the string's null terminator byte */ /* the null transition only needs the string's null terminator byte */
return aa_dfa_next(dfa, start, 0); return aa_dfa_next(dfa, start, 0);
...@@ -99,6 +99,12 @@ static inline bool path_mediated_fs(struct dentry *dentry) ...@@ -99,6 +99,12 @@ static inline bool path_mediated_fs(struct dentry *dentry)
return !(dentry->d_sb->s_flags & SB_NOUSER); return !(dentry->d_sb->s_flags & SB_NOUSER);
} }
struct aa_str_table {
int size;
char **table;
};
void aa_free_str_table(struct aa_str_table *table);
struct counted_str { struct counted_str {
struct kref count; struct kref count;
......
...@@ -125,18 +125,18 @@ static inline size_t table_size(size_t len, size_t el_size) ...@@ -125,18 +125,18 @@ static inline size_t table_size(size_t len, size_t el_size)
int aa_setup_dfa_engine(void); int aa_setup_dfa_engine(void);
void aa_teardown_dfa_engine(void); void aa_teardown_dfa_engine(void);
#define aa_state_t unsigned int
struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
const char *str, int len); const char *str, int len);
unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start,
const char *str); const char *str);
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c);
const char c); aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state);
unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
unsigned int state);
unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
const char *str, const char **retpos); const char *str, const char **retpos);
unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, int n, const char **retpos); const char *str, int n, const char **retpos);
void aa_dfa_free_kref(struct kref *kref); void aa_dfa_free_kref(struct kref *kref);
...@@ -156,7 +156,7 @@ struct match_workbuf N = { \ ...@@ -156,7 +156,7 @@ struct match_workbuf N = { \
.len = 0, \ .len = 0, \
} }
unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_leftmatch(struct aa_dfa *dfa, aa_state_t start,
const char *str, unsigned int *count); const char *str, unsigned int *count);
/** /**
......
...@@ -59,6 +59,7 @@ struct aa_sk_ctx { ...@@ -59,6 +59,7 @@ struct aa_sk_ctx {
DEFINE_AUDIT_DATA(NAME, \ DEFINE_AUDIT_DATA(NAME, \
((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
LSM_AUDIT_DATA_NONE, \ LSM_AUDIT_DATA_NONE, \
AA_CLASS_NET, \
OP); \ OP); \
NAME.u.net = &(NAME ## _net); \ NAME.u.net = &(NAME ## _net); \
aad(&NAME)->net.type = (T); \ aad(&NAME)->net.type = (T); \
......
...@@ -65,29 +65,90 @@ extern const char *aa_file_perm_names[]; ...@@ -65,29 +65,90 @@ extern const char *aa_file_perm_names[];
struct aa_perms { struct aa_perms {
u32 allow; u32 allow;
u32 audit; /* set only when allow is set */
u32 deny; /* explicit deny, or conflict if allow also set */ u32 deny; /* explicit deny, or conflict if allow also set */
u32 quiet; /* set only when ~allow | deny */
u32 kill; /* set only when ~allow | deny */
u32 stop; /* set only when ~allow | deny */
u32 complain; /* accumulates only used when ~allow & ~deny */ u32 subtree; /* allow perm on full subtree only when allow is set */
u32 cond; /* set only when ~allow and ~deny */ u32 cond; /* set only when ~allow and ~deny */
u32 hide; /* set only when ~allow | deny */ u32 kill; /* set only when ~allow | deny */
u32 complain; /* accumulates only used when ~allow & ~deny */
u32 prompt; /* accumulates only used when ~allow & ~deny */ u32 prompt; /* accumulates only used when ~allow & ~deny */
/* Reserved: u32 audit; /* set only when allow is set */
* u32 subtree; / * set only when allow is set * / u32 quiet; /* set only when ~allow | deny */
*/ u32 hide; /* set only when ~allow | deny */
u16 xindex;
u32 xindex;
u32 tag; /* tag string index, if present */
u32 label; /* label string index, if present */
}; };
/*
* Indexes are broken into a 24 bit index and 8 bit flag.
* For the index to be valid there must be a value in the flag
*/
#define AA_INDEX_MASK 0x00ffffff
#define AA_INDEX_FLAG_MASK 0xff000000
#define AA_INDEX_NONE 0
#define ALL_PERMS_MASK 0xffffffff #define ALL_PERMS_MASK 0xffffffff
extern struct aa_perms nullperms; extern struct aa_perms nullperms;
extern struct aa_perms allperms; extern struct aa_perms allperms;
/**
* aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
* @accum - perms struct to accumulate into
* @addend - perms struct to add to @accum
*/
static inline void aa_perms_accum_raw(struct aa_perms *accum,
struct aa_perms *addend)
{
accum->deny |= addend->deny;
accum->allow &= addend->allow & ~addend->deny;
accum->audit |= addend->audit & addend->allow;
accum->quiet &= addend->quiet & ~addend->allow;
accum->kill |= addend->kill & ~addend->allow;
accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
accum->hide &= addend->hide & ~addend->allow;
accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
accum->subtree |= addend->subtree & ~addend->deny;
if (!accum->xindex)
accum->xindex = addend->xindex;
if (!accum->tag)
accum->tag = addend->tag;
if (!accum->label)
accum->label = addend->label;
}
/**
* aa_perms_accum - accumulate perms, masking off overlapping perms
* @accum - perms struct to accumulate into
* @addend - perms struct to add to @accum
*/
static inline void aa_perms_accum(struct aa_perms *accum,
struct aa_perms *addend)
{
accum->deny |= addend->deny;
accum->allow &= addend->allow & ~accum->deny;
accum->audit |= addend->audit & accum->allow;
accum->quiet &= addend->quiet & ~accum->allow;
accum->kill |= addend->kill & ~accum->allow;
accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
accum->hide &= addend->hide & ~accum->allow;
accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
accum->subtree &= addend->subtree & ~accum->deny;
if (!accum->xindex)
accum->xindex = addend->xindex;
if (!accum->tag)
accum->tag = addend->tag;
if (!accum->label)
accum->label = addend->label;
}
#define xcheck(FN1, FN2) \ #define xcheck(FN1, FN2) \
({ \ ({ \
...@@ -133,6 +194,9 @@ extern struct aa_perms allperms; ...@@ -133,6 +194,9 @@ extern struct aa_perms allperms;
xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2))) xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2)))
extern struct aa_perms default_perms;
void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs,
u32 mask); u32 mask);
void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
...@@ -141,11 +205,10 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, ...@@ -141,11 +205,10 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
u32 chrsmask, const char * const *names, u32 namesmask); u32 chrsmask, const char * const *names, u32 namesmask);
void aa_apply_modes_to_perms(struct aa_profile *profile, void aa_apply_modes_to_perms(struct aa_profile *profile,
struct aa_perms *perms); struct aa_perms *perms);
void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
struct aa_perms *perms);
void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend); void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend);
void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend); void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label, void aa_profile_match_label(struct aa_profile *profile,
struct aa_ruleset *rules, struct aa_label *label,
int type, u32 request, struct aa_perms *perms); int type, u32 request, struct aa_perms *perms);
int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
u32 request, int type, u32 *deny, u32 request, int type, u32 *deny,
......
...@@ -44,6 +44,8 @@ extern const char *const aa_profile_mode_names[]; ...@@ -44,6 +44,8 @@ extern const char *const aa_profile_mode_names[];
#define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN) #define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN)
#define USER_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_USER)
#define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL) #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL)
#define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT) #define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT)
...@@ -67,20 +69,47 @@ enum profile_mode { ...@@ -67,20 +69,47 @@ enum profile_mode {
APPARMOR_COMPLAIN, /* allow and log access violations */ APPARMOR_COMPLAIN, /* allow and log access violations */
APPARMOR_KILL, /* kill task on access violation */ APPARMOR_KILL, /* kill task on access violation */
APPARMOR_UNCONFINED, /* profile set to unconfined */ APPARMOR_UNCONFINED, /* profile set to unconfined */
APPARMOR_USER, /* modified complain mode to userspace */
}; };
/* struct aa_policydb - match engine for a policy /* struct aa_policydb - match engine for a policy
* dfa: dfa pattern match * dfa: dfa pattern match
* perms: table of permissions
* strs: table of strings, index by x
* start: set of start states for the different classes of data * start: set of start states for the different classes of data
*/ */
struct aa_policydb { struct aa_policydb {
/* Generic policy DFA specific rule types will be subsections of it */
struct aa_dfa *dfa; struct aa_dfa *dfa;
unsigned int start[AA_CLASS_LAST + 1]; struct {
struct aa_perms *perms;
u32 size;
};
struct aa_str_table trans;
aa_state_t start[AA_CLASS_LAST + 1];
}; };
static inline void aa_destroy_policydb(struct aa_policydb *policy)
{
aa_put_dfa(policy->dfa);
if (policy->perms)
kvfree(policy->perms);
aa_free_str_table(&policy->trans);
}
static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy,
aa_state_t state)
{
unsigned int index = ACCEPT_TABLE(policy->dfa)[state];
if (!(policy->perms))
return &default_perms;
return &(policy->perms[index]);
}
/* struct aa_data - generic data structure /* struct aa_data - generic data structure
* key: name for retrieving this data * key: name for retrieving this data
* size: size of data in bytes * size: size of data in bytes
...@@ -94,6 +123,47 @@ struct aa_data { ...@@ -94,6 +123,47 @@ struct aa_data {
struct rhash_head head; struct rhash_head head;
}; };
/* struct aa_ruleset - data covering mediation rules
* @list: list the rule is on
* @size: the memory consumed by this ruleset
* @policy: general match rules governing policy
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
* @rlimits: rlimits for the profile
* @secmark_count: number of secmark entries
* @secmark: secmark label match info
*/
struct aa_ruleset {
struct list_head list;
int size;
/* TODO: merge policy and file */
struct aa_policydb policy;
struct aa_policydb file;
struct aa_caps caps;
struct aa_rlimit rlimits;
int secmark_count;
struct aa_secmark *secmark;
};
/* struct aa_attachment - data and rules for a profiles attachment
* @list:
* @xmatch_str: human readable attachment string
* @xmatch: optional extended matching for unconfined executables names
* @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @xattr_count: number of xattrs in table
* @xattrs: table of xattrs
*/
struct aa_attachment {
const char *xmatch_str;
struct aa_policydb xmatch;
unsigned int xmatch_len;
int xattr_count;
char **xattrs;
};
/* struct aa_profile - basic confinement data /* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...) * @base - base components of the profile (name, refcount, lists, lock ...)
...@@ -101,18 +171,13 @@ struct aa_data { ...@@ -101,18 +171,13 @@ struct aa_data {
* @parent: parent of profile * @parent: parent of profile
* @ns: namespace the profile is in * @ns: namespace the profile is in
* @rename: optional profile name that this profile renamed * @rename: optional profile name that this profile renamed
* @attach: human readable attachment string *
* @xmatch: optional extended matching for unconfined executables names
* @xmatch_len: xmatch prefix len, used to determine xmatch priority
* @audit: the auditing mode of the profile * @audit: the auditing mode of the profile
* @mode: the enforcement mode of the profile * @mode: the enforcement mode of the profile
* @path_flags: flags controlling path generation behavior * @path_flags: flags controlling path generation behavior
* @disconnected: what to prepend if attach_disconnected is specified * @disconnected: what to prepend if attach_disconnected is specified
* @size: the memory consumed by this profiles rules * @attach: attachment rules for the profile
* @policy: general match rules governing policy * @rules: rules to be enforced
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
* @rlimits: rlimits for the profile
* *
* @dents: dentries for the profiles file entries in apparmorfs * @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs * @dirname: name of the profile dir in apparmorfs
...@@ -137,26 +202,13 @@ struct aa_profile { ...@@ -137,26 +202,13 @@ struct aa_profile {
struct aa_ns *ns; struct aa_ns *ns;
const char *rename; const char *rename;
const char *attach;
struct aa_dfa *xmatch;
unsigned int xmatch_len;
enum audit_mode audit; enum audit_mode audit;
long mode; long mode;
u32 path_flags; u32 path_flags;
const char *disconnected; const char *disconnected;
int size;
struct aa_policydb policy; struct aa_attachment attach;
struct aa_file_rules file; struct list_head rules;
struct aa_caps caps;
int xattr_count;
char **xattrs;
struct aa_rlimit rlimits;
int secmark_count;
struct aa_secmark *secmark;
struct aa_loaddata *rawdata; struct aa_loaddata *rawdata;
unsigned char *hash; unsigned char *hash;
...@@ -179,9 +231,12 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); ...@@ -179,9 +231,12 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
void aa_free_proxy_kref(struct kref *kref); void aa_free_proxy_kref(struct kref *kref);
struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp);
struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy, struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy,
gfp_t gfp); gfp_t gfp);
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
gfp_t gfp);
struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp); const char *base, gfp_t gfp);
void aa_free_profile(struct aa_profile *profile); void aa_free_profile(struct aa_profile *profile);
void aa_free_profile_kref(struct kref *kref); void aa_free_profile_kref(struct kref *kref);
...@@ -217,24 +272,34 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) ...@@ -217,24 +272,34 @@ static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p)
return labels_profile(aa_get_newest_label(&p->label)); return labels_profile(aa_get_newest_label(&p->label));
} }
static inline unsigned int PROFILE_MEDIATES(struct aa_profile *profile, static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules,
unsigned char class) unsigned char class)
{ {
if (class <= AA_CLASS_LAST) if (class <= AA_CLASS_LAST)
return profile->policy.start[class]; return rules->policy.start[class];
else else
return aa_dfa_match_len(profile->policy.dfa, return aa_dfa_match_len(rules->policy.dfa,
profile->policy.start[0], &class, 1); rules->policy.start[0], &class, 1);
} }
static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
u16 AF) { {
unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET);
__be16 be_af = cpu_to_be16(AF); __be16 be_af = cpu_to_be16(AF);
if (!state) if (!state)
return 0; return DFA_NOMATCH;
return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); return aa_dfa_match_len(rules->policy.dfa, state, (char *) &be_af, 2);
}
static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head,
unsigned char class)
{
struct aa_ruleset *rule;
/* TODO: change to list walk */
rule = list_first_entry(head, typeof(*rule), list);
return RULE_MEDIATES(rule, class);
} }
/** /**
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* Code to provide backwards compatibility with older policy versions,
* by converting/mapping older policy formats into the newer internal
* formats.
*
* Copyright 2022 Canonical Ltd.
*/
#ifndef __POLICY_COMPAT_H
#define __POLICY_COMPAT_H
#include "policy.h"
#define K_ABI_MASK 0x3ff
#define FORCE_COMPLAIN_FLAG 0x800
#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
#define VERSION_LE(X, Y) (((X) & K_ABI_MASK) <= ((Y) & K_ABI_MASK))
#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
#define v5 5 /* base version */
#define v6 6 /* per entry policydb mediation check */
#define v7 7
#define v8 8 /* full network masking */
#define v9 9 /* xbits are used as permission bits in policydb */
int aa_compat_map_xmatch(struct aa_policydb *policy);
int aa_compat_map_policy(struct aa_policydb *policy, u32 version);
int aa_compat_map_file(struct aa_policydb *policy);
#endif /* __POLICY_COMPAT_H */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/dcache.h> #include <linux/dcache.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
struct aa_load_ent { struct aa_load_ent {
struct list_head list; struct list_head list;
struct aa_profile *new; struct aa_profile *new;
...@@ -35,6 +36,7 @@ struct aa_load_ent *aa_load_ent_alloc(void); ...@@ -35,6 +36,7 @@ struct aa_load_ent *aa_load_ent_alloc(void);
#define PACKED_MODE_COMPLAIN 1 #define PACKED_MODE_COMPLAIN 1
#define PACKED_MODE_KILL 2 #define PACKED_MODE_KILL 2
#define PACKED_MODE_UNCONFINED 3 #define PACKED_MODE_UNCONFINED 3
#define PACKED_MODE_USER 4
struct aa_ns; struct aa_ns;
...@@ -170,7 +172,7 @@ bool aa_unpack_X(struct aa_ext *e, enum aa_code code); ...@@ -170,7 +172,7 @@ bool aa_unpack_X(struct aa_ext *e, enum aa_code code);
bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name); bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name);
bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name); bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name);
bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name); bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name);
size_t aa_unpack_array(struct aa_ext *e, const char *name); bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size);
size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name); size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name);
int aa_unpack_str(struct aa_ext *e, const char **string, const char *name); int aa_unpack_str(struct aa_ext *e, const char **string, const char *name);
int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name); int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name);
......
...@@ -45,7 +45,7 @@ static const char *audit_signal_mask(u32 mask) ...@@ -45,7 +45,7 @@ static const char *audit_signal_mask(u32 mask)
} }
/** /**
* audit_cb - call back for signal specific audit fields * audit_signal_cb() - call back for signal specific audit fields
* @ab: audit_buffer (NOT NULL) * @ab: audit_buffer (NOT NULL)
* @va: audit struct to audit values of (NOT NULL) * @va: audit struct to audit values of (NOT NULL)
*/ */
...@@ -78,19 +78,21 @@ static int profile_signal_perm(struct aa_profile *profile, ...@@ -78,19 +78,21 @@ static int profile_signal_perm(struct aa_profile *profile,
struct aa_label *peer, u32 request, struct aa_label *peer, u32 request,
struct common_audit_data *sa) struct common_audit_data *sa)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_perms perms; struct aa_perms perms;
unsigned int state; aa_state_t state;
if (profile_unconfined(profile) || if (profile_unconfined(profile) ||
!PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) !ANY_RULE_MEDIATES(&profile->rules, AA_CLASS_SIGNAL))
return 0; return 0;
aad(sa)->peer = peer; aad(sa)->peer = peer;
/* TODO: secondary cache check <profile, profile, perm> */ /* TODO: secondary cache check <profile, profile, perm> */
state = aa_dfa_next(profile->policy.dfa, state = aa_dfa_next(rules->policy.dfa,
profile->policy.start[AA_CLASS_SIGNAL], rules->policy.start[AA_CLASS_SIGNAL],
aad(sa)->signal); aad(sa)->signal);
aa_label_match(profile, peer, state, false, request, &perms); aa_label_match(profile, rules, peer, state, false, request, &perms);
aa_apply_modes_to_perms(profile, &perms); aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
} }
...@@ -98,7 +100,7 @@ static int profile_signal_perm(struct aa_profile *profile, ...@@ -98,7 +100,7 @@ static int profile_signal_perm(struct aa_profile *profile,
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
{ {
struct aa_profile *profile; struct aa_profile *profile;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_SIGNAL, OP_SIGNAL);
aad(&sa)->signal = map_signal_num(sig); aad(&sa)->signal = map_signal_num(sig);
aad(&sa)->unmappedsig = sig; aad(&sa)->unmappedsig = sig;
......
...@@ -197,15 +197,18 @@ static bool vec_is_stale(struct aa_profile **vec, int n) ...@@ -197,15 +197,18 @@ static bool vec_is_stale(struct aa_profile **vec, int n)
return false; return false;
} }
static long union_vec_flags(struct aa_profile **vec, int n, long mask) static long accum_vec_flags(struct aa_profile **vec, int n)
{ {
long u = 0; long u = FLAG_UNCONFINED;
int i; int i;
AA_BUG(!vec); AA_BUG(!vec);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
u |= vec[i]->label.flags & mask; u |= vec[i]->label.flags & (FLAG_DEBUG1 | FLAG_DEBUG2 |
FLAG_STALE);
if (!(u & vec[i]->label.flags & FLAG_UNCONFINED))
u &= ~FLAG_UNCONFINED;
} }
return u; return u;
...@@ -1097,8 +1100,7 @@ static struct aa_label *label_merge_insert(struct aa_label *new, ...@@ -1097,8 +1100,7 @@ static struct aa_label *label_merge_insert(struct aa_label *new,
else if (k == b->size) else if (k == b->size)
return aa_get_label(b); return aa_get_label(b);
} }
new->flags |= union_vec_flags(new->vec, new->size, FLAG_UNCONFINED | new->flags |= accum_vec_flags(new->vec, new->size);
FLAG_DEBUG1 | FLAG_DEBUG2);
ls = labels_set(new); ls = labels_set(new);
write_lock_irqsave(&ls->lock, flags); write_lock_irqsave(&ls->lock, flags);
label = __label_insert(labels_set(new), new, false); label = __label_insert(labels_set(new), new, false);
...@@ -1254,32 +1256,27 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b, ...@@ -1254,32 +1256,27 @@ struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
return label; return label;
} }
static inline bool label_is_visible(struct aa_profile *profile,
struct aa_label *label)
{
return aa_ns_visible(profile->ns, labels_ns(label), true);
}
/* match a profile and its associated ns component if needed /* match a profile and its associated ns component if needed
* Assumes visibility test has already been done. * Assumes visibility test has already been done.
* If a subns profile is not to be matched should be prescreened with * If a subns profile is not to be matched should be prescreened with
* visibility test. * visibility test.
*/ */
static inline unsigned int match_component(struct aa_profile *profile, static inline aa_state_t match_component(struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_profile *tp, struct aa_profile *tp,
unsigned int state) aa_state_t state)
{ {
const char *ns_name; const char *ns_name;
if (profile->ns == tp->ns) if (profile->ns == tp->ns)
return aa_dfa_match(profile->policy.dfa, state, tp->base.hname); return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
/* try matching with namespace name and then profile */ /* try matching with namespace name and then profile */
ns_name = aa_ns_name(profile->ns, tp->ns, true); ns_name = aa_ns_name(profile->ns, tp->ns, true);
state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1); state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
state = aa_dfa_match(profile->policy.dfa, state, ns_name); state = aa_dfa_match(rules->policy.dfa, state, ns_name);
state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1); state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
return aa_dfa_match(profile->policy.dfa, state, tp->base.hname); return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
} }
/** /**
...@@ -1298,8 +1295,9 @@ static inline unsigned int match_component(struct aa_profile *profile, ...@@ -1298,8 +1295,9 @@ static inline unsigned int match_component(struct aa_profile *profile,
* check to be stacked. * check to be stacked.
*/ */
static int label_compound_match(struct aa_profile *profile, static int label_compound_match(struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label, struct aa_label *label,
unsigned int state, bool subns, u32 request, aa_state_t state, bool subns, u32 request,
struct aa_perms *perms) struct aa_perms *perms)
{ {
struct aa_profile *tp; struct aa_profile *tp;
...@@ -1309,7 +1307,7 @@ static int label_compound_match(struct aa_profile *profile, ...@@ -1309,7 +1307,7 @@ static int label_compound_match(struct aa_profile *profile,
label_for_each(i, label, tp) { label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns)) if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue; continue;
state = match_component(profile, tp, state); state = match_component(profile, rules, tp, state);
if (!state) if (!state)
goto fail; goto fail;
goto next; goto next;
...@@ -1323,12 +1321,12 @@ static int label_compound_match(struct aa_profile *profile, ...@@ -1323,12 +1321,12 @@ static int label_compound_match(struct aa_profile *profile,
label_for_each_cont(i, label, tp) { label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns)) if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue; continue;
state = aa_dfa_match(profile->policy.dfa, state, "//&"); state = aa_dfa_match(rules->policy.dfa, state, "//&");
state = match_component(profile, tp, state); state = match_component(profile, rules, tp, state);
if (!state) if (!state)
goto fail; goto fail;
} }
aa_compute_perms(profile->policy.dfa, state, perms); *perms = *aa_lookup_perms(&rules->policy, state);
aa_apply_modes_to_perms(profile, perms); aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request) if ((perms->allow & request) != request)
return -EACCES; return -EACCES;
...@@ -1343,6 +1341,7 @@ static int label_compound_match(struct aa_profile *profile, ...@@ -1343,6 +1341,7 @@ static int label_compound_match(struct aa_profile *profile,
/** /**
* label_components_match - find perms for all subcomponents of a label * label_components_match - find perms for all subcomponents of a label
* @profile: profile to find perms for * @profile: profile to find perms for
* @rules: ruleset to search
* @label: label to check access permissions for * @label: label to check access permissions for
* @start: state to start match in * @start: state to start match in
* @subns: whether to do permission checks on components in a subns * @subns: whether to do permission checks on components in a subns
...@@ -1356,20 +1355,21 @@ static int label_compound_match(struct aa_profile *profile, ...@@ -1356,20 +1355,21 @@ static int label_compound_match(struct aa_profile *profile,
* check to be stacked. * check to be stacked.
*/ */
static int label_components_match(struct aa_profile *profile, static int label_components_match(struct aa_profile *profile,
struct aa_label *label, unsigned int start, struct aa_ruleset *rules,
struct aa_label *label, aa_state_t start,
bool subns, u32 request, bool subns, u32 request,
struct aa_perms *perms) struct aa_perms *perms)
{ {
struct aa_profile *tp; struct aa_profile *tp;
struct label_it i; struct label_it i;
struct aa_perms tmp; struct aa_perms tmp;
unsigned int state = 0; aa_state_t state = 0;
/* find first subcomponent to test */ /* find first subcomponent to test */
label_for_each(i, label, tp) { label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns)) if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue; continue;
state = match_component(profile, tp, start); state = match_component(profile, rules, tp, start);
if (!state) if (!state)
goto fail; goto fail;
goto next; goto next;
...@@ -1379,16 +1379,16 @@ static int label_components_match(struct aa_profile *profile, ...@@ -1379,16 +1379,16 @@ static int label_components_match(struct aa_profile *profile,
return 0; return 0;
next: next:
aa_compute_perms(profile->policy.dfa, state, &tmp); tmp = *aa_lookup_perms(&rules->policy, state);
aa_apply_modes_to_perms(profile, &tmp); aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp); aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) { label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns)) if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue; continue;
state = match_component(profile, tp, start); state = match_component(profile, rules, tp, start);
if (!state) if (!state)
goto fail; goto fail;
aa_compute_perms(profile->policy.dfa, state, &tmp); tmp = *aa_lookup_perms(&rules->policy, state);
aa_apply_modes_to_perms(profile, &tmp); aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp); aa_perms_accum(perms, &tmp);
} }
...@@ -1406,6 +1406,7 @@ static int label_components_match(struct aa_profile *profile, ...@@ -1406,6 +1406,7 @@ static int label_components_match(struct aa_profile *profile,
/** /**
* aa_label_match - do a multi-component label match * aa_label_match - do a multi-component label match
* @profile: profile to match against (NOT NULL) * @profile: profile to match against (NOT NULL)
* @rules: ruleset to search
* @label: label to match (NOT NULL) * @label: label to match (NOT NULL)
* @state: state to start in * @state: state to start in
* @subns: whether to match subns components * @subns: whether to match subns components
...@@ -1414,18 +1415,18 @@ static int label_components_match(struct aa_profile *profile, ...@@ -1414,18 +1415,18 @@ static int label_components_match(struct aa_profile *profile,
* *
* Returns: the state the match finished in, may be the none matching state * Returns: the state the match finished in, may be the none matching state
*/ */
int aa_label_match(struct aa_profile *profile, struct aa_label *label, int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
unsigned int state, bool subns, u32 request, struct aa_label *label, aa_state_t state, bool subns,
struct aa_perms *perms) u32 request, struct aa_perms *perms)
{ {
int error = label_compound_match(profile, label, state, subns, request, int error = label_compound_match(profile, rules, label, state, subns,
perms); request, perms);
if (!error) if (!error)
return error; return error;
*perms = allperms; *perms = allperms;
return label_components_match(profile, label, state, subns, request, return label_components_match(profile, rules, label, state, subns,
perms); request, perms);
} }
......
...@@ -25,6 +25,25 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK, ...@@ -25,6 +25,25 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
.quiet = ALL_PERMS_MASK, .quiet = ALL_PERMS_MASK,
.hide = ALL_PERMS_MASK }; .hide = ALL_PERMS_MASK };
/**
* aa_free_str_table - free entries str table
* @str: the string table to free (MAYBE NULL)
*/
void aa_free_str_table(struct aa_str_table *t)
{
int i;
if (t) {
if (!t->table)
return;
for (i = 0; i < t->size; i++)
kfree_sensitive(t->table[i]);
kfree_sensitive(t->table);
t->table = NULL;
}
}
/** /**
* aa_split_fqname - split a fqname into a profile and namespace name * aa_split_fqname - split a fqname into a profile and namespace name
* @fqname: a full qualified name in namespace profile format (NOT NULL) * @fqname: a full qualified name in namespace profile format (NOT NULL)
...@@ -124,7 +143,7 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, ...@@ -124,7 +143,7 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
void aa_info_message(const char *str) void aa_info_message(const char *str)
{ {
if (audit_enabled) { if (audit_enabled) {
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
aad(&sa)->info = str; aad(&sa)->info = str;
aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
...@@ -308,103 +327,22 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms) ...@@ -308,103 +327,22 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
perms->kill = ALL_PERMS_MASK; perms->kill = ALL_PERMS_MASK;
else if (COMPLAIN_MODE(profile)) else if (COMPLAIN_MODE(profile))
perms->complain = ALL_PERMS_MASK; perms->complain = ALL_PERMS_MASK;
/* else if (USER_MODE(profile))
* TODO: perms->prompt = ALL_PERMS_MASK;
* else if (PROMPT_MODE(profile))
* perms->prompt = ALL_PERMS_MASK;
*/
}
static u32 map_other(u32 x)
{
return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
((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) |
map_xbits(dfa_user_xbits(dfa, state)),
.audit = dfa_user_audit(dfa, state),
.quiet = dfa_user_quiet(dfa, state) |
map_xbits(dfa_other_xbits(dfa, state)),
};
/* 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));
}
/**
* aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
* @accum - perms struct to accumulate into
* @addend - perms struct to add to @accum
*/
void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
{
accum->deny |= addend->deny;
accum->allow &= addend->allow & ~addend->deny;
accum->audit |= addend->audit & addend->allow;
accum->quiet &= addend->quiet & ~addend->allow;
accum->kill |= addend->kill & ~addend->allow;
accum->stop |= addend->stop & ~addend->allow;
accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
accum->hide &= addend->hide & ~addend->allow;
accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
}
/**
* aa_perms_accum - accumulate perms, masking off overlapping perms
* @accum - perms struct to accumulate into
* @addend - perms struct to add to @accum
*/
void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
{
accum->deny |= addend->deny;
accum->allow &= addend->allow & ~accum->deny;
accum->audit |= addend->audit & accum->allow;
accum->quiet &= addend->quiet & ~accum->allow;
accum->kill |= addend->kill & ~accum->allow;
accum->stop |= addend->stop & ~accum->allow;
accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
accum->hide &= addend->hide & ~accum->allow;
accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
} }
void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label, void aa_profile_match_label(struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label,
int type, u32 request, struct aa_perms *perms) int type, u32 request, struct aa_perms *perms)
{ {
/* TODO: doesn't yet handle extended types */ /* TODO: doesn't yet handle extended types */
unsigned int state; aa_state_t state;
state = aa_dfa_next(profile->policy.dfa, state = aa_dfa_next(rules->policy.dfa,
profile->policy.start[AA_CLASS_LABEL], rules->policy.start[AA_CLASS_LABEL],
type); type);
aa_label_match(profile, label, state, false, request, perms); aa_label_match(profile, rules, label, state, false, request, perms);
} }
...@@ -413,13 +351,16 @@ int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, ...@@ -413,13 +351,16 @@ int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
u32 request, int type, u32 *deny, u32 request, int type, u32 *deny,
struct common_audit_data *sa) struct common_audit_data *sa)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_perms perms; struct aa_perms perms;
aad(sa)->label = &profile->label; aad(sa)->label = &profile->label;
aad(sa)->peer = &target->label; aad(sa)->peer = &target->label;
aad(sa)->request = request; aad(sa)->request = request;
aa_profile_match_label(profile, &target->label, type, request, &perms); aa_profile_match_label(profile, rules, &target->label, type, request,
&perms);
aa_apply_modes_to_perms(profile, &perms); aa_apply_modes_to_perms(profile, &perms);
*deny |= request & perms.deny; *deny |= request & perms.deny;
return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb); return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
#include <linux/zlib.h> #include <linux/zstd.h>
#include <net/sock.h> #include <net/sock.h>
#include <uapi/linux/mount.h> #include <uapi/linux/mount.h>
...@@ -163,12 +163,15 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, ...@@ -163,12 +163,15 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
struct label_it i; struct label_it i;
label_for_each_confined(i, label, profile) { label_for_each_confined(i, label, profile) {
struct aa_ruleset *rules;
if (COMPLAIN_MODE(profile)) if (COMPLAIN_MODE(profile))
continue; continue;
rules = list_first_entry(&profile->rules,
typeof(*rules), list);
*effective = cap_intersect(*effective, *effective = cap_intersect(*effective,
profile->caps.allow); rules->caps.allow);
*permitted = cap_intersect(*permitted, *permitted = cap_intersect(*permitted,
profile->caps.allow); rules->caps.allow);
} }
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -661,7 +664,8 @@ static int apparmor_setprocattr(const char *name, void *value, ...@@ -661,7 +664,8 @@ static int apparmor_setprocattr(const char *name, void *value,
char *command, *largs = NULL, *args = value; char *command, *largs = NULL, *args = value;
size_t arg_size; size_t arg_size;
int error; int error;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE,
OP_SETPROCATTR);
if (size == 0) if (size == 0)
return -EINVAL; return -EINVAL;
...@@ -751,7 +755,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm) ...@@ -751,7 +755,7 @@ static void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
} }
/** /**
* apparmor_bprm_committed_cred - do cleanup after new creds committed * apparmor_bprm_committed_creds() - do cleanup after new creds committed
* @bprm: binprm for the exec (NOT NULL) * @bprm: binprm for the exec (NOT NULL)
*/ */
static void apparmor_bprm_committed_creds(struct linux_binprm *bprm) static void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
...@@ -1205,10 +1209,10 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb ...@@ -1205,10 +1209,10 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb
#endif #endif
/* /*
* The cred blob is a pointer to, not an instance of, an aa_task_ctx. * The cred blob is a pointer to, not an instance of, an aa_label.
*/ */
struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = { struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = {
.lbs_cred = sizeof(struct aa_task_ctx *), .lbs_cred = sizeof(struct aa_label *),
.lbs_file = sizeof(struct aa_file_ctx), .lbs_file = sizeof(struct aa_file_ctx),
.lbs_task = sizeof(struct aa_task_ctx), .lbs_task = sizeof(struct aa_task_ctx),
}; };
...@@ -1373,7 +1377,7 @@ module_param_named(export_binary, aa_g_export_binary, aabool, 0600); ...@@ -1373,7 +1377,7 @@ module_param_named(export_binary, aa_g_export_binary, aabool, 0600);
#endif #endif
/* policy loaddata compression level */ /* policy loaddata compression level */
int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION; int aa_g_rawdata_compression_level = AA_DEFAULT_CLEVEL;
module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level, module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
aacompressionlevel, 0400); aacompressionlevel, 0400);
...@@ -1555,9 +1559,8 @@ static int param_set_aacompressionlevel(const char *val, ...@@ -1555,9 +1559,8 @@ static int param_set_aacompressionlevel(const char *val,
error = param_set_int(val, kp); error = param_set_int(val, kp);
aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level, aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level,
Z_NO_COMPRESSION, AA_MIN_CLEVEL, AA_MAX_CLEVEL);
Z_BEST_COMPRESSION); pr_info("AppArmor: policy rawdata compression level set to %d\n",
pr_info("AppArmor: policy rawdata compression level set to %u\n",
aa_g_rawdata_compression_level); aa_g_rawdata_compression_level);
return error; return error;
......
...@@ -31,7 +31,7 @@ static char stacksplitdfa_src[] = { ...@@ -31,7 +31,7 @@ static char stacksplitdfa_src[] = {
}; };
struct aa_dfa *stacksplitdfa; struct aa_dfa *stacksplitdfa;
int aa_setup_dfa_engine(void) int __init aa_setup_dfa_engine(void)
{ {
int error; int error;
...@@ -59,7 +59,7 @@ int aa_setup_dfa_engine(void) ...@@ -59,7 +59,7 @@ int aa_setup_dfa_engine(void)
return 0; return 0;
} }
void aa_teardown_dfa_engine(void) void __init aa_teardown_dfa_engine(void)
{ {
aa_put_dfa(stacksplitdfa); aa_put_dfa(stacksplitdfa);
aa_put_dfa(nulldfa); aa_put_dfa(nulldfa);
...@@ -436,17 +436,17 @@ do { \ ...@@ -436,17 +436,17 @@ do { \
* *
* Returns: final state reached after input is consumed * Returns: final state reached after input is consumed
*/ */
unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
const char *str, int len) const char *str, int len)
{ {
u16 *def = DEFAULT_TABLE(dfa); u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa); u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa); u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa); u16 *check = CHECK_TABLE(dfa);
unsigned int state = start; aa_state_t state = start;
if (state == 0) if (state == DFA_NOMATCH)
return 0; return DFA_NOMATCH;
/* current state is <state>, matching character *str */ /* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) { if (dfa->tables[YYTD_ID_EC]) {
...@@ -476,17 +476,16 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, ...@@ -476,17 +476,16 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
* *
* Returns: final state reached after input is consumed * Returns: final state reached after input is consumed
*/ */
unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
const char *str)
{ {
u16 *def = DEFAULT_TABLE(dfa); u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa); u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa); u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa); u16 *check = CHECK_TABLE(dfa);
unsigned int state = start; aa_state_t state = start;
if (state == 0) if (state == DFA_NOMATCH)
return 0; return DFA_NOMATCH;
/* current state is <state>, matching character *str */ /* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) { if (dfa->tables[YYTD_ID_EC]) {
...@@ -515,8 +514,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, ...@@ -515,8 +514,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
* *
* Returns: state reach after input @c * Returns: state reach after input @c
*/ */
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
const char c)
{ {
u16 *def = DEFAULT_TABLE(dfa); u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa); u32 *base = BASE_TABLE(dfa);
...@@ -534,7 +532,7 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, ...@@ -534,7 +532,7 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
return state; return state;
} }
unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state) aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
{ {
u16 *def = DEFAULT_TABLE(dfa); u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa); u32 *base = BASE_TABLE(dfa);
...@@ -564,7 +562,7 @@ unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state) ...@@ -564,7 +562,7 @@ unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state)
* *
* Returns: final state reached after input is consumed * Returns: final state reached after input is consumed
*/ */
unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, const char **retpos) const char *str, const char **retpos)
{ {
u16 *def = DEFAULT_TABLE(dfa); u16 *def = DEFAULT_TABLE(dfa);
...@@ -572,10 +570,10 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, ...@@ -572,10 +570,10 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
u16 *next = NEXT_TABLE(dfa); u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa); u16 *check = CHECK_TABLE(dfa);
u32 *accept = ACCEPT_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa);
unsigned int state = start, pos; aa_state_t state = start, pos;
if (state == 0) if (state == DFA_NOMATCH)
return 0; return DFA_NOMATCH;
/* current state is <state>, matching character *str */ /* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) { if (dfa->tables[YYTD_ID_EC]) {
...@@ -625,7 +623,7 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, ...@@ -625,7 +623,7 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
* *
* Returns: final state reached after input is consumed * Returns: final state reached after input is consumed
*/ */
unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, int n, const char **retpos) const char *str, int n, const char **retpos)
{ {
u16 *def = DEFAULT_TABLE(dfa); u16 *def = DEFAULT_TABLE(dfa);
...@@ -633,11 +631,11 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, ...@@ -633,11 +631,11 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
u16 *next = NEXT_TABLE(dfa); u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa); u16 *check = CHECK_TABLE(dfa);
u32 *accept = ACCEPT_TABLE(dfa); u32 *accept = ACCEPT_TABLE(dfa);
unsigned int state = start, pos; aa_state_t state = start, pos;
*retpos = NULL; *retpos = NULL;
if (state == 0) if (state == DFA_NOMATCH)
return 0; return DFA_NOMATCH;
/* current state is <state>, matching character *str */ /* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) { if (dfa->tables[YYTD_ID_EC]) {
...@@ -677,11 +675,11 @@ do { \ ...@@ -677,11 +675,11 @@ do { \
} while (0) } while (0)
/* For DFAs that don't support extended tagging of states */ /* For DFAs that don't support extended tagging of states */
static bool is_loop(struct match_workbuf *wb, unsigned int state, static bool is_loop(struct match_workbuf *wb, aa_state_t state,
unsigned int *adjust) unsigned int *adjust)
{ {
unsigned int pos = wb->pos; aa_state_t pos = wb->pos;
unsigned int i; aa_state_t i;
if (wb->history[pos] < state) if (wb->history[pos] < state)
return false; return false;
...@@ -700,7 +698,7 @@ static bool is_loop(struct match_workbuf *wb, unsigned int state, ...@@ -700,7 +698,7 @@ static bool is_loop(struct match_workbuf *wb, unsigned int state,
return true; return true;
} }
static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start, static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,
const char *str, struct match_workbuf *wb, const char *str, struct match_workbuf *wb,
unsigned int *count) unsigned int *count)
{ {
...@@ -708,7 +706,7 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start, ...@@ -708,7 +706,7 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
u32 *base = BASE_TABLE(dfa); u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa); u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa); u16 *check = CHECK_TABLE(dfa);
unsigned int state = start, pos; aa_state_t state = start, pos;
AA_BUG(!dfa); AA_BUG(!dfa);
AA_BUG(!str); AA_BUG(!str);
...@@ -716,8 +714,8 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start, ...@@ -716,8 +714,8 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
AA_BUG(!count); AA_BUG(!count);
*count = 0; *count = 0;
if (state == 0) if (state == DFA_NOMATCH)
return 0; return DFA_NOMATCH;
/* current state is <state>, matching character *str */ /* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) { if (dfa->tables[YYTD_ID_EC]) {
...@@ -781,7 +779,7 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start, ...@@ -781,7 +779,7 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
* *
* Returns: final state reached after input is consumed * Returns: final state reached after input is consumed
*/ */
unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start, aa_state_t aa_dfa_leftmatch(struct aa_dfa *dfa, aa_state_t start,
const char *str, unsigned int *count) const char *str, unsigned int *count)
{ {
DEFINE_MATCH_WB(wb); DEFINE_MATCH_WB(wb);
......
...@@ -134,7 +134,7 @@ static int audit_mount(struct aa_profile *profile, const char *op, ...@@ -134,7 +134,7 @@ static int audit_mount(struct aa_profile *profile, const char *op,
struct aa_perms *perms, const char *info, int error) struct aa_perms *perms, const char *info, int error)
{ {
int audit_type = AUDIT_APPARMOR_AUTO; int audit_type = AUDIT_APPARMOR_AUTO;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, op);
if (likely(!error)) { if (likely(!error)) {
u32 mask = perms->audit; u32 mask = perms->audit;
...@@ -190,7 +190,7 @@ static int audit_mount(struct aa_profile *profile, const char *op, ...@@ -190,7 +190,7 @@ static int audit_mount(struct aa_profile *profile, const char *op,
* *
* Returns: next state after flags match * Returns: next state after flags match
*/ */
static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, static aa_state_t match_mnt_flags(struct aa_dfa *dfa, aa_state_t state,
unsigned long flags) unsigned long flags)
{ {
unsigned int i; unsigned int i;
...@@ -203,25 +203,6 @@ static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, ...@@ -203,25 +203,6 @@ static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
return state; return state;
} }
/**
* compute_mnt_perms - compute mount permission associated with @state
* @dfa: dfa to match against (NOT NULL)
* @state: state match finished in
*
* Returns: mount permissions
*/
static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
unsigned int state)
{
struct aa_perms perms = {
.allow = dfa_user_allow(dfa, state),
.audit = dfa_user_audit(dfa, state),
.quiet = dfa_user_quiet(dfa, state),
};
return perms;
}
static const char * const mnt_info_table[] = { static const char * const mnt_info_table[] = {
"match succeeded", "match succeeded",
"failed mntpnt match", "failed mntpnt match",
...@@ -236,50 +217,52 @@ static const char * const mnt_info_table[] = { ...@@ -236,50 +217,52 @@ static const char * const mnt_info_table[] = {
* Returns 0 on success else element that match failed in, this is the * Returns 0 on success else element that match failed in, this is the
* index into the mnt_info_table above * index into the mnt_info_table above
*/ */
static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, static int do_match_mnt(struct aa_policydb *policy, aa_state_t start,
const char *mntpnt, const char *devname, const char *mntpnt, const char *devname,
const char *type, unsigned long flags, const char *type, unsigned long flags,
void *data, bool binary, struct aa_perms *perms) void *data, bool binary, struct aa_perms *perms)
{ {
unsigned int state; aa_state_t state;
AA_BUG(!dfa); AA_BUG(!policy);
AA_BUG(!policy->dfa);
AA_BUG(!policy->perms);
AA_BUG(!perms); AA_BUG(!perms);
state = aa_dfa_match(dfa, start, mntpnt); state = aa_dfa_match(policy->dfa, start, mntpnt);
state = aa_dfa_null_transition(dfa, state); state = aa_dfa_null_transition(policy->dfa, state);
if (!state) if (!state)
return 1; return 1;
if (devname) if (devname)
state = aa_dfa_match(dfa, state, devname); state = aa_dfa_match(policy->dfa, state, devname);
state = aa_dfa_null_transition(dfa, state); state = aa_dfa_null_transition(policy->dfa, state);
if (!state) if (!state)
return 2; return 2;
if (type) if (type)
state = aa_dfa_match(dfa, state, type); state = aa_dfa_match(policy->dfa, state, type);
state = aa_dfa_null_transition(dfa, state); state = aa_dfa_null_transition(policy->dfa, state);
if (!state) if (!state)
return 3; return 3;
state = match_mnt_flags(dfa, state, flags); state = match_mnt_flags(policy->dfa, state, flags);
if (!state) if (!state)
return 4; return 4;
*perms = compute_mnt_perms(dfa, state); *perms = *aa_lookup_perms(policy, state);
if (perms->allow & AA_MAY_MOUNT) if (perms->allow & AA_MAY_MOUNT)
return 0; return 0;
/* only match data if not binary and the DFA flags data is expected */ /* only match data if not binary and the DFA flags data is expected */
if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) { if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
state = aa_dfa_null_transition(dfa, state); state = aa_dfa_null_transition(policy->dfa, state);
if (!state) if (!state)
return 4; return 4;
state = aa_dfa_match(dfa, state, data); state = aa_dfa_match(policy->dfa, state, data);
if (!state) if (!state)
return 5; return 5;
*perms = compute_mnt_perms(dfa, state); *perms = *aa_lookup_perms(policy, state);
if (perms->allow & AA_MAY_MOUNT) if (perms->allow & AA_MAY_MOUNT)
return 0; return 0;
} }
...@@ -320,13 +303,15 @@ static int match_mnt_path_str(struct aa_profile *profile, ...@@ -320,13 +303,15 @@ static int match_mnt_path_str(struct aa_profile *profile,
{ {
struct aa_perms perms = { }; struct aa_perms perms = { };
const char *mntpnt = NULL, *info = NULL; const char *mntpnt = NULL, *info = NULL;
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
int pos, error; int pos, error;
AA_BUG(!profile); AA_BUG(!profile);
AA_BUG(!mntpath); AA_BUG(!mntpath);
AA_BUG(!buffer); AA_BUG(!buffer);
if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
return 0; return 0;
error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
...@@ -341,8 +326,8 @@ static int match_mnt_path_str(struct aa_profile *profile, ...@@ -341,8 +326,8 @@ static int match_mnt_path_str(struct aa_profile *profile,
} }
error = -EACCES; error = -EACCES;
pos = do_match_mnt(profile->policy.dfa, pos = do_match_mnt(&rules->policy,
profile->policy.start[AA_CLASS_MOUNT], rules->policy.start[AA_CLASS_MOUNT],
mntpnt, devname, type, flags, data, binary, &perms); mntpnt, devname, type, flags, data, binary, &perms);
if (pos) { if (pos) {
info = mnt_info_table[pos]; info = mnt_info_table[pos];
...@@ -375,12 +360,14 @@ static int match_mnt(struct aa_profile *profile, const struct path *path, ...@@ -375,12 +360,14 @@ static int match_mnt(struct aa_profile *profile, const struct path *path,
bool binary) bool binary)
{ {
const char *devname = NULL, *info = NULL; const char *devname = NULL, *info = NULL;
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
int error = -EACCES; int error = -EACCES;
AA_BUG(!profile); AA_BUG(!profile);
AA_BUG(devpath && !devbuffer); AA_BUG(devpath && !devbuffer);
if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
return 0; return 0;
if (devpath) { if (devpath) {
...@@ -582,15 +569,17 @@ int aa_new_mount(struct aa_label *label, const char *dev_name, ...@@ -582,15 +569,17 @@ int aa_new_mount(struct aa_label *label, const char *dev_name,
static int profile_umount(struct aa_profile *profile, const struct path *path, static int profile_umount(struct aa_profile *profile, const struct path *path,
char *buffer) char *buffer)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_perms perms = { }; struct aa_perms perms = { };
const char *name = NULL, *info = NULL; const char *name = NULL, *info = NULL;
unsigned int state; aa_state_t state;
int error; int error;
AA_BUG(!profile); AA_BUG(!profile);
AA_BUG(!path); AA_BUG(!path);
if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
return 0; return 0;
error = aa_path_name(path, path_flags(profile, path), buffer, &name, error = aa_path_name(path, path_flags(profile, path), buffer, &name,
...@@ -598,10 +587,10 @@ static int profile_umount(struct aa_profile *profile, const struct path *path, ...@@ -598,10 +587,10 @@ static int profile_umount(struct aa_profile *profile, const struct path *path,
if (error) if (error)
goto audit; goto audit;
state = aa_dfa_match(profile->policy.dfa, state = aa_dfa_match(rules->policy.dfa,
profile->policy.start[AA_CLASS_MOUNT], rules->policy.start[AA_CLASS_MOUNT],
name); name);
perms = compute_mnt_perms(profile->policy.dfa, state); perms = *aa_lookup_perms(&rules->policy, state);
if (AA_MAY_UMOUNT & ~perms.allow) if (AA_MAY_UMOUNT & ~perms.allow)
error = -EACCES; error = -EACCES;
...@@ -641,10 +630,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile, ...@@ -641,10 +630,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
const struct path *old_path, const struct path *old_path,
char *old_buffer) char *old_buffer)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
const char *old_name, *new_name = NULL, *info = NULL; const char *old_name, *new_name = NULL, *info = NULL;
const char *trans_name = NULL; const char *trans_name = NULL;
struct aa_perms perms = { }; struct aa_perms perms = { };
unsigned int state; aa_state_t state;
int error; int error;
AA_BUG(!profile); AA_BUG(!profile);
...@@ -652,7 +643,7 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile, ...@@ -652,7 +643,7 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
AA_BUG(!old_path); AA_BUG(!old_path);
if (profile_unconfined(profile) || if (profile_unconfined(profile) ||
!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT)) !RULE_MEDIATES(rules, AA_CLASS_MOUNT))
return aa_get_newest_label(&profile->label); return aa_get_newest_label(&profile->label);
error = aa_path_name(old_path, path_flags(profile, old_path), error = aa_path_name(old_path, path_flags(profile, old_path),
...@@ -667,12 +658,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile, ...@@ -667,12 +658,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
goto audit; goto audit;
error = -EACCES; error = -EACCES;
state = aa_dfa_match(profile->policy.dfa, state = aa_dfa_match(rules->policy.dfa,
profile->policy.start[AA_CLASS_MOUNT], rules->policy.start[AA_CLASS_MOUNT],
new_name); new_name);
state = aa_dfa_null_transition(profile->policy.dfa, state); state = aa_dfa_null_transition(rules->policy.dfa, state);
state = aa_dfa_match(profile->policy.dfa, state, old_name); state = aa_dfa_match(rules->policy.dfa, state, old_name);
perms = compute_mnt_perms(profile->policy.dfa, state); perms = *aa_lookup_perms(&rules->policy, state);
if (AA_MAY_PIVOTROOT & perms.allow) if (AA_MAY_PIVOTROOT & perms.allow)
error = 0; error = 0;
......
...@@ -108,8 +108,10 @@ void audit_net_cb(struct audit_buffer *ab, void *va) ...@@ -108,8 +108,10 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
u32 request, u16 family, int type) u32 request, u16 family, int type)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_perms perms = { }; struct aa_perms perms = { };
unsigned int state; aa_state_t state;
__be16 buffer[2]; __be16 buffer[2];
AA_BUG(family >= AF_MAX); AA_BUG(family >= AF_MAX);
...@@ -117,15 +119,15 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, ...@@ -117,15 +119,15 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
if (profile_unconfined(profile)) if (profile_unconfined(profile))
return 0; return 0;
state = PROFILE_MEDIATES(profile, AA_CLASS_NET); state = RULE_MEDIATES(rules, AA_CLASS_NET);
if (!state) if (!state)
return 0; return 0;
buffer[0] = cpu_to_be16(family); buffer[0] = cpu_to_be16(family);
buffer[1] = cpu_to_be16((u16) type); buffer[1] = cpu_to_be16((u16) type);
state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, state = aa_dfa_match_len(rules->policy.dfa, state, (char *) &buffer,
4); 4);
aa_compute_perms(profile->policy.dfa, state, &perms); perms = *aa_lookup_perms(&rules->policy, state);
aa_apply_modes_to_perms(profile, &perms); aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_net_cb); return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
...@@ -216,25 +218,27 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid, ...@@ -216,25 +218,27 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
{ {
int i, ret; int i, ret;
struct aa_perms perms = { }; struct aa_perms perms = { };
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
if (profile->secmark_count == 0) if (rules->secmark_count == 0)
return 0; return 0;
for (i = 0; i < profile->secmark_count; i++) { for (i = 0; i < rules->secmark_count; i++) {
if (!profile->secmark[i].secid) { if (!rules->secmark[i].secid) {
ret = apparmor_secmark_init(&profile->secmark[i]); ret = apparmor_secmark_init(&rules->secmark[i]);
if (ret) if (ret)
return ret; return ret;
} }
if (profile->secmark[i].secid == secid || if (rules->secmark[i].secid == secid ||
profile->secmark[i].secid == AA_SECID_WILDCARD) { rules->secmark[i].secid == AA_SECID_WILDCARD) {
if (profile->secmark[i].deny) if (rules->secmark[i].deny)
perms.deny = ALL_PERMS_MASK; perms.deny = ALL_PERMS_MASK;
else else
perms.allow = ALL_PERMS_MASK; perms.allow = ALL_PERMS_MASK;
if (profile->secmark[i].audit) if (rules->secmark[i].audit)
perms.audit = ALL_PERMS_MASK; perms.audit = ALL_PERMS_MASK;
} }
} }
......
...@@ -94,6 +94,7 @@ const char *const aa_profile_mode_names[] = { ...@@ -94,6 +94,7 @@ const char *const aa_profile_mode_names[] = {
"complain", "complain",
"kill", "kill",
"unconfined", "unconfined",
"user",
}; };
...@@ -192,6 +193,42 @@ static void aa_free_data(void *ptr, void *arg) ...@@ -192,6 +193,42 @@ static void aa_free_data(void *ptr, void *arg)
kfree_sensitive(data); kfree_sensitive(data);
} }
static void free_attachment(struct aa_attachment *attach)
{
int i;
for (i = 0; i < attach->xattr_count; i++)
kfree_sensitive(attach->xattrs[i]);
kfree_sensitive(attach->xattrs);
aa_destroy_policydb(&attach->xmatch);
}
static void free_ruleset(struct aa_ruleset *rules)
{
int i;
aa_destroy_policydb(&rules->file);
aa_destroy_policydb(&rules->policy);
aa_free_cap_rules(&rules->caps);
aa_free_rlimit_rules(&rules->rlimits);
for (i = 0; i < rules->secmark_count; i++)
kfree_sensitive(rules->secmark[i].label);
kfree_sensitive(rules->secmark);
kfree_sensitive(rules);
}
struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp)
{
struct aa_ruleset *rules;
rules = kzalloc(sizeof(*rules), gfp);
if (rules)
INIT_LIST_HEAD(&rules->list);
return rules;
}
/** /**
* aa_free_profile - free a profile * aa_free_profile - free a profile
* @profile: the profile to free (MAYBE NULL) * @profile: the profile to free (MAYBE NULL)
...@@ -204,8 +241,8 @@ static void aa_free_data(void *ptr, void *arg) ...@@ -204,8 +241,8 @@ static void aa_free_data(void *ptr, void *arg)
*/ */
void aa_free_profile(struct aa_profile *profile) void aa_free_profile(struct aa_profile *profile)
{ {
struct aa_ruleset *rule, *tmp;
struct rhashtable *rht; struct rhashtable *rht;
int i;
AA_DEBUG("%s(%p)\n", __func__, profile); AA_DEBUG("%s(%p)\n", __func__, profile);
...@@ -219,19 +256,17 @@ void aa_free_profile(struct aa_profile *profile) ...@@ -219,19 +256,17 @@ void aa_free_profile(struct aa_profile *profile)
aa_put_ns(profile->ns); aa_put_ns(profile->ns);
kfree_sensitive(profile->rename); kfree_sensitive(profile->rename);
aa_free_file_rules(&profile->file); free_attachment(&profile->attach);
aa_free_cap_rules(&profile->caps);
aa_free_rlimit_rules(&profile->rlimits);
for (i = 0; i < profile->xattr_count; i++) /*
kfree_sensitive(profile->xattrs[i]); * at this point there are no tasks that can have a reference
kfree_sensitive(profile->xattrs); * to rules
for (i = 0; i < profile->secmark_count; i++) */
kfree_sensitive(profile->secmark[i].label); list_for_each_entry_safe(rule, tmp, &profile->rules, list) {
kfree_sensitive(profile->secmark); list_del_init(&rule->list);
free_ruleset(rule);
}
kfree_sensitive(profile->dirname); kfree_sensitive(profile->dirname);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);
if (profile->data) { if (profile->data) {
rht = profile->data; rht = profile->data;
...@@ -258,6 +293,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, ...@@ -258,6 +293,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
gfp_t gfp) gfp_t gfp)
{ {
struct aa_profile *profile; struct aa_profile *profile;
struct aa_ruleset *rules;
/* freed by free_profile - usually through aa_put_profile */ /* freed by free_profile - usually through aa_put_profile */
profile = kzalloc(struct_size(profile, label.vec, 2), gfp); profile = kzalloc(struct_size(profile, label.vec, 2), gfp);
...@@ -269,6 +305,14 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, ...@@ -269,6 +305,14 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
if (!aa_label_init(&profile->label, 1, gfp)) if (!aa_label_init(&profile->label, 1, gfp))
goto fail; goto fail;
INIT_LIST_HEAD(&profile->rules);
/* allocate the first ruleset, but leave it empty */
rules = aa_alloc_ruleset(gfp);
if (!rules)
goto fail;
list_add(&rules->list, &profile->rules);
/* update being set needed by fs interface */ /* update being set needed by fs interface */
if (!proxy) { if (!proxy) {
proxy = aa_alloc_proxy(&profile->label, gfp); proxy = aa_alloc_proxy(&profile->label, gfp);
...@@ -380,6 +424,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, ...@@ -380,6 +424,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
return &profile->base; return &profile->base;
} }
/**
* __create_missing_ancestors - create place holders for missing ancestores
* @ns: namespace to lookup profile in (NOT NULL)
* @hname: hierarchical profile name to find parent of (NOT NULL)
* @gfp: type of allocation.
*
* Returns: NULL on error, parent profile on success
*
* Requires: ns mutex lock held
*
* Returns: unrefcounted parent policy or NULL if error creating
* place holder profiles.
*/
static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns,
const char *hname,
gfp_t gfp)
{
struct aa_policy *policy;
struct aa_profile *parent, *profile = NULL;
char *split;
AA_BUG(!ns);
AA_BUG(!hname);
policy = &ns->base;
for (split = strstr(hname, "//"); split;) {
parent = profile;
profile = __strn_find_child(&policy->profiles, hname,
split - hname);
if (!profile) {
const char *name = kstrndup(hname, split - hname,
gfp);
if (!name)
return NULL;
profile = aa_alloc_null(parent, name, gfp);
kfree(name);
if (!profile)
return NULL;
if (!parent)
profile->ns = aa_get_ns(ns);
}
policy = &profile->base;
hname = split + 2;
split = strstr(hname, "//");
}
if (!profile)
return &ns->base;
return &profile->base;
}
/** /**
* __lookupn_profile - lookup the profile matching @hname * __lookupn_profile - lookup the profile matching @hname
* @base: base list to start looking up profile name from (NOT NULL) * @base: base list to start looking up profile name from (NOT NULL)
...@@ -481,8 +576,36 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, ...@@ -481,8 +576,36 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
return profile; return profile;
} }
struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
gfp_t gfp)
{
struct aa_profile *profile;
struct aa_ruleset *rules;
profile = aa_alloc_profile(name, NULL, gfp);
if (!profile)
return NULL;
/* TODO: ideally we should inherit abi from parent */
profile->label.flags |= FLAG_NULL;
rules = list_first_entry(&profile->rules, typeof(*rules), list);
rules->file.dfa = aa_get_dfa(nulldfa);
rules->policy.dfa = aa_get_dfa(nulldfa);
if (parent) {
profile->path_flags = parent->path_flags;
/* released on free_profile */
rcu_assign_pointer(profile->parent, aa_get_profile(parent));
profile->ns = aa_get_ns(parent->ns);
}
return profile;
}
/** /**
* aa_new_null_profile - create or find a null-X learning profile * aa_new_learning_profile - create or find a null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL) * @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat * @hat: true if the null- learning profile is a hat
* @base: name to base the null profile off of * @base: name to base the null profile off of
...@@ -499,7 +622,7 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, ...@@ -499,7 +622,7 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
* *
* Returns: new refcounted profile else NULL on failure * Returns: new refcounted profile else NULL on failure
*/ */
struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp) const char *base, gfp_t gfp)
{ {
struct aa_profile *p, *profile; struct aa_profile *p, *profile;
...@@ -531,21 +654,12 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, ...@@ -531,21 +654,12 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
if (profile) if (profile)
goto out; goto out;
profile = aa_alloc_profile(name, NULL, gfp); profile = aa_alloc_null(parent, name, gfp);
if (!profile) if (!profile)
goto fail; goto fail;
profile->mode = APPARMOR_COMPLAIN; profile->mode = APPARMOR_COMPLAIN;
profile->label.flags |= FLAG_NULL;
if (hat) if (hat)
profile->label.flags |= FLAG_HAT; profile->label.flags |= FLAG_HAT;
profile->path_flags = parent->path_flags;
/* released on free_profile */
rcu_assign_pointer(profile->parent, aa_get_profile(parent));
profile->ns = aa_get_ns(parent->ns);
profile->file.dfa = aa_get_dfa(nulldfa);
profile->policy.dfa = aa_get_dfa(nulldfa);
mutex_lock_nested(&profile->ns->lock, profile->ns->level); mutex_lock_nested(&profile->ns->lock, profile->ns->level);
p = __find_child(&parent->base.profiles, bname); p = __find_child(&parent->base.profiles, bname);
...@@ -618,7 +732,7 @@ static int audit_policy(struct aa_label *label, const char *op, ...@@ -618,7 +732,7 @@ static int audit_policy(struct aa_label *label, const char *op,
const char *ns_name, const char *name, const char *ns_name, const char *name,
const char *info, int error) const char *info, int error)
{ {
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, op);
aad(&sa)->iface.ns = ns_name; aad(&sa)->iface.ns = ns_name;
aad(&sa)->name = name; aad(&sa)->name = name;
...@@ -970,6 +1084,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, ...@@ -970,6 +1084,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
/* setup parent and ns info */ /* setup parent and ns info */
list_for_each_entry(ent, &lh, list) { list_for_each_entry(ent, &lh, list) {
struct aa_policy *policy; struct aa_policy *policy;
struct aa_profile *p;
if (aa_g_export_binary) if (aa_g_export_binary)
ent->new->rawdata = aa_get_loaddata(udata); ent->new->rawdata = aa_get_loaddata(udata);
...@@ -994,22 +1109,39 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, ...@@ -994,22 +1109,39 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
continue; continue;
/* no ref on policy only use inside lock */ /* no ref on policy only use inside lock */
p = NULL;
policy = __lookup_parent(ns, ent->new->base.hname); policy = __lookup_parent(ns, ent->new->base.hname);
if (!policy) { if (!policy) {
struct aa_profile *p; /* first check for parent in the load set */
p = __list_lookup_parent(&lh, ent->new); p = __list_lookup_parent(&lh, ent->new);
if (!p) { if (!p) {
/*
* fill in missing parent with null
* profile that doesn't have
* permissions. This allows for
* individual profile loading where
* the child is loaded before the
* parent, and outside of the current
* atomic set. This unfortunately can
* happen with some userspaces. The
* null profile will be replaced once
* the parent is loaded.
*/
policy = __create_missing_ancestors(ns,
ent->new->base.hname,
GFP_KERNEL);
if (!policy) {
error = -ENOENT; error = -ENOENT;
info = "parent does not exist"; info = "parent does not exist";
goto fail_lock; goto fail_lock;
} }
rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); }
} else if (policy != &ns->base) { }
if (!p && policy != &ns->base)
/* released on profile replacement or free_profile */ /* released on profile replacement or free_profile */
struct aa_profile *p = (struct aa_profile *) policy; p = (struct aa_profile *) policy;
rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
} }
}
/* create new fs entries for introspection if needed */ /* create new fs entries for introspection if needed */
if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) { if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) {
...@@ -1170,7 +1302,7 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj, ...@@ -1170,7 +1302,7 @@ ssize_t aa_remove_profiles(struct aa_ns *policy_ns, struct aa_label *subj,
if (!name) { if (!name) {
/* remove namespace - can only happen if fqname[0] == ':' */ /* remove namespace - can only happen if fqname[0] == ':' */
mutex_lock_nested(&ns->parent->lock, ns->level); mutex_lock_nested(&ns->parent->lock, ns->parent->level);
__aa_bump_ns_revision(ns); __aa_bump_ns_revision(ns);
__aa_remove_ns(ns); __aa_remove_ns(ns);
mutex_unlock(&ns->parent->lock); mutex_unlock(&ns->parent->lock);
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
* This file contains AppArmor functions for unpacking policy loaded
* from userspace.
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2022 Canonical Ltd.
*
* Code to provide backwards compatibility with older policy versions,
* by converting/mapping older policy formats into the newer internal
* formats.
*/
#include <linux/ctype.h>
#include <linux/errno.h>
#include "include/lib.h"
#include "include/policy_unpack.h"
#include "include/policy_compat.h"
/* remap old accept table embedded permissions to separate permission table */
static u32 dfa_map_xindex(u16 mask)
{
u16 old_index = (mask >> 10) & 0xf;
u32 index = 0;
if (mask & 0x100)
index |= AA_X_UNSAFE;
if (mask & 0x200)
index |= AA_X_INHERIT;
if (mask & 0x80)
index |= AA_X_UNCONFINED;
if (old_index == 1) {
index |= AA_X_UNCONFINED;
} else if (old_index == 2) {
index |= AA_X_NAME;
} else if (old_index == 3) {
index |= AA_X_NAME | AA_X_CHILD;
} else if (old_index) {
index |= AA_X_TABLE;
index |= old_index - 4;
}
return index;
}
/*
* map old dfa inline permissions to new format
*/
#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) \
(dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff))
#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)
#define dfa_other_xindex(dfa, state) \
dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
/**
* map_old_perms - map old file perms layout to the new layout
* @old: permission set in old mapping
*
* Returns: new permission mapping
*/
static u32 map_old_perms(u32 old)
{
u32 new = old & 0xf;
if (old & MAY_READ)
new |= AA_MAY_GETATTR | AA_MAY_OPEN;
if (old & MAY_WRITE)
new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
if (old & 0x10)
new |= AA_MAY_LINK;
/* the old mapping lock and link_subset flags where overlaid
* and use was determined by part of a pair that they were in
*/
if (old & 0x20)
new |= AA_MAY_LOCK | AA_LINK_SUBSET;
if (old & 0x40) /* AA_EXEC_MMAP */
new |= AA_EXEC_MMAP;
return new;
}
static void compute_fperms_allow(struct aa_perms *perms, struct aa_dfa *dfa,
aa_state_t state)
{
perms->allow |= AA_MAY_GETATTR;
/* change_profile wasn't determined by ownership in old mapping */
if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
perms->allow |= AA_MAY_CHANGE_PROFILE;
if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
perms->allow |= AA_MAY_ONEXEC;
}
static struct aa_perms compute_fperms_user(struct aa_dfa *dfa,
aa_state_t state)
{
struct aa_perms perms = { };
perms.allow = map_old_perms(dfa_user_allow(dfa, state));
perms.audit = map_old_perms(dfa_user_audit(dfa, state));
perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
perms.xindex = dfa_user_xindex(dfa, state);
compute_fperms_allow(&perms, dfa, state);
return perms;
}
static struct aa_perms compute_fperms_other(struct aa_dfa *dfa,
aa_state_t state)
{
struct aa_perms perms = { };
perms.allow = map_old_perms(dfa_other_allow(dfa, state));
perms.audit = map_old_perms(dfa_other_audit(dfa, state));
perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
perms.xindex = dfa_other_xindex(dfa, state);
compute_fperms_allow(&perms, dfa, state);
return perms;
}
/**
* compute_fperms - convert dfa compressed perms to internal perms and store
* them so they can be retrieved later.
* @dfa: a dfa using fperms to remap to internal permissions
*
* Returns: remapped perm table
*/
static struct aa_perms *compute_fperms(struct aa_dfa *dfa)
{
aa_state_t state;
unsigned int state_count;
struct aa_perms *table;
AA_BUG(!dfa);
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
/* DFAs are restricted from having a state_count of less than 2 */
table = kvcalloc(state_count * 2, sizeof(struct aa_perms), GFP_KERNEL);
if (!table)
return NULL;
/* zero init so skip the trap state (state == 0) */
for (state = 1; state < state_count; state++) {
table[state * 2] = compute_fperms_user(dfa, state);
table[state * 2 + 1] = compute_fperms_other(dfa, state);
}
return table;
}
static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch)
{
struct aa_perms *perms;
int state;
int state_count;
AA_BUG(!xmatch);
state_count = xmatch->tables[YYTD_ID_BASE]->td_lolen;
/* DFAs are restricted from having a state_count of less than 2 */
perms = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL);
/* zero init so skip the trap state (state == 0) */
for (state = 1; state < state_count; state++)
perms[state].allow = dfa_user_allow(xmatch, state);
return perms;
}
static u32 map_other(u32 x)
{
return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
((x & 0x60) << 19); /* SETOPT/GETOPT */
}
static u32 map_xbits(u32 x)
{
return ((x & 0x1) << 7) |
((x & 0x7e) << 9);
}
static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
aa_state_t state,
u32 version)
{
struct aa_perms perms = { };
perms.allow = dfa_user_allow(dfa, state);
perms.audit = dfa_user_audit(dfa, state);
perms.quiet = dfa_user_quiet(dfa, state);
/*
* This mapping is convulated due to history.
* v1-v4: only file perms, which are handled by compute_fperms
* v5: added policydb which dropped 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 no way to force auditing on the
* perms represented by the xbits
*/
perms.allow |= map_other(dfa_other_allow(dfa, state));
if (VERSION_LE(version, v8))
perms.allow |= AA_MAY_LOCK;
else
perms.allow |= map_xbits(dfa_user_xbits(dfa, state));
/*
* for v5-v9 perm mapping in the policydb, the other set is used
* to extend the general perm set
*/
perms.audit |= map_other(dfa_other_audit(dfa, state));
perms.quiet |= map_other(dfa_other_quiet(dfa, state));
if (VERSION_GT(version, v8))
perms.quiet |= map_xbits(dfa_other_xbits(dfa, state));
return perms;
}
static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version)
{
unsigned int state;
unsigned int state_count;
struct aa_perms *table;
AA_BUG(!dfa);
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
/* DFAs are restricted from having a state_count of less than 2 */
table = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL);
if (!table)
return NULL;
/* zero init so skip the trap state (state == 0) */
for (state = 1; state < state_count; state++)
table[state] = compute_perms_entry(dfa, state, version);
return table;
}
/**
* remap_dfa_accept - remap old dfa accept table to be an index
* @dfa: dfa to do the remapping on
* @factor: scaling factor for the index conversion.
*
* Used in conjunction with compute_Xperms, it converts old style perms
* that are encoded in the dfa accept tables to the new style where
* there is a permission table and the accept table is an index into
* the permission table.
*/
static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor)
{
unsigned int state;
unsigned int state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
AA_BUG(!dfa);
for (state = 0; state < state_count; state++)
ACCEPT_TABLE(dfa)[state] = state * factor;
kvfree(dfa->tables[YYTD_ID_ACCEPT2]);
dfa->tables[YYTD_ID_ACCEPT2] = NULL;
}
/* TODO: merge different dfa mappings into single map_policy fn */
int aa_compat_map_xmatch(struct aa_policydb *policy)
{
policy->perms = compute_xmatch_perms(policy->dfa);
if (!policy->perms)
return -ENOMEM;
remap_dfa_accept(policy->dfa, 1);
return 0;
}
int aa_compat_map_policy(struct aa_policydb *policy, u32 version)
{
policy->perms = compute_perms(policy->dfa, version);
if (!policy->perms)
return -ENOMEM;
remap_dfa_accept(policy->dfa, 1);
return 0;
}
int aa_compat_map_file(struct aa_policydb *policy)
{
policy->perms = compute_fperms(policy->dfa);
if (!policy->perms)
return -ENOMEM;
remap_dfa_accept(policy->dfa, 2);
return 0;
}
...@@ -84,15 +84,13 @@ static struct aa_profile *alloc_unconfined(const char *name) ...@@ -84,15 +84,13 @@ static struct aa_profile *alloc_unconfined(const char *name)
{ {
struct aa_profile *profile; struct aa_profile *profile;
profile = aa_alloc_profile(name, NULL, GFP_KERNEL); profile = aa_alloc_null(NULL, name, GFP_KERNEL);
if (!profile) if (!profile)
return NULL; return NULL;
profile->label.flags |= FLAG_IX_ON_NAME_ERROR | profile->label.flags |= FLAG_IX_ON_NAME_ERROR |
FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
profile->mode = APPARMOR_UNCONFINED; profile->mode = APPARMOR_UNCONFINED;
profile->file.dfa = aa_get_dfa(nulldfa);
profile->policy.dfa = aa_get_dfa(nulldfa);
return profile; return profile;
} }
...@@ -134,7 +132,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) ...@@ -134,7 +132,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
return ns; return ns;
fail_unconfined: fail_unconfined:
kfree_sensitive(ns->base.hname); aa_policy_destroy(&ns->base);
fail_ns: fail_ns:
kfree_sensitive(ns); kfree_sensitive(ns);
return NULL; return NULL;
......
...@@ -17,26 +17,18 @@ ...@@ -17,26 +17,18 @@
#include <kunit/visibility.h> #include <kunit/visibility.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/zlib.h> #include <linux/zstd.h>
#include "include/apparmor.h" #include "include/apparmor.h"
#include "include/audit.h" #include "include/audit.h"
#include "include/cred.h" #include "include/cred.h"
#include "include/crypto.h" #include "include/crypto.h"
#include "include/file.h"
#include "include/match.h" #include "include/match.h"
#include "include/path.h" #include "include/path.h"
#include "include/policy.h" #include "include/policy.h"
#include "include/policy_unpack.h" #include "include/policy_unpack.h"
#include "include/policy_compat.h"
#define K_ABI_MASK 0x3ff
#define FORCE_COMPLAIN_FLAG 0x800
#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
#define v5 5 /* base version */
#define v6 6 /* per entry policydb mediation check */
#define v7 7
#define v8 8 /* full network masking */
/* audit callback for unpack fields */ /* audit callback for unpack fields */
static void audit_cb(struct audit_buffer *ab, void *va) static void audit_cb(struct audit_buffer *ab, void *va)
...@@ -71,7 +63,7 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, ...@@ -71,7 +63,7 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
int error) int error)
{ {
struct aa_profile *profile = labels_profile(aa_current_raw_label()); struct aa_profile *profile = labels_profile(aa_current_raw_label());
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
if (e) if (e)
aad(&sa)->iface.pos = e->pos - e->start; aad(&sa)->iface.pos = e->pos - e->start;
aad(&sa)->iface.ns = ns_name; aad(&sa)->iface.ns = ns_name;
...@@ -321,22 +313,21 @@ VISIBLE_IF_KUNIT bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *nam ...@@ -321,22 +313,21 @@ VISIBLE_IF_KUNIT bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *nam
} }
EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u64); EXPORT_SYMBOL_IF_KUNIT(aa_unpack_u64);
VISIBLE_IF_KUNIT size_t aa_unpack_array(struct aa_ext *e, const char *name) VISIBLE_IF_KUNIT bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size)
{ {
void *pos = e->pos; void *pos = e->pos;
if (aa_unpack_nameX(e, AA_ARRAY, name)) { if (aa_unpack_nameX(e, AA_ARRAY, name)) {
int size;
if (!aa_inbounds(e, sizeof(u16))) if (!aa_inbounds(e, sizeof(u16)))
goto fail; goto fail;
size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos)); *size = le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(u16); e->pos += sizeof(u16);
return size; return true;
} }
fail: fail:
e->pos = pos; e->pos = pos;
return 0; return false;
} }
EXPORT_SYMBOL_IF_KUNIT(aa_unpack_array); EXPORT_SYMBOL_IF_KUNIT(aa_unpack_array);
...@@ -411,10 +402,11 @@ EXPORT_SYMBOL_IF_KUNIT(aa_unpack_strdup); ...@@ -411,10 +402,11 @@ EXPORT_SYMBOL_IF_KUNIT(aa_unpack_strdup);
/** /**
* unpack_dfa - unpack a file rule dfa * unpack_dfa - unpack a file rule dfa
* @e: serialized data extent information (NOT NULL) * @e: serialized data extent information (NOT NULL)
* @flags: dfa flags to check
* *
* returns dfa or ERR_PTR or NULL if no dfa * returns dfa or ERR_PTR or NULL if no dfa
*/ */
static struct aa_dfa *unpack_dfa(struct aa_ext *e) static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags)
{ {
char *blob = NULL; char *blob = NULL;
size_t size; size_t size;
...@@ -430,8 +422,6 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) ...@@ -430,8 +422,6 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
size_t sz = blob - (char *) e->start - size_t sz = blob - (char *) e->start -
((e->pos - e->start) & 7); ((e->pos - e->start) & 7);
size_t pad = ALIGN(sz, 8) - sz; size_t pad = ALIGN(sz, 8) - sz;
int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32);
if (aa_g_paranoid_load) if (aa_g_paranoid_load)
flags |= DFA_FLAG_VERIFY_STATES; flags |= DFA_FLAG_VERIFY_STATES;
dfa = aa_dfa_unpack(blob + pad, size - pad, flags); dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
...@@ -447,28 +437,32 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) ...@@ -447,28 +437,32 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
/** /**
* unpack_trans_table - unpack a profile transition table * unpack_trans_table - unpack a profile transition table
* @e: serialized data extent information (NOT NULL) * @e: serialized data extent information (NOT NULL)
* @profile: profile to add the accept table to (NOT NULL) * @table: str table to unpack to (NOT NULL)
* *
* Returns: true if table successfully unpacked * Returns: true if table successfully unpacked or not present
*/ */
static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs)
{ {
void *saved_pos = e->pos; void *saved_pos = e->pos;
char **table = NULL;
/* exec table is optional */ /* exec table is optional */
if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) { if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) {
int i, size; u16 size;
int i;
size = aa_unpack_array(e, NULL); if (!aa_unpack_array(e, NULL, &size))
/* currently 4 exec bits and entries 0-3 are reserved iupcx */ /*
if (size > 16 - 4) * Note: index into trans table array is a max
* of 2^24, but unpack array can only unpack
* an array of 2^16 in size atm so no need
* for size check here
*/
goto fail; goto fail;
profile->file.trans.table = kcalloc(size, sizeof(char *), table = kcalloc(size, sizeof(char *), GFP_KERNEL);
GFP_KERNEL); if (!table)
if (!profile->file.trans.table)
goto fail; goto fail;
profile->file.trans.size = size;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
char *str; char *str;
int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL); int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL);
...@@ -477,7 +471,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) ...@@ -477,7 +471,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
*/ */
if (!size2) if (!size2)
goto fail; goto fail;
profile->file.trans.table[i] = str; table[i] = str;
/* verify that name doesn't start with space */ /* verify that name doesn't start with space */
if (isspace(*str)) if (isspace(*str))
goto fail; goto fail;
...@@ -511,11 +505,14 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) ...@@ -511,11 +505,14 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
goto fail; goto fail;
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
strs->table = table;
strs->size = size;
} }
return true; return true;
fail: fail:
aa_free_domain_entries(&profile->file.trans); kfree_sensitive(table);
e->pos = saved_pos; e->pos = saved_pos;
return false; return false;
} }
...@@ -525,15 +522,17 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) ...@@ -525,15 +522,17 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
void *pos = e->pos; void *pos = e->pos;
if (aa_unpack_nameX(e, AA_STRUCT, "xattrs")) { if (aa_unpack_nameX(e, AA_STRUCT, "xattrs")) {
int i, size; u16 size;
int i;
size = aa_unpack_array(e, NULL); if (!aa_unpack_array(e, NULL, &size))
profile->xattr_count = size; goto fail;
profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL); profile->attach.xattr_count = size;
if (!profile->xattrs) profile->attach.xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
if (!profile->attach.xattrs)
goto fail; goto fail;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (!aa_unpack_strdup(e, &profile->xattrs[i], NULL)) if (!aa_unpack_strdup(e, &profile->attach.xattrs[i], NULL))
goto fail; goto fail;
} }
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
...@@ -549,27 +548,29 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) ...@@ -549,27 +548,29 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
return false; return false;
} }
static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile) static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules)
{ {
void *pos = e->pos; void *pos = e->pos;
int i, size; u16 size;
int i;
if (aa_unpack_nameX(e, AA_STRUCT, "secmark")) { if (aa_unpack_nameX(e, AA_STRUCT, "secmark")) {
size = aa_unpack_array(e, NULL); if (!aa_unpack_array(e, NULL, &size))
goto fail;
profile->secmark = kcalloc(size, sizeof(struct aa_secmark), rules->secmark = kcalloc(size, sizeof(struct aa_secmark),
GFP_KERNEL); GFP_KERNEL);
if (!profile->secmark) if (!rules->secmark)
goto fail; goto fail;
profile->secmark_count = size; rules->secmark_count = size;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (!unpack_u8(e, &profile->secmark[i].audit, NULL)) if (!unpack_u8(e, &rules->secmark[i].audit, NULL))
goto fail; goto fail;
if (!unpack_u8(e, &profile->secmark[i].deny, NULL)) if (!unpack_u8(e, &rules->secmark[i].deny, NULL))
goto fail; goto fail;
if (!aa_unpack_strdup(e, &profile->secmark[i].label, NULL)) if (!aa_unpack_strdup(e, &rules->secmark[i].label, NULL))
goto fail; goto fail;
} }
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
...@@ -581,39 +582,40 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile) ...@@ -581,39 +582,40 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
return true; return true;
fail: fail:
if (profile->secmark) { if (rules->secmark) {
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
kfree(profile->secmark[i].label); kfree(rules->secmark[i].label);
kfree(profile->secmark); kfree(rules->secmark);
profile->secmark_count = 0; rules->secmark_count = 0;
profile->secmark = NULL; rules->secmark = NULL;
} }
e->pos = pos; e->pos = pos;
return false; return false;
} }
static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) static bool unpack_rlimits(struct aa_ext *e, struct aa_ruleset *rules)
{ {
void *pos = e->pos; void *pos = e->pos;
/* rlimits are optional */ /* rlimits are optional */
if (aa_unpack_nameX(e, AA_STRUCT, "rlimits")) { if (aa_unpack_nameX(e, AA_STRUCT, "rlimits")) {
int i, size; u16 size;
int i;
u32 tmp = 0; u32 tmp = 0;
if (!aa_unpack_u32(e, &tmp, NULL)) if (!aa_unpack_u32(e, &tmp, NULL))
goto fail; goto fail;
profile->rlimits.mask = tmp; rules->rlimits.mask = tmp;
size = aa_unpack_array(e, NULL); if (!aa_unpack_array(e, NULL, &size) ||
if (size > RLIM_NLIMITS) size > RLIM_NLIMITS)
goto fail; goto fail;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
u64 tmp2 = 0; u64 tmp2 = 0;
int a = aa_map_resource(i); int a = aa_map_resource(i);
if (!aa_unpack_u64(e, &tmp2, NULL)) if (!aa_unpack_u64(e, &tmp2, NULL))
goto fail; goto fail;
profile->rlimits.limits[a].rlim_max = tmp2; rules->rlimits.limits[a].rlim_max = tmp2;
} }
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL)) if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
goto fail; goto fail;
...@@ -627,6 +629,140 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) ...@@ -627,6 +629,140 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
return false; return false;
} }
static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm)
{
if (version != 1)
return false;
return aa_unpack_u32(e, &perm->allow, NULL) &&
aa_unpack_u32(e, &perm->allow, NULL) &&
aa_unpack_u32(e, &perm->deny, NULL) &&
aa_unpack_u32(e, &perm->subtree, NULL) &&
aa_unpack_u32(e, &perm->cond, NULL) &&
aa_unpack_u32(e, &perm->kill, NULL) &&
aa_unpack_u32(e, &perm->complain, NULL) &&
aa_unpack_u32(e, &perm->prompt, NULL) &&
aa_unpack_u32(e, &perm->audit, NULL) &&
aa_unpack_u32(e, &perm->quiet, NULL) &&
aa_unpack_u32(e, &perm->hide, NULL) &&
aa_unpack_u32(e, &perm->xindex, NULL) &&
aa_unpack_u32(e, &perm->tag, NULL) &&
aa_unpack_u32(e, &perm->label, NULL);
}
static ssize_t unpack_perms_table(struct aa_ext *e, struct aa_perms **perms)
{
void *pos = e->pos;
u16 size = 0;
AA_BUG(!perms);
/*
* policy perms are optional, in which case perms are embedded
* in the dfa accept table
*/
if (aa_unpack_nameX(e, AA_STRUCT, "perms")) {
int i;
u32 version;
if (!aa_unpack_u32(e, &version, "version"))
goto fail_reset;
if (!aa_unpack_array(e, NULL, &size))
goto fail_reset;
*perms = kcalloc(size, sizeof(struct aa_perms), GFP_KERNEL);
if (!*perms)
goto fail_reset;
for (i = 0; i < size; i++) {
if (!unpack_perm(e, version, &(*perms)[i]))
goto fail;
}
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
goto fail;
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
} else
*perms = NULL;
return size;
fail:
kfree(*perms);
fail_reset:
e->pos = pos;
return -EPROTO;
}
static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
bool required_dfa, bool required_trans,
const char **info)
{
void *pos = e->pos;
int i, flags, error = -EPROTO;
ssize_t size;
size = unpack_perms_table(e, &policy->perms);
if (size < 0) {
error = size;
policy->perms = NULL;
*info = "failed to unpack - perms";
goto fail;
}
policy->size = size;
if (policy->perms) {
/* perms table present accept is index */
flags = TO_ACCEPT1_FLAG(YYTD_DATA32);
} else {
/* packed perms in accept1 and accept2 */
flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32);
}
policy->dfa = unpack_dfa(e, flags);
if (IS_ERR(policy->dfa)) {
error = PTR_ERR(policy->dfa);
policy->dfa = NULL;
*info = "failed to unpack - dfa";
goto fail;
} else if (!policy->dfa) {
if (required_dfa) {
*info = "missing required dfa";
goto fail;
}
goto out;
}
/*
* only unpack the following if a dfa is present
*
* sadly start was given different names for file and policydb
* but since it is optional we can try both
*/
if (!aa_unpack_u32(e, &policy->start[0], "start"))
/* default start state */
policy->start[0] = DFA_START;
if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) {
/* default start state for xmatch and file dfa */
policy->start[AA_CLASS_FILE] = DFA_START;
} /* setup class index */
for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
policy->start[i] = aa_dfa_next(policy->dfa, policy->start[0],
i);
}
if (!unpack_trans_table(e, &policy->trans) && required_trans) {
*info = "failed to unpack profile transition table";
goto fail;
}
/* TODO: move compat mapping here, requires dfa merging first */
/* TODO: move verify here, it has to be done after compat mappings */
out:
return 0;
fail:
e->pos = pos;
return error;
}
static u32 strhash(const void *data, u32 len, u32 seed) static u32 strhash(const void *data, u32 len, u32 seed)
{ {
const char * const *key = data; const char * const *key = data;
...@@ -651,6 +787,7 @@ static int datacmp(struct rhashtable_compare_arg *arg, const void *obj) ...@@ -651,6 +787,7 @@ static int datacmp(struct rhashtable_compare_arg *arg, const void *obj)
*/ */
static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
{ {
struct aa_ruleset *rules;
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
const char *tmpname, *tmpns = NULL, *name = NULL; const char *tmpname, *tmpns = NULL, *name = NULL;
const char *info = "failed to unpack profile"; const char *info = "failed to unpack profile";
...@@ -658,7 +795,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -658,7 +795,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
struct rhashtable_params params = { 0 }; struct rhashtable_params params = { 0 };
char *key = NULL; char *key = NULL;
struct aa_data *data; struct aa_data *data;
int i, error = -EPROTO; int error = -EPROTO;
kernel_cap_t tmpcap; kernel_cap_t tmpcap;
u32 tmp; u32 tmp;
...@@ -677,36 +814,46 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -677,36 +814,46 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
if (!*ns_name) { if (!*ns_name) {
info = "out of memory"; info = "out of memory";
error = -ENOMEM;
goto fail; goto fail;
} }
name = tmpname; name = tmpname;
} }
profile = aa_alloc_profile(name, NULL, GFP_KERNEL); profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
if (!profile) if (!profile) {
return ERR_PTR(-ENOMEM); info = "out of memory";
error = -ENOMEM;
goto fail;
}
rules = list_first_entry(&profile->rules, typeof(*rules), list);
/* profile renaming is optional */ /* profile renaming is optional */
(void) aa_unpack_str(e, &profile->rename, "rename"); (void) aa_unpack_str(e, &profile->rename, "rename");
/* attachment string is optional */ /* attachment string is optional */
(void) aa_unpack_str(e, &profile->attach, "attach"); (void) aa_unpack_str(e, &profile->attach.xmatch_str, "attach");
/* xmatch is optional and may be NULL */ /* xmatch is optional and may be NULL */
profile->xmatch = unpack_dfa(e); error = unpack_pdb(e, &profile->attach.xmatch, false, false, &info);
if (IS_ERR(profile->xmatch)) { if (error) {
error = PTR_ERR(profile->xmatch);
profile->xmatch = NULL;
info = "bad xmatch"; info = "bad xmatch";
goto fail; goto fail;
} }
/* xmatch_len is not optional if xmatch is set */
if (profile->xmatch) { /* neither xmatch_len not xmatch_perms are optional if xmatch is set */
if (profile->attach.xmatch.dfa) {
if (!aa_unpack_u32(e, &tmp, NULL)) { if (!aa_unpack_u32(e, &tmp, NULL)) {
info = "missing xmatch len"; info = "missing xmatch len";
goto fail; goto fail;
} }
profile->xmatch_len = tmp; profile->attach.xmatch_len = tmp;
profile->attach.xmatch.start[AA_CLASS_XMATCH] = DFA_START;
error = aa_compat_map_xmatch(&profile->attach.xmatch);
if (error) {
info = "failed to convert xmatch permission table";
goto fail;
}
} }
/* disconnected attachment string is optional */ /* disconnected attachment string is optional */
...@@ -737,6 +884,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -737,6 +884,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
} else if (tmp == PACKED_MODE_UNCONFINED) { } else if (tmp == PACKED_MODE_UNCONFINED) {
profile->mode = APPARMOR_UNCONFINED; profile->mode = APPARMOR_UNCONFINED;
profile->label.flags |= FLAG_UNCONFINED; profile->label.flags |= FLAG_UNCONFINED;
} else if (tmp == PACKED_MODE_USER) {
profile->mode = APPARMOR_USER;
} else { } else {
goto fail; goto fail;
} }
...@@ -757,11 +906,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -757,11 +906,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
profile->path_flags = PATH_MEDIATE_DELETED; profile->path_flags = PATH_MEDIATE_DELETED;
info = "failed to unpack profile capabilities"; info = "failed to unpack profile capabilities";
if (!aa_unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.allow.cap[0]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.audit.cap[0]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.quiet.cap[0]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &tmpcap.cap[0], NULL)) if (!aa_unpack_u32(e, &tmpcap.cap[0], NULL))
goto fail; goto fail;
...@@ -769,11 +918,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -769,11 +918,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
info = "failed to unpack upper profile capabilities"; info = "failed to unpack upper profile capabilities";
if (aa_unpack_nameX(e, AA_STRUCT, "caps64")) { if (aa_unpack_nameX(e, AA_STRUCT, "caps64")) {
/* optional upper half of 64 bit caps */ /* optional upper half of 64 bit caps */
if (!aa_unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.allow.cap[1]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &(profile->caps.audit.cap[1]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.audit.cap[1]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.quiet.cap[1]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &(tmpcap.cap[1]), NULL)) if (!aa_unpack_u32(e, &(tmpcap.cap[1]), NULL))
goto fail; goto fail;
...@@ -784,9 +933,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -784,9 +933,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
info = "failed to unpack extended profile capabilities"; info = "failed to unpack extended profile capabilities";
if (aa_unpack_nameX(e, AA_STRUCT, "capsx")) { if (aa_unpack_nameX(e, AA_STRUCT, "capsx")) {
/* optional extended caps mediation mask */ /* optional extended caps mediation mask */
if (!aa_unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.extended.cap[0]), NULL))
goto fail; goto fail;
if (!aa_unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) if (!aa_unpack_u32(e, &(rules->caps.extended.cap[1]), NULL))
goto fail; goto fail;
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
...@@ -797,12 +946,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -797,12 +946,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
goto fail; goto fail;
} }
if (!unpack_rlimits(e, profile)) { if (!unpack_rlimits(e, rules)) {
info = "failed to unpack profile rlimits"; info = "failed to unpack profile rlimits";
goto fail; goto fail;
} }
if (!unpack_secmark(e, profile)) { if (!unpack_secmark(e, rules)) {
info = "failed to unpack profile secmark rules"; info = "failed to unpack profile secmark rules";
goto fail; goto fail;
} }
...@@ -810,59 +959,52 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -810,59 +959,52 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (aa_unpack_nameX(e, AA_STRUCT, "policydb")) { if (aa_unpack_nameX(e, AA_STRUCT, "policydb")) {
/* generic policy dfa - optional and may be NULL */ /* generic policy dfa - optional and may be NULL */
info = "failed to unpack policydb"; info = "failed to unpack policydb";
profile->policy.dfa = unpack_dfa(e); error = unpack_pdb(e, &rules->policy, true, false,
if (IS_ERR(profile->policy.dfa)) { &info);
error = PTR_ERR(profile->policy.dfa); if (error)
profile->policy.dfa = NULL;
goto fail;
} else if (!profile->policy.dfa) {
error = -EPROTO;
goto fail; goto fail;
} /* Fixup: drop when we get rid of start array */
if (!aa_unpack_u32(e, &profile->policy.start[0], "start")) if (aa_dfa_next(rules->policy.dfa, rules->policy.start[0],
/* default start state */ AA_CLASS_FILE))
profile->policy.start[0] = DFA_START; rules->policy.start[AA_CLASS_FILE] =
/* setup class index */ aa_dfa_next(rules->policy.dfa,
for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) { rules->policy.start[0],
profile->policy.start[i] = AA_CLASS_FILE);
aa_dfa_next(profile->policy.dfa,
profile->policy.start[0],
i);
}
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail; goto fail;
error = aa_compat_map_policy(&rules->policy, e->version);
if (error) {
info = "failed to remap policydb permission table";
goto fail;
}
} else } else
profile->policy.dfa = aa_get_dfa(nulldfa); rules->policy.dfa = aa_get_dfa(nulldfa);
/* get file rules */ /* get file rules */
profile->file.dfa = unpack_dfa(e); error = unpack_pdb(e, &rules->file, false, true, &info);
if (IS_ERR(profile->file.dfa)) { if (error) {
error = PTR_ERR(profile->file.dfa); goto fail;
profile->file.dfa = NULL; } else if (rules->file.dfa) {
info = "failed to unpack profile file rules"; error = aa_compat_map_file(&rules->file);
goto fail; if (error) {
} else if (profile->file.dfa) { info = "failed to remap file permission table";
if (!aa_unpack_u32(e, &profile->file.start, "dfa_start"))
/* default start state */
profile->file.start = DFA_START;
} else if (profile->policy.dfa &&
profile->policy.start[AA_CLASS_FILE]) {
profile->file.dfa = aa_get_dfa(profile->policy.dfa);
profile->file.start = profile->policy.start[AA_CLASS_FILE];
} else
profile->file.dfa = aa_get_dfa(nulldfa);
if (!unpack_trans_table(e, profile)) {
info = "failed to unpack profile transition table";
goto fail; goto fail;
} }
} else if (rules->policy.dfa &&
rules->policy.start[AA_CLASS_FILE]) {
rules->file.dfa = aa_get_dfa(rules->policy.dfa);
rules->file.start[AA_CLASS_FILE] = rules->policy.start[AA_CLASS_FILE];
} else
rules->file.dfa = aa_get_dfa(nulldfa);
error = -EPROTO;
if (aa_unpack_nameX(e, AA_STRUCT, "data")) { if (aa_unpack_nameX(e, AA_STRUCT, "data")) {
info = "out of memory"; info = "out of memory";
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
if (!profile->data) if (!profile->data) {
error = -ENOMEM;
goto fail; goto fail;
}
params.nelem_hint = 3; params.nelem_hint = 3;
params.key_len = sizeof(void *); params.key_len = sizeof(void *);
params.key_offset = offsetof(struct aa_data, key); params.key_offset = offsetof(struct aa_data, key);
...@@ -879,6 +1021,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -879,6 +1021,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
data = kzalloc(sizeof(*data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) { if (!data) {
kfree_sensitive(key); kfree_sensitive(key);
error = -ENOMEM;
goto fail; goto fail;
} }
...@@ -888,6 +1031,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -888,6 +1031,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (data->size && !data->data) { if (data->size && !data->data) {
kfree_sensitive(data->key); kfree_sensitive(data->key);
kfree_sensitive(data); kfree_sensitive(data);
error = -ENOMEM;
goto fail; goto fail;
} }
...@@ -909,6 +1053,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) ...@@ -909,6 +1053,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
return profile; return profile;
fail: fail:
if (error == 0)
/* default error covers most cases */
error = -EPROTO;
if (*ns_name) {
kfree(*ns_name);
*ns_name = NULL;
}
if (profile) if (profile)
name = NULL; name = NULL;
else if (!name) else if (!name)
...@@ -946,7 +1097,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) ...@@ -946,7 +1097,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
* if not specified use previous version * if not specified use previous version
* Mask off everything that is not kernel abi version * Mask off everything that is not kernel abi version
*/ */
if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) { if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v9)) {
audit_iface(NULL, NULL, NULL, "unsupported interface version", audit_iface(NULL, NULL, NULL, "unsupported interface version",
e, error); e, error);
return error; return error;
...@@ -987,11 +1138,51 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) ...@@ -987,11 +1138,51 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
{ {
int i; int i;
for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) if (!verify_xindex(ACCEPT_TABLE(dfa)[i], table_size))
return false;
}
return true;
}
static bool verify_perm(struct aa_perms *perm)
{
/* TODO: allow option to just force the perms into a valid state */
if (perm->allow & perm->deny)
return false; return false;
if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) if (perm->subtree & ~perm->allow)
return false;
if (perm->cond & (perm->allow | perm->deny))
return false;
if (perm->kill & perm->allow)
return false;
if (perm->complain & (perm->allow | perm->deny))
return false;
if (perm->prompt & (perm->allow | perm->deny))
return false;
if (perm->complain & perm->prompt)
return false;
if (perm->hide & perm->allow)
return false;
return true;
}
static bool verify_perms(struct aa_policydb *pdb)
{
int i;
for (i = 0; i < pdb->size; i++) {
if (!verify_perm(&pdb->perms[i]))
return false;
/* verify indexes into str table */
if (pdb->perms[i].xindex >= pdb->trans.size)
return false;
if (pdb->perms[i].tag >= pdb->trans.size)
return false;
if (pdb->perms[i].label >= pdb->trans.size)
return false; return false;
} }
return true; return true;
} }
...@@ -1000,14 +1191,38 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) ...@@ -1000,14 +1191,38 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
* @profile: profile to verify (NOT NULL) * @profile: profile to verify (NOT NULL)
* *
* Returns: 0 if passes verification else error * Returns: 0 if passes verification else error
*
* This verification is post any unpack mapping or changes
*/ */
static int verify_profile(struct aa_profile *profile) static int verify_profile(struct aa_profile *profile)
{ {
if (profile->file.dfa && struct aa_ruleset *rules = list_first_entry(&profile->rules,
!verify_dfa_xindex(profile->file.dfa, typeof(*rules), list);
profile->file.trans.size)) { if (!rules)
audit_iface(profile, NULL, NULL, "Invalid named transition", return 0;
NULL, -EPROTO);
if ((rules->file.dfa && !verify_dfa_xindex(rules->file.dfa,
rules->file.trans.size)) ||
(rules->policy.dfa &&
!verify_dfa_xindex(rules->policy.dfa, rules->policy.trans.size))) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid named transition", NULL, -EPROTO);
return -EPROTO;
}
if (!verify_perms(&rules->file)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;
}
if (!verify_perms(&rules->policy)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;
}
if (!verify_perms(&profile->attach.xmatch)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO; return -EPROTO;
} }
...@@ -1033,81 +1248,73 @@ struct aa_load_ent *aa_load_ent_alloc(void) ...@@ -1033,81 +1248,73 @@ struct aa_load_ent *aa_load_ent_alloc(void)
return ent; return ent;
} }
static int deflate_compress(const char *src, size_t slen, char **dst, static int compress_zstd(const char *src, size_t slen, char **dst, size_t *dlen)
size_t *dlen)
{ {
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
int error; const zstd_parameters params =
struct z_stream_s strm; zstd_get_params(aa_g_rawdata_compression_level, slen);
void *stgbuf, *dstbuf; const size_t wksp_len = zstd_cctx_workspace_bound(&params.cParams);
size_t stglen = deflateBound(slen); void *wksp = NULL;
zstd_cctx *ctx = NULL;
memset(&strm, 0, sizeof(strm)); size_t out_len = zstd_compress_bound(slen);
void *out = NULL;
int ret = 0;
if (stglen < slen) out = kvzalloc(out_len, GFP_KERNEL);
return -EFBIG; if (!out) {
ret = -ENOMEM;
strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS, goto cleanup;
MAX_MEM_LEVEL),
GFP_KERNEL);
if (!strm.workspace)
return -ENOMEM;
error = zlib_deflateInit(&strm, aa_g_rawdata_compression_level);
if (error != Z_OK) {
error = -ENOMEM;
goto fail_deflate_init;
} }
stgbuf = kvzalloc(stglen, GFP_KERNEL); wksp = kvzalloc(wksp_len, GFP_KERNEL);
if (!stgbuf) { if (!wksp) {
error = -ENOMEM; ret = -ENOMEM;
goto fail_stg_alloc; goto cleanup;
} }
strm.next_in = src; ctx = zstd_init_cctx(wksp, wksp_len);
strm.avail_in = slen; if (!ctx) {
strm.next_out = stgbuf; ret = -EINVAL;
strm.avail_out = stglen; goto cleanup;
}
error = zlib_deflate(&strm, Z_FINISH); out_len = zstd_compress_cctx(ctx, out, out_len, src, slen, &params);
if (error != Z_STREAM_END) { if (zstd_is_error(out_len) || out_len >= slen) {
error = -EINVAL; ret = -EINVAL;
goto fail_deflate; goto cleanup;
} }
error = 0;
if (is_vmalloc_addr(stgbuf)) { if (is_vmalloc_addr(out)) {
dstbuf = kvzalloc(strm.total_out, GFP_KERNEL); *dst = kvzalloc(out_len, GFP_KERNEL);
if (dstbuf) { if (*dst) {
memcpy(dstbuf, stgbuf, strm.total_out); memcpy(*dst, out, out_len);
kvfree(stgbuf); kvfree(out);
out = NULL;
} }
} else } else {
/* /*
* If the staging buffer was kmalloc'd, then using krealloc is * If the staging buffer was kmalloc'd, then using krealloc is
* probably going to be faster. The destination buffer will * probably going to be faster. The destination buffer will
* always be smaller, so it's just shrunk, avoiding a memcpy * always be smaller, so it's just shrunk, avoiding a memcpy
*/ */
dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL); *dst = krealloc(out, out_len, GFP_KERNEL);
}
if (!dstbuf) { if (!*dst) {
error = -ENOMEM; ret = -ENOMEM;
goto fail_deflate; goto cleanup;
} }
*dst = dstbuf; *dlen = out_len;
*dlen = strm.total_out;
fail_stg_alloc: cleanup:
zlib_deflateEnd(&strm); if (ret) {
fail_deflate_init: kvfree(out);
kvfree(strm.workspace); *dst = NULL;
return error; }
fail_deflate: kvfree(wksp);
kvfree(stgbuf); return ret;
goto fail_stg_alloc;
#else #else
*dlen = slen; *dlen = slen;
return 0; return 0;
...@@ -1116,7 +1323,6 @@ static int deflate_compress(const char *src, size_t slen, char **dst, ...@@ -1116,7 +1323,6 @@ static int deflate_compress(const char *src, size_t slen, char **dst,
static int compress_loaddata(struct aa_loaddata *data) static int compress_loaddata(struct aa_loaddata *data)
{ {
AA_BUG(data->compressed_size > 0); AA_BUG(data->compressed_size > 0);
/* /*
...@@ -1125,11 +1331,12 @@ static int compress_loaddata(struct aa_loaddata *data) ...@@ -1125,11 +1331,12 @@ static int compress_loaddata(struct aa_loaddata *data)
*/ */
if (aa_g_rawdata_compression_level != 0) { if (aa_g_rawdata_compression_level != 0) {
void *udata = data->data; void *udata = data->data;
int error = deflate_compress(udata, data->size, &data->data, int error = compress_zstd(udata, data->size, &data->data,
&data->compressed_size); &data->compressed_size);
if (error) if (error) {
data->compressed_size = data->size;
return error; return error;
}
if (udata != data->data) if (udata != data->data)
kvfree(udata); kvfree(udata);
} else } else
...@@ -1155,6 +1362,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, ...@@ -1155,6 +1362,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
{ {
struct aa_load_ent *tmp, *ent; struct aa_load_ent *tmp, *ent;
struct aa_profile *profile = NULL; struct aa_profile *profile = NULL;
char *ns_name = NULL;
int error; int error;
struct aa_ext e = { struct aa_ext e = {
.start = udata->data, .start = udata->data,
...@@ -1164,7 +1372,6 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, ...@@ -1164,7 +1372,6 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
*ns = NULL; *ns = NULL;
while (e.pos < e.end) { while (e.pos < e.end) {
char *ns_name = NULL;
void *start; void *start;
error = verify_header(&e, e.pos == e.start, ns); error = verify_header(&e, e.pos == e.start, ns);
if (error) if (error)
...@@ -1195,6 +1402,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, ...@@ -1195,6 +1402,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
ent->new = profile; ent->new = profile;
ent->ns_name = ns_name; ent->ns_name = ns_name;
ns_name = NULL;
list_add_tail(&ent->list, lh); list_add_tail(&ent->list, lh);
} }
udata->abi = e.version & K_ABI_MASK; udata->abi = e.version & K_ABI_MASK;
...@@ -1215,6 +1423,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, ...@@ -1215,6 +1423,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
return 0; return 0;
fail_profile: fail_profile:
kfree(ns_name);
aa_put_profile(profile); aa_put_profile(profile);
fail: fail:
......
...@@ -143,12 +143,11 @@ static void policy_unpack_test_inbounds_when_out_of_bounds(struct kunit *test) ...@@ -143,12 +143,11 @@ static void policy_unpack_test_inbounds_when_out_of_bounds(struct kunit *test)
static void policy_unpack_test_unpack_array_with_null_name(struct kunit *test) static void policy_unpack_test_unpack_array_with_null_name(struct kunit *test)
{ {
struct policy_unpack_fixture *puf = test->priv; struct policy_unpack_fixture *puf = test->priv;
u16 array_size; u16 array_size = 0;
puf->e->pos += TEST_ARRAY_BUF_OFFSET; puf->e->pos += TEST_ARRAY_BUF_OFFSET;
array_size = aa_unpack_array(puf->e, NULL); KUNIT_EXPECT_TRUE(test, aa_unpack_array(puf->e, NULL, &array_size));
KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE); KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1); puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1);
...@@ -158,12 +157,11 @@ static void policy_unpack_test_unpack_array_with_name(struct kunit *test) ...@@ -158,12 +157,11 @@ static void policy_unpack_test_unpack_array_with_name(struct kunit *test)
{ {
struct policy_unpack_fixture *puf = test->priv; struct policy_unpack_fixture *puf = test->priv;
const char name[] = TEST_ARRAY_NAME; const char name[] = TEST_ARRAY_NAME;
u16 array_size; u16 array_size = 0;
puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET; puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
array_size = aa_unpack_array(puf->e, name); KUNIT_EXPECT_TRUE(test, aa_unpack_array(puf->e, name, &array_size));
KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE); KUNIT_EXPECT_EQ(test, array_size, (u16)TEST_ARRAY_SIZE);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1); puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16) + 1);
...@@ -178,9 +176,7 @@ static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test) ...@@ -178,9 +176,7 @@ static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test)
puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET; puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
puf->e->end = puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16); puf->e->end = puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16);
array_size = aa_unpack_array(puf->e, name); KUNIT_EXPECT_FALSE(test, aa_unpack_array(puf->e, name, &array_size));
KUNIT_EXPECT_EQ(test, array_size, 0);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET); puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET);
} }
......
...@@ -17,14 +17,13 @@ ...@@ -17,14 +17,13 @@
/** /**
* aa_getprocattr - Return the profile information for @profile * aa_getprocattr - Return the label information for @label
* @profile: the profile to print profile info about (NOT NULL) * @label: the label to print label info about (NOT NULL)
* @string: Returns - string containing the profile info (NOT NULL) * @string: Returns - string containing the label info (NOT NULL)
* *
* Requires: profile != NULL * Requires: label != NULL && string != NULL
* *
* Creates a string containing the namespace_name://profile_name for * Creates a string containing the label information for @label.
* @profile.
* *
* Returns: size of string placed in @string else error code on failure * Returns: size of string placed in @string else error code on failure
*/ */
......
...@@ -45,6 +45,8 @@ static void audit_cb(struct audit_buffer *ab, void *va) ...@@ -45,6 +45,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
* @profile: profile being enforced (NOT NULL) * @profile: profile being enforced (NOT NULL)
* @resource: rlimit being auditing * @resource: rlimit being auditing
* @value: value being set * @value: value being set
* @peer: aa_albel of the task being set
* @info: info being auditing
* @error: error value * @error: error value
* *
* Returns: 0 or sa->error else other error code on failure * Returns: 0 or sa->error else other error code on failure
...@@ -53,7 +55,8 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, ...@@ -53,7 +55,8 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource,
unsigned long value, struct aa_label *peer, unsigned long value, struct aa_label *peer,
const char *info, int error) const char *info, int error)
{ {
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETRLIMIT); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_RLIMITS,
OP_SETRLIMIT);
aad(&sa)->rlim.rlim = resource; aad(&sa)->rlim.rlim = resource;
aad(&sa)->rlim.max = value; aad(&sa)->rlim.max = value;
...@@ -65,7 +68,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, ...@@ -65,7 +68,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource,
} }
/** /**
* aa_map_resouce - map compiled policy resource to internal # * aa_map_resource - map compiled policy resource to internal #
* @resource: flattened policy resource number * @resource: flattened policy resource number
* *
* Returns: resource # for the current architecture. * Returns: resource # for the current architecture.
...@@ -81,10 +84,12 @@ int aa_map_resource(int resource) ...@@ -81,10 +84,12 @@ int aa_map_resource(int resource)
static int profile_setrlimit(struct aa_profile *profile, unsigned int resource, static int profile_setrlimit(struct aa_profile *profile, unsigned int resource,
struct rlimit *new_rlim) struct rlimit *new_rlim)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
int e = 0; int e = 0;
if (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
profile->rlimits.limits[resource].rlim_max) rules->rlimits.limits[resource].rlim_max)
e = -EACCES; e = -EACCES;
return audit_resource(profile, resource, new_rlim->rlim_max, NULL, NULL, return audit_resource(profile, resource, new_rlim->rlim_max, NULL, NULL,
e); e);
...@@ -152,12 +157,15 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l) ...@@ -152,12 +157,15 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
* to the lesser of the tasks hard limit and the init tasks soft limit * to the lesser of the tasks hard limit and the init tasks soft limit
*/ */
label_for_each_confined(i, old_l, old) { label_for_each_confined(i, old_l, old) {
if (old->rlimits.mask) { struct aa_ruleset *rules = list_first_entry(&old->rules,
typeof(*rules),
list);
if (rules->rlimits.mask) {
int j; int j;
for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, for (j = 0, mask = 1; j < RLIM_NLIMITS; j++,
mask <<= 1) { mask <<= 1) {
if (old->rlimits.mask & mask) { if (rules->rlimits.mask & mask) {
rlim = current->signal->rlim + j; rlim = current->signal->rlim + j;
initrlim = init_task.signal->rlim + j; initrlim = init_task.signal->rlim + j;
rlim->rlim_cur = min(rlim->rlim_max, rlim->rlim_cur = min(rlim->rlim_max,
...@@ -169,17 +177,20 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l) ...@@ -169,17 +177,20 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
/* set any new hard limits as dictated by the new profile */ /* set any new hard limits as dictated by the new profile */
label_for_each_confined(i, new_l, new) { label_for_each_confined(i, new_l, new) {
struct aa_ruleset *rules = list_first_entry(&new->rules,
typeof(*rules),
list);
int j; int j;
if (!new->rlimits.mask) if (!rules->rlimits.mask)
continue; continue;
for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) { for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) {
if (!(new->rlimits.mask & mask)) if (!(rules->rlimits.mask & mask))
continue; continue;
rlim = current->signal->rlim + j; rlim = current->signal->rlim + j;
rlim->rlim_max = min(rlim->rlim_max, rlim->rlim_max = min(rlim->rlim_max,
new->rlimits.limits[j].rlim_max); rules->rlimits.limits[j].rlim_max);
/* soft limit should not exceed hard limit */ /* soft limit should not exceed hard limit */
rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
} }
......
...@@ -31,7 +31,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task) ...@@ -31,7 +31,7 @@ struct aa_label *aa_get_task_label(struct task_struct *task)
struct aa_label *p; struct aa_label *p;
rcu_read_lock(); rcu_read_lock();
p = aa_get_newest_label(__aa_task_raw_label(task)); p = aa_get_newest_cred_label(__task_cred(task));
rcu_read_unlock(); rcu_read_unlock();
return p; return p;
...@@ -223,16 +223,18 @@ static void audit_ptrace_cb(struct audit_buffer *ab, void *va) ...@@ -223,16 +223,18 @@ static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
FLAGS_NONE, GFP_ATOMIC); FLAGS_NONE, GFP_ATOMIC);
} }
/* assumes check for PROFILE_MEDIATES is already done */ /* assumes check for RULE_MEDIATES is already done */
/* TODO: conditionals */ /* TODO: conditionals */
static int profile_ptrace_perm(struct aa_profile *profile, static int profile_ptrace_perm(struct aa_profile *profile,
struct aa_label *peer, u32 request, struct aa_label *peer, u32 request,
struct common_audit_data *sa) struct common_audit_data *sa)
{ {
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_perms perms = { }; struct aa_perms perms = { };
aad(sa)->peer = peer; aad(sa)->peer = peer;
aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, aa_profile_match_label(profile, rules, peer, AA_CLASS_PTRACE, request,
&perms); &perms);
aa_apply_modes_to_perms(profile, &perms); aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
...@@ -243,7 +245,7 @@ static int profile_tracee_perm(struct aa_profile *tracee, ...@@ -243,7 +245,7 @@ static int profile_tracee_perm(struct aa_profile *tracee,
struct common_audit_data *sa) struct common_audit_data *sa)
{ {
if (profile_unconfined(tracee) || unconfined(tracer) || if (profile_unconfined(tracee) || unconfined(tracer) ||
!PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) !ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE))
return 0; return 0;
return profile_ptrace_perm(tracee, tracer, request, sa); return profile_ptrace_perm(tracee, tracer, request, sa);
...@@ -256,7 +258,7 @@ static int profile_tracer_perm(struct aa_profile *tracer, ...@@ -256,7 +258,7 @@ static int profile_tracer_perm(struct aa_profile *tracer,
if (profile_unconfined(tracer)) if (profile_unconfined(tracer))
return 0; return 0;
if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) if (ANY_RULE_MEDIATES(&tracer->rules, AA_CLASS_PTRACE))
return profile_ptrace_perm(tracer, tracee, request, sa); return profile_ptrace_perm(tracer, tracee, request, sa);
/* profile uses the old style capability check for ptrace */ /* profile uses the old style capability check for ptrace */
...@@ -285,7 +287,7 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, ...@@ -285,7 +287,7 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
{ {
struct aa_profile *profile; struct aa_profile *profile;
u32 xrequest = request << PTRACE_PERM_SHIFT; u32 xrequest = request << PTRACE_PERM_SHIFT;
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_PTRACE, OP_PTRACE);
return xcheck_labels(tracer, tracee, profile, return xcheck_labels(tracer, tracee, profile,
profile_tracer_perm(profile, tracee, request, &sa), profile_tracer_perm(profile, tracee, request, &sa),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment