diff options
Diffstat (limited to 'security/apparmor/policy_unpack.c')
-rw-r--r-- | security/apparmor/policy_unpack.c | 113 |
1 files changed, 84 insertions, 29 deletions
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 5e578ef0ddff..7523971e37d9 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -13,7 +13,7 @@ * All policy is validated before it is used. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <kunit/visibility.h> #include <linux/ctype.h> #include <linux/errno.h> @@ -29,6 +29,7 @@ #include "include/policy.h" #include "include/policy_unpack.h" #include "include/policy_compat.h" +#include "include/signal.h" /* audit callback for unpack fields */ static void audit_cb(struct audit_buffer *ab, void *va) @@ -598,8 +599,8 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_ruleset *rules) fail: if (rules->secmark) { for (i = 0; i < size; i++) - kfree(rules->secmark[i].label); - kfree(rules->secmark); + kfree_sensitive(rules->secmark[i].label); + kfree_sensitive(rules->secmark); rules->secmark_count = 0; rules->secmark = NULL; } @@ -645,10 +646,13 @@ fail: static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm) { + u32 reserved; + if (version != 1) return false; - return aa_unpack_u32(e, &perm->allow, NULL) && + /* reserved entry is for later expansion, discard for now */ + return aa_unpack_u32(e, &reserved, NULL) && aa_unpack_u32(e, &perm->allow, NULL) && aa_unpack_u32(e, &perm->deny, NULL) && aa_unpack_u32(e, &perm->subtree, NULL) && @@ -713,6 +717,7 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, void *pos = e->pos; int i, flags, error = -EPROTO; ssize_t size; + u32 version = 0; pdb = aa_alloc_pdb(GFP_KERNEL); if (!pdb) @@ -730,6 +735,9 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, if (pdb->perms) { /* perms table present accept is index */ flags = TO_ACCEPT1_FLAG(YYTD_DATA32); + if (aa_unpack_u32(e, &version, "permsv") && version > 2) + /* accept2 used for dfa flags */ + flags |= TO_ACCEPT2_FLAG(YYTD_DATA32); } else { /* packed perms in accept1 and accept2 */ flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | @@ -747,33 +755,60 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, *info = "missing required dfa"; goto fail; } - goto out; + } else { + /* + * only unpack the following if a dfa is present + * + * sadly start was given different names for file and policydb + * but since it is optional we can try both + */ + if (!aa_unpack_u32(e, &pdb->start[0], "start")) + /* default start state */ + 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 */ + pdb->start[AA_CLASS_FILE] = DFA_START; + } /* setup class index */ + for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { + pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0], + i); + } } + /* accept2 is in some cases being allocated, even with perms */ + if (pdb->perms && !pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + /* add dfa flags table missing in v2 */ + u32 noents = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_lolen; + u16 tdflags = pdb->dfa->tables[YYTD_ID_ACCEPT]->td_flags; + size_t tsize = table_size(noents, tdflags); + + pdb->dfa->tables[YYTD_ID_ACCEPT2] = kvzalloc(tsize, GFP_KERNEL); + if (!pdb->dfa->tables[YYTD_ID_ACCEPT2]) { + *info = "failed to alloc dfa flags table"; + goto out; + } + pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_lolen = noents; + pdb->dfa->tables[YYTD_ID_ACCEPT2]->td_flags = tdflags; + } /* - * only unpack the following if a dfa is present - * - * sadly start was given different names for file and policydb - * but since it is optional we can try both + * Unfortunately due to a bug in earlier userspaces, a + * transition table may be present even when the dfa is + * not. For compatibility reasons unpack and discard. */ - if (!aa_unpack_u32(e, &pdb->start[0], "start")) - /* default start state */ - 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 */ - pdb->start[AA_CLASS_FILE] = DFA_START; - } /* setup class index */ - for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { - pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0], - i); - } if (!unpack_trans_table(e, &pdb->trans) && required_trans) { *info = "failed to unpack profile transition table"; goto fail; } - /* TODO: move compat mapping here, requires dfa merging first */ - /* TODO: move verify here, it has to be done after compat mappings */ + if (!pdb->dfa && pdb->trans.table) + aa_free_str_table(&pdb->trans); + + /* TODO: + * - move compat mapping here, requires dfa merging first + * - move verify here, it has to be done after compat mappings + * - move free of unneeded trans table here, has to be done + * after perm mapping. + */ out: *policy = pdb; return 0; @@ -851,7 +886,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) error = -ENOMEM; goto fail; } - rules = list_first_entry(&profile->rules, typeof(*rules), list); + rules = profile->label.rules[0]; /* profile renaming is optional */ (void) aa_unpack_str(e, &profile->rename, "rename"); @@ -887,6 +922,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) (void) aa_unpack_strdup(e, &disconnected, "disconnected"); profile->disconnected = disconnected; + /* optional */ + (void) aa_unpack_u32(e, &profile->signal, "kill"); + if (profile->signal < 1 || profile->signal > MAXMAPPED_SIG) { + info = "profile kill.signal invalid value"; + goto fail; + } /* per profile debug flags (complain, audit) */ if (!aa_unpack_nameX(e, AA_STRUCT, "flags")) { info = "profile missing flags"; @@ -1071,6 +1112,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) if (rhashtable_insert_fast(profile->data, &data->head, profile->data->p)) { + kvfree_sensitive(data->data, data->size); kfree_sensitive(data->key); kfree_sensitive(data); info = "failed to insert data to table"; @@ -1089,6 +1131,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + aa_compute_profile_mediates(profile); + return profile; fail: @@ -1203,21 +1247,32 @@ static bool verify_perm(struct aa_perms *perm) static bool verify_perms(struct aa_policydb *pdb) { int i; + int xidx, xmax = -1; for (i = 0; i < pdb->size; i++) { if (!verify_perm(&pdb->perms[i])) return false; /* verify indexes into str table */ - if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE && - (pdb->perms[i].xindex & AA_X_INDEX_MASK) >= pdb->trans.size) - return false; + if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { + xidx = pdb->perms[i].xindex & AA_X_INDEX_MASK; + if (xidx >= pdb->trans.size) + return false; + if (xmax < xidx) + xmax = xidx; + } if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) return false; if (pdb->perms[i].label && pdb->perms[i].label >= pdb->trans.size) return false; } - + /* deal with incorrectly constructed string tables */ + if (xmax == -1) { + aa_free_str_table(&pdb->trans); + } else if (pdb->trans.size > xmax + 1) { + if (!aa_resize_str_table(&pdb->trans, xmax + 1, GFP_KERNEL)) + return false; + } return true; } @@ -1231,8 +1286,8 @@ static bool verify_perms(struct aa_policydb *pdb) */ static int verify_profile(struct aa_profile *profile) { - struct aa_ruleset *rules = list_first_entry(&profile->rules, - typeof(*rules), list); + struct aa_ruleset *rules = profile->label.rules[0]; + if (!rules) return 0; |