Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f3b3f2d authored by John Johansen's avatar John Johansen
Browse files

apparmor: add profile permission query ability



Allow userspace to query a profile about permissions, through the
transaction interface that is already used to allow userspace to
query about key,value data.

Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent 2d679f3c
Loading
Loading
Loading
Loading
+102 −1
Original line number Diff line number Diff line
@@ -595,6 +595,40 @@ static const struct file_operations aa_fs_ns_revision_fops = {
	.release	= ns_revision_release,
};

static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
			     const char *match_str, size_t match_len)
{
	struct aa_perms tmp;
	struct aa_dfa *dfa;
	unsigned int state = 0;

	if (unconfined(profile))
		return;
	if (profile->file.dfa && *match_str == AA_CLASS_FILE) {
		dfa = profile->file.dfa;
		state = aa_dfa_match_len(dfa, profile->file.start,
					 match_str + 1, match_len - 1);
		tmp = nullperms;
		if (state) {
			struct path_cond cond = { };

			tmp = aa_compute_fperms(dfa, state, &cond);
		}
	} else if (profile->policy.dfa) {
		if (!PROFILE_MEDIATES_SAFE(profile, *match_str))
			return;	/* no change to current perms */
		dfa = profile->policy.dfa;
		state = aa_dfa_match_len(dfa, profile->policy.start[0],
					 match_str, match_len);
		if (state)
			aa_compute_perms(dfa, state, &tmp);
		else
			tmp = nullperms;
	}
	aa_apply_modes_to_perms(profile, &tmp);
}


/**
 * query_data - queries a policy and writes its data to buf
 * @buf: the resulting data is stored here (NOT NULL)
@@ -673,6 +707,64 @@ static ssize_t query_data(char *buf, size_t buf_len,
	return out - buf;
}

/**
 * query_label - queries a label and writes permissions to buf
 * @buf: the resulting permissions string is stored here (NOT NULL)
 * @buf_len: size of buf
 * @query: binary query string to match against the dfa
 * @query_len: size of query
 * @view_only: only compute for querier's view
 *
 * The buffers pointed to by buf and query may overlap. The query buffer is
 * parsed before buf is written to.
 *
 * The query should look like "LABEL_NAME\0DFA_STRING" where LABEL_NAME is
 * the name of the label, in the current namespace, that is to be queried and
 * DFA_STRING is a binary string to match against the label(s)'s DFA.
 *
 * LABEL_NAME must be NUL terminated. DFA_STRING may contain NUL characters
 * but must *not* be NUL terminated.
 *
 * Returns: number of characters written to buf or -errno on failure
 */
static ssize_t query_label(char *buf, size_t buf_len,
			   char *query, size_t query_len, bool view_only)
{
	struct aa_profile *profile, *curr;
	char *label_name, *match_str;
	size_t label_name_len, match_len;
	struct aa_perms perms;

	if (!query_len)
		return -EINVAL;

	label_name = query;
	label_name_len = strnlen(query, query_len);
	if (!label_name_len || label_name_len == query_len)
		return -EINVAL;

	/**
	 * The extra byte is to account for the null byte between the
	 * profile name and dfa string. profile_name_len is greater
	 * than zero and less than query_len, so a byte can be safely
	 * added or subtracted.
	 */
	match_str = label_name + label_name_len + 1;
	match_len = query_len - label_name_len - 1;

	curr = aa_current_profile();
	profile = aa_fqlookupn_profile(curr, label_name, label_name_len);
	if (!profile)
		return -ENOENT;

	perms = allperms;
	profile_query_cb(profile, &perms, match_str, match_len);

	return scnprintf(buf, buf_len,
		      "allow 0x%08x\ndeny 0x%08x\naudit 0x%08x\nquiet 0x%08x\n",
		      perms.allow, perms.deny, perms.audit, perms.quiet);
}

/*
 * Transaction based IO.
 * The file expects a write which triggers the transaction, and then
@@ -773,6 +865,9 @@ static int multi_transaction_release(struct inode *inode, struct file *file)
	return 0;
}

#define QUERY_CMD_PROFILE	"profile\0"
#define QUERY_CMD_PROFILE_LEN	8

#define QUERY_CMD_DATA		"data\0"
#define QUERY_CMD_DATA_LEN	5

@@ -810,7 +905,12 @@ static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
	if (IS_ERR(t))
		return PTR_ERR(t);

	if (count > QUERY_CMD_DATA_LEN &&
	if (count > QUERY_CMD_PROFILE_LEN &&
	    !memcmp(t->data, QUERY_CMD_PROFILE, QUERY_CMD_PROFILE_LEN)) {
		len = query_label(t->data, MULTI_TRANSACTION_LIMIT,
				  t->data + QUERY_CMD_PROFILE_LEN,
				  count - QUERY_CMD_PROFILE_LEN, true);
	} else if (count > QUERY_CMD_DATA_LEN &&
		   !memcmp(t->data, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
		len = query_data(t->data, MULTI_TRANSACTION_LIMIT,
				 t->data + QUERY_CMD_DATA_LEN,
@@ -1952,6 +2052,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
};

static struct aa_sfs_entry aa_sfs_entry_query_label[] = {
	AA_SFS_FILE_STRING("perms", "allow deny audit quiet"),
	AA_SFS_FILE_BOOLEAN("data",		1),
	AA_SFS_FILE_BOOLEAN("multi_transaction",	1),
	{ }