[apparmor] [PATCH 3/5] apparmor: create new learning profile in complain mode upon disconnect exec
Ryan Lee
ryan.lee at canonical.com
Tue Mar 4 20:55:52 UTC 2025
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.lee at canonical.com>
---
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..eaf8baa743f3 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 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
More information about the AppArmor
mailing list