Commit c140dcd1 authored by John Johansen's avatar John Johansen
Browse files

apparmor: make str table more generic and be able to have multiple entries



The strtable is currently limited to a single entry string on unpack
even though domain has the concept of multiple entries within it. Make
this a reality as it will be used for tags and more advanced domain
transitions.

Reviewed-by: default avatarGeorgia Garcia <georgia.garcia@canonical.com>
Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent 6fc367bf
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -529,7 +529,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 (next = rules->file->trans.table[index]; next;
	for (next = rules->file->trans.table[index].strs; next;
	     next = next_name(xtype, next)) {
		const char *lookup = (*next == '&') ? next + 1 : next;
		*name = next;
+9 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ extern struct aa_dfa *stacksplitdfa;
#define DEBUG_DOMAIN 4
#define DEBUG_POLICY 8
#define DEBUG_INTERFACE 0x10
#define DEBUG_UNPACK 0x40

#define DEBUG_ALL 0x1f		/* update if new DEBUG_X added */
#define DEBUG_PARSE_ERROR (-1)
@@ -119,13 +120,19 @@ static inline bool path_mediated_fs(struct dentry *dentry)
	return !(dentry->d_sb->s_flags & SB_NOUSER);
}

struct aa_str_table_ent {
	int count;
	int size;
	char *strs;
};

struct aa_str_table {
	int size;
	char **table;
	struct aa_str_table_ent *table;
};

void aa_free_str_table(struct aa_str_table *table);
bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp);
void aa_destroy_str_table(struct aa_str_table *table);

struct counted_str {
	struct kref count;
+6 −5
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ static struct val_table_ent debug_values_table[] = {
	{ "domain", DEBUG_DOMAIN },
	{ "policy", DEBUG_POLICY },
	{ "interface", DEBUG_INTERFACE },
	{ "unpack", DEBUG_UNPACK },
	{ NULL, 0 }
};

@@ -118,7 +119,7 @@ int aa_print_debug_params(char *buffer)

bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp)
{
	char **n;
	struct aa_str_table_ent *n;
	int i;

	if (t->size == newsize)
@@ -129,7 +130,7 @@ bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp)
	for (i = 0; i < min(t->size, newsize); i++)
		n[i] = t->table[i];
	for (; i < t->size; i++)
		kfree_sensitive(t->table[i]);
		kfree_sensitive(t->table[i].strs);
	if (newsize > t->size)
		memset(&n[t->size], 0, (newsize-t->size)*sizeof(*n));
	kfree_sensitive(t->table);
@@ -140,10 +141,10 @@ bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp)
}

/**
 * aa_free_str_table - free entries str table
 * aa_destroy_str_table - free entries str table
 * @t: the string table to free  (MAYBE NULL)
 */
void aa_free_str_table(struct aa_str_table *t)
void aa_destroy_str_table(struct aa_str_table *t)
{
	int i;

@@ -152,7 +153,7 @@ void aa_free_str_table(struct aa_str_table *t)
			return;

		for (i = 0; i < t->size; i++)
			kfree_sensitive(t->table[i]);
			kfree_sensitive(t->table[i].strs);
		kfree_sensitive(t->table);
		t->table = NULL;
		t->size = 0;
+1 −1
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ static void aa_free_pdb(struct aa_policydb *pdb)
	if (pdb) {
		aa_put_dfa(pdb->dfa);
		kvfree(pdb->perms);
		aa_free_str_table(&pdb->trans);
		aa_destroy_str_table(&pdb->trans);
		kfree(pdb);
	}
}
+73 −37
Original line number Diff line number Diff line
@@ -450,20 +450,72 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags)
	return dfa;
}

static int process_strs_entry(char *str, int size, bool multi)
{
	int c = 1;

	if (size <= 0)
		return -1;
	if (multi) {
		if (size < 2)
			return -2;
		/* multi ends with double \0 */
		if (str[size - 2])
			return -3;
	}

	char *save = str;
	char *pos = str;
	char *end = multi ? str + size - 2 : str + size - 1;
	/* count # of internal \0 */
	while (str < end) {
		if (str == pos) {
			/* starts with ... */
			if (!*str) {
				AA_DEBUG(DEBUG_UNPACK,
					 "starting with null save=%lu size %d c=%d",
					 str - save, size, c);
				return -4;
			}
			if (isspace(*str))
				return -5;
			if (*str == ':') {
				/* :ns_str\0str\0
				 * first character after : must be valid
				 */
				if (!str[1])
					return -6;
			}
		} else if (!*str) {
			if (*pos == ':')
				*str = ':';
			else
				c++;
			pos = str +  1;
		}
		str++;
	} /* while */

	return c;
}

