apparmor: convert attaching profiles via xattrs to use dfa matching

This converts profile attachment based on xattrs to a fixed extended
conditional using dfa matching.

This has a couple of advantages
- pattern matching can be used for the xattr match

- xattrs can be optional for an attachment or marked as required

- the xattr attachment conditional will be able to be combined with
  other extended conditionals when the flexible extended conditional
  work lands.

The xattr fixed extended conditional is appended to the xmatch
conditional. If an xattr attachment is specified the profile xmatch
will be generated regardless of whether there is a pattern match on
the executable name.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
This commit is contained in:
John Johansen 2017-12-12 15:28:05 -08:00
parent 8e51f9087f
commit 73f488cd90
5 changed files with 46 additions and 60 deletions

View File

@ -2147,6 +2147,10 @@ static struct aa_sfs_entry aa_sfs_entry_signal[] = {
{ }
};
static struct aa_sfs_entry aa_sfs_entry_attach[] = {
AA_SFS_FILE_BOOLEAN("xattr", 1),
{ }
};
static struct aa_sfs_entry aa_sfs_entry_domain[] = {
AA_SFS_FILE_BOOLEAN("change_hat", 1),
AA_SFS_FILE_BOOLEAN("change_hatv", 1),
@ -2155,6 +2159,7 @@ static struct aa_sfs_entry aa_sfs_entry_domain[] = {
AA_SFS_FILE_BOOLEAN("stack", 1),
AA_SFS_FILE_BOOLEAN("fix_binfmt_elf_mmap", 1),
AA_SFS_FILE_BOOLEAN("post_nnp_subset", 1),
AA_SFS_DIR("attach_conditions", aa_sfs_entry_attach),
AA_SFS_FILE_STRING("version", "1.2"),
{ }
};

View File

@ -306,11 +306,12 @@ static int change_profile_perms(struct aa_profile *profile,
* aa_xattrs_match - check whether a file matches the xattrs defined in profile
* @bprm: binprm struct for the process to validate
* @profile: profile to match against (NOT NULL)
* @state: state to start match in
*
* Returns: number of extended attributes that matched, or < 0 on error
*/
static int aa_xattrs_match(const struct linux_binprm *bprm,
struct aa_profile *profile)
struct aa_profile *profile, unsigned int state)
{
int i;
size_t size;
@ -321,27 +322,40 @@ static int aa_xattrs_match(const struct linux_binprm *bprm,
if (!bprm || !profile->xattr_count)
return 0;
/* transition from exec match to xattr set */
state = aa_dfa_null_transition(profile->xmatch, state);
d = bprm->file->f_path.dentry;
for (i = 0; i < profile->xattr_count; i++) {
size = vfs_getxattr_alloc(d, profile->xattrs[i], &value,
value_size, GFP_KERNEL);
if (size < 0) {
ret = -EINVAL;
goto out;
if (size >= 0) {
u32 perm;
/* Check the xattr value, not just presence */
state = aa_dfa_match_len(profile->xmatch, state, value,
size);
perm = dfa_user_allow(profile->xmatch, state);
if (!(perm & MAY_EXEC)) {
ret = -EINVAL;
goto out;
}
}
/* Check the xattr value, not just presence */
if (profile->xattr_lens[i]) {
if (profile->xattr_lens[i] != size) {
ret = -EINVAL;
goto out;
}
if (memcmp(value, profile->xattr_values[i], size)) {
/* transition to next element */
state = aa_dfa_null_transition(profile->xmatch, state);
if (size < 0) {
/*
* No xattr match, so verify if transition to
* next element was valid. IFF so the xattr
* was optional.
*/
if (!state) {
ret = -EINVAL;
goto out;
}
/* don't count missing optional xattr as matched */
ret--;
}
}
@ -403,13 +417,16 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
perm = dfa_user_allow(profile->xmatch, state);
/* any accepting state means a valid match. */
if (perm & MAY_EXEC) {
int ret = aa_xattrs_match(bprm, profile);
int ret = aa_xattrs_match(bprm, profile, state);
/* Fail matching if the xattrs don't match */
if (ret < 0)
continue;
/* The new match isn't more specific
/*
* TODO: allow for more flexible best match
*
* The new match isn't more specific
* than the current best match
*/
if (profile->xmatch_len == len &&
@ -428,9 +445,11 @@ static struct aa_profile *__attach_match(const struct linux_binprm *bprm,
xattrs = ret;
conflict = false;
}
} else if (!strcmp(profile->base.name, name) &&
aa_xattrs_match(bprm, profile) >= 0)
/* exact non-re match, no more searching required */
} else if (!strcmp(profile->base.name, name))
/*
* old exact non-re match, without conditionals such
* as xattrs. no more searching required
*/
return profile;
}
@ -652,7 +671,8 @@ static struct aa_label *profile_transition(struct aa_profile *profile,
* met, and fail execution otherwise
*/
label_for_each(i, new, component) {
if (aa_xattrs_match(bprm, component) < 0) {
if (aa_xattrs_match(bprm, component, state) <
0) {
error = -EACCES;
info = "required xattrs not present";
perms.allow &= ~MAY_EXEC;

View File

@ -151,8 +151,6 @@ struct aa_profile {
int xattr_count;
char **xattrs;
size_t *xattr_lens;
char **xattr_values;
struct aa_rlimit rlimits;

View File

@ -228,13 +228,9 @@ void aa_free_profile(struct aa_profile *profile)
aa_free_cap_rules(&profile->caps);
aa_free_rlimit_rules(&profile->rlimits);
for (i = 0; i < profile->xattr_count; i++) {
for (i = 0; i < profile->xattr_count; i++)
kzfree(profile->xattrs[i]);
kzfree(profile->xattr_values[i]);
}
kzfree(profile->xattrs);
kzfree(profile->xattr_lens);
kzfree(profile->xattr_values);
kzfree(profile->dirname);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);

View File

@ -540,8 +540,7 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
size = unpack_array(e, NULL);
profile->xattr_count = size;
profile->xattrs = kcalloc(size, sizeof(char *),
GFP_KERNEL);
profile->xattrs = kcalloc(size, sizeof(char *), GFP_KERNEL);
if (!profile->xattrs)
goto fail;
for (i = 0; i < size; i++) {
@ -554,38 +553,6 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile)
goto fail;
}
if (unpack_nameX(e, AA_STRUCT, "xattr_values")) {
int i, size;
size = unpack_array(e, NULL);
/* Must be the same number of xattr values as xattrs */
if (size != profile->xattr_count)
goto fail;
profile->xattr_lens = kcalloc(size, sizeof(size_t),
GFP_KERNEL);
if (!profile->xattr_lens)
goto fail;
profile->xattr_values = kcalloc(size, sizeof(char *),
GFP_KERNEL);
if (!profile->xattr_values)
goto fail;
for (i = 0; i < size; i++) {
profile->xattr_lens[i] = unpack_blob(e,
&profile->xattr_values[i], NULL);
profile->xattr_values[i] =
kvmemdup(profile->xattr_values[i],
profile->xattr_lens[i]);
}
if (!unpack_nameX(e, AA_ARRAYEND, NULL))
goto fail;
if (!unpack_nameX(e, AA_STRUCTEND, NULL))
goto fail;
}
return 1;
fail: