[apparmor] [PATCH v2 3/5] apparmor: create new learning profile in complain mode upon disconnect exec

Ryan Lee ryan.lee at canonical.com
Wed Mar 12 17:46:01 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>
---

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




More information about the AppArmor mailing list