/**
 * unpack_trans_table - unpack a profile transition table
 * unpack_strs_table - unpack a profile transition table
 * @e: serialized data extent information  (NOT NULL)
 * @name: name of table (MAY BE NULL)
 * @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
 */
static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs)
static bool unpack_strs_table(struct aa_ext *e, const char *name, bool multi,
			      struct aa_str_table *strs)
{
	void *saved_pos = e->pos;
	char **table = NULL;
	struct aa_str_table_ent *table = NULL;

	/* exec table is optional */
	if (aa_unpack_nameX(e, AA_STRUCT, "xtable")) {
	if (aa_unpack_nameX(e, AA_STRUCT, name)) {
		u16 size;
		int i;

@@ -475,7 +527,8 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs)
			 * for size check here
			 */
			goto fail;
		table = kcalloc(size, sizeof(char *), GFP_KERNEL);
		table = kcalloc(size, sizeof(struct aa_str_table_ent),
				GFP_KERNEL);
		if (!table)
			goto fail;

@@ -483,42 +536,24 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs)
		strs->size = size;
		for (i = 0; i < size; i++) {
			char *str;
			int c, j, pos, size2 = aa_unpack_strdup(e, &str, NULL);
			int c, size2 = aa_unpack_strdup(e, &str, NULL);
			/* aa_unpack_strdup verifies that the last character is
			 * null termination byte.
			 */
			if (!size2)
				goto fail;
			table[i] = str;
			/* verify that name doesn't start with space */
			if (isspace(*str))
			c = process_strs_entry(str, size2, multi);
			if (c <= 0) {
				AA_DEBUG(DEBUG_UNPACK, "process_strs %d", c);
				goto fail;

			/* count internal #  of internal \0 */
			for (c = j = 0; j < size2 - 1; j++) {
				if (!str[j]) {
					pos = j;
					c++;
				}
			}
			if (*str == ':') {
				/* first character after : must be valid */
				if (!str[1])
					goto fail;
				/* beginning with : requires an embedded \0,
				 * verify that exactly 1 internal \0 exists
				 * trailing \0 already verified by aa_unpack_strdup
				 *
				 * convert \0 back to : for label_parse
				 */
				if (c == 1)
					str[pos] = ':';
				else if (c > 1)
					goto fail;
			} else if (c)
			if (!multi && c > 1) {
				AA_DEBUG(DEBUG_UNPACK, "!multi && c > 1");
				/* fail - all other cases with embedded \0 */
				goto fail;
			}
			table[i].strs = str;
			table[i].count = c;
			table[i].size = size2;
		}
		if (!aa_unpack_nameX(e, AA_ARRAYEND, NULL))
			goto fail;
		if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL))
@@ -527,7 +562,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_str_table *strs)
	return true;

fail:
	aa_free_str_table(strs);
	aa_destroy_str_table(strs);
	e->pos = saved_pos;
	return false;
}
@@ -795,13 +830,14 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
	 * transition table may be present even when the dfa is
	 * not. For compatibility reasons unpack and discard.
	 */
	if (!unpack_trans_table(e, &pdb->trans) && required_trans) {
	if (!unpack_strs_table(e, "xtable", false, &pdb->trans) &&
	    required_trans) {
		*info = "failed to unpack profile transition table";
		goto fail;
	}

	if (!pdb->dfa && pdb->trans.table)
		aa_free_str_table(&pdb->trans);
		aa_destroy_str_table(&pdb->trans);

	/* TODO:
	 * - move compat mapping here, requires dfa merging first
@@ -1268,7 +1304,7 @@ static bool verify_perms(struct aa_policydb *pdb)
	}
	/* deal with incorrectly constructed string tables */
	if (xmax == -1) {
		aa_free_str_table(&pdb->trans);
		aa_destroy_str_table(&pdb->trans);
	} else if (pdb->trans.size > xmax + 1) {
		if (!aa_resize_str_table(&pdb->trans, xmax + 1, GFP_KERNEL))
			return false;