mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
apparmor: compute policydb permission on profile load
Rather than computing policydb permissions for each access permissions can be computed once on profile load and stored for lookup. Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
e48ffd24c1
commit
e2967ede22
@ -633,7 +633,7 @@ static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
|
||||
state = aa_dfa_match_len(dfa, profile->policy.start[0],
|
||||
match_str, match_len);
|
||||
if (state)
|
||||
aa_compute_perms(dfa, state, &tmp);
|
||||
tmp = *aa_lookup_perms(profile->policy.perms, state);
|
||||
}
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum_raw(perms, &tmp);
|
||||
|
@ -133,6 +133,17 @@ extern struct aa_perms allperms;
|
||||
xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2)))
|
||||
|
||||
|
||||
extern struct aa_perms default_perms;
|
||||
|
||||
static inline struct aa_perms *aa_lookup_perms(struct aa_perms *perms,
|
||||
unsigned int state)
|
||||
{
|
||||
if (!(perms))
|
||||
return &default_perms;
|
||||
|
||||
return &(perms[state]);
|
||||
}
|
||||
|
||||
void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs,
|
||||
u32 mask);
|
||||
void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
|
||||
@ -141,8 +152,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
|
||||
u32 chrsmask, const char * const *names, u32 namesmask);
|
||||
void aa_apply_modes_to_perms(struct aa_profile *profile,
|
||||
struct aa_perms *perms);
|
||||
void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
|
||||
struct aa_perms *perms);
|
||||
void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend);
|
||||
void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
|
||||
void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
|
||||
|
@ -77,6 +77,7 @@ enum profile_mode {
|
||||
struct aa_policydb {
|
||||
/* Generic policy DFA specific rule types will be subsections of it */
|
||||
struct aa_dfa *dfa;
|
||||
struct aa_perms *perms;
|
||||
unsigned int start[AA_CLASS_LAST + 1];
|
||||
|
||||
};
|
||||
|
@ -1328,7 +1328,7 @@ next:
|
||||
if (!state)
|
||||
goto fail;
|
||||
}
|
||||
aa_compute_perms(profile->policy.dfa, state, perms);
|
||||
*perms = *aa_lookup_perms(profile->policy.perms, state);
|
||||
aa_apply_modes_to_perms(profile, perms);
|
||||
if ((perms->allow & request) != request)
|
||||
return -EACCES;
|
||||
@ -1379,7 +1379,7 @@ static int label_components_match(struct aa_profile *profile,
|
||||
return 0;
|
||||
|
||||
next:
|
||||
aa_compute_perms(profile->policy.dfa, state, &tmp);
|
||||
tmp = *aa_lookup_perms(profile->policy.perms, state);
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum(perms, &tmp);
|
||||
label_for_each_cont(i, label, tp) {
|
||||
@ -1388,7 +1388,7 @@ next:
|
||||
state = match_component(profile, tp, start);
|
||||
if (!state)
|
||||
goto fail;
|
||||
aa_compute_perms(profile->policy.dfa, state, &tmp);
|
||||
tmp = *aa_lookup_perms(profile->policy.perms, state);
|
||||
aa_apply_modes_to_perms(profile, &tmp);
|
||||
aa_perms_accum(perms, &tmp);
|
||||
}
|
||||
|
@ -315,48 +315,6 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *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);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -203,25 +203,6 @@ static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* compute_mnt_perms - compute mount permission associated with @state
|
||||
* @dfa: dfa to match against (NOT NULL)
|
||||
* @state: state match finished in
|
||||
*
|
||||
* Returns: mount permissions
|
||||
*/
|
||||
static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
|
||||
unsigned int state)
|
||||
{
|
||||
struct aa_perms perms = {
|
||||
.allow = dfa_user_allow(dfa, state),
|
||||
.audit = dfa_user_audit(dfa, state),
|
||||
.quiet = dfa_user_quiet(dfa, state),
|
||||
};
|
||||
|
||||
return perms;
|
||||
}
|
||||
|
||||
static const char * const mnt_info_table[] = {
|
||||
"match succeeded",
|
||||
"failed mntpnt match",
|
||||
@ -236,50 +217,52 @@ static const char * const mnt_info_table[] = {
|
||||
* Returns 0 on success else element that match failed in, this is the
|
||||
* index into the mnt_info_table above
|
||||
*/
|
||||
static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
|
||||
static int do_match_mnt(struct aa_policydb *policy, unsigned int start,
|
||||
const char *mntpnt, const char *devname,
|
||||
const char *type, unsigned long flags,
|
||||
void *data, bool binary, struct aa_perms *perms)
|
||||
{
|
||||
unsigned int state;
|
||||
|
||||
AA_BUG(!dfa);
|
||||
AA_BUG(!policy);
|
||||
AA_BUG(!policy->dfa);
|
||||
AA_BUG(!policy->perms);
|
||||
AA_BUG(!perms);
|
||||
|
||||
state = aa_dfa_match(dfa, start, mntpnt);
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_match(policy->dfa, start, mntpnt);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 1;
|
||||
|
||||
if (devname)
|
||||
state = aa_dfa_match(dfa, state, devname);
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_match(policy->dfa, state, devname);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 2;
|
||||
|
||||
if (type)
|
||||
state = aa_dfa_match(dfa, state, type);
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_match(policy->dfa, state, type);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 3;
|
||||
|
||||
state = match_mnt_flags(dfa, state, flags);
|
||||
state = match_mnt_flags(policy->dfa, state, flags);
|
||||
if (!state)
|
||||
return 4;
|
||||
*perms = compute_mnt_perms(dfa, state);
|
||||
*perms = *aa_lookup_perms(policy->perms, state);
|
||||
if (perms->allow & AA_MAY_MOUNT)
|
||||
return 0;
|
||||
|
||||
/* only match data if not binary and the DFA flags data is expected */
|
||||
if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
|
||||
state = aa_dfa_null_transition(dfa, state);
|
||||
state = aa_dfa_null_transition(policy->dfa, state);
|
||||
if (!state)
|
||||
return 4;
|
||||
|
||||
state = aa_dfa_match(dfa, state, data);
|
||||
state = aa_dfa_match(policy->dfa, state, data);
|
||||
if (!state)
|
||||
return 5;
|
||||
*perms = compute_mnt_perms(dfa, state);
|
||||
*perms = *aa_lookup_perms(policy->perms, state);
|
||||
if (perms->allow & AA_MAY_MOUNT)
|
||||
return 0;
|
||||
}
|
||||
@ -341,7 +324,7 @@ static int match_mnt_path_str(struct aa_profile *profile,
|
||||
}
|
||||
|
||||
error = -EACCES;
|
||||
pos = do_match_mnt(profile->policy.dfa,
|
||||
pos = do_match_mnt(&profile->policy,
|
||||
profile->policy.start[AA_CLASS_MOUNT],
|
||||
mntpnt, devname, type, flags, data, binary, &perms);
|
||||
if (pos) {
|
||||
@ -601,7 +584,7 @@ static int profile_umount(struct aa_profile *profile, const struct path *path,
|
||||
state = aa_dfa_match(profile->policy.dfa,
|
||||
profile->policy.start[AA_CLASS_MOUNT],
|
||||
name);
|
||||
perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
perms = *aa_lookup_perms(profile->policy.perms, state);
|
||||
if (AA_MAY_UMOUNT & ~perms.allow)
|
||||
error = -EACCES;
|
||||
|
||||
@ -672,7 +655,7 @@ static struct aa_label *build_pivotroot(struct aa_profile *profile,
|
||||
new_name);
|
||||
state = aa_dfa_null_transition(profile->policy.dfa, state);
|
||||
state = aa_dfa_match(profile->policy.dfa, state, old_name);
|
||||
perms = compute_mnt_perms(profile->policy.dfa, state);
|
||||
perms = *aa_lookup_perms(profile->policy.perms, state);
|
||||
|
||||
if (AA_MAY_PIVOTROOT & perms.allow)
|
||||
error = 0;
|
||||
|
@ -125,7 +125,7 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa,
|
||||
buffer[1] = cpu_to_be16((u16) type);
|
||||
state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer,
|
||||
4);
|
||||
aa_compute_perms(profile->policy.dfa, state, &perms);
|
||||
perms = *aa_lookup_perms(profile->policy.perms, state);
|
||||
aa_apply_modes_to_perms(profile, &perms);
|
||||
|
||||
return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
|
||||
|
@ -233,7 +233,7 @@ void aa_free_profile(struct aa_profile *profile)
|
||||
aa_put_dfa(profile->xmatch);
|
||||
kvfree(profile->xmatch_perms);
|
||||
aa_put_dfa(profile->policy.dfa);
|
||||
|
||||
kvfree(profile->policy.perms);
|
||||
if (profile->data) {
|
||||
rht = profile->data;
|
||||
profile->data = NULL;
|
||||
|
@ -756,7 +756,7 @@ static struct aa_perms *compute_fperms(struct aa_dfa *dfa)
|
||||
|
||||
state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
|
||||
/* DFAs are restricted from having a state_count of less than 2 */
|
||||
table = kvzalloc(state_count * 2 * sizeof(struct aa_perms), GFP_KERNEL);
|
||||
table = kvcalloc(state_count * 2, sizeof(struct aa_perms), GFP_KERNEL);
|
||||
if (!table)
|
||||
return NULL;
|
||||
|
||||
@ -789,6 +789,54 @@ static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch)
|
||||
return perms_table;
|
||||
}
|
||||
|
||||
static u32 map_other(u32 x)
|
||||
{
|
||||
return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
|
||||
((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
|
||||
((x & 0x60) << 19); /* SETOPT/GETOPT */
|
||||
}
|
||||
|
||||
static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
|
||||
unsigned int state)
|
||||
{
|
||||
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);
|
||||
|
||||
/* for v5 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));
|
||||
|
||||
return perms;
|
||||
}
|
||||
|
||||
static struct aa_perms *compute_perms(struct aa_dfa *dfa)
|
||||
{
|
||||
int state;
|
||||
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);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* unpack_profile - unpack a serialized profile
|
||||
* @e: serialized data extent information (NOT NULL)
|
||||
@ -986,6 +1034,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
|
||||
goto fail;
|
||||
} else
|
||||
profile->policy.dfa = aa_get_dfa(nulldfa);
|
||||
profile->policy.perms = compute_perms(profile->policy.dfa);
|
||||
if (!profile->policy.perms) {
|
||||
info = "failed to remap policydb permission table";
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* get file rules */
|
||||
profile->file.dfa = unpack_dfa(e);
|
||||
|
Loading…
Reference in New Issue
Block a user