+ Features
- switch to zstd compression for profile raw data + Cleanups - Simplify obtain 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE7cSDD705q2rFEEf7BS82cBjVw9gFAmOZwywACgkQBS82cBjV w9jBjRAAmj4gyK0L3eGY4IV2BpvnkHwHY4lOObJulTwILOOj0Pz8CJqRCa/HDCGj aOlnwqksPsAjadzzfi58D6TnT+3fOuskbcMgTyvX5jraTXPrUl90+hXorbXKuLrw iaX6QxW8soNW/s3oJhrC2HxbIhGA9VpVnmQpVZpJMmz5bU2xmzL62FCN8x88kytr 9CygaudPrvwYJf5pPd62p7ltj2S6lFwZ6dVCyiDQGTc+Gyng4G8p4MCfI1CwMMyo mAUeeRnoeeBwH3tSy/Wsr72jPKjsMASpcMHo3ns/dVSw/ug2FYYToZbfxT/uAa6O WVHfS1Kv/5afG9xxyfocWecd+Yp3lsXq9F+q36uOT9NeJmlej9aJr5sWMcvV3sru QVNN7tFZbHqCnLhpl6RDH/NiguweNYQXrl2lukXZe/FKu/KDasFIOzL+IAt2TqZE 3mWrha7Q7j/gdBw8+fHHGtXCx0NSQlz1oFLo/y/mI7ztwUPJsBYbH5+108iP0ys/ 7Kd+jkYRucJB4upGH4meQbN6f/rrs3+m/b/j0Q8RCFHAs2f+mYZeN/JOHCo0T4YH KO1W60846fPs+7yZTVxWYFpR/kIuXksyxMWpEEZFFtF4MNoaeM1uypBWqm/JmKYr 8oDtEyiOd/qmZnWRcuO3/bmdoJUZY1zTXWA0dlScYc8vR4KC+EE= =6GKy -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
93761c93e9
|
@ -85,8 +85,8 @@ config SECURITY_APPARMOR_HASH_DEFAULT
|
|||
config SECURITY_APPARMOR_EXPORT_BINARY
|
||||
bool "Allow exporting the raw binary policy"
|
||||
depends on SECURITY_APPARMOR_INTROSPECT_POLICY
|
||||
select ZLIB_INFLATE
|
||||
select ZLIB_DEFLATE
|
||||
select ZSTD_COMPRESS
|
||||
select ZSTD_DECOMPRESS
|
||||
default y
|
||||
help
|
||||
This option allows reading back binary policy as it was loaded.
|
||||
|
|
|
@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
|
|||
|
||||
apparmor-y := apparmorfs.o audit.o capability.o task.o ipc.o lib.o match.o \
|
||||
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
|
||||
resource.o secid.o file.o policy_ns.o label.o mount.o net.o
|
||||
resource.o secid.o file.o policy_ns.o label.o mount.o net.o \
|
||||
policy_compat.o
|
||||
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
|
||||
|
||||
obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/zlib.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <uapi/linux/major.h>
|
||||
#include <uapi/linux/magic.h>
|
||||
|
||||
|
@ -611,29 +611,30 @@ static const struct file_operations aa_fs_ns_revision_fops = {
|
|||
static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
|
||||
const char *match_str, size_t match_len)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms tmp = { };
|
||||
struct aa_dfa *dfa;
|
||||
unsigned int state = 0;
|
||||
aa_state_t state = DFA_NOMATCH;
|
||||
|
||||
if (profile_unconfined(profile))
|
||||
return;
|
||||
if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
|
||||
dfa = profile->file.dfa;
|
||||
state = aa_dfa_match_len(dfa, profile->file.start,
|
||||
if (rules->file.dfa && *match_str == AA_CLASS_FILE) {
|
||||
state = aa_dfa_match_len(rules->file.dfa,
|
||||
rules->file.start[AA_CLASS_FILE],
|
||||
match_str + 1, match_len - 1);
|
||||
if (state) {
|
||||
struct path_cond cond = { };
|
||||
|
||||
tmp = aa_compute_fperms(dfa, state, &cond);
|
||||
tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
|
||||
}
|
||||
} else if (profile->policy.dfa) {
|
||||
if (!PROFILE_MEDIATES(profile, *match_str))
|
||||
} else if (rules->policy.dfa) {
|
||||
if (!RULE_MEDIATES(rules, *match_str))
|
||||
return; /* no change to current perms */
|
||||
dfa = profile->policy.dfa;
|
||||
state = aa_dfa_match_len(dfa, profile->policy.start[0],
|
||||
state = aa_dfa_match_len(rules->policy.dfa,
|
||||
rules->policy.start[0],
|
||||
match_str, match_len);
|
||||
if (state)
|
||||
aa_compute_perms(dfa, state, &tmp);
|
||||
tmp = *aa_lookup_perms(&rules->policy, state);
|
||||
}
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum_raw(perms, &tmp);
|
||||
|
@ -868,8 +869,10 @@ static struct multi_transaction *multi_transaction_new(struct file *file,
|
|||
if (!t)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
kref_init(&t->count);
|
||||
if (copy_from_user(t->data, buf, size))
|
||||
if (copy_from_user(t->data, buf, size)) {
|
||||
put_multi_transaction(t);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
@ -1090,9 +1093,9 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v)
|
|||
struct aa_proxy *proxy = seq->private;
|
||||
struct aa_label *label = aa_get_label_rcu(&proxy->label);
|
||||
struct aa_profile *profile = labels_profile(label);
|
||||
if (profile->attach)
|
||||
seq_printf(seq, "%s\n", profile->attach);
|
||||
else if (profile->xmatch)
|
||||
if (profile->attach.xmatch_str)
|
||||
seq_printf(seq, "%s\n", profile->attach.xmatch_str);
|
||||
else if (profile->attach.xmatch.dfa)
|
||||
seq_puts(seq, "<unknown>\n");
|
||||
else
|
||||
seq_printf(seq, "%s\n", profile->base.name);
|
||||
|
@ -1197,10 +1200,24 @@ static int seq_ns_name_show(struct seq_file *seq, void *v)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int seq_ns_compress_min_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
seq_printf(seq, "%d\n", AA_MIN_CLEVEL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seq_ns_compress_max_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
seq_printf(seq, "%d\n", AA_MAX_CLEVEL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEQ_NS_FOPS(stacked);
|
||||
SEQ_NS_FOPS(nsstacked);
|
||||
SEQ_NS_FOPS(level);
|
||||
SEQ_NS_FOPS(name);
|
||||
SEQ_NS_FOPS(compress_min);
|
||||
SEQ_NS_FOPS(compress_max);
|
||||
|
||||
|
||||
/* policy/raw_data/ * file ops */
|
||||
|
@ -1295,42 +1312,34 @@ SEQ_RAWDATA_FOPS(revision);
|
|||
SEQ_RAWDATA_FOPS(hash);
|
||||
SEQ_RAWDATA_FOPS(compressed_size);
|
||||
|
||||
static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
|
||||
static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
if (aa_g_rawdata_compression_level != 0) {
|
||||
int error = 0;
|
||||
struct z_stream_s strm;
|
||||
if (slen < dlen) {
|
||||
const size_t wksp_len = zstd_dctx_workspace_bound();
|
||||
zstd_dctx *ctx;
|
||||
void *wksp;
|
||||
size_t out_len;
|
||||
int ret = 0;
|
||||
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
|
||||
strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
|
||||
if (!strm.workspace)
|
||||
return -ENOMEM;
|
||||
|
||||
strm.next_in = src;
|
||||
strm.avail_in = slen;
|
||||
|
||||
error = zlib_inflateInit(&strm);
|
||||
if (error != Z_OK) {
|
||||
error = -ENOMEM;
|
||||
goto fail_inflate_init;
|
||||
wksp = kvzalloc(wksp_len, GFP_KERNEL);
|
||||
if (!wksp) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
strm.next_out = dst;
|
||||
strm.avail_out = dlen;
|
||||
|
||||
error = zlib_inflate(&strm, Z_FINISH);
|
||||
if (error != Z_STREAM_END)
|
||||
error = -EINVAL;
|
||||
else
|
||||
error = 0;
|
||||
|
||||
zlib_inflateEnd(&strm);
|
||||
fail_inflate_init:
|
||||
kvfree(strm.workspace);
|
||||
|
||||
return error;
|
||||
ctx = zstd_init_dctx(wksp, wksp_len);
|
||||
if (ctx == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
|
||||
if (zstd_is_error(out_len)) {
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
cleanup:
|
||||
kvfree(wksp);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1379,7 +1388,7 @@ static int rawdata_open(struct inode *inode, struct file *file)
|
|||
|
||||
private->loaddata = loaddata;
|
||||
|
||||
error = deflate_decompress(loaddata->data, loaddata->compressed_size,
|
||||
error = decompress_zstd(loaddata->data, loaddata->compressed_size,
|
||||
RAWDATA_F_DATA_BUF(private),
|
||||
loaddata->size);
|
||||
if (error)
|
||||
|
@ -2392,6 +2401,8 @@ static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
|
|||
AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
|
||||
AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
|
||||
AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
|
||||
AA_SFS_FILE_FOPS("raw_data_compression_level_min", 0444, &seq_ns_compress_min_fops),
|
||||
AA_SFS_FILE_FOPS("raw_data_compression_level_max", 0444, &seq_ns_compress_max_fops),
|
||||
AA_SFS_DIR("features", aa_sfs_entry_features),
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -36,6 +36,43 @@ static const char *const aa_audit_type[] = {
|
|||
"AUTO"
|
||||
};
|
||||
|
||||
static const char *const aa_class_names[] = {
|
||||
"none",
|
||||
"unknown",
|
||||
"file",
|
||||
"cap",
|
||||
"net",
|
||||
"rlimits",
|
||||
"domain",
|
||||
"mount",
|
||||
"unknown",
|
||||
"ptrace",
|
||||
"signal",
|
||||
"xmatch",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"net",
|
||||
"unknown",
|
||||
"label",
|
||||
"posix_mqueue",
|
||||
"io_uring",
|
||||
"module",
|
||||
"lsm",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"unknown",
|
||||
"X",
|
||||
"dbus",
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Currently AppArmor auditing is fed straight into the audit framework.
|
||||
*
|
||||
|
@ -46,7 +83,7 @@ static const char *const aa_audit_type[] = {
|
|||
*/
|
||||
|
||||
/**
|
||||
* audit_base - core AppArmor function.
|
||||
* audit_pre() - core AppArmor function.
|
||||
* @ab: audit buffer to fill (NOT NULL)
|
||||
* @ca: audit structure containing data to audit (NOT NULL)
|
||||
*
|
||||
|
@ -65,6 +102,12 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
|
|||
audit_log_format(ab, " operation=\"%s\"", aad(sa)->op);
|
||||
}
|
||||
|
||||
if (aad(sa)->class)
|
||||
audit_log_format(ab, " class=\"%s\"",
|
||||
aad(sa)->class <= AA_CLASS_LAST ?
|
||||
aa_class_names[aad(sa)->class] :
|
||||
"unknown");
|
||||
|
||||
if (aad(sa)->info) {
|
||||
audit_log_format(ab, " info=\"%s\"", aad(sa)->info);
|
||||
if (aad(sa)->error)
|
||||
|
|
|
@ -64,6 +64,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
|
|||
static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
|
||||
int cap, int error)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct audit_cache *ent;
|
||||
int type = AUDIT_APPARMOR_AUTO;
|
||||
|
||||
|
@ -72,13 +74,13 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
|
|||
if (likely(!error)) {
|
||||
/* test if auditing is being forced */
|
||||
if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
|
||||
!cap_raised(profile->caps.audit, cap)))
|
||||
!cap_raised(rules->caps.audit, cap)))
|
||||
return 0;
|
||||
type = AUDIT_APPARMOR_AUDIT;
|
||||
} else if (KILL_MODE(profile) ||
|
||||
cap_raised(profile->caps.kill, cap)) {
|
||||
cap_raised(rules->caps.kill, cap)) {
|
||||
type = AUDIT_APPARMOR_KILL;
|
||||
} else if (cap_raised(profile->caps.quiet, cap) &&
|
||||
} else if (cap_raised(rules->caps.quiet, cap) &&
|
||||
AUDIT_MODE(profile) != AUDIT_NOQUIET &&
|
||||
AUDIT_MODE(profile) != AUDIT_ALL) {
|
||||
/* quiet auditing */
|
||||
|
@ -114,10 +116,12 @@ static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile,
|
|||
static int profile_capable(struct aa_profile *profile, int cap,
|
||||
unsigned int opts, struct common_audit_data *sa)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
int error;
|
||||
|
||||
if (cap_raised(profile->caps.allow, cap) &&
|
||||
!cap_raised(profile->caps.denied, cap))
|
||||
if (cap_raised(rules->caps.allow, cap) &&
|
||||
!cap_raised(rules->caps.denied, cap))
|
||||
error = 0;
|
||||
else
|
||||
error = -EPERM;
|
||||
|
@ -148,7 +152,7 @@ int aa_capable(struct aa_label *label, int cap, unsigned int opts)
|
|||
{
|
||||
struct aa_profile *profile;
|
||||
int error = 0;
|
||||
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE);
|
||||
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, AA_CLASS_CAP, OP_CAPABLE);
|
||||
|
||||
sa.u.cap = cap;
|
||||
error = fn_for_each_confined(label, profile,
|
||||
|
|
|
@ -29,24 +29,6 @@
|
|||
#include "include/policy.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
|
||||
* @to_label: profile to change to (NOT NULL)
|
||||
|
@ -95,23 +77,25 @@ out:
|
|||
* If a subns profile is not to be matched should be prescreened with
|
||||
* visibility test.
|
||||
*/
|
||||
static inline unsigned int match_component(struct aa_profile *profile,
|
||||
static inline aa_state_t match_component(struct aa_profile *profile,
|
||||
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;
|
||||
|
||||
if (stack)
|
||||
state = aa_dfa_match(profile->file.dfa, state, "&");
|
||||
state = aa_dfa_match(rules->file.dfa, state, "&");
|
||||
if (profile->ns == tp->ns)
|
||||
return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
|
||||
return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
|
||||
|
||||
/* try matching with namespace name and then profile */
|
||||
ns_name = aa_ns_name(profile->ns, tp->ns, true);
|
||||
state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
|
||||
state = aa_dfa_match(profile->file.dfa, state, ns_name);
|
||||
state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
|
||||
return aa_dfa_match(profile->file.dfa, state, tp->base.hname);
|
||||
state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
|
||||
state = aa_dfa_match(rules->file.dfa, state, ns_name);
|
||||
state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
|
||||
return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,9 +116,11 @@ static inline unsigned int match_component(struct aa_profile *profile,
|
|||
*/
|
||||
static int label_compound_match(struct aa_profile *profile,
|
||||
struct aa_label *label, bool stack,
|
||||
unsigned int state, bool subns, u32 request,
|
||||
aa_state_t state, bool subns, u32 request,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_profile *tp;
|
||||
struct label_it i;
|
||||
struct path_cond cond = { };
|
||||
|
@ -157,12 +143,12 @@ next:
|
|||
label_for_each_cont(i, label, tp) {
|
||||
if (!aa_ns_visible(profile->ns, tp->ns, subns))
|
||||
continue;
|
||||
state = aa_dfa_match(profile->file.dfa, state, "//&");
|
||||
state = aa_dfa_match(rules->file.dfa, state, "//&");
|
||||
state = match_component(profile, tp, false, state);
|
||||
if (!state)
|
||||
goto fail;
|
||||
}
|
||||
*perms = aa_compute_fperms(profile->file.dfa, state, &cond);
|
||||
*perms = *(aa_lookup_fperms(&(rules->file), state, &cond));
|
||||
aa_apply_modes_to_perms(profile, perms);
|
||||
if ((perms->allow & request) != request)
|
||||
return -EACCES;
|
||||
|
@ -192,14 +178,16 @@ fail:
|
|||
*/
|
||||
static int label_components_match(struct aa_profile *profile,
|
||||
struct aa_label *label, bool stack,
|
||||
unsigned int start, bool subns, u32 request,
|
||||
aa_state_t start, bool subns, u32 request,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_profile *tp;
|
||||
struct label_it i;
|
||||
struct aa_perms tmp;
|
||||
struct path_cond cond = { };
|
||||
unsigned int state = 0;
|
||||
aa_state_t state = 0;
|
||||
|
||||
/* find first subcomponent to test */
|
||||
label_for_each(i, label, tp) {
|
||||
|
@ -215,7 +203,7 @@ static int label_components_match(struct aa_profile *profile,
|
|||
return 0;
|
||||
|
||||
next:
|
||||
tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
|
||||
tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum(perms, &tmp);
|
||||
label_for_each_cont(i, label, tp) {
|
||||
|
@ -224,7 +212,7 @@ next:
|
|||
state = match_component(profile, tp, stack, start);
|
||||
if (!state)
|
||||
goto fail;
|
||||
tmp = aa_compute_fperms(profile->file.dfa, state, &cond);
|
||||
tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum(perms, &tmp);
|
||||
}
|
||||
|
@ -252,7 +240,7 @@ fail:
|
|||
* Returns: the state the match finished in, may be the none matching state
|
||||
*/
|
||||
static int label_match(struct aa_profile *profile, struct aa_label *label,
|
||||
bool stack, unsigned int state, bool subns, u32 request,
|
||||
bool stack, aa_state_t state, bool subns, u32 request,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
int error;
|
||||
|
@ -286,7 +274,7 @@ static int label_match(struct aa_profile *profile, struct aa_label *label,
|
|||
*/
|
||||
static int change_profile_perms(struct aa_profile *profile,
|
||||
struct aa_label *target, bool stack,
|
||||
u32 request, unsigned int start,
|
||||
u32 request, aa_state_t start,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
if (profile_unconfined(profile)) {
|
||||
|
@ -308,44 +296,47 @@ static int change_profile_perms(struct aa_profile *profile,
|
|||
* Returns: number of extended attributes that matched, or < 0 on error
|
||||
*/
|
||||
static int aa_xattrs_match(const struct linux_binprm *bprm,
|
||||
struct aa_profile *profile, unsigned int state)
|
||||
struct aa_profile *profile, aa_state_t state)
|
||||
{
|
||||
int i;
|
||||
struct dentry *d;
|
||||
char *value = NULL;
|
||||
int size, value_size = 0, ret = profile->xattr_count;
|
||||
struct aa_attachment *attach = &profile->attach;
|
||||
int size, value_size = 0, ret = attach->xattr_count;
|
||||
|
||||
if (!bprm || !profile->xattr_count)
|
||||
if (!bprm || !attach->xattr_count)
|
||||
return 0;
|
||||
might_sleep();
|
||||
|
||||
/* transition from exec match to xattr set */
|
||||
state = aa_dfa_outofband_transition(profile->xmatch, state);
|
||||
state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
|
||||
d = bprm->file->f_path.dentry;
|
||||
|
||||
for (i = 0; i < profile->xattr_count; i++) {
|
||||
size = vfs_getxattr_alloc(&init_user_ns, d, profile->xattrs[i],
|
||||
for (i = 0; i < attach->xattr_count; i++) {
|
||||
size = vfs_getxattr_alloc(&init_user_ns, d, attach->xattrs[i],
|
||||
&value, value_size, GFP_KERNEL);
|
||||
if (size >= 0) {
|
||||
u32 perm;
|
||||
u32 index, perm;
|
||||
|
||||
/*
|
||||
* Check the xattr presence before value. This ensure
|
||||
* that not present xattr can be distinguished from a 0
|
||||
* length value or rule that matches any value
|
||||
*/
|
||||
state = aa_dfa_null_transition(profile->xmatch, state);
|
||||
state = aa_dfa_null_transition(attach->xmatch.dfa,
|
||||
state);
|
||||
/* Check xattr value */
|
||||
state = aa_dfa_match_len(profile->xmatch, state, value,
|
||||
size);
|
||||
perm = dfa_user_allow(profile->xmatch, state);
|
||||
state = aa_dfa_match_len(attach->xmatch.dfa, state,
|
||||
value, size);
|
||||
index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
|
||||
perm = attach->xmatch.perms[index].allow;
|
||||
if (!(perm & MAY_EXEC)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* transition to next element */
|
||||
state = aa_dfa_outofband_transition(profile->xmatch, state);
|
||||
state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
|
||||
if (size < 0) {
|
||||
/*
|
||||
* No xattr match, so verify if transition to
|
||||
|
@ -397,6 +388,8 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
|
|||
rcu_read_lock();
|
||||
restart:
|
||||
list_for_each_entry_rcu(profile, head, base.list) {
|
||||
struct aa_attachment *attach = &profile->attach;
|
||||
|
||||
if (profile->label.flags & FLAG_NULL &&
|
||||
&profile->label == ns_unconfined(profile->ns))
|
||||
continue;
|
||||
|
@ -412,13 +405,16 @@ restart:
|
|||
* as another profile, signal a conflict and refuse to
|
||||
* match.
|
||||
*/
|
||||
if (profile->xmatch) {
|
||||
unsigned int state, count;
|
||||
u32 perm;
|
||||
if (attach->xmatch.dfa) {
|
||||
unsigned int count;
|
||||
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);
|
||||
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. */
|
||||
if (perm & MAY_EXEC) {
|
||||
int ret = 0;
|
||||
|
@ -426,7 +422,7 @@ restart:
|
|||
if (count < candidate_len)
|
||||
continue;
|
||||
|
||||
if (bprm && profile->xattr_count) {
|
||||
if (bprm && attach->xattr_count) {
|
||||
long rev = READ_ONCE(ns->revision);
|
||||
|
||||
if (!aa_get_profile_not0(profile))
|
||||
|
@ -465,7 +461,7 @@ restart:
|
|||
* xattrs, or a longer match
|
||||
*/
|
||||
candidate = profile;
|
||||
candidate_len = max(count, profile->xmatch_len);
|
||||
candidate_len = max(count, attach->xmatch_len);
|
||||
candidate_xattrs = ret;
|
||||
conflict = false;
|
||||
}
|
||||
|
@ -509,6 +505,8 @@ static const char *next_name(int xtype, const char *name)
|
|||
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
|
||||
const char **name)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_label *label = NULL;
|
||||
u32 xtype = xindex & AA_X_TYPE_MASK;
|
||||
int index = xindex & AA_X_INDEX_MASK;
|
||||
|
@ -519,7 +517,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
|
|||
/* TODO: move lookup parsing to unpack time so this is a straight
|
||||
* index into the resultant label
|
||||
*/
|
||||
for (*name = profile->file.trans.table[index]; !label && *name;
|
||||
for (*name = rules->file.trans.table[index]; !label && *name;
|
||||
*name = next_name(xtype, *name)) {
|
||||
if (xindex & AA_X_CHILD) {
|
||||
struct aa_profile *new_profile;
|
||||
|
@ -558,6 +556,8 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
|
|||
const char **lookupname,
|
||||
const char **info)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_label *new = NULL;
|
||||
struct aa_ns *ns = profile->ns;
|
||||
u32 xtype = xindex & AA_X_TYPE_MASK;
|
||||
|
@ -570,7 +570,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
|
|||
break;
|
||||
case AA_X_TABLE:
|
||||
/* TODO: fix when perm mapping done at unload */
|
||||
stack = profile->file.trans.table[xindex & AA_X_INDEX_MASK];
|
||||
stack = rules->file.trans.table[xindex & AA_X_INDEX_MASK];
|
||||
if (*stack != '&') {
|
||||
/* released by caller */
|
||||
new = x_table_lookup(profile, xindex, lookupname);
|
||||
|
@ -624,9 +624,11 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
|
|||
char *buffer, struct path_cond *cond,
|
||||
bool *secure_exec)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_label *new = NULL;
|
||||
const char *info = NULL, *name = NULL, *target = NULL;
|
||||
unsigned int state = profile->file.start;
|
||||
aa_state_t state = rules->file.start[AA_CLASS_FILE];
|
||||
struct aa_perms perms = {};
|
||||
bool nonewprivs = false;
|
||||
int error = 0;
|
||||
|
@ -660,7 +662,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
|
|||
}
|
||||
|
||||
/* find exec permissions for name */
|
||||
state = aa_str_perms(profile->file.dfa, state, name, cond, &perms);
|
||||
state = aa_str_perms(&(rules->file), state, name, cond, &perms);
|
||||
if (perms.allow & MAY_EXEC) {
|
||||
/* exec permission determine how to transition */
|
||||
new = x_to_label(profile, bprm, name, perms.xindex, &target,
|
||||
|
@ -678,7 +680,7 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
|
|||
/* no exec permission - learning mode */
|
||||
struct aa_profile *new_profile = NULL;
|
||||
|
||||
new_profile = aa_new_null_profile(profile, false, name,
|
||||
new_profile = aa_new_learning_profile(profile, false, name,
|
||||
GFP_KERNEL);
|
||||
if (!new_profile) {
|
||||
error = -ENOMEM;
|
||||
|
@ -722,7 +724,9 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
|
|||
char *buffer, struct path_cond *cond,
|
||||
bool *secure_exec)
|
||||
{
|
||||
unsigned int state = profile->file.start;
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
aa_state_t state = rules->file.start[AA_CLASS_FILE];
|
||||
struct aa_perms perms = {};
|
||||
const char *xname = NULL, *info = "change_profile onexec";
|
||||
int error = -EACCES;
|
||||
|
@ -755,7 +759,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
|
|||
}
|
||||
|
||||
/* find exec permissions for name */
|
||||
state = aa_str_perms(profile->file.dfa, state, xname, cond, &perms);
|
||||
state = aa_str_perms(&(rules->file), state, xname, cond, &perms);
|
||||
if (!(perms.allow & AA_MAY_ONEXEC)) {
|
||||
info = "no change_onexec valid for executable";
|
||||
goto audit;
|
||||
|
@ -764,7 +768,7 @@ static int profile_onexec(struct aa_profile *profile, struct aa_label *onexec,
|
|||
* onexec permission is linked to exec with a standard pairing
|
||||
* exec\0change_profile
|
||||
*/
|
||||
state = aa_dfa_null_transition(profile->file.dfa, state);
|
||||
state = aa_dfa_null_transition(rules->file.dfa, state);
|
||||
error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
|
||||
state, &perms);
|
||||
if (error) {
|
||||
|
@ -1004,7 +1008,7 @@ static struct aa_label *build_change_hat(struct aa_profile *profile,
|
|||
if (!hat) {
|
||||
error = -ENOENT;
|
||||
if (COMPLAIN_MODE(profile)) {
|
||||
hat = aa_new_null_profile(profile, true, name,
|
||||
hat = aa_new_learning_profile(profile, true, name,
|
||||
GFP_KERNEL);
|
||||
if (!hat) {
|
||||
info = "failed null profile create";
|
||||
|
@ -1261,12 +1265,15 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
|
|||
struct aa_label *target, bool stack,
|
||||
u32 request, struct aa_perms *perms)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
const char *info = NULL;
|
||||
int error = 0;
|
||||
|
||||
if (!error)
|
||||
error = change_profile_perms(profile, target, stack, request,
|
||||
profile->file.start, perms);
|
||||
rules->file.start[AA_CLASS_FILE],
|
||||
perms);
|
||||
if (error)
|
||||
error = aa_audit_file(profile, perms, op, request, name,
|
||||
NULL, target, GLOBAL_ROOT_UID, info,
|
||||
|
@ -1353,7 +1360,7 @@ int aa_change_profile(const char *fqname, int flags)
|
|||
!COMPLAIN_MODE(labels_profile(label)))
|
||||
goto audit;
|
||||
/* released below */
|
||||
tprofile = aa_new_null_profile(labels_profile(label), false,
|
||||
tprofile = aa_new_learning_profile(labels_profile(label), false,
|
||||
fqname, GFP_KERNEL);
|
||||
if (!tprofile) {
|
||||
info = "failed null profile create";
|
||||
|
|
|
@ -95,7 +95,7 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
|
|||
kuid_t ouid, const char *info, int error)
|
||||
{
|
||||
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;
|
||||
aad(&sa)->request = request;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
const struct path *path, int flags, char *buffer,
|
||||
const char **name, struct path_cond *cond, u32 request)
|
||||
|
@ -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
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* aa_compute_fperms - convert dfa compressed perms to internal perms
|
||||
* @dfa: dfa to compute perms for (NOT NULL)
|
||||
* aa_lookup_fperms - convert dfa compressed perms to internal perms
|
||||
* @dfa: dfa to lookup perms for (NOT NULL)
|
||||
* @state: state in dfa
|
||||
* @cond: conditions to consider (NOT NULL)
|
||||
*
|
||||
* TODO: convert from dfa + state to permission entry, do computation conversion
|
||||
* at load time.
|
||||
* TODO: convert from dfa + state to permission entry
|
||||
*
|
||||
* 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 path_cond *cond)
|
||||
struct aa_perms default_perms = {};
|
||||
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
|
||||
* 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 = { };
|
||||
unsigned int index = ACCEPT_TABLE(file_rules->dfa)[state];
|
||||
|
||||
if (uid_eq(current_fsuid(), cond->uid)) {
|
||||
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);
|
||||
} 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;
|
||||
if (!(file_rules->perms))
|
||||
return &default_perms;
|
||||
|
||||
/* 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;
|
||||
if (uid_eq(current_fsuid(), cond->uid))
|
||||
return &(file_rules->perms[index]);
|
||||
|
||||
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,
|
|||
*
|
||||
* 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,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
unsigned int state;
|
||||
state = aa_dfa_match(dfa, start, name);
|
||||
*perms = aa_compute_fperms(dfa, state, cond);
|
||||
aa_state_t state;
|
||||
state = aa_dfa_match(file_rules->dfa, start, name);
|
||||
*perms = *(aa_lookup_fperms(file_rules, state, cond));
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
|
||||
u32 request, struct path_cond *cond, int flags,
|
||||
static 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)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
int e = 0;
|
||||
|
||||
if (profile_unconfined(profile))
|
||||
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)
|
||||
e = -EACCES;
|
||||
return aa_audit_file(profile, perms, op, request, name, NULL, NULL,
|
||||
|
@ -360,11 +306,13 @@ static int profile_path_link(struct aa_profile *profile,
|
|||
const struct path *target, char *buffer2,
|
||||
struct path_cond *cond)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
const char *lname, *tname = NULL;
|
||||
struct aa_perms lperms = {}, perms;
|
||||
const char *info = NULL;
|
||||
u32 request = AA_MAY_LINK;
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
int error;
|
||||
|
||||
error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
|
||||
|
@ -380,15 +328,16 @@ static int profile_path_link(struct aa_profile *profile,
|
|||
|
||||
error = -EACCES;
|
||||
/* 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);
|
||||
|
||||
if (!(lperms.allow & AA_MAY_LINK))
|
||||
goto audit;
|
||||
|
||||
/* test to see if target can be paired with link */
|
||||
state = aa_dfa_null_transition(profile->file.dfa, state);
|
||||
aa_str_perms(profile->file.dfa, state, tname, cond, &perms);
|
||||
state = aa_dfa_null_transition(rules->file.dfa, state);
|
||||
aa_str_perms(&(rules->file), state, tname, cond, &perms);
|
||||
|
||||
/* force audit/quiet masks for link are stored in the second entry
|
||||
* in the link pair.
|
||||
|
@ -410,8 +359,8 @@ static int profile_path_link(struct aa_profile *profile,
|
|||
/* Do link perm subset test requiring allowed permission on link are
|
||||
* a subset of the allowed permissions on target.
|
||||
*/
|
||||
aa_str_perms(profile->file.dfa, profile->file.start, tname, cond,
|
||||
&perms);
|
||||
aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
|
||||
tname, cond, &perms);
|
||||
|
||||
/* AA_MAY_LINK is not considered in the subset test */
|
||||
request = lperms.allow & ~AA_MAY_LINK;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
/*
|
||||
* 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_FILE 2
|
||||
#define AA_CLASS_CAP 3
|
||||
|
@ -26,10 +26,18 @@
|
|||
#define AA_CLASS_MOUNT 7
|
||||
#define AA_CLASS_PTRACE 9
|
||||
#define AA_CLASS_SIGNAL 10
|
||||
#define AA_CLASS_XMATCH 11
|
||||
#define AA_CLASS_NET 14
|
||||
#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 */
|
||||
extern enum audit_mode aa_g_audit;
|
||||
|
@ -43,4 +51,15 @@ extern bool aa_g_logsyscall;
|
|||
extern bool aa_g_paranoid_load;
|
||||
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 */
|
||||
|
|
|
@ -107,6 +107,7 @@ enum audit_type {
|
|||
struct apparmor_audit_data {
|
||||
int error;
|
||||
int type;
|
||||
u16 class;
|
||||
const char *op;
|
||||
struct aa_label *label;
|
||||
const char *name;
|
||||
|
@ -155,9 +156,12 @@ struct apparmor_audit_data {
|
|||
|
||||
/* macros for dealing with apparmor_audit_data structure */
|
||||
#define aad(SA) ((SA)->apparmor_audit_data)
|
||||
#define DEFINE_AUDIT_DATA(NAME, T, X) \
|
||||
#define DEFINE_AUDIT_DATA(NAME, T, C, X) \
|
||||
/* 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 = \
|
||||
{ \
|
||||
.type = (T), \
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
* __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
|
||||
*
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
#ifndef __AA_DOMAIN_H
|
||||
#define __AA_DOMAIN_H
|
||||
|
||||
struct aa_domain {
|
||||
int size;
|
||||
char **table;
|
||||
};
|
||||
|
||||
#define AA_CHANGE_NOFLAGS 0
|
||||
#define AA_CHANGE_TEST 1
|
||||
#define AA_CHANGE_CHILD 2
|
||||
|
@ -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);
|
||||
|
||||
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_profile(const char *fqname, int flags);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "match.h"
|
||||
#include "perms.h"
|
||||
|
||||
struct aa_policydb;
|
||||
struct aa_profile;
|
||||
struct path;
|
||||
|
||||
|
@ -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
|
||||
* - 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_SHIFT 10
|
||||
#define AA_X_NONE 0x0000
|
||||
#define AA_X_NAME 0x0400 /* use executable name px */
|
||||
#define AA_X_TABLE 0x0800 /* use a specified name ->n# */
|
||||
#define AA_X_TYPE_MASK 0x0c000000
|
||||
#define AA_X_NONE AA_INDEX_NONE
|
||||
#define AA_X_NAME 0x04000000 /* use executable name px */
|
||||
#define AA_X_TABLE 0x08000000 /* use a specified name ->n# */
|
||||
|
||||
#define AA_X_UNSAFE 0x1000
|
||||
#define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */
|
||||
#define AA_X_INHERIT 0x4000
|
||||
#define AA_X_UNCONFINED 0x8000
|
||||
#define AA_X_UNSAFE 0x10000000
|
||||
#define AA_X_CHILD 0x20000000
|
||||
#define AA_X_INHERIT 0x40000000
|
||||
#define AA_X_UNCONFINED 0x80000000
|
||||
|
||||
/* need to make conditional which ones are being set */
|
||||
struct path_cond {
|
||||
|
@ -108,90 +108,17 @@ struct path_cond {
|
|||
|
||||
#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,
|
||||
const char *op, u32 request, const char *name,
|
||||
const char *target, struct aa_label *tlabel, kuid_t ouid,
|
||||
const char *info, int error);
|
||||
|
||||
/**
|
||||
* struct aa_file_rules - components used for file rule permissions
|
||||
* @dfa: dfa to match path names and conditionals against
|
||||
* @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,
|
||||
struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules,
|
||||
aa_state_t state, struct path_cond *cond);
|
||||
aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start,
|
||||
const char *name, struct path_cond *cond,
|
||||
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,
|
||||
const struct path *path, int flags, u32 request,
|
||||
struct path_cond *cond);
|
||||
|
@ -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);
|
||||
|
||||
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
|
||||
|
|
|
@ -261,7 +261,7 @@ for ((I).i = (I).j = 0; \
|
|||
struct label_it i; \
|
||||
int ret = 0; \
|
||||
label_for_each(i, (L), profile) { \
|
||||
if (PROFILE_MEDIATES(profile, (C))) { \
|
||||
if (RULE_MEDIATES(&profile->rules, (C))) { \
|
||||
ret = 1; \
|
||||
break; \
|
||||
} \
|
||||
|
@ -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)
|
||||
{
|
||||
const char *pos;
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
|
||||
state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos);
|
||||
if (!ACCEPT_TABLE(stacksplitdfa)[state])
|
||||
|
@ -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)
|
||||
{
|
||||
const char *pos;
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
|
||||
state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos);
|
||||
if (!ACCEPT_TABLE(stacksplitdfa)[state])
|
||||
|
@ -357,9 +357,10 @@ static inline const char *aa_label_str_split(const char *str)
|
|||
|
||||
|
||||
struct aa_perms;
|
||||
int aa_label_match(struct aa_profile *profile, struct aa_label *label,
|
||||
unsigned int state, bool subns, u32 request,
|
||||
struct aa_perms *perms);
|
||||
struct aa_ruleset;
|
||||
int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
|
||||
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)
|
|||
* character which is not used in standard matching and is only
|
||||
* used to separate pairs.
|
||||
*/
|
||||
static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
|
||||
unsigned int start)
|
||||
static inline aa_state_t aa_dfa_null_transition(struct aa_dfa *dfa,
|
||||
aa_state_t start)
|
||||
{
|
||||
/* the null transition only needs the string's null terminator byte */
|
||||
return aa_dfa_next(dfa, start, 0);
|
||||
|
@ -99,6 +99,12 @@ static inline bool path_mediated_fs(struct dentry *dentry)
|
|||
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 kref count;
|
||||
|
|
|
@ -125,18 +125,18 @@ static inline size_t table_size(size_t len, size_t el_size)
|
|||
int aa_setup_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);
|
||||
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);
|
||||
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);
|
||||
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
|
||||
const char c);
|
||||
unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa,
|
||||
unsigned int state);
|
||||
unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
|
||||
aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c);
|
||||
aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state);
|
||||
aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
|
||||
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);
|
||||
|
||||
void aa_dfa_free_kref(struct kref *kref);
|
||||
|
@ -156,7 +156,7 @@ struct match_workbuf N = { \
|
|||
.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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,6 +59,7 @@ struct aa_sk_ctx {
|
|||
DEFINE_AUDIT_DATA(NAME, \
|
||||
((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \
|
||||
LSM_AUDIT_DATA_NONE, \
|
||||
AA_CLASS_NET, \
|
||||
OP); \
|
||||
NAME.u.net = &(NAME ## _net); \
|
||||
aad(&NAME)->net.type = (T); \
|
||||
|
|
|
@ -65,29 +65,90 @@ extern const char *aa_file_perm_names[];
|
|||
|
||||
struct aa_perms {
|
||||
u32 allow;
|
||||
u32 audit; /* set only when allow is 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 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 */
|
||||
|
||||
/* Reserved:
|
||||
* u32 subtree; / * set only when allow is set * /
|
||||
*/
|
||||
u16 xindex;
|
||||
u32 audit; /* set only when allow is set */
|
||||
u32 quiet; /* set only when ~allow | deny */
|
||||
u32 hide; /* set only when ~allow | deny */
|
||||
|
||||
|
||||
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
|
||||
extern struct aa_perms nullperms;
|
||||
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) \
|
||||
({ \
|
||||
|
@ -133,6 +194,9 @@ extern struct aa_perms allperms;
|
|||
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,
|
||||
u32 mask);
|
||||
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,
|
|||
u32 chrsmask, const char * const *names, u32 namesmask);
|
||||
void aa_apply_modes_to_perms(struct aa_profile *profile,
|
||||
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_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 aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
|
||||
u32 request, int type, u32 *deny,
|
||||
|
|
|
@ -44,6 +44,8 @@ extern const char *const aa_profile_mode_names[];
|
|||
|
||||
#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 PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT)
|
||||
|
@ -67,20 +69,47 @@ enum profile_mode {
|
|||
APPARMOR_COMPLAIN, /* allow and log access violations */
|
||||
APPARMOR_KILL, /* kill task on access violation */
|
||||
APPARMOR_UNCONFINED, /* profile set to unconfined */
|
||||
APPARMOR_USER, /* modified complain mode to userspace */
|
||||
};
|
||||
|
||||
|
||||
/* struct aa_policydb - match engine for a policy
|
||||
* 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
|
||||
*/
|
||||
struct aa_policydb {
|
||||
/* Generic policy DFA specific rule types will be subsections of it */
|
||||
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
|
||||
* key: name for retrieving this data
|
||||
* size: size of data in bytes
|
||||
|
@ -94,6 +123,47 @@ struct aa_data {
|
|||
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
|
||||
* @base - base components of the profile (name, refcount, lists, lock ...)
|
||||
|
@ -101,18 +171,13 @@ struct aa_data {
|
|||
* @parent: parent of profile
|
||||
* @ns: namespace the profile is in
|
||||
* @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
|
||||
* @mode: the enforcement mode of the profile
|
||||
* @path_flags: flags controlling path generation behavior
|
||||
* @disconnected: what to prepend if attach_disconnected is specified
|
||||
* @size: the memory consumed by this profiles rules
|
||||
* @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
|
||||
* @attach: attachment rules for the profile
|
||||
* @rules: rules to be enforced
|
||||
*
|
||||
* @dents: dentries for the profiles file entries in apparmorfs
|
||||
* @dirname: name of the profile dir in apparmorfs
|
||||
|
@ -137,26 +202,13 @@ struct aa_profile {
|
|||
struct aa_ns *ns;
|
||||
const char *rename;
|
||||
|
||||
const char *attach;
|
||||
struct aa_dfa *xmatch;
|
||||
unsigned int xmatch_len;
|
||||
enum audit_mode audit;
|
||||
long mode;
|
||||
u32 path_flags;
|
||||
const char *disconnected;
|
||||
int size;
|
||||
|
||||
struct aa_policydb policy;
|
||||
struct aa_file_rules file;
|
||||
struct aa_caps caps;
|
||||
|
||||
int xattr_count;
|
||||
char **xattrs;
|
||||
|
||||
struct aa_rlimit rlimits;
|
||||
|
||||
int secmark_count;
|
||||
struct aa_secmark *secmark;
|
||||
struct aa_attachment attach;
|
||||
struct list_head rules;
|
||||
|
||||
struct aa_loaddata *rawdata;
|
||||
unsigned char *hash;
|
||||
|
@ -179,9 +231,12 @@ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
|
|||
|
||||
|
||||
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,
|
||||
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);
|
||||
void aa_free_profile(struct aa_profile *profile);
|
||||
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)
|
|||
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)
|
||||
{
|
||||
if (class <= AA_CLASS_LAST)
|
||||
return profile->policy.start[class];
|
||||
return rules->policy.start[class];
|
||||
else
|
||||
return aa_dfa_match_len(profile->policy.dfa,
|
||||
profile->policy.start[0], &class, 1);
|
||||
return aa_dfa_match_len(rules->policy.dfa,
|
||||
rules->policy.start[0], &class, 1);
|
||||
}
|
||||
|
||||
static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
|
||||
u16 AF) {
|
||||
unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
|
||||
static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
|
||||
{
|
||||
aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NET);
|
||||
__be16 be_af = cpu_to_be16(AF);
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2);
|
||||
return DFA_NOMATCH;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* 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 @@
|
|||
#include <linux/dcache.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
|
||||
struct aa_load_ent {
|
||||
struct list_head list;
|
||||
struct aa_profile *new;
|
||||
|
@ -35,6 +36,7 @@ struct aa_load_ent *aa_load_ent_alloc(void);
|
|||
#define PACKED_MODE_COMPLAIN 1
|
||||
#define PACKED_MODE_KILL 2
|
||||
#define PACKED_MODE_UNCONFINED 3
|
||||
#define PACKED_MODE_USER 4
|
||||
|
||||
struct aa_ns;
|
||||
|
||||
|
@ -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_u32(struct aa_ext *e, u32 *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);
|
||||
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);
|
||||
|
|
|
@ -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)
|
||||
* @va: audit struct to audit values of (NOT NULL)
|
||||
*/
|
||||
|
@ -78,19 +78,21 @@ static int profile_signal_perm(struct aa_profile *profile,
|
|||
struct aa_label *peer, u32 request,
|
||||
struct common_audit_data *sa)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms perms;
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
|
||||
if (profile_unconfined(profile) ||
|
||||
!PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
|
||||
!ANY_RULE_MEDIATES(&profile->rules, AA_CLASS_SIGNAL))
|
||||
return 0;
|
||||
|
||||
aad(sa)->peer = peer;
|
||||
/* TODO: secondary cache check <profile, profile, perm> */
|
||||
state = aa_dfa_next(profile->policy.dfa,
|
||||
profile->policy.start[AA_CLASS_SIGNAL],
|
||||
state = aa_dfa_next(rules->policy.dfa,
|
||||
rules->policy.start[AA_CLASS_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);
|
||||
return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
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)->unmappedsig = sig;
|
||||
|
|
|
@ -197,15 +197,18 @@ static bool vec_is_stale(struct aa_profile **vec, int n)
|
|||
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;
|
||||
|
||||
AA_BUG(!vec);
|
||||
|
||||
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;
|
||||
|
@ -1097,8 +1100,7 @@ static struct aa_label *label_merge_insert(struct aa_label *new,
|
|||
else if (k == b->size)
|
||||
return aa_get_label(b);
|
||||
}
|
||||
new->flags |= union_vec_flags(new->vec, new->size, FLAG_UNCONFINED |
|
||||
FLAG_DEBUG1 | FLAG_DEBUG2);
|
||||
new->flags |= accum_vec_flags(new->vec, new->size);
|
||||
ls = labels_set(new);
|
||||
write_lock_irqsave(&ls->lock, flags);
|
||||
label = __label_insert(labels_set(new), new, false);
|
||||
|
@ -1254,32 +1256,27 @@ out:
|
|||
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
|
||||
* Assumes visibility test has already been done.
|
||||
* If a subns profile is not to be matched should be prescreened with
|
||||
* visibility test.
|
||||
*/
|
||||
static inline unsigned int match_component(struct aa_profile *profile,
|
||||
static inline aa_state_t match_component(struct aa_profile *profile,
|
||||
struct aa_ruleset *rules,
|
||||
struct aa_profile *tp,
|
||||
unsigned int state)
|
||||
aa_state_t state)
|
||||
{
|
||||
const char *ns_name;
|
||||
|
||||
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 */
|
||||
ns_name = aa_ns_name(profile->ns, tp->ns, true);
|
||||
state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
|
||||
state = aa_dfa_match(profile->policy.dfa, state, ns_name);
|
||||
state = aa_dfa_match_len(profile->policy.dfa, state, ":", 1);
|
||||
return aa_dfa_match(profile->policy.dfa, state, tp->base.hname);
|
||||
state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
|
||||
state = aa_dfa_match(rules->policy.dfa, state, ns_name);
|
||||
state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
|
||||
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,
|
|||
* check to be stacked.
|
||||
*/
|
||||
static int label_compound_match(struct aa_profile *profile,
|
||||
struct aa_ruleset *rules,
|
||||
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_profile *tp;
|
||||
|
@ -1309,7 +1307,7 @@ static int label_compound_match(struct aa_profile *profile,
|
|||
label_for_each(i, label, tp) {
|
||||
if (!aa_ns_visible(profile->ns, tp->ns, subns))
|
||||
continue;
|
||||
state = match_component(profile, tp, state);
|
||||
state = match_component(profile, rules, tp, state);
|
||||
if (!state)
|
||||
goto fail;
|
||||
goto next;
|
||||
|
@ -1323,12 +1321,12 @@ next:
|
|||
label_for_each_cont(i, label, tp) {
|
||||
if (!aa_ns_visible(profile->ns, tp->ns, subns))
|
||||
continue;
|
||||
state = aa_dfa_match(profile->policy.dfa, state, "//&");
|
||||
state = match_component(profile, tp, state);
|
||||
state = aa_dfa_match(rules->policy.dfa, state, "//&");
|
||||
state = match_component(profile, rules, tp, state);
|
||||
if (!state)
|
||||
goto fail;
|
||||
}
|
||||
aa_compute_perms(profile->policy.dfa, state, perms);
|
||||
*perms = *aa_lookup_perms(&rules->policy, state);
|
||||
aa_apply_modes_to_perms(profile, perms);
|
||||
if ((perms->allow & request) != request)
|
||||
return -EACCES;
|
||||
|
@ -1343,6 +1341,7 @@ fail:
|
|||
/**
|
||||
* label_components_match - find perms for all subcomponents of a label
|
||||
* @profile: profile to find perms for
|
||||
* @rules: ruleset to search
|
||||
* @label: label to check access permissions for
|
||||
* @start: state to start match in
|
||||
* @subns: whether to do permission checks on components in a subns
|
||||
|
@ -1356,20 +1355,21 @@ fail:
|
|||
* check to be stacked.
|
||||
*/
|
||||
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,
|
||||
struct aa_perms *perms)
|
||||
{
|
||||
struct aa_profile *tp;
|
||||
struct label_it i;
|
||||
struct aa_perms tmp;
|
||||
unsigned int state = 0;
|
||||
aa_state_t state = 0;
|
||||
|
||||
/* find first subcomponent to test */
|
||||
label_for_each(i, label, tp) {
|
||||
if (!aa_ns_visible(profile->ns, tp->ns, subns))
|
||||
continue;
|
||||
state = match_component(profile, tp, start);
|
||||
state = match_component(profile, rules, tp, start);
|
||||
if (!state)
|
||||
goto fail;
|
||||
goto next;
|
||||
|
@ -1379,16 +1379,16 @@ static int label_components_match(struct aa_profile *profile,
|
|||
return 0;
|
||||
|
||||
next:
|
||||
aa_compute_perms(profile->policy.dfa, state, &tmp);
|
||||
tmp = *aa_lookup_perms(&rules->policy, state);
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum(perms, &tmp);
|
||||
label_for_each_cont(i, label, tp) {
|
||||
if (!aa_ns_visible(profile->ns, tp->ns, subns))
|
||||
continue;
|
||||
state = match_component(profile, tp, start);
|
||||
state = match_component(profile, rules, tp, start);
|
||||
if (!state)
|
||||
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_perms_accum(perms, &tmp);
|
||||
}
|
||||
|
@ -1406,6 +1406,7 @@ fail:
|
|||
/**
|
||||
* aa_label_match - do a multi-component label match
|
||||
* @profile: profile to match against (NOT NULL)
|
||||
* @rules: ruleset to search
|
||||
* @label: label to match (NOT NULL)
|
||||
* @state: state to start in
|
||||
* @subns: whether to match subns components
|
||||
|
@ -1414,18 +1415,18 @@ fail:
|
|||
*
|
||||
* 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,
|
||||
unsigned int state, bool subns, u32 request,
|
||||
struct aa_perms *perms)
|
||||
int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
|
||||
struct aa_label *label, aa_state_t state, bool subns,
|
||||
u32 request, struct aa_perms *perms)
|
||||
{
|
||||
int error = label_compound_match(profile, label, state, subns, request,
|
||||
perms);
|
||||
int error = label_compound_match(profile, rules, label, state, subns,
|
||||
request, perms);
|
||||
if (!error)
|
||||
return error;
|
||||
|
||||
*perms = allperms;
|
||||
return label_components_match(profile, label, state, subns, request,
|
||||
perms);
|
||||
return label_components_match(profile, rules, label, state, subns,
|
||||
request, perms);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,25 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
|
|||
.quiet = 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
|
||||
* @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,
|
|||
void aa_info_message(const char *str)
|
||||
{
|
||||
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;
|
||||
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)
|
|||
perms->kill = ALL_PERMS_MASK;
|
||||
else if (COMPLAIN_MODE(profile))
|
||||
perms->complain = ALL_PERMS_MASK;
|
||||
/*
|
||||
* TODO:
|
||||
* else if (PROMPT_MODE(profile))
|
||||
* perms->prompt = ALL_PERMS_MASK;
|
||||
*/
|
||||
else if (USER_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)
|
||||
{
|
||||
/* TODO: doesn't yet handle extended types */
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
|
||||
state = aa_dfa_next(profile->policy.dfa,
|
||||
profile->policy.start[AA_CLASS_LABEL],
|
||||
state = aa_dfa_next(rules->policy.dfa,
|
||||
rules->policy.start[AA_CLASS_LABEL],
|
||||
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,
|
|||
u32 request, int type, u32 *deny,
|
||||
struct common_audit_data *sa)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms perms;
|
||||
|
||||
aad(sa)->label = &profile->label;
|
||||
aad(sa)->peer = &target->label;
|
||||
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);
|
||||
*deny |= request & perms.deny;
|
||||
return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/user_namespace.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/zlib.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <net/sock.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
|
@ -163,12 +163,15 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
|
|||
struct label_it i;
|
||||
|
||||
label_for_each_confined(i, label, profile) {
|
||||
struct aa_ruleset *rules;
|
||||
if (COMPLAIN_MODE(profile))
|
||||
continue;
|
||||
rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
*effective = cap_intersect(*effective,
|
||||
profile->caps.allow);
|
||||
rules->caps.allow);
|
||||
*permitted = cap_intersect(*permitted,
|
||||
profile->caps.allow);
|
||||
rules->caps.allow);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
@ -661,7 +664,8 @@ static int apparmor_setprocattr(const char *name, void *value,
|
|||
char *command, *largs = NULL, *args = value;
|
||||
size_t arg_size;
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
@ -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)
|
||||
*/
|
||||
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
|
|||
#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 = {
|
||||
.lbs_cred = sizeof(struct aa_task_ctx *),
|
||||
.lbs_cred = sizeof(struct aa_label *),
|
||||
.lbs_file = sizeof(struct aa_file_ctx),
|
||||
.lbs_task = sizeof(struct aa_task_ctx),
|
||||
};
|
||||
|
@ -1373,7 +1377,7 @@ module_param_named(export_binary, aa_g_export_binary, aabool, 0600);
|
|||
#endif
|
||||
|
||||
/* policy loaddata compression level */
|
||||
int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION;
|
||||
int aa_g_rawdata_compression_level = AA_DEFAULT_CLEVEL;
|
||||
module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
|
||||
aacompressionlevel, 0400);
|
||||
|
||||
|
@ -1555,9 +1559,8 @@ static int param_set_aacompressionlevel(const char *val,
|
|||
error = param_set_int(val, kp);
|
||||
|
||||
aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level,
|
||||
Z_NO_COMPRESSION,
|
||||
Z_BEST_COMPRESSION);
|
||||
pr_info("AppArmor: policy rawdata compression level set to %u\n",
|
||||
AA_MIN_CLEVEL, AA_MAX_CLEVEL);
|
||||
pr_info("AppArmor: policy rawdata compression level set to %d\n",
|
||||
aa_g_rawdata_compression_level);
|
||||
|
||||
return error;
|
||||
|
|
|
@ -31,7 +31,7 @@ static char stacksplitdfa_src[] = {
|
|||
};
|
||||
struct aa_dfa *stacksplitdfa;
|
||||
|
||||
int aa_setup_dfa_engine(void)
|
||||
int __init aa_setup_dfa_engine(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
|
@ -59,7 +59,7 @@ int aa_setup_dfa_engine(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void aa_teardown_dfa_engine(void)
|
||||
void __init aa_teardown_dfa_engine(void)
|
||||
{
|
||||
aa_put_dfa(stacksplitdfa);
|
||||
aa_put_dfa(nulldfa);
|
||||
|
@ -436,17 +436,17 @@ do { \
|
|||
*
|
||||
* 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)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
unsigned int state = start;
|
||||
aa_state_t state = start;
|
||||
|
||||
if (state == 0)
|
||||
return 0;
|
||||
if (state == DFA_NOMATCH)
|
||||
return DFA_NOMATCH;
|
||||
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
|
@ -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
|
||||
*/
|
||||
unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
|
||||
const char *str)
|
||||
aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
unsigned int state = start;
|
||||
aa_state_t state = start;
|
||||
|
||||
if (state == 0)
|
||||
return 0;
|
||||
if (state == DFA_NOMATCH)
|
||||
return DFA_NOMATCH;
|
||||
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
|
@ -515,8 +514,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
|
|||
*
|
||||
* Returns: state reach after input @c
|
||||
*/
|
||||
unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
|
||||
const char c)
|
||||
aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
|
@ -534,7 +532,7 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int 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);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
|
@ -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
|
||||
*/
|
||||
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)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
|
@ -572,10 +570,10 @@ unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start,
|
|||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *accept = ACCEPT_TABLE(dfa);
|
||||
unsigned int state = start, pos;
|
||||
aa_state_t state = start, pos;
|
||||
|
||||
if (state == 0)
|
||||
return 0;
|
||||
if (state == DFA_NOMATCH)
|
||||
return DFA_NOMATCH;
|
||||
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
|
@ -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
|
||||
*/
|
||||
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)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
|
@ -633,11 +631,11 @@ unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start,
|
|||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
u32 *accept = ACCEPT_TABLE(dfa);
|
||||
unsigned int state = start, pos;
|
||||
aa_state_t state = start, pos;
|
||||
|
||||
*retpos = NULL;
|
||||
if (state == 0)
|
||||
return 0;
|
||||
if (state == DFA_NOMATCH)
|
||||
return DFA_NOMATCH;
|
||||
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
|
@ -677,11 +675,11 @@ do { \
|
|||
} while (0)
|
||||
|
||||
/* 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 pos = wb->pos;
|
||||
unsigned int i;
|
||||
aa_state_t pos = wb->pos;
|
||||
aa_state_t i;
|
||||
|
||||
if (wb->history[pos] < state)
|
||||
return false;
|
||||
|
@ -700,7 +698,7 @@ static bool is_loop(struct match_workbuf *wb, unsigned int state,
|
|||
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,
|
||||
unsigned int *count)
|
||||
{
|
||||
|
@ -708,7 +706,7 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
|
|||
u32 *base = BASE_TABLE(dfa);
|
||||
u16 *next = NEXT_TABLE(dfa);
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
unsigned int state = start, pos;
|
||||
aa_state_t state = start, pos;
|
||||
|
||||
AA_BUG(!dfa);
|
||||
AA_BUG(!str);
|
||||
|
@ -716,8 +714,8 @@ static unsigned int leftmatch_fb(struct aa_dfa *dfa, unsigned int start,
|
|||
AA_BUG(!count);
|
||||
|
||||
*count = 0;
|
||||
if (state == 0)
|
||||
return 0;
|
||||
if (state == DFA_NOMATCH)
|
||||
return DFA_NOMATCH;
|
||||
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC]) {
|
||||
|
@ -781,7 +779,7 @@ out:
|
|||
*
|
||||
* 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)
|
||||
{
|
||||
DEFINE_MATCH_WB(wb);
|
||||
|
|
|
@ -134,7 +134,7 @@ static int audit_mount(struct aa_profile *profile, const char *op,
|
|||
struct aa_perms *perms, const char *info, int error)
|
||||
{
|
||||
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)) {
|
||||
u32 mask = perms->audit;
|
||||
|
@ -190,7 +190,7 @@ static int audit_mount(struct aa_profile *profile, const char *op,
|
|||
*
|
||||
* 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 int i;
|
||||
|
@ -203,25 +203,6 @@ static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int 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[] = {
|
||||
"match succeeded",
|
||||
"failed mntpnt match",
|
||||
|
@ -236,50 +217,52 @@ static const char * const mnt_info_table[] = {
|
|||
* Returns 0 on success else element that match failed in, this is the
|
||||
* 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 *type, unsigned long flags,
|
||||
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);
|
||||
|
||||
state = aa_dfa_match(dfa, start, mntpnt);
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_match(policy->dfa, start, mntpnt);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 1;
|
||||
|
||||
if (devname)
|
||||
state = aa_dfa_match(dfa, state, devname);
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_match(policy->dfa, state, devname);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 2;
|
||||
|
||||
if (type)
|
||||
state = aa_dfa_match(dfa, state, type);
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_match(policy->dfa, state, type);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 3;
|
||||
|
||||
state = match_mnt_flags(dfa, state, flags);
|
||||
state = match_mnt_flags(policy->dfa, state, flags);
|
||||
if (!state)
|
||||
return 4;
|
||||
*perms = compute_mnt_perms(dfa, state);
|
||||
*perms = *aa_lookup_perms(policy, state);
|
||||
if (perms->allow & AA_MAY_MOUNT)
|
||||
return 0;
|
||||
|
||||
/* only match data if not binary and the DFA flags data is expected */
|
||||
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)
|
||||
return 4;
|
||||
|
||||
state = aa_dfa_match(dfa, state, data);
|
||||
state = aa_dfa_match(policy->dfa, state, data);
|
||||
if (!state)
|
||||
return 5;
|
||||
*perms = compute_mnt_perms(dfa, state);
|
||||
*perms = *aa_lookup_perms(policy, state);
|
||||
if (perms->allow & AA_MAY_MOUNT)
|
||||
return 0;
|
||||
}
|
||||
|
@ -320,13 +303,15 @@ static int match_mnt_path_str(struct aa_profile *profile,
|
|||
{
|
||||
struct aa_perms perms = { };
|
||||
const char *mntpnt = NULL, *info = NULL;
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
int pos, error;
|
||||
|
||||
AA_BUG(!profile);
|
||||
AA_BUG(!mntpath);
|
||||
AA_BUG(!buffer);
|
||||
|
||||
if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
|
||||
if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
|
||||
return 0;
|
||||
|
||||
error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
|
||||
|
@ -341,8 +326,8 @@ static int match_mnt_path_str(struct aa_profile *profile,
|
|||
}
|
||||
|
||||
error = -EACCES;
|
||||
pos = do_match_mnt(profile->policy.dfa,
|
||||
profile->policy.start[AA_CLASS_MOUNT],
|
||||
pos = do_match_mnt(&rules->policy,
|
||||
rules->policy.start[AA_CLASS_MOUNT],
|
||||
mntpnt, devname, type, flags, data, binary, &perms);
|
||||
if (pos) {
|
||||
info = mnt_info_table[pos];
|
||||
|
@ -375,12 +360,14 @@ static int match_mnt(struct aa_profile *profile, const struct path *path,
|
|||
bool binary)
|
||||
{
|
||||
const char *devname = NULL, *info = NULL;
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
int error = -EACCES;
|
||||
|
||||
AA_BUG(!profile);
|
||||
AA_BUG(devpath && !devbuffer);
|
||||
|
||||
if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
|
||||
if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
|
||||
return 0;
|
||||
|
||||
if (devpath) {
|
||||
|
@ -582,15 +569,17 @@ out:
|
|||
static int profile_umount(struct aa_profile *profile, const struct path *path,
|
||||
char *buffer)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms perms = { };
|
||||
const char *name = NULL, *info = NULL;
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
int error;
|
||||
|
||||
AA_BUG(!profile);
|
||||
AA_BUG(!path);
|
||||
|
||||
if (!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
|
||||
if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
|
||||
return 0;
|
||||
|
||||
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,
|
|||
if (error)
|
||||
goto audit;
|
||||
|
||||
state = aa_dfa_match(profile->policy.dfa,
|
||||
profile->policy.start[AA_CLASS_MOUNT],
|
||||
state = aa_dfa_match(rules->policy.dfa,
|
||||
rules->policy.start[AA_CLASS_MOUNT],
|
||||
name);
|
||||
perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
perms = *aa_lookup_perms(&rules->policy, state);
|
||||
if (AA_MAY_UMOUNT & ~perms.allow)
|
||||
error = -EACCES;
|
||||
|
||||
|
@ -641,10 +630,12 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
|
|||
const struct path *old_path,
|
||||
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 *trans_name = NULL;
|
||||
struct aa_perms perms = { };
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
int error;
|
||||
|
||||
AA_BUG(!profile);
|
||||
|
@ -652,7 +643,7 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
|
|||
AA_BUG(!old_path);
|
||||
|
||||
if (profile_unconfined(profile) ||
|
||||
!PROFILE_MEDIATES(profile, AA_CLASS_MOUNT))
|
||||
!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
|
||||
return aa_get_newest_label(&profile->label);
|
||||
|
||||
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,
|
|||
goto audit;
|
||||
|
||||
error = -EACCES;
|
||||
state = aa_dfa_match(profile->policy.dfa,
|
||||
profile->policy.start[AA_CLASS_MOUNT],
|
||||
state = aa_dfa_match(rules->policy.dfa,
|
||||
rules->policy.start[AA_CLASS_MOUNT],
|
||||
new_name);
|
||||
state = aa_dfa_null_transition(profile->policy.dfa, state);
|
||||
state = aa_dfa_match(profile->policy.dfa, state, old_name);
|
||||
perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
state = aa_dfa_null_transition(rules->policy.dfa, state);
|
||||
state = aa_dfa_match(rules->policy.dfa, state, old_name);
|
||||
perms = *aa_lookup_perms(&rules->policy, state);
|
||||
|
||||
if (AA_MAY_PIVOTROOT & perms.allow)
|
||||
error = 0;
|
||||
|
|
|
@ -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,
|
||||
u32 request, u16 family, int type)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms perms = { };
|
||||
unsigned int state;
|
||||
aa_state_t state;
|
||||
__be16 buffer[2];
|
||||
|
||||
AA_BUG(family >= AF_MAX);
|
||||
|
@ -117,15 +119,15 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
|
|||
|
||||
if (profile_unconfined(profile))
|
||||
return 0;
|
||||
state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
|
||||
state = RULE_MEDIATES(rules, AA_CLASS_NET);
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
buffer[0] = cpu_to_be16(family);
|
||||
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);
|
||||
aa_compute_perms(profile->policy.dfa, state, &perms);
|
||||
perms = *aa_lookup_perms(&rules->policy, state);
|
||||
aa_apply_modes_to_perms(profile, &perms);
|
||||
|
||||
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,
|
|||
{
|
||||
int i, ret;
|
||||
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;
|
||||
|
||||
for (i = 0; i < profile->secmark_count; i++) {
|
||||
if (!profile->secmark[i].secid) {
|
||||
ret = apparmor_secmark_init(&profile->secmark[i]);
|
||||
for (i = 0; i < rules->secmark_count; i++) {
|
||||
if (!rules->secmark[i].secid) {
|
||||
ret = apparmor_secmark_init(&rules->secmark[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (profile->secmark[i].secid == secid ||
|
||||
profile->secmark[i].secid == AA_SECID_WILDCARD) {
|
||||
if (profile->secmark[i].deny)
|
||||
if (rules->secmark[i].secid == secid ||
|
||||
rules->secmark[i].secid == AA_SECID_WILDCARD) {
|
||||
if (rules->secmark[i].deny)
|
||||
perms.deny = ALL_PERMS_MASK;
|
||||
else
|
||||
perms.allow = ALL_PERMS_MASK;
|
||||
|
||||
if (profile->secmark[i].audit)
|
||||
if (rules->secmark[i].audit)
|
||||
perms.audit = ALL_PERMS_MASK;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ const char *const aa_profile_mode_names[] = {
|
|||
"complain",
|
||||
"kill",
|
||||
"unconfined",
|
||||
"user",
|
||||
};
|
||||
|
||||
|
||||
|
@ -192,6 +193,42 @@ static void aa_free_data(void *ptr, void *arg)
|
|||
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
|
||||
* @profile: the profile to free (MAYBE NULL)
|
||||
|
@ -204,8 +241,8 @@ static void aa_free_data(void *ptr, void *arg)
|
|||
*/
|
||||
void aa_free_profile(struct aa_profile *profile)
|
||||
{
|
||||
struct aa_ruleset *rule, *tmp;
|
||||
struct rhashtable *rht;
|
||||
int i;
|
||||
|
||||
AA_DEBUG("%s(%p)\n", __func__, profile);
|
||||
|
||||
|
@ -219,19 +256,17 @@ void aa_free_profile(struct aa_profile *profile)
|
|||
aa_put_ns(profile->ns);
|
||||
kfree_sensitive(profile->rename);
|
||||
|
||||
aa_free_file_rules(&profile->file);
|
||||
aa_free_cap_rules(&profile->caps);
|
||||
aa_free_rlimit_rules(&profile->rlimits);
|
||||
free_attachment(&profile->attach);
|
||||
|
||||
for (i = 0; i < profile->xattr_count; i++)
|
||||
kfree_sensitive(profile->xattrs[i]);
|
||||
kfree_sensitive(profile->xattrs);
|
||||
for (i = 0; i < profile->secmark_count; i++)
|
||||
kfree_sensitive(profile->secmark[i].label);
|
||||
kfree_sensitive(profile->secmark);
|
||||
/*
|
||||
* at this point there are no tasks that can have a reference
|
||||
* to rules
|
||||
*/
|
||||
list_for_each_entry_safe(rule, tmp, &profile->rules, list) {
|
||||
list_del_init(&rule->list);
|
||||
free_ruleset(rule);
|
||||
}
|
||||
kfree_sensitive(profile->dirname);
|
||||
aa_put_dfa(profile->xmatch);
|
||||
aa_put_dfa(profile->policy.dfa);
|
||||
|
||||
if (profile->data) {
|
||||
rht = profile->data;
|
||||
|
@ -258,6 +293,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
|
|||
gfp_t gfp)
|
||||
{
|
||||
struct aa_profile *profile;
|
||||
struct aa_ruleset *rules;
|
||||
|
||||
/* freed by free_profile - usually through aa_put_profile */
|
||||
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,
|
|||
if (!aa_label_init(&profile->label, 1, gfp))
|
||||
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 */
|
||||
if (!proxy) {
|
||||
proxy = aa_alloc_proxy(&profile->label, gfp);
|
||||
|
@ -380,6 +424,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
|
|||
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
|
||||
* @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,
|
|||
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)
|
||||
* @hat: true if the null- learning profile is a hat
|
||||
* @base: name to base the null profile off of
|
||||
|
@ -499,7 +622,7 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
|
|||
*
|
||||
* 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)
|
||||
{
|
||||
struct aa_profile *p, *profile;
|
||||
|
@ -531,21 +654,12 @@ name:
|
|||
if (profile)
|
||||
goto out;
|
||||
|
||||
profile = aa_alloc_profile(name, NULL, gfp);
|
||||
profile = aa_alloc_null(parent, name, gfp);
|
||||
if (!profile)
|
||||
goto fail;
|
||||
|
||||
profile->mode = APPARMOR_COMPLAIN;
|
||||
profile->label.flags |= FLAG_NULL;
|
||||
if (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);
|
||||
p = __find_child(&parent->base.profiles, bname);
|
||||
|
@ -618,7 +732,7 @@ static int audit_policy(struct aa_label *label, const char *op,
|
|||
const char *ns_name, const char *name,
|
||||
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)->name = name;
|
||||
|
@ -970,6 +1084,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
|
|||
/* setup parent and ns info */
|
||||
list_for_each_entry(ent, &lh, list) {
|
||||
struct aa_policy *policy;
|
||||
struct aa_profile *p;
|
||||
|
||||
if (aa_g_export_binary)
|
||||
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,
|
|||
continue;
|
||||
|
||||
/* no ref on policy only use inside lock */
|
||||
p = NULL;
|
||||
policy = __lookup_parent(ns, ent->new->base.hname);
|
||||
if (!policy) {
|
||||
struct aa_profile *p;
|
||||
/* first check for parent in the load set */
|
||||
p = __list_lookup_parent(&lh, ent->new);
|
||||
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;
|
||||
info = "parent does not exist";
|
||||
goto fail_lock;
|
||||
}
|
||||
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
|
||||
} else if (policy != &ns->base) {
|
||||
/* released on profile replacement or free_profile */
|
||||
struct aa_profile *p = (struct aa_profile *) policy;
|
||||
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
|
||||
}
|
||||
}
|
||||
if (!p && policy != &ns->base)
|
||||
/* released on profile replacement or free_profile */
|
||||
p = (struct aa_profile *) policy;
|
||||
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
|
||||
}
|
||||
|
||||
/* create new fs entries for introspection if needed */
|
||||
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,
|
|||
|
||||
if (!name) {
|
||||
/* 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_remove_ns(ns);
|
||||
mutex_unlock(&ns->parent->lock);
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
// 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)
|
|||
{
|
||||
struct aa_profile *profile;
|
||||
|
||||
profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
|
||||
profile = aa_alloc_null(NULL, name, GFP_KERNEL);
|
||||
if (!profile)
|
||||
return NULL;
|
||||
|
||||
profile->label.flags |= FLAG_IX_ON_NAME_ERROR |
|
||||
FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
|
||||
profile->mode = APPARMOR_UNCONFINED;
|
||||
profile->file.dfa = aa_get_dfa(nulldfa);
|
||||
profile->policy.dfa = aa_get_dfa(nulldfa);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
@ -134,7 +132,7 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
|
|||
return ns;
|
||||
|
||||
fail_unconfined:
|
||||
kfree_sensitive(ns->base.hname);
|
||||
aa_policy_destroy(&ns->base);
|
||||
fail_ns:
|
||||
kfree_sensitive(ns);
|
||||
return NULL;
|
||||
|
|
|
@ -17,26 +17,18 @@
|
|||
#include <kunit/visibility.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/zlib.h>
|
||||
#include <linux/zstd.h>
|
||||
|
||||
#include "include/apparmor.h"
|
||||
#include "include/audit.h"
|
||||
#include "include/cred.h"
|
||||
#include "include/crypto.h"
|
||||
#include "include/file.h"
|
||||
#include "include/match.h"
|
||||
#include "include/path.h"
|
||||
#include "include/policy.h"
|
||||
#include "include/policy_unpack.h"
|
||||
|
||||
#define K_ABI_MASK 0x3ff
|
||||
#define FORCE_COMPLAIN_FLAG 0x800
|
||||
#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK))
|
||||
#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK))
|
||||
|
||||
#define v5 5 /* base version */
|
||||
#define v6 6 /* per entry policydb mediation check */
|
||||
#define v7 7
|
||||
#define v8 8 /* full network masking */
|
||||
#include "include/policy_compat.h"
|
||||
|
||||
/* audit callback for unpack fields */
|
||||
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,
|
|||
int error)
|
||||
{
|
||||
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)
|
||||
aad(&sa)->iface.pos = e->pos - e->start;
|
||||
aad(&sa)->iface.ns = ns_name;
|
||||
|
@ -321,22 +313,21 @@ fail:
|
|||
}
|
||||
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;
|
||||
|
||||
if (aa_unpack_nameX(e, AA_ARRAY, name)) {
|
||||
int size;
|
||||
if (!aa_inbounds(e, sizeof(u16)))
|
||||
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);
|
||||
return size;
|
||||
return true;
|
||||
}
|
||||
|
||||
fail:
|
||||
e->pos = pos;
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(aa_unpack_array);
|
||||
|
||||
|
@ -411,10 +402,11 @@ EXPORT_SYMBOL_IF_KUNIT(aa_unpack_strdup);
|
|||
/**
|
||||
* unpack_dfa - unpack a file rule dfa
|
||||
* @e: serialized data extent information (NOT NULL)
|
||||
* @flags: dfa flags to check
|
||||
*
|
||||
* 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;
|
||||
size_t size;
|
||||
|
@ -430,8 +422,6 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
|
|||
size_t sz = blob - (char *) e->start -
|
||||
((e->pos - e->start) & 7);
|
||||
size_t pad = ALIGN(sz, 8) - sz;
|
||||
int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) |
|
||||
TO_ACCEPT2_FLAG(YYTD_DATA32);
|
||||
if (aa_g_paranoid_load)
|
||||
flags |= DFA_FLAG_VERIFY_STATES;
|
||||
dfa = aa_dfa_unpack(blob + pad, size - pad, flags);
|
||||
|
@ -447,28 +437,32 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
|
|||
/**
|
||||
* unpack_trans_table - unpack a profile transition table
|
||||
* @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;
|
||||
char **table = NULL;
|
||||
|
||||
/* exec table is optional */
|
||||
if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) {
|
||||
int i, size;
|
||||
u16 size;
|
||||
int i;
|
||||
|
||||
size = aa_unpack_array(e, NULL);
|
||||
/* currently 4 exec bits and entries 0-3 are reserved iupcx */
|
||||
if (size > 16 - 4)
|
||||
if (!aa_unpack_array(e, NULL, &size))
|
||||
/*
|
||||
* 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;
|
||||
profile->file.trans.table = kcalloc(size, sizeof(char *),
|
||||
GFP_KERNEL);
|
||||
if (!profile->file.trans.table)
|
||||
table = kcalloc(size, sizeof(char *), GFP_KERNEL);
|
||||
if (!table)
|
||||
goto fail;
|
||||
|
||||
profile->file.trans.size = size;
|
||||
for (i = 0; i < size; i++) {
|
||||
char *str;
|
||||
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)
|
|||
*/
|
||||
if (!size2)
|
||||
goto fail;
|
||||
profile->file.trans.table[i] = str;
|
||||
table[i] = str;
|
||||
/* verify that name doesn't start with space */
|
||||
if (isspace(*str))
|
||||
goto fail;
|
||||
|
@ -511,11 +505,14 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
|
|||
goto fail;
|
||||
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
|
||||
goto fail;
|
||||
|
||||
strs->table = table;
|
||||
strs->size = size;
|
||||
}
|
||||
return true;
|
||||
|
||||
fail:
|
||||
aa_free_domain_entries(&profile->file.trans);
|
||||
kfree_sensitive(table);
|
||||
e->pos = saved_pos;
|
||||
return false;
|
||||
}
|
||||
|
@ -525,15 +522,17 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
|
|||
void *pos = e->pos;
|
||||
|
||||
if (aa_unpack_nameX(e, AA_STRUCT, "xattrs")) {
|
||||
int i, size;
|
||||
u16 size;
|
||||
int i;
|
||||
|
||||
size = aa_unpack_array(e, NULL);
|
||||
profile->xattr_count = size;
|
||||
profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
|
||||
if (!profile->xattrs)
|
||||
if (!aa_unpack_array(e, NULL, &size))
|
||||
goto fail;
|
||||
profile->attach.xattr_count = size;
|
||||
profile->attach.xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
|
||||
if (!profile->attach.xattrs)
|
||||
goto fail;
|
||||
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;
|
||||
}
|
||||
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
|
@ -549,27 +548,29 @@ fail:
|
|||
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;
|
||||
int i, size;
|
||||
u16 size;
|
||||
int i;
|
||||
|
||||
if (aa_unpack_nameX(e, AA_STRUCT, "secmark")) {
|
||||
size = aa_unpack_array(e, NULL);
|
||||
|
||||
profile->secmark = kcalloc(size, sizeof(struct aa_secmark),
|
||||
GFP_KERNEL);
|
||||
if (!profile->secmark)
|
||||
if (!aa_unpack_array(e, NULL, &size))
|
||||
goto fail;
|
||||
|
||||
profile->secmark_count = size;
|
||||
rules->secmark = kcalloc(size, sizeof(struct aa_secmark),
|
||||
GFP_KERNEL);
|
||||
if (!rules->secmark)
|
||||
goto fail;
|
||||
|
||||
rules->secmark_count = size;
|
||||
|
||||
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;
|
||||
if (!unpack_u8(e, &profile->secmark[i].deny, NULL))
|
||||
if (!unpack_u8(e, &rules->secmark[i].deny, NULL))
|
||||
goto fail;
|
||||
if (!aa_unpack_strdup(e, &profile->secmark[i].label, NULL))
|
||||
if (!aa_unpack_strdup(e, &rules->secmark[i].label, NULL))
|
||||
goto fail;
|
||||
}
|
||||
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
|
@ -581,39 +582,40 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
|
|||
return true;
|
||||
|
||||
fail:
|
||||
if (profile->secmark) {
|
||||
if (rules->secmark) {
|
||||
for (i = 0; i < size; i++)
|
||||
kfree(profile->secmark[i].label);
|
||||
kfree(profile->secmark);
|
||||
profile->secmark_count = 0;
|
||||
profile->secmark = NULL;
|
||||
kfree(rules->secmark[i].label);
|
||||
kfree(rules->secmark);
|
||||
rules->secmark_count = 0;
|
||||
rules->secmark = NULL;
|
||||
}
|
||||
|
||||
e->pos = pos;
|
||||
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;
|
||||
|
||||
/* rlimits are optional */
|
||||
if (aa_unpack_nameX(e, AA_STRUCT, "rlimits")) {
|
||||
int i, size;
|
||||
u16 size;
|
||||
int i;
|
||||
u32 tmp = 0;
|
||||
if (!aa_unpack_u32(e, &tmp, NULL))
|
||||
goto fail;
|
||||
profile->rlimits.mask = tmp;
|
||||
rules->rlimits.mask = tmp;
|
||||
|
||||
size = aa_unpack_array(e, NULL);
|
||||
if (size > RLIM_NLIMITS)
|
||||
if (!aa_unpack_array(e, NULL, &size) ||
|
||||
size > RLIM_NLIMITS)
|
||||
goto fail;
|
||||
for (i = 0; i < size; i++) {
|
||||
u64 tmp2 = 0;
|
||||
int a = aa_map_resource(i);
|
||||
if (!aa_unpack_u64(e, &tmp2, NULL))
|
||||
goto fail;
|
||||
profile->rlimits.limits[a].rlim_max = tmp2;
|
||||
rules->rlimits.limits[a].rlim_max = tmp2;
|
||||
}
|
||||
if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
|
||||
goto fail;
|
||||
|
@ -627,6 +629,140 @@ fail:
|
|||
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)
|
||||
{
|
||||
const char * const *key = data;
|
||||
|
@ -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)
|
||||
{
|
||||
struct aa_ruleset *rules;
|
||||
struct aa_profile *profile = NULL;
|
||||
const char *tmpname, *tmpns = NULL, *name = NULL;
|
||||
const char *info = "failed to unpack profile";
|
||||
|
@ -658,7 +795,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
struct rhashtable_params params = { 0 };
|
||||
char *key = NULL;
|
||||
struct aa_data *data;
|
||||
int i, error = -EPROTO;
|
||||
int error = -EPROTO;
|
||||
kernel_cap_t tmpcap;
|
||||
u32 tmp;
|
||||
|
||||
|
@ -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);
|
||||
if (!*ns_name) {
|
||||
info = "out of memory";
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
name = tmpname;
|
||||
}
|
||||
|
||||
profile = aa_alloc_profile(name, NULL, GFP_KERNEL);
|
||||
if (!profile)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!profile) {
|
||||
info = "out of memory";
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
rules = list_first_entry(&profile->rules, typeof(*rules), list);
|
||||
|
||||
/* profile renaming is optional */
|
||||
(void) aa_unpack_str(e, &profile->rename, "rename");
|
||||
|
||||
/* 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 */
|
||||
profile->xmatch = unpack_dfa(e);
|
||||
if (IS_ERR(profile->xmatch)) {
|
||||
error = PTR_ERR(profile->xmatch);
|
||||
profile->xmatch = NULL;
|
||||
error = unpack_pdb(e, &profile->attach.xmatch, false, false, &info);
|
||||
if (error) {
|
||||
info = "bad xmatch";
|
||||
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)) {
|
||||
info = "missing xmatch len";
|
||||
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 */
|
||||
|
@ -737,6 +884,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
} else if (tmp == PACKED_MODE_UNCONFINED) {
|
||||
profile->mode = APPARMOR_UNCONFINED;
|
||||
profile->label.flags |= FLAG_UNCONFINED;
|
||||
} else if (tmp == PACKED_MODE_USER) {
|
||||
profile->mode = APPARMOR_USER;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -757,11 +906,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
profile->path_flags = PATH_MEDIATE_DELETED;
|
||||
|
||||
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;
|
||||
if (!aa_unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
|
||||
if (!aa_unpack_u32(e, &(rules->caps.audit.cap[0]), NULL))
|
||||
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;
|
||||
if (!aa_unpack_u32(e, &tmpcap.cap[0], NULL))
|
||||
goto fail;
|
||||
|
@ -769,11 +918,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
info = "failed to unpack upper profile capabilities";
|
||||
if (aa_unpack_nameX(e, AA_STRUCT, "caps64")) {
|
||||
/* 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;
|
||||
if (!aa_unpack_u32(e, &(profile->caps.audit.cap[1]), NULL))
|
||||
if (!aa_unpack_u32(e, &(rules->caps.audit.cap[1]), NULL))
|
||||
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;
|
||||
if (!aa_unpack_u32(e, &(tmpcap.cap[1]), NULL))
|
||||
goto fail;
|
||||
|
@ -784,9 +933,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
info = "failed to unpack extended profile capabilities";
|
||||
if (aa_unpack_nameX(e, AA_STRUCT, "capsx")) {
|
||||
/* 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;
|
||||
if (!aa_unpack_u32(e, &(profile->caps.extended.cap[1]), NULL))
|
||||
if (!aa_unpack_u32(e, &(rules->caps.extended.cap[1]), NULL))
|
||||
goto fail;
|
||||
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
|
||||
goto fail;
|
||||
|
@ -797,12 +946,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (!unpack_rlimits(e, profile)) {
|
||||
if (!unpack_rlimits(e, rules)) {
|
||||
info = "failed to unpack profile rlimits";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!unpack_secmark(e, profile)) {
|
||||
if (!unpack_secmark(e, rules)) {
|
||||
info = "failed to unpack profile secmark rules";
|
||||
goto fail;
|
||||
}
|
||||
|
@ -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")) {
|
||||
/* generic policy dfa - optional and may be NULL */
|
||||
info = "failed to unpack policydb";
|
||||
profile->policy.dfa = unpack_dfa(e);
|
||||
if (IS_ERR(profile->policy.dfa)) {
|
||||
error = PTR_ERR(profile->policy.dfa);
|
||||
profile->policy.dfa = NULL;
|
||||
error = unpack_pdb(e, &rules->policy, true, false,
|
||||
&info);
|
||||
if (error)
|
||||
goto fail;
|
||||
} else if (!profile->policy.dfa) {
|
||||
error = -EPROTO;
|
||||
goto fail;
|
||||
}
|
||||
if (!aa_unpack_u32(e, &profile->policy.start[0], "start"))
|
||||
/* default start state */
|
||||
profile->policy.start[0] = DFA_START;
|
||||
/* setup class index */
|
||||
for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) {
|
||||
profile->policy.start[i] =
|
||||
aa_dfa_next(profile->policy.dfa,
|
||||
profile->policy.start[0],
|
||||
i);
|
||||
}
|
||||
/* Fixup: drop when we get rid of start array */
|
||||
if (aa_dfa_next(rules->policy.dfa, rules->policy.start[0],
|
||||
AA_CLASS_FILE))
|
||||
rules->policy.start[AA_CLASS_FILE] =
|
||||
aa_dfa_next(rules->policy.dfa,
|
||||
rules->policy.start[0],
|
||||
AA_CLASS_FILE);
|
||||
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
|
||||
goto fail;
|
||||
} else
|
||||
profile->policy.dfa = aa_get_dfa(nulldfa);
|
||||
|
||||
/* get file rules */
|
||||
profile->file.dfa = unpack_dfa(e);
|
||||
if (IS_ERR(profile->file.dfa)) {
|
||||
error = PTR_ERR(profile->file.dfa);
|
||||
profile->file.dfa = NULL;
|
||||
info = "failed to unpack profile file rules";
|
||||
goto fail;
|
||||
} else if (profile->file.dfa) {
|
||||
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";
|
||||
error = aa_compat_map_policy(&rules->policy, e->version);
|
||||
if (error) {
|
||||
info = "failed to remap policydb permission table";
|
||||
goto fail;
|
||||
}
|
||||
} else
|
||||
rules->policy.dfa = aa_get_dfa(nulldfa);
|
||||
|
||||
/* get file rules */
|
||||
error = unpack_pdb(e, &rules->file, false, true, &info);
|
||||
if (error) {
|
||||
goto fail;
|
||||
} else if (rules->file.dfa) {
|
||||
error = aa_compat_map_file(&rules->file);
|
||||
if (error) {
|
||||
info = "failed to remap file permission table";
|
||||
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")) {
|
||||
info = "out of memory";
|
||||
profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
|
||||
if (!profile->data)
|
||||
if (!profile->data) {
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
|
||||
}
|
||||
params.nelem_hint = 3;
|
||||
params.key_len = sizeof(void *);
|
||||
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)
|
|||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
kfree_sensitive(key);
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -888,6 +1031,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
if (data->size && !data->data) {
|
||||
kfree_sensitive(data->key);
|
||||
kfree_sensitive(data);
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -909,6 +1053,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
|||
return profile;
|
||||
|
||||
fail:
|
||||
if (error == 0)
|
||||
/* default error covers most cases */
|
||||
error = -EPROTO;
|
||||
if (*ns_name) {
|
||||
kfree(*ns_name);
|
||||
*ns_name = NULL;
|
||||
}
|
||||
if (profile)
|
||||
name = NULL;
|
||||
else if (!name)
|
||||
|
@ -946,7 +1097,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
|
|||
* if not specified use previous version
|
||||
* Mask off everything that is not kernel abi version
|
||||
*/
|
||||
if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
|
||||
if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v9)) {
|
||||
audit_iface(NULL, NULL, NULL, "unsupported interface version",
|
||||
e, error);
|
||||
return error;
|
||||
|
@ -987,27 +1138,91 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size)
|
|||
{
|
||||
int i;
|
||||
for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
|
||||
if (!verify_xindex(dfa_user_xindex(dfa, i), table_size))
|
||||
return false;
|
||||
if (!verify_xindex(dfa_other_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;
|
||||
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 true;
|
||||
}
|
||||
|
||||
/**
|
||||
* verify_profile - Do post unpack analysis to verify profile consistency
|
||||
* @profile: profile to verify (NOT NULL)
|
||||
*
|
||||
* Returns: 0 if passes verification else error
|
||||
*
|
||||
* This verification is post any unpack mapping or changes
|
||||
*/
|
||||
static int verify_profile(struct aa_profile *profile)
|
||||
{
|
||||
if (profile->file.dfa &&
|
||||
!verify_dfa_xindex(profile->file.dfa,
|
||||
profile->file.trans.size)) {
|
||||
audit_iface(profile, NULL, NULL, "Invalid named transition",
|
||||
NULL, -EPROTO);
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
if (!rules)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1033,81 +1248,73 @@ struct aa_load_ent *aa_load_ent_alloc(void)
|
|||
return ent;
|
||||
}
|
||||
|
||||
static int deflate_compress(const char *src, size_t slen, char **dst,
|
||||
size_t *dlen)
|
||||
static int compress_zstd(const char *src, size_t slen, char **dst, size_t *dlen)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
|
||||
int error;
|
||||
struct z_stream_s strm;
|
||||
void *stgbuf, *dstbuf;
|
||||
size_t stglen = deflateBound(slen);
|
||||
const zstd_parameters params =
|
||||
zstd_get_params(aa_g_rawdata_compression_level, slen);
|
||||
const size_t wksp_len = zstd_cctx_workspace_bound(¶ms.cParams);
|
||||
void *wksp = NULL;
|
||||
zstd_cctx *ctx = NULL;
|
||||
size_t out_len = zstd_compress_bound(slen);
|
||||
void *out = NULL;
|
||||
int ret = 0;
|
||||
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
|
||||
if (stglen < slen)
|
||||
return -EFBIG;
|
||||
|
||||
strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS,
|
||||
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;
|
||||
out = kvzalloc(out_len, GFP_KERNEL);
|
||||
if (!out) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
stgbuf = kvzalloc(stglen, GFP_KERNEL);
|
||||
if (!stgbuf) {
|
||||
error = -ENOMEM;
|
||||
goto fail_stg_alloc;
|
||||
wksp = kvzalloc(wksp_len, GFP_KERNEL);
|
||||
if (!wksp) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
strm.next_in = src;
|
||||
strm.avail_in = slen;
|
||||
strm.next_out = stgbuf;
|
||||
strm.avail_out = stglen;
|
||||
|
||||
error = zlib_deflate(&strm, Z_FINISH);
|
||||
if (error != Z_STREAM_END) {
|
||||
error = -EINVAL;
|
||||
goto fail_deflate;
|
||||
ctx = zstd_init_cctx(wksp, wksp_len);
|
||||
if (!ctx) {
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
error = 0;
|
||||
|
||||
if (is_vmalloc_addr(stgbuf)) {
|
||||
dstbuf = kvzalloc(strm.total_out, GFP_KERNEL);
|
||||
if (dstbuf) {
|
||||
memcpy(dstbuf, stgbuf, strm.total_out);
|
||||
kvfree(stgbuf);
|
||||
out_len = zstd_compress_cctx(ctx, out, out_len, src, slen, ¶ms);
|
||||
if (zstd_is_error(out_len) || out_len >= slen) {
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
} else
|
||||
|
||||
if (is_vmalloc_addr(out)) {
|
||||
*dst = kvzalloc(out_len, GFP_KERNEL);
|
||||
if (*dst) {
|
||||
memcpy(*dst, out, out_len);
|
||||
kvfree(out);
|
||||
out = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If the staging buffer was kmalloc'd, then using krealloc is
|
||||
* probably going to be faster. The destination buffer will
|
||||
* always be smaller, so it's just shrunk, avoiding a memcpy
|
||||
*/
|
||||
dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL);
|
||||
|
||||
if (!dstbuf) {
|
||||
error = -ENOMEM;
|
||||
goto fail_deflate;
|
||||
*dst = krealloc(out, out_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
*dst = dstbuf;
|
||||
*dlen = strm.total_out;
|
||||
if (!*dst) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fail_stg_alloc:
|
||||
zlib_deflateEnd(&strm);
|
||||
fail_deflate_init:
|
||||
kvfree(strm.workspace);
|
||||
return error;
|
||||
*dlen = out_len;
|
||||
|
||||
fail_deflate:
|
||||
kvfree(stgbuf);
|
||||
goto fail_stg_alloc;
|
||||
cleanup:
|
||||
if (ret) {
|
||||
kvfree(out);
|
||||
*dst = NULL;
|
||||
}
|
||||
|
||||
kvfree(wksp);
|
||||
return ret;
|
||||
#else
|
||||
*dlen = slen;
|
||||
return 0;
|
||||
|
@ -1116,7 +1323,6 @@ fail_deflate:
|
|||
|
||||
static int compress_loaddata(struct aa_loaddata *data)
|
||||
{
|
||||
|
||||
AA_BUG(data->compressed_size > 0);
|
||||
|
||||
/*
|
||||
|
@ -1125,11 +1331,12 @@ static int compress_loaddata(struct aa_loaddata *data)
|
|||
*/
|
||||
if (aa_g_rawdata_compression_level != 0) {
|
||||
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);
|
||||
if (error)
|
||||
if (error) {
|
||||
data->compressed_size = data->size;
|
||||
return error;
|
||||
|
||||
}
|
||||
if (udata != data->data)
|
||||
kvfree(udata);
|
||||
} else
|
||||
|
@ -1155,6 +1362,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
|
|||
{
|
||||
struct aa_load_ent *tmp, *ent;
|
||||
struct aa_profile *profile = NULL;
|
||||
char *ns_name = NULL;
|
||||
int error;
|
||||
struct aa_ext e = {
|
||||
.start = udata->data,
|
||||
|
@ -1164,7 +1372,6 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
|
|||
|
||||
*ns = NULL;
|
||||
while (e.pos < e.end) {
|
||||
char *ns_name = NULL;
|
||||
void *start;
|
||||
error = verify_header(&e, e.pos == e.start, ns);
|
||||
if (error)
|
||||
|
@ -1195,6 +1402,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
|
|||
|
||||
ent->new = profile;
|
||||
ent->ns_name = ns_name;
|
||||
ns_name = NULL;
|
||||
list_add_tail(&ent->list, lh);
|
||||
}
|
||||
udata->abi = e.version & K_ABI_MASK;
|
||||
|
@ -1215,6 +1423,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
|
|||
return 0;
|
||||
|
||||
fail_profile:
|
||||
kfree(ns_name);
|
||||
aa_put_profile(profile);
|
||||
|
||||
fail:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
struct policy_unpack_fixture *puf = test->priv;
|
||||
u16 array_size;
|
||||
u16 array_size = 0;
|
||||
|
||||
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_PTR_EQ(test, puf->e->pos,
|
||||
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)
|
|||
{
|
||||
struct policy_unpack_fixture *puf = test->priv;
|
||||
const char name[] = TEST_ARRAY_NAME;
|
||||
u16 array_size;
|
||||
u16 array_size = 0;
|
||||
|
||||
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_PTR_EQ(test, puf->e->pos,
|
||||
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)
|
|||
puf->e->pos += TEST_NAMED_ARRAY_BUF_OFFSET;
|
||||
puf->e->end = puf->e->start + TEST_ARRAY_BUF_OFFSET + sizeof(u16);
|
||||
|
||||
array_size = aa_unpack_array(puf->e, name);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, array_size, 0);
|
||||
KUNIT_EXPECT_FALSE(test, aa_unpack_array(puf->e, name, &array_size));
|
||||
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
|
||||
puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET);
|
||||
}
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
|
||||
|
||||
/**
|
||||
* aa_getprocattr - Return the profile information for @profile
|
||||
* @profile: the profile to print profile info about (NOT NULL)
|
||||
* @string: Returns - string containing the profile info (NOT NULL)
|
||||
* aa_getprocattr - Return the label information for @label
|
||||
* @label: the label to print label info about (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
|
||||
* @profile.
|
||||
* Creates a string containing the label information for @label.
|
||||
*
|
||||
* 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)
|
|||
* @profile: profile being enforced (NOT NULL)
|
||||
* @resource: rlimit being auditing
|
||||
* @value: value being set
|
||||
* @peer: aa_albel of the task being set
|
||||
* @info: info being auditing
|
||||
* @error: error value
|
||||
*
|
||||
* 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,
|
|||
unsigned long value, struct aa_label *peer,
|
||||
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.max = value;
|
||||
|
@ -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
|
||||
*
|
||||
* Returns: resource # for the current architecture.
|
||||
|
@ -81,10 +84,12 @@ int aa_map_resource(int resource)
|
|||
static int profile_setrlimit(struct aa_profile *profile, unsigned int resource,
|
||||
struct rlimit *new_rlim)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
int e = 0;
|
||||
|
||||
if (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
|
||||
profile->rlimits.limits[resource].rlim_max)
|
||||
if (rules->rlimits.mask & (1 << resource) && new_rlim->rlim_max >
|
||||
rules->rlimits.limits[resource].rlim_max)
|
||||
e = -EACCES;
|
||||
return audit_resource(profile, resource, new_rlim->rlim_max, NULL, NULL,
|
||||
e);
|
||||
|
@ -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
|
||||
*/
|
||||
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;
|
||||
|
||||
for (j = 0, mask = 1; j < RLIM_NLIMITS; j++,
|
||||
mask <<= 1) {
|
||||
if (old->rlimits.mask & mask) {
|
||||
if (rules->rlimits.mask & mask) {
|
||||
rlim = current->signal->rlim + j;
|
||||
initrlim = init_task.signal->rlim + j;
|
||||
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)
|
|||
|
||||
/* set any new hard limits as dictated by the new profile */
|
||||
label_for_each_confined(i, new_l, new) {
|
||||
struct aa_ruleset *rules = list_first_entry(&new->rules,
|
||||
typeof(*rules),
|
||||
list);
|
||||
int j;
|
||||
|
||||
if (!new->rlimits.mask)
|
||||
if (!rules->rlimits.mask)
|
||||
continue;
|
||||
for (j = 0, mask = 1; j < RLIM_NLIMITS; j++, mask <<= 1) {
|
||||
if (!(new->rlimits.mask & mask))
|
||||
if (!(rules->rlimits.mask & mask))
|
||||
continue;
|
||||
|
||||
rlim = current->signal->rlim + j;
|
||||
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 */
|
||||
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)
|
|||
struct aa_label *p;
|
||||
|
||||
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();
|
||||
|
||||
return p;
|
||||
|
@ -223,16 +223,18 @@ static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
|
|||
FLAGS_NONE, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/* assumes check for PROFILE_MEDIATES is already done */
|
||||
/* assumes check for RULE_MEDIATES is already done */
|
||||
/* TODO: conditionals */
|
||||
static int profile_ptrace_perm(struct aa_profile *profile,
|
||||
struct aa_label *peer, u32 request,
|
||||
struct common_audit_data *sa)
|
||||
{
|
||||
struct aa_ruleset *rules = list_first_entry(&profile->rules,
|
||||
typeof(*rules), list);
|
||||
struct aa_perms perms = { };
|
||||
|
||||
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);
|
||||
aa_apply_modes_to_perms(profile, &perms);
|
||||
return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
|
||||
|
@ -243,7 +245,7 @@ static int profile_tracee_perm(struct aa_profile *tracee,
|
|||
struct common_audit_data *sa)
|
||||
{
|
||||
if (profile_unconfined(tracee) || unconfined(tracer) ||
|
||||
!PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE))
|
||||
!ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE))
|
||||
return 0;
|
||||
|
||||
return profile_ptrace_perm(tracee, tracer, request, sa);
|
||||
|
@ -256,7 +258,7 @@ static int profile_tracer_perm(struct aa_profile *tracer,
|
|||
if (profile_unconfined(tracer))
|
||||
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);
|
||||
|
||||
/* 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,
|
|||
{
|
||||
struct aa_profile *profile;
|
||||
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,
|
||||
profile_tracer_perm(profile, tracee, request, &sa),
|
||||
|
|
Loading…
Reference in New Issue