* @multi: allow multiple strings on a single entry
* @strs: str table to unpack to (NOT NULL)
*
- * Returns: true if table successfully unpacked or not present
+ * Returns: 0 if table successfully unpacked or not present, else error
*/
-static bool unpack_strs_table(struct aa_ext *e, const char *name, bool multi,
+static int unpack_strs_table(struct aa_ext *e, const char *name, bool multi,
struct aa_str_table *strs)
{
void *saved_pos = e->pos;
struct aa_str_table_ent *table = NULL;
+ int error = -EPROTO;
/* exec table is optional */
if (aa_unpack_nameX(e, AA_STRUCT, name)) {
goto fail;
table = kcalloc(size, sizeof(struct aa_str_table_ent),
GFP_KERNEL);
- if (!table)
+ if (!table) {
+ error = -ENOMEM;
goto fail;
-
+ }
strs->table = table;
strs->size = size;
for (i = 0; i < size; i++) {
*/
c = process_strs_entry(str, size2, multi);
if (c <= 0) {
- AA_DEBUG(DEBUG_UNPACK, "process_strs %d", c);
+ AA_DEBUG(DEBUG_UNPACK, "process_strs %d i %d pos %ld",
+ c, i, e->pos - saved_pos);
goto fail;
}
if (!multi && c > 1) {
if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
}
- return true;
+ return 0;
fail:
aa_destroy_str_table(strs);
e->pos = saved_pos;
- return false;
+ return error;
}
static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
return false;
}
+
+static bool verify_tags(struct aa_tags_struct *tags, const char **info)
+{
+ if ((tags->hdrs.size && !tags->hdrs.table) ||
+ (!tags->hdrs.size && tags->hdrs.table)) {
+ *info = "failed verification tag.hdrs disagree";
+ return false;
+ }
+ if ((tags->sets.size && !tags->sets.table) ||
+ (!tags->sets.size && tags->sets.table)) {
+ *info = "failed verification tag.sets disagree";
+ return false;
+ }
+ if ((tags->strs.size && !tags->strs.table) ||
+ (!tags->strs.size && tags->strs.table)) {
+ *info = "failed verification tags->strs disagree";
+ return false;
+ }
+ /* no data present */
+ if (!tags->sets.size && !tags->hdrs.size && !tags->strs.size) {
+ return true;
+ } else if (!(tags->sets.size && tags->hdrs.size && tags->strs.size)) {
+ /* some data present but not all */
+ *info = "failed verification tags partial data present";
+ return false;
+ }
+
+ u32 i;
+
+ for (i = 0; i < tags->sets.size; i++) {
+ /* count followed by count indexes into hdrs */
+ u32 cnt = tags->sets.table[i];
+
+ if (i+cnt >= tags->sets.size) {
+ AA_DEBUG(DEBUG_UNPACK,
+ "tagset too large %d+%d > sets.table[%d]",
+ i, cnt, tags->sets.size);
+ *info = "failed verification tagset too large";
+ return false;
+ }
+ for (; cnt; cnt--) {
+ if (tags->sets.table[++i] >= tags->hdrs.size) {
+ AA_DEBUG(DEBUG_UNPACK,
+ "tagsets idx out of bounds cnt %d sets.table[%d] >= %d",
+ cnt, i-1, tags->hdrs.size);
+ *info = "failed verification tagsets idx out of bounds";
+ return false;
+ }
+ }
+ }
+ for (i = 0; i < tags->hdrs.size; i++) {
+ u32 idx = tags->hdrs.table[i].tags;
+
+ if (idx >= tags->strs.size) {
+ AA_DEBUG(DEBUG_UNPACK,
+ "tag.hdrs idx oob idx %d > tags->strs.size=%d",
+ idx, tags->strs.size);
+ *info = "failed verification tags.hdrs idx out of bounds";
+ return false;
+ }
+ if (tags->hdrs.table[i].count != tags->strs.table[idx].count) {
+ AA_DEBUG(DEBUG_UNPACK, "hdrs.table[%d].count=%d != tags->strs.table[%d]=%d",
+ i, tags->hdrs.table[i].count, idx, tags->strs.table[idx].count);
+ *info = "failed verification tagd.hdrs[idx].count";
+ return false;
+ }
+ if (tags->hdrs.table[i].size != tags->strs.table[idx].size) {
+ AA_DEBUG(DEBUG_UNPACK, "hdrs.table[%d].size=%d != strs.table[%d].size=%d",
+ i, tags->hdrs.table[i].size, idx, tags->strs.table[idx].size);
+ *info = "failed verification tagd.hdrs[idx].size";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int unpack_tagsets(struct aa_ext *e, struct aa_tags_struct *tags)
+{
+ u32 *sets;
+ u16 i, size;
+ int error = -EPROTO;
+ void *pos = e->pos;
+
+ if (!aa_unpack_array(e, "sets", &size))
+ goto fail_reset;
+ sets = kcalloc(size, sizeof(u32), GFP_KERNEL);
+ if (!sets) {
+ error = -ENOMEM;
+ goto fail_reset;
+ }
+ for (i = 0; i < size; i++) {
+ if (!aa_unpack_u32(e, &sets[i], NULL))
+ goto fail;
+ }
+ if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+
+ tags->sets.size = size;
+ tags->sets.table = sets;
+
+ return 0;
+
+fail:
+ kfree_sensitive(sets);
+fail_reset:
+ e->pos = pos;
+ return error;
+}
+
+static bool unpack_tag_header_ent(struct aa_ext *e, struct aa_tags_header *h)
+{
+ return aa_unpack_u32(e, &h->mask, NULL) &&
+ aa_unpack_u32(e, &h->count, NULL) &&
+ aa_unpack_u32(e, &h->size, NULL) &&
+ aa_unpack_u32(e, &h->tags, NULL);
+}
+
+static int unpack_tag_headers(struct aa_ext *e, struct aa_tags_struct *tags)
+{
+ struct aa_tags_header *hdrs;
+ u16 i, size;
+ int error = -EPROTO;
+ void *pos = e->pos;
+
+ if (!aa_unpack_array(e, "hdrs", &size))
+ goto fail_reset;
+ hdrs = kcalloc(size, sizeof(struct aa_tags_header), GFP_KERNEL);
+ if (!hdrs) {
+ error = -ENOMEM;
+ goto fail_reset;
+ }
+ for (i = 0; i < size; i++) {
+ if (!unpack_tag_header_ent(e, &hdrs[i]))
+ goto fail;
+ }
+ if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+
+ tags->hdrs.size = size;
+ tags->hdrs.table = hdrs;
+ AA_DEBUG(DEBUG_UNPACK, "headers %ld size %d", (long) hdrs, size);
+ return true;
+
+fail:
+ kfree_sensitive(hdrs);
+fail_reset:
+ e->pos = pos;
+ return error;
+}
+
+
+static size_t unpack_tags(struct aa_ext *e, struct aa_tags_struct *tags,
+ const char **info)
+{
+ int error = -EPROTO;
+ void *pos = e->pos;
+
+ AA_BUG(!tags);
+ /* policy tags are optional */
+ if (aa_unpack_nameX(e, AA_STRUCT, "tags")) {
+ u32 version;
+
+ if (!aa_unpack_u32(e, &version, "version") || version != 1) {
+ *info = "invalid tags version";
+ goto fail_reset;
+ }
+ error = unpack_strs_table(e, "strs", true, &tags->strs);
+ if (error) {
+ *info = "failed to unpack profile tag.strs";
+ goto fail;
+ }
+ error = unpack_tag_headers(e, tags);
+ if (error) {
+ *info = "failed to unpack profile tag.headers";
+ goto fail;
+ }
+ error = unpack_tagsets(e, tags);
+ if (error) {
+ *info = "failed to unpack profile tag.sets";
+ goto fail;
+ }
+ if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+
+ if (!verify_tags(tags, info))
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ aa_destroy_tags(tags);
+fail_reset:
+ e->pos = pos;
+ return error;
+}
+
static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm)
{
u32 reserved;
if (!pdb)
return -ENOMEM;
+ AA_DEBUG(DEBUG_UNPACK, "unpacking tags");
+ if (unpack_tags(e, &pdb->tags, info) < 0)
+ goto fail;
+ AA_DEBUG(DEBUG_UNPACK, "done unpacking tags");
+
size = unpack_perms_table(e, &pdb->perms);
if (size < 0) {
error = size;
* transition table may be present even when the dfa is
* not. For compatibility reasons unpack and discard.
*/
- if (!unpack_strs_table(e, "xtable", false, &pdb->trans) &&
- required_trans) {
+ error = unpack_strs_table(e, "xtable", false, &pdb->trans);
+ if (error && required_trans) {
*info = "failed to unpack profile transition table";
goto fail;
}
goto fail;
} else if (rules->file->dfa) {
if (!rules->file->perms) {
+ AA_DEBUG(DEBUG_UNPACK, "compat mapping perms");
error = aa_compat_map_file(rules->file);
if (error) {
info = "failed to remap file permission table";
if (xmax < xidx)
xmax = xidx;
}
- if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size)
+ if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->tags.sets.size)
return false;
if (pdb->perms[i].label &&
pdb->perms[i].label >= pdb->trans.size)