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

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

Merge tag 'apparmor-pr-2022-12-14' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor

Pull apparmor updates from John Johansen:
 "Features:
   - switch to zstd compression for profile raw data

  Cleanups:
   - simplify obtaining the newest label on a cred
   - remove useless static inline functions
   - compute permission conversion on policy unpack
   - refactor code to share common permissins
   - refactor unpack to group policy backwards compatiblity code
   - add __init annotation to aa_{setup/teardown}_dfa_engine()

  Bug Fixes:
   - fix a memleak in
       - multi_transaction_new()
       - free_ruleset()
       - unpack_profile()
       - alloc_ns()
   - fix lockdep warning when removing a namespace
   - fix regression in stacking due to label flags
   - fix loading of child before parent
   - fix kernel-doc comments that differ from fns
   - fix spelling errors in comments
   - store return value of unpack_perms_table() to signed variable"

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