diff options
Diffstat (limited to 'security/apparmor/policy_unpack.c')
| -rw-r--r-- | security/apparmor/policy_unpack.c | 279 |
1 files changed, 169 insertions, 110 deletions
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 694fb7a09962..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,22 +29,24 @@ #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) { struct common_audit_data *sa = va; + struct apparmor_audit_data *ad = aad(sa); - if (aad(sa)->iface.ns) { + if (ad->iface.ns) { audit_log_format(ab, " ns="); - audit_log_untrustedstring(ab, aad(sa)->iface.ns); + audit_log_untrustedstring(ab, ad->iface.ns); } - if (aad(sa)->name) { + if (ad->name) { audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, aad(sa)->name); + audit_log_untrustedstring(ab, ad->name); } - if (aad(sa)->iface.pos) - audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); + if (ad->iface.pos) + audit_log_format(ab, " offset=%ld", ad->iface.pos); } /** @@ -63,18 +65,18 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, int error) { struct aa_profile *profile = labels_profile(aa_current_raw_label()); - DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL); + DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL); if (e) - aad(&sa)->iface.pos = e->pos - e->start; - aad(&sa)->iface.ns = ns_name; + ad.iface.pos = e->pos - e->start; + ad.iface.ns = ns_name; if (new) - aad(&sa)->name = new->base.hname; + ad.name = new->base.hname; else - aad(&sa)->name = name; - aad(&sa)->info = info; - aad(&sa)->error = error; + ad.name = name; + ad.info = info; + ad.error = error; - return aa_audit(AUDIT_APPARMOR_STATUS, profile, &sa, audit_cb); + return aa_audit(AUDIT_APPARMOR_STATUS, profile, &ad, audit_cb); } void __aa_loaddata_update(struct aa_loaddata *data, long revision) @@ -86,10 +88,13 @@ void __aa_loaddata_update(struct aa_loaddata *data, long revision) data->revision = revision; if ((data->dents[AAFS_LOADDATA_REVISION])) { - d_inode(data->dents[AAFS_LOADDATA_DIR])->i_mtime = - current_time(d_inode(data->dents[AAFS_LOADDATA_DIR])); - d_inode(data->dents[AAFS_LOADDATA_REVISION])->i_mtime = - current_time(d_inode(data->dents[AAFS_LOADDATA_REVISION])); + struct inode *inode; + + inode = d_inode(data->dents[AAFS_LOADDATA_DIR]); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + + inode = d_inode(data->dents[AAFS_LOADDATA_REVISION]); + inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); } } @@ -474,6 +479,8 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) if (!table) goto fail; + strs->table = table; + strs->size = size; for (i = 0; i < size; i++) { char *str; int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL); @@ -516,14 +523,11 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs) goto fail; if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; - - strs->table = table; - strs->size = size; } return true; fail: - kfree_sensitive(table); + aa_free_str_table(strs); e->pos = saved_pos; return false; } @@ -595,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; } @@ -642,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) && @@ -702,74 +709,112 @@ 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; + u32 version = 0; + + pdb = aa_alloc_pdb(GFP_KERNEL); + if (!pdb) + return -ENOMEM; - size = unpack_perms_table(e, &policy->perms); + 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); + 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) | 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; } - 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, &policy->start[0], "start")) - /* default start state */ - policy->start[0] = DFA_START; - if (!aa_unpack_u32(e, &policy->start[AA_CLASS_FILE], "dfa_start")) { - /* default start state for xmatch and file dfa */ - policy->start[AA_CLASS_FILE] = DFA_START; - } /* setup class index */ - for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { - policy->start[i] = aa_dfa_next(policy->dfa, policy->start[0], - i); - } - if (!unpack_trans_table(e, &policy->trans) && required_trans) { + 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; fail: + aa_put_pdb(pdb); e->pos = pos; return error; } @@ -804,7 +849,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) const char *info = "failed to unpack profile"; size_t ns_len; struct rhashtable_params params = { 0 }; - char *key = NULL; + char *key = NULL, *disconnected = NULL; struct aa_data *data; int error = -EPROTO; kernel_cap_t tmpcap; @@ -822,6 +867,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); if (tmpns) { + if (!tmpname) { + info = "empty profile name"; + goto fail; + } *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); if (!*ns_name) { info = "out of memory"; @@ -837,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"); @@ -853,15 +902,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; @@ -870,8 +919,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) } /* disconnected attachment string is optional */ - (void) aa_unpack_str(e, &profile->disconnected, "disconnected"); + (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"; @@ -977,16 +1033,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"; @@ -994,44 +1050,27 @@ 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]) { + aa_put_pdb(rules->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; + aa_put_pdb(rules->file); + rules->file = aa_get_pdb(nullpdb); } error = -EPROTO; if (aa_unpack_nameX(e, AA_STRUCT, "data")) { @@ -1073,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"; @@ -1091,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: @@ -1167,7 +1209,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) /** * verify_dfa_accept_index - verify accept indexes are in range of perms table * @dfa: the dfa to check accept indexes are in range - * table_size: the permission table size the indexes should be within + * @table_size: the permission table size the indexes should be within */ static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size) { @@ -1205,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; } @@ -1233,31 +1286,37 @@ 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; - if ((rules->file.dfa && !verify_dfa_accept_index(rules->file.dfa, - rules->file.size)) || - (rules->policy.dfa && - !verify_dfa_accept_index(rules->policy.dfa, rules->policy.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)) { audit_iface(profile, NULL, NULL, - "Unpack: Invalid named transition", NULL, -EPROTO); + "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; |
