Trying to execute a binary located at a disconnected path (e.g. through execveat() with AT_EMPTY_PATH) would result in a permission denial due to a path lookup failure, even when in complain mode. Instead, create a new learning profile, as would be done for any other complain mode execution of a binary not covered by a profile's execution rules. Because of the path aliasing that can occur in situations with disconnected paths, do not behave as if attach_disconnected was specified as a profile flag (unless, of course, the loaded profile itself has that flag set).
Signed-off-by: Ryan Lee <ryan....@canonical.com> --- v1 -> v2: fix grammar nit identified by Christian Boltz security/apparmor/domain.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 9703ec2bfa78..e383f37a1536 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -661,6 +661,13 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, AA_DEBUG(DEBUG_DOMAIN, "name lookup ix on error"); error = 0; new = aa_get_newest_label(&profile->label); + } else if (COMPLAIN_MODE(profile)) { + AA_DEBUG(DEBUG_DOMAIN, "name lookup ix on error"); + error = 0; + + name = bprm->filename; + // TODO: helper function to detangle control flow (?) + goto create_learning_profile; } name = bprm->filename; goto audit; @@ -837,7 +844,7 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, error = fn_for_each_in_ns(label, profile, profile_onexec(subj_cred, profile, onexec, stack, bprm, buffer, cond, unsafe)); - if (error) + if (error && !COMPLAIN_MODE(profile)) return ERR_PTR(error); new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_get_newest_label(onexec), @@ -850,7 +857,7 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, error = fn_for_each_in_ns(label, profile, profile_onexec(subj_cred, profile, onexec, stack, bprm, buffer, cond, unsafe)); - if (error) + if (error && !COMPLAIN_MODE(profile)) return ERR_PTR(error); new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_label_merge(&profile->label, onexec, @@ -860,17 +867,28 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred, cond, unsafe)); } - if (new) - return new; - - /* TODO: get rid of GLOBAL_ROOT_UID */ - error = fn_for_each_in_ns(label, profile, + /* + * error should only be set at this point if we're in complain mode + * Any remaining error after this block would be an error in the + * auditing process itself, which we'd want to bubble up + */ + if (error) { + /* TODO: get rid of GLOBAL_ROOT_UID */ + error = fn_for_each_in_ns(label, profile, aa_audit_file(subj_cred, profile, &nullperms, OP_CHANGE_ONEXEC, AA_MAY_ONEXEC, bprm->filename, NULL, onexec, GLOBAL_ROOT_UID, "failed to build target label", -ENOMEM, false)); - return ERR_PTR(error); + } + if (error) { + // Decrement refcount on any learning profile created earlier + aa_put_label(new); + return ERR_PTR(error); + } + + AA_BUG(!new); + return new; } /** -- 2.43.0