apparmor: refcount the pdb

With the move to permission tables the dfa is no longer a stand
alone entity when used, needing a minimum of a permission table.
However it still could be shared among different pdbs each using
a different permission table.

Instead of duping the permission table when sharing a pdb, add a
refcount to the pdb so it can be easily shared.

Reviewed-by: Georgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen 2023-04-28 05:32:52 -07:00
parent 75c77e9e07
commit 98b824ff89
15 changed files with 261 additions and 211 deletions

View File

@ -619,23 +619,23 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
if (profile_unconfined(profile))
return;
if (rules->file.dfa && *match_str == AA_CLASS_FILE) {
state = aa_dfa_match_len(rules->file.dfa,
rules->file.start[AA_CLASS_FILE],
if (rules->file->dfa && *match_str == AA_CLASS_FILE) {
state = aa_dfa_match_len(rules->file->dfa,
rules->file->start[AA_CLASS_FILE],
match_str + 1, match_len - 1);
if (state) {
struct path_cond cond = { };
tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
tmp = *(aa_lookup_fperms(rules->file, state, &cond));
}
} else if (rules->policy.dfa) {
} else if (rules->policy->dfa) {
if (!RULE_MEDIATES(rules, *match_str))
return; /* no change to current perms */
state = aa_dfa_match_len(rules->policy.dfa,
rules->policy.start[0],
state = aa_dfa_match_len(rules->policy->dfa,
rules->policy->start[0],
match_str, match_len);
if (state)
tmp = *aa_lookup_perms(&rules->policy, state);
tmp = *aa_lookup_perms(rules->policy, state);
}
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum_raw(perms, &tmp);
@ -1096,7 +1096,7 @@ static int seq_profile_attach_show(struct seq_file *seq, void *v)
struct aa_profile *profile = labels_profile(label);
if (profile->attach.xmatch_str)
seq_printf(seq, "%s\n", profile->attach.xmatch_str);
else if (profile->attach.xmatch.dfa)
else if (profile->attach.xmatch->dfa)
seq_puts(seq, "<unknown>\n");
else
seq_printf(seq, "%s\n", profile->base.name);

View File

@ -77,7 +77,7 @@ out:
/**** TODO: dedup to aa_label_match - needs perm and dfa, merging
* specifically this is an exact copy of aa_label_match except
* aa_compute_perms is replaced with aa_compute_fperms
* and policy.dfa with file.dfa
* and policy->dfa with file->dfa
****/
/* match a profile and its associated ns component if needed
* Assumes visibility test has already been done.
@ -93,16 +93,16 @@ static inline aa_state_t match_component(struct aa_profile *profile,
const char *ns_name;
if (stack)
state = aa_dfa_match(rules->file.dfa, state, "&");
state = aa_dfa_match(rules->file->dfa, state, "&");
if (profile->ns == tp->ns)
return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
return aa_dfa_match(rules->file->dfa, state, tp->base.hname);
/* try matching with namespace name and then profile */
ns_name = aa_ns_name(profile->ns, tp->ns, true);
state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
state = aa_dfa_match(rules->file.dfa, state, ns_name);
state = aa_dfa_match_len(rules->file.dfa, state, ":", 1);
return aa_dfa_match(rules->file.dfa, state, tp->base.hname);
state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
state = aa_dfa_match(rules->file->dfa, state, ns_name);
state = aa_dfa_match_len(rules->file->dfa, state, ":", 1);
return aa_dfa_match(rules->file->dfa, state, tp->base.hname);
}
/**
@ -150,12 +150,12 @@ next:
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue;
state = aa_dfa_match(rules->file.dfa, state, "//&");
state = aa_dfa_match(rules->file->dfa, state, "//&");
state = match_component(profile, tp, false, state);
if (!state)
goto fail;
}
*perms = *(aa_lookup_fperms(&(rules->file), state, &cond));
*perms = *(aa_lookup_fperms(rules->file, state, &cond));
aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request)
return -EACCES;
@ -210,7 +210,7 @@ static int label_components_match(struct aa_profile *profile,
return 0;
next:
tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
tmp = *(aa_lookup_fperms(rules->file, state, &cond));
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) {
@ -219,7 +219,7 @@ next:
state = match_component(profile, tp, stack, start);
if (!state)
goto fail;
tmp = *(aa_lookup_fperms(&(rules->file), state, &cond));
tmp = *(aa_lookup_fperms(rules->file, state, &cond));
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
}
@ -317,7 +317,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
might_sleep();
/* transition from exec match to xattr set */
state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
d = bprm->file->f_path.dentry;
for (i = 0; i < attach->xattr_count; i++) {
@ -331,20 +331,20 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
* that not present xattr can be distinguished from a 0
* length value or rule that matches any value
*/
state = aa_dfa_null_transition(attach->xmatch.dfa,
state = aa_dfa_null_transition(attach->xmatch->dfa,
state);
/* Check xattr value */
state = aa_dfa_match_len(attach->xmatch.dfa, state,
state = aa_dfa_match_len(attach->xmatch->dfa, state,
value, size);
index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
perm = attach->xmatch.perms[index].allow;
index = ACCEPT_TABLE(attach->xmatch->dfa)[state];
perm = attach->xmatch->perms[index].allow;
if (!(perm & MAY_EXEC)) {
ret = -EINVAL;
goto out;
}
}
/* transition to next element */
state = aa_dfa_outofband_transition(attach->xmatch.dfa, state);
state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
if (size < 0) {
/*
* No xattr match, so verify if transition to
@ -413,16 +413,16 @@ restart:
* as another profile, signal a conflict and refuse to
* match.
*/
if (attach->xmatch.dfa) {
if (attach->xmatch->dfa) {
unsigned int count;
aa_state_t state;
u32 index, perm;
state = aa_dfa_leftmatch(attach->xmatch.dfa,
attach->xmatch.start[AA_CLASS_XMATCH],
state = aa_dfa_leftmatch(attach->xmatch->dfa,
attach->xmatch->start[AA_CLASS_XMATCH],
name, &count);
index = ACCEPT_TABLE(attach->xmatch.dfa)[state];
perm = attach->xmatch.perms[index].allow;
index = ACCEPT_TABLE(attach->xmatch->dfa)[state];
perm = attach->xmatch->perms[index].allow;
/* any accepting state means a valid match. */
if (perm & MAY_EXEC) {
int ret = 0;
@ -525,7 +525,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
/* TODO: move lookup parsing to unpack time so this is a straight
* index into the resultant label
*/
for (*name = rules->file.trans.table[index]; !label && *name;
for (*name = rules->file->trans.table[index]; !label && *name;
*name = next_name(xtype, *name)) {
if (xindex & AA_X_CHILD) {
struct aa_profile *new_profile;
@ -579,7 +579,7 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
break;
case AA_X_TABLE:
/* TODO: fix when perm mapping done at unload */
stack = rules->file.trans.table[xindex & AA_X_INDEX_MASK];
stack = rules->file->trans.table[xindex & AA_X_INDEX_MASK];
if (*stack != '&') {
/* released by caller */
new = x_table_lookup(profile, xindex, lookupname);
@ -638,7 +638,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
typeof(*rules), list);
struct aa_label *new = NULL;
const char *info = NULL, *name = NULL, *target = NULL;
aa_state_t state = rules->file.start[AA_CLASS_FILE];
aa_state_t state = rules->file->start[AA_CLASS_FILE];
struct aa_perms perms = {};
bool nonewprivs = false;
int error = 0;
@ -672,7 +672,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
}
/* find exec permissions for name */
state = aa_str_perms(&(rules->file), state, name, cond, &perms);
state = aa_str_perms(rules->file, state, name, cond, &perms);
if (perms.allow & MAY_EXEC) {
/* exec permission determine how to transition */
new = x_to_label(profile, bprm, name, perms.xindex, &target,
@ -738,7 +738,7 @@ static int profile_onexec(const struct cred *subj_cred,
{
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
aa_state_t state = rules->file.start[AA_CLASS_FILE];
aa_state_t state = rules->file->start[AA_CLASS_FILE];
struct aa_perms perms = {};
const char *xname = NULL, *info = "change_profile onexec";
int error = -EACCES;
@ -771,7 +771,7 @@ static int profile_onexec(const struct cred *subj_cred,
}
/* find exec permissions for name */
state = aa_str_perms(&(rules->file), state, xname, cond, &perms);
state = aa_str_perms(rules->file, state, xname, cond, &perms);
if (!(perms.allow & AA_MAY_ONEXEC)) {
info = "no change_onexec valid for executable";
goto audit;
@ -780,7 +780,7 @@ static int profile_onexec(const struct cred *subj_cred,
* onexec permission is linked to exec with a standard pairing
* exec\0change_profile
*/
state = aa_dfa_null_transition(rules->file.dfa, state);
state = aa_dfa_null_transition(rules->file->dfa, state);
error = change_profile_perms(profile, onexec, stack, AA_MAY_ONEXEC,
state, &perms);
if (error) {
@ -1300,7 +1300,7 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
if (!error)
error = change_profile_perms(profile, target, stack, request,
rules->file.start[AA_CLASS_FILE],
rules->file->start[AA_CLASS_FILE],
perms);
if (error)
error = aa_audit_file(subj_cred, profile, perms, op, request,

View File

@ -236,7 +236,7 @@ static int __aa_path_perm(const char *op, const struct cred *subj_cred,
if (profile_unconfined(profile))
return 0;
aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
name, cond, perms);
if (request & ~perms->allow)
e = -EACCES;
@ -353,16 +353,16 @@ static int profile_path_link(const struct cred *subj_cred,
error = -EACCES;
/* aa_str_perms - handles the case of the dfa being NULL */
state = aa_str_perms(&(rules->file),
rules->file.start[AA_CLASS_FILE], lname,
state = aa_str_perms(rules->file,
rules->file->start[AA_CLASS_FILE], lname,
cond, &lperms);
if (!(lperms.allow & AA_MAY_LINK))
goto audit;
/* test to see if target can be paired with link */
state = aa_dfa_null_transition(rules->file.dfa, state);
aa_str_perms(&(rules->file), state, tname, cond, &perms);
state = aa_dfa_null_transition(rules->file->dfa, state);
aa_str_perms(rules->file, state, tname, cond, &perms);
/* force audit/quiet masks for link are stored in the second entry
* in the link pair.
@ -384,7 +384,7 @@ static int profile_path_link(const struct cred *subj_cred,
/* Do link perm subset test requiring allowed permission on link are
* a subset of the allowed permissions on target.
*/
aa_str_perms(&(rules->file), rules->file.start[AA_CLASS_FILE],
aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
tname, cond, &perms);
/* AA_MAY_LINK is not considered in the subset test */

View File

@ -16,6 +16,8 @@
#include "match.h"
extern struct aa_dfa *stacksplitdfa;
/*
* DEBUG remains global (no per profile flag) since it is mostly used in sysctl
* which is not related to profile accesses.

View File

@ -102,9 +102,6 @@ struct aa_dfa {
struct table_header *tables[YYTD_ID_TSIZE];
};
extern struct aa_dfa *nulldfa;
extern struct aa_dfa *stacksplitdfa;
#define byte_to_byte(X) (X)
#define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \
@ -122,9 +119,6 @@ static inline size_t table_size(size_t len, size_t el_size)
return ALIGN(sizeof(struct table_header) + len * el_size, 8);
}
int aa_setup_dfa_engine(void);
void aa_teardown_dfa_engine(void);
#define aa_state_t unsigned int
struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);

View File

@ -74,12 +74,14 @@ enum profile_mode {
/* struct aa_policydb - match engine for a policy
* count: refcount for the pdb
* dfa: dfa pattern match
* perms: table of permissions
* strs: table of strings, index by x
* start: set of start states for the different classes of data
*/
struct aa_policydb {
struct kref count;
struct aa_dfa *dfa;
struct {
struct aa_perms *perms;
@ -89,13 +91,36 @@ struct aa_policydb {
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);
extern struct aa_policydb *nullpdb;
struct aa_policydb *aa_alloc_pdb(gfp_t gfp);
void aa_pdb_free_kref(struct kref *kref);
/**
* aa_get_pdb - increment refcount on @pdb
* @pdb: policydb (MAYBE NULL)
*
* Returns: pointer to @pdb if @pdb is NULL will return NULL
* Requires: @pdb must be held with valid refcount when called
*/
static inline struct aa_policydb *aa_get_pdb(struct aa_policydb *pdb)
{
if (pdb)
kref_get(&(pdb->count));
return pdb;
}
/**
* aa_put_pdb - put a pdb refcount
* @pdb: pdb to put refcount (MAYBE NULL)
*
* Requires: if @pdb != NULL that a valid refcount be held
*/
static inline void aa_put_pdb(struct aa_policydb *pdb)
{
if (pdb)
kref_put(&pdb->count, aa_pdb_free_kref);
}
static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy,
@ -139,8 +164,8 @@ struct aa_ruleset {
int size;
/* TODO: merge policy and file */
struct aa_policydb policy;
struct aa_policydb file;
struct aa_policydb *policy;
struct aa_policydb *file;
struct aa_caps caps;
struct aa_rlimit rlimits;
@ -159,7 +184,7 @@ struct aa_ruleset {
*/
struct aa_attachment {
const char *xmatch_str;
struct aa_policydb xmatch;
struct aa_policydb *xmatch;
unsigned int xmatch_len;
int xattr_count;
char **xattrs;
@ -267,10 +292,10 @@ static inline aa_state_t RULE_MEDIATES(struct aa_ruleset *rules,
unsigned char class)
{
if (class <= AA_CLASS_LAST)
return rules->policy.start[class];
return rules->policy->start[class];
else
return aa_dfa_match_len(rules->policy.dfa,
rules->policy.start[0], &class, 1);
return aa_dfa_match_len(rules->policy->dfa,
rules->policy->start[0], &class, 1);
}
static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
@ -280,7 +305,7 @@ static inline aa_state_t RULE_MEDIATES_AF(struct aa_ruleset *rules, u16 AF)
if (!state)
return DFA_NOMATCH;
return aa_dfa_match_len(rules->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,

View File

@ -92,8 +92,8 @@ static int profile_signal_perm(const struct cred *cred,
ad->subj_cred = cred;
ad->peer = peer;
/* TODO: secondary cache check <profile, profile, perm> */
state = aa_dfa_next(rules->policy.dfa,
rules->policy.start[AA_CLASS_SIGNAL],
state = aa_dfa_next(rules->policy->dfa,
rules->policy->start[AA_CLASS_SIGNAL],
ad->signal);
aa_label_match(profile, rules, peer, state, false, request, &perms);
aa_apply_modes_to_perms(profile, &perms);

View File

@ -1270,14 +1270,14 @@ static inline aa_state_t match_component(struct aa_profile *profile,
const char *ns_name;
if (profile->ns == tp->ns)
return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
return aa_dfa_match(rules->policy->dfa, state, tp->base.hname);
/* try matching with namespace name and then profile */
ns_name = aa_ns_name(profile->ns, tp->ns, true);
state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
state = aa_dfa_match(rules->policy.dfa, state, ns_name);
state = aa_dfa_match_len(rules->policy.dfa, state, ":", 1);
return aa_dfa_match(rules->policy.dfa, state, tp->base.hname);
state = aa_dfa_match_len(rules->policy->dfa, state, ":", 1);
state = aa_dfa_match(rules->policy->dfa, state, ns_name);
state = aa_dfa_match_len(rules->policy->dfa, state, ":", 1);
return aa_dfa_match(rules->policy->dfa, state, tp->base.hname);
}
/**
@ -1323,12 +1323,12 @@ next:
label_for_each_cont(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, subns))
continue;
state = aa_dfa_match(rules->policy.dfa, state, "//&");
state = aa_dfa_match(rules->policy->dfa, state, "//&");
state = match_component(profile, rules, tp, state);
if (!state)
goto fail;
}
*perms = *aa_lookup_perms(&rules->policy, state);
*perms = *aa_lookup_perms(rules->policy, state);
aa_apply_modes_to_perms(profile, perms);
if ((perms->allow & request) != request)
return -EACCES;
@ -1381,7 +1381,7 @@ static int label_components_match(struct aa_profile *profile,
return 0;
next:
tmp = *aa_lookup_perms(&rules->policy, state);
tmp = *aa_lookup_perms(rules->policy, state);
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
label_for_each_cont(i, label, tp) {
@ -1390,7 +1390,7 @@ next:
state = match_component(profile, rules, tp, start);
if (!state)
goto fail;
tmp = *aa_lookup_perms(&rules->policy, state);
tmp = *aa_lookup_perms(rules->policy, state);
aa_apply_modes_to_perms(profile, &tmp);
aa_perms_accum(perms, &tmp);
}

View File

@ -341,8 +341,8 @@ void aa_profile_match_label(struct aa_profile *profile,
/* TODO: doesn't yet handle extended types */
aa_state_t state;
state = aa_dfa_next(rules->policy.dfa,
rules->policy.start[AA_CLASS_LABEL],
state = aa_dfa_next(rules->policy->dfa,
rules->policy->start[AA_CLASS_LABEL],
type);
aa_label_match(profile, rules, label, state, false, request, perms);
}

View File

@ -1887,6 +1887,69 @@ static int __init apparmor_nf_ip_init(void)
__initcall(apparmor_nf_ip_init);
#endif
static char nulldfa_src[] = {
#include "nulldfa.in"
};
struct aa_dfa *nulldfa;
static char stacksplitdfa_src[] = {
#include "stacksplitdfa.in"
};
struct aa_dfa *stacksplitdfa;
struct aa_policydb *nullpdb;
static int __init aa_setup_dfa_engine(void)
{
int error = -ENOMEM;
nullpdb = aa_alloc_pdb(GFP_KERNEL);
if (!nullpdb)
return -ENOMEM;
nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32));
if (IS_ERR(nulldfa)) {
error = PTR_ERR(nulldfa);
goto fail;
}
nullpdb->dfa = aa_get_dfa(nulldfa);
nullpdb->perms = kcalloc(2, sizeof(struct aa_perms), GFP_KERNEL);
if (!nullpdb->perms)
goto fail;
nullpdb->size = 2;
stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src,
sizeof(stacksplitdfa_src),
TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32));
if (IS_ERR(stacksplitdfa)) {
error = PTR_ERR(stacksplitdfa);
goto fail;
}
return 0;
fail:
aa_put_pdb(nullpdb);
aa_put_dfa(nulldfa);
nullpdb = NULL;
nulldfa = NULL;
stacksplitdfa = NULL;
return error;
}
static void __init aa_teardown_dfa_engine(void)
{
aa_put_dfa(stacksplitdfa);
aa_put_dfa(nulldfa);
aa_put_pdb(nullpdb);
nullpdb = NULL;
stacksplitdfa = NULL;
nulldfa = NULL;
}
static int __init apparmor_init(void)
{
int error;

View File

@ -21,50 +21,6 @@
#define base_idx(X) ((X) & 0xffffff)
static char nulldfa_src[] = {
#include "nulldfa.in"
};
struct aa_dfa *nulldfa;
static char stacksplitdfa_src[] = {
#include "stacksplitdfa.in"
};
struct aa_dfa *stacksplitdfa;
int __init aa_setup_dfa_engine(void)
{
int error;
nulldfa = aa_dfa_unpack(nulldfa_src, sizeof(nulldfa_src),
TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32));
if (IS_ERR(nulldfa)) {
error = PTR_ERR(nulldfa);
nulldfa = NULL;
return error;
}
stacksplitdfa = aa_dfa_unpack(stacksplitdfa_src,
sizeof(stacksplitdfa_src),
TO_ACCEPT1_FLAG(YYTD_DATA32) |
TO_ACCEPT2_FLAG(YYTD_DATA32));
if (IS_ERR(stacksplitdfa)) {
aa_put_dfa(nulldfa);
nulldfa = NULL;
error = PTR_ERR(stacksplitdfa);
stacksplitdfa = NULL;
return error;
}
return 0;
}
void __init aa_teardown_dfa_engine(void)
{
aa_put_dfa(stacksplitdfa);
aa_put_dfa(nulldfa);
}
/**
* unpack_table - unpack a dfa table (one of accept, default, base, next check)
* @blob: data to unpack (NOT NULL)

View File

@ -332,8 +332,8 @@ static int match_mnt_path_str(const struct cred *subj_cred,
}
error = -EACCES;
pos = do_match_mnt(&rules->policy,
rules->policy.start[AA_CLASS_MOUNT],
pos = do_match_mnt(rules->policy,
rules->policy->start[AA_CLASS_MOUNT],
mntpnt, devname, type, flags, data, binary, &perms);
if (pos) {
info = mnt_info_table[pos];
@ -606,10 +606,10 @@ static int profile_umount(const struct cred *subj_cred,
if (error)
goto audit;
state = aa_dfa_match(rules->policy.dfa,
rules->policy.start[AA_CLASS_MOUNT],
state = aa_dfa_match(rules->policy->dfa,
rules->policy->start[AA_CLASS_MOUNT],
name);
perms = *aa_lookup_perms(&rules->policy, state);
perms = *aa_lookup_perms(rules->policy, state);
if (AA_MAY_UMOUNT & ~perms.allow)
error = -EACCES;
@ -680,12 +680,12 @@ static struct aa_label *build_pivotroot(const struct cred *subj_cred,
goto audit;
error = -EACCES;
state = aa_dfa_match(rules->policy.dfa,
rules->policy.start[AA_CLASS_MOUNT],
state = aa_dfa_match(rules->policy->dfa,
rules->policy->start[AA_CLASS_MOUNT],
new_name);
state = aa_dfa_null_transition(rules->policy.dfa, state);
state = aa_dfa_match(rules->policy.dfa, state, old_name);
perms = *aa_lookup_perms(&rules->policy, state);
state = aa_dfa_null_transition(rules->policy->dfa, state);
state = aa_dfa_match(rules->policy->dfa, state, old_name);
perms = *aa_lookup_perms(rules->policy, state);
if (AA_MAY_PIVOTROOT & perms.allow)
error = 0;

View File

@ -127,9 +127,9 @@ int aa_profile_af_perm(struct aa_profile *profile,
buffer[0] = cpu_to_be16(family);
buffer[1] = cpu_to_be16((u16) type);
state = aa_dfa_match_len(rules->policy.dfa, state, (char *) &buffer,
state = aa_dfa_match_len(rules->policy->dfa, state, (char *) &buffer,
4);
perms = *aa_lookup_perms(&rules->policy, state);
perms = *aa_lookup_perms(rules->policy, state);
aa_apply_modes_to_perms(profile, &perms);
return aa_check_perms(profile, &perms, request, ad, audit_net_cb);

View File

@ -98,6 +98,41 @@ const char *const aa_profile_mode_names[] = {
};
static void aa_free_pdb(struct aa_policydb *policy)
{
if (policy) {
aa_put_dfa(policy->dfa);
if (policy->perms)
kvfree(policy->perms);
aa_free_str_table(&policy->trans);
}
}
/**
* aa_pdb_free_kref - free aa_policydb by kref (called by aa_put_pdb)
* @kr: kref callback for freeing of a dfa (NOT NULL)
*/
void aa_pdb_free_kref(struct kref *kref)
{
struct aa_policydb *pdb = container_of(kref, struct aa_policydb, count);
aa_free_pdb(pdb);
}
struct aa_policydb *aa_alloc_pdb(gfp_t gfp)
{
struct aa_policydb *pdb = kzalloc(sizeof(struct aa_policydb), gfp);
if (!pdb)
return NULL;
kref_init(&pdb->count);
return pdb;
}
/**
* __add_profile - add a profiles to list and label tree
* @list: list to add it to (NOT NULL)
@ -200,15 +235,15 @@ static void free_attachment(struct aa_attachment *attach)
for (i = 0; i < attach->xattr_count; i++)
kfree_sensitive(attach->xattrs[i]);
kfree_sensitive(attach->xattrs);
aa_destroy_policydb(&attach->xmatch);
aa_put_pdb(attach->xmatch);
}
static void free_ruleset(struct aa_ruleset *rules)
{
int i;
aa_destroy_policydb(&rules->file);
aa_destroy_policydb(&rules->policy);
aa_put_pdb(rules->file);
aa_put_pdb(rules->policy);
aa_free_cap_rules(&rules->caps);
aa_free_rlimit_rules(&rules->rlimits);
@ -590,16 +625,8 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
/* 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->file.perms = kcalloc(2, sizeof(struct aa_perms), gfp);
if (!rules->file.perms)
goto fail;
rules->file.size = 2;
rules->policy.dfa = aa_get_dfa(nulldfa);
rules->policy.perms = kcalloc(2, sizeof(struct aa_perms), gfp);
if (!rules->policy.perms)
goto fail;
rules->policy.size = 2;
rules->file = aa_get_pdb(nullpdb);
rules->policy = aa_get_pdb(nullpdb);
if (parent) {
profile->path_flags = parent->path_flags;
@ -610,11 +637,6 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
}
return profile;
fail:
aa_free_profile(profile);
return NULL;
}
/**

View File

@ -703,24 +703,29 @@ fail_reset:
return -EPROTO;
}
static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
bool required_dfa, bool required_trans,
const char **info)
{
struct aa_policydb *pdb;
void *pos = e->pos;
int i, flags, error = -EPROTO;
ssize_t size;
size = unpack_perms_table(e, &policy->perms);
pdb = aa_alloc_pdb(GFP_KERNEL);
if (!pdb)
return -ENOMEM;
size = unpack_perms_table(e, &pdb->perms);
if (size < 0) {
error = size;
policy->perms = NULL;
pdb->perms = NULL;
*info = "failed to unpack - perms";
goto fail;
}
policy->size = size;
pdb->size = size;
if (policy->perms) {
if (pdb->perms) {
/* perms table present accept is index */
flags = TO_ACCEPT1_FLAG(YYTD_DATA32);
} else {
@ -729,13 +734,13 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
TO_ACCEPT2_FLAG(YYTD_DATA32);
}
policy->dfa = unpack_dfa(e, flags);
if (IS_ERR(policy->dfa)) {
error = PTR_ERR(policy->dfa);
policy->dfa = NULL;
pdb->dfa = unpack_dfa(e, flags);
if (IS_ERR(pdb->dfa)) {
error = PTR_ERR(pdb->dfa);
pdb->dfa = NULL;
*info = "failed to unpack - dfa";
goto fail;
} else if (!policy->dfa) {
} else if (!pdb->dfa) {
if (required_dfa) {
*info = "missing required dfa";
goto fail;
@ -749,18 +754,18 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
* sadly start was given different names for file and policydb
* but since it is optional we can try both
*/
if (!aa_unpack_u32(e, &policy->start[0], "start"))
if (!aa_unpack_u32(e, &pdb->start[0], "start"))
/* default start state */
policy->start[0] = DFA_START;
if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) {
pdb->start[0] = DFA_START;
if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) {
/* default start state for xmatch and file dfa */
policy->start[AA_CLASS_FILE] = DFA_START;
pdb->start[AA_CLASS_FILE] = DFA_START;
} /* setup class index */
for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
policy->start[i] = aa_dfa_next(policy->dfa, policy->start[0],
pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
i);
}
if (!unpack_trans_table(e, &policy->trans) && required_trans) {
if (!unpack_trans_table(e, &pdb->trans) && required_trans) {
*info = "failed to unpack profile transition table";
goto fail;
}
@ -768,9 +773,11 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb *policy,
/* TODO: move compat mapping here, requires dfa merging first */
/* TODO: move verify here, it has to be done after compat mappings */
out:
*policy = pdb;
return 0;
fail:
aa_put_pdb(pdb);
e->pos = pos;
return error;
}
@ -854,15 +861,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
}
/* neither xmatch_len not xmatch_perms are optional if xmatch is set */
if (profile->attach.xmatch.dfa) {
if (profile->attach.xmatch->dfa) {
if (!aa_unpack_u32(e, &tmp, NULL)) {
info = "missing xmatch len";
goto fail;
}
profile->attach.xmatch_len = tmp;
profile->attach.xmatch.start[AA_CLASS_XMATCH] = DFA_START;
if (!profile->attach.xmatch.perms) {
error = aa_compat_map_xmatch(&profile->attach.xmatch);
profile->attach.xmatch->start[AA_CLASS_XMATCH] = DFA_START;
if (!profile->attach.xmatch->perms) {
error = aa_compat_map_xmatch(profile->attach.xmatch);
if (error) {
info = "failed to convert xmatch permission table";
goto fail;
@ -979,16 +986,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (error)
goto fail;
/* Fixup: drop when we get rid of start array */
if (aa_dfa_next(rules->policy.dfa, rules->policy.start[0],
if (aa_dfa_next(rules->policy->dfa, rules->policy->start[0],
AA_CLASS_FILE))
rules->policy.start[AA_CLASS_FILE] =
aa_dfa_next(rules->policy.dfa,
rules->policy.start[0],
rules->policy->start[AA_CLASS_FILE] =
aa_dfa_next(rules->policy->dfa,
rules->policy->start[0],
AA_CLASS_FILE);
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
if (!rules->policy.perms) {
error = aa_compat_map_policy(&rules->policy,
if (!rules->policy->perms) {
error = aa_compat_map_policy(rules->policy,
e->version);
if (error) {
info = "failed to remap policydb permission table";
@ -996,44 +1003,25 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
}
}
} else {
rules->policy.dfa = aa_get_dfa(nulldfa);
rules->policy.perms = kcalloc(2, sizeof(struct aa_perms),
GFP_KERNEL);
if (!rules->policy.perms)
goto fail;
rules->policy.size = 2;
rules->policy = aa_get_pdb(nullpdb);
}
/* get file rules */
error = unpack_pdb(e, &rules->file, false, true, &info);
if (error) {
goto fail;
} else if (rules->file.dfa) {
if (!rules->file.perms) {
error = aa_compat_map_file(&rules->file);
} else if (rules->file->dfa) {
if (!rules->file->perms) {
error = aa_compat_map_file(rules->file);
if (error) {
info = "failed to remap file permission table";
goto fail;
}
}
} else if (rules->policy.dfa &&
rules->policy.start[AA_CLASS_FILE]) {
rules->file.dfa = aa_get_dfa(rules->policy.dfa);
rules->file.start[AA_CLASS_FILE] = rules->policy.start[AA_CLASS_FILE];
rules->file.perms = kcalloc(rules->policy.size,
sizeof(struct aa_perms),
GFP_KERNEL);
if (!rules->file.perms)
goto fail;
memcpy(rules->file.perms, rules->policy.perms,
rules->policy.size * sizeof(struct aa_perms));
rules->file.size = rules->policy.size;
} else if (rules->policy->dfa &&
rules->policy->start[AA_CLASS_FILE]) {
rules->file = aa_get_pdb(rules->policy);
} else {
rules->file.dfa = aa_get_dfa(nulldfa);
rules->file.perms = kcalloc(2, sizeof(struct aa_perms),
GFP_KERNEL);
if (!rules->file.perms)
goto fail;
rules->file.size = 2;
rules->file = aa_get_pdb(nullpdb);
}
error = -EPROTO;
if (aa_unpack_nameX(e, AA_STRUCT, "data")) {
@ -1240,32 +1228,32 @@ static int verify_profile(struct aa_profile *profile)
if (!rules)
return 0;
if (rules->file.dfa && !verify_dfa_accept_index(rules->file.dfa,
rules->file.size)) {
if (rules->file->dfa && !verify_dfa_accept_index(rules->file->dfa,
rules->file->size)) {
audit_iface(profile, NULL, NULL,
"Unpack: file Invalid named transition", NULL,
-EPROTO);
return -EPROTO;
}
if (rules->policy.dfa &&
!verify_dfa_accept_index(rules->policy.dfa, rules->policy.size)) {
if (rules->policy->dfa &&
!verify_dfa_accept_index(rules->policy->dfa, rules->policy->size)) {
audit_iface(profile, NULL, NULL,
"Unpack: policy Invalid named transition", NULL,
-EPROTO);
return -EPROTO;
}
if (!verify_perms(&rules->file)) {
if (!verify_perms(rules->file)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;
}
if (!verify_perms(&rules->policy)) {
if (!verify_perms(rules->policy)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;
}
if (!verify_perms(&profile->attach.xmatch)) {
if (!verify_perms(profile->attach.xmatch)) {
audit_iface(profile, NULL, NULL,
"Unpack: Invalid perm index", NULL, -EPROTO);
return -EPROTO;