Blob Blame History Raw
From: John Johansen <john.johansen@canonical.com>
Date: Tue, 5 Aug 2014 09:48:54 -0700
Subject: apparmor: Fix: replacement not being applied due to cred not being updated
Patch-mainline: not yet
References: bnc#885599
The task cred can only be updated by the task it self. So profile
replacement is achieved by the task recognizing its profile is no longer
valid and updating its own cred with the new profile.

However this can not be done in all hooks because 'updating' a cred does
a GFP_KERNEL allocation. For these paths fallback grabbing a reference
to the new profile if the current cred is stale.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Miklos Szeredi <mszeredi@suse.cz>
---
 security/apparmor/include/context.h |   31 +++++++++++++++++++++++++++++++
 security/apparmor/lsm.c             |   10 ++++++----
 2 files changed, 37 insertions(+), 4 deletions(-)

--- a/security/apparmor/include/context.h
+++ b/security/apparmor/include/context.h
@@ -139,6 +139,37 @@ static inline struct aa_profile *__aa_cu
 }
 
 /**
+ * aa_begin_current_profile - find newest version of the current tasks profile
+ *
+ * Returns: newest version of confining profile (NOT NULL)
+ *
+ * This fn will not update the tasks cred, so it is safe inside of locks
+ *
+ * The returned reference must be put with aa_end_current_profile()
+ */
+static inline struct aa_profile *aa_begin_current_profile(void)
+{
+	struct aa_profile *p = __aa_current_profile();
+
+	if (PROFILE_INVALID(p))
+		p = aa_get_newest_profile(p);
+	return p;
+}
+
+/**
+ * aa_end_current_label - put a reference found with aa_begin_current_label
+ * @profile: profile reference to put
+ *
+ * Should only be used with a reference obtained with aa_begin_current_profile
+ * and never used in situations where the task cred may be updated
+ */
+static inline void aa_end_current_profile(struct aa_profile *profile)
+{
+	if (profile != __aa_current_profile())
+		aa_put_profile(profile);
+}
+
+/**
  * aa_current_profile - find the current tasks confining profile and do updates
  *
  * Returns: up to date confining profile or the ns unconfined profile (NOT NULL)
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -156,10 +156,10 @@ static int common_perm(int op, struct pa
 	struct aa_profile *profile;
 	int error = 0;
 
-	profile = __aa_current_profile();
+	profile = aa_begin_current_profile();
 	if (!unconfined(profile))
 		error = aa_path_perm(op, profile, path, 0, mask, cond);
-
+	aa_end_current_profile(profile);
 	return error;
 }
 
@@ -426,7 +426,7 @@ static int common_file_perm(int op, stru
 	    !mediated_filesystem(file->f_path.dentry))
 		return 0;
 
-	profile = __aa_current_profile();
+	profile = aa_begin_current_profile();
 
 	/* revalidate access, if task is unconfined, or the cached cred
 	 * doesn't match or if the request is for more permissions than
@@ -439,6 +439,7 @@ static int common_file_perm(int op, stru
 	    ((fprofile != profile) || (mask & ~fcxt->allow)))
 		error = aa_file_perm(op, profile, file, mask);
 
+	aa_end_current_profile(profile);
 	return error;
 }
 
@@ -597,12 +598,13 @@ fail:
 static int apparmor_task_setrlimit(struct task_struct *task,
 		unsigned int resource, struct rlimit *new_rlim)
 {
-	struct aa_profile *profile = __aa_current_profile();
+	struct aa_profile *profile = aa_begin_current_profile();
 	int error = 0;
 
 	if (!unconfined(profile))
 		error = aa_task_setrlimit(profile, task, resource, new_rlim);
 
+	aa_end_current_profile(profile);
 	return error;
 }