Blob Blame History Raw
From 4349ae2aac8cb88ec0ab8219303d5ed071180ed9 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Fri, 9 Jun 2017 16:06:21 -0700
Subject: [PATCH 55/65] apparmor: move path_link mediation to using labels
Git-commit: 8014370f1257619226b79cb6de8e28563fbbc070
Patch-mainline: v4.13-rc1
References: FATE#323500

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
---
 security/apparmor/file.c         | 101 ++++++++++++++++++++++-----------------
 security/apparmor/include/file.h |   2 +-
 security/apparmor/lsm.c          |   3 +-
 3 files changed, 59 insertions(+), 47 deletions(-)

diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 1b216f889131..eb4c3006af67 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -370,66 +370,40 @@ static inline bool xindex_is_subset(u32 link, u32 target)
 	return 1;
 }
 
-/**
- * aa_path_link - Handle hard link permission check
- * @profile: the profile being enforced  (NOT NULL)
- * @old_dentry: the target dentry  (NOT NULL)
- * @new_dir: directory the new link will be created in  (NOT NULL)
- * @new_dentry: the link being created  (NOT NULL)
- *
- * Handle the permission test for a link & target pair.  Permission
- * is encoded as a pair where the link permission is determined
- * first, and if allowed, the target is tested.  The target test
- * is done from the point of the link match (not start of DFA)
- * making the target permission dependent on the link permission match.
- *
- * The subset test if required forces that permissions granted
- * on link are a subset of the permission granted to target.
- *
- * Returns: %0 if allowed else error
- */
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
-		 const struct path *new_dir, struct dentry *new_dentry)
+static int profile_path_link(struct aa_profile *profile,
+			     const struct path *link, char *buffer,
+			     const struct path *target, char *buffer2,
+			     struct path_cond *cond)
 {
-	struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
-	struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
-	struct path_cond cond = {
-		d_backing_inode(old_dentry)->i_uid,
-		d_backing_inode(old_dentry)->i_mode
-	};
-	char *buffer = NULL, *buffer2 = NULL;
-	const char *lname, *tname = NULL, *info = NULL;
-	struct aa_perms lperms, perms;
+	const char *lname, *tname = NULL;
+	struct aa_perms lperms = {}, perms;
+	const char *info = NULL;
 	u32 request = AA_MAY_LINK;
 	unsigned int state;
 	int error;
 
-	get_buffers(buffer, buffer2);
-	lperms = nullperms;
-
-	/* buffer freed below, lname is pointer in buffer */
-	error = aa_path_name(&link, profile->path_flags, buffer, &lname,
-			     &info, profile->disconnected);
+	error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
+			  buffer, &lname, cond, AA_MAY_LINK);
 	if (error)
 		goto audit;
 
 	/* buffer2 freed below, tname is pointer in buffer2 */
-	error = aa_path_name(&target, profile->path_flags, buffer2, &tname,
-			     &info, profile->disconnected);
+	error = path_name(OP_LINK, &profile->label, target, profile->path_flags,
+			  buffer2, &tname, cond, AA_MAY_LINK);
 	if (error)
 		goto audit;
 
 	error = -EACCES;
 	/* aa_str_perms - handles the case of the dfa being NULL */
 	state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
-			     &cond, &lperms);
+			     cond, &lperms);
 
 	if (!(lperms.allow & AA_MAY_LINK))
 		goto audit;
 
 	/* test to see if target can be paired with link */
 	state = aa_dfa_null_transition(profile->file.dfa, state);
-	aa_str_perms(profile->file.dfa, state, tname, &cond, &perms);
+	aa_str_perms(profile->file.dfa, state, tname, cond, &perms);
 
 	/* force audit/quiet masks for link are stored in the second entry
 	 * in the link pair.
@@ -440,6 +414,7 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 
 	if (!(perms.allow & AA_MAY_LINK)) {
 		info = "target restricted";
+		lperms = perms;
 		goto audit;
 	}
 
@@ -447,10 +422,10 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 	if (!(perms.allow & AA_LINK_SUBSET))
 		goto done_tests;
 
-	/* Do link perm subset test requiring allowed permission on link are a
-	 * subset of the allowed permissions on target.
+	/* Do link perm subset test requiring allowed permission on link are
+	 * a subset of the allowed permissions on target.
 	 */
-	aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond,
+	aa_str_perms(profile->file.dfa, profile->file.start, tname, cond,
 		     &perms);
 
 	/* AA_MAY_LINK is not considered in the subset test */
@@ -472,8 +447,46 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 	error = 0;
 
 audit:
-	error = aa_audit_file(profile, &lperms, OP_LINK, request,
-			      lname, tname, NULL, cond.uid, info, error);
+	return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname,
+			     NULL, cond->uid, info, error);
+}
+
+/**
+ * aa_path_link - Handle hard link permission check
+ * @label: the label being enforced  (NOT NULL)
+ * @old_dentry: the target dentry  (NOT NULL)
+ * @new_dir: directory the new link will be created in  (NOT NULL)
+ * @new_dentry: the link being created  (NOT NULL)
+ *
+ * Handle the permission test for a link & target pair.  Permission
+ * is encoded as a pair where the link permission is determined
+ * first, and if allowed, the target is tested.  The target test
+ * is done from the point of the link match (not start of DFA)
+ * making the target permission dependent on the link permission match.
+ *
+ * The subset test if required forces that permissions granted
+ * on link are a subset of the permission granted to target.
+ *
+ * Returns: %0 if allowed else error
+ */
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
+		 const struct path *new_dir, struct dentry *new_dentry)
+{
+	struct path link = { new_dir->mnt, new_dentry };
+	struct path target = { new_dir->mnt, old_dentry };
+	struct path_cond cond = {
+		d_backing_inode(old_dentry)->i_uid,
+		d_backing_inode(old_dentry)->i_mode
+	};
+	char *buffer = NULL, *buffer2 = NULL;
+	struct aa_profile *profile;
+	int error;
+
+	/* buffer freed below, lname is pointer in buffer */
+	get_buffers(buffer, buffer2);
+	error = fn_for_each_confined(label, profile,
+			profile_path_link(profile, &link, buffer, &target,
+					  buffer2, &cond));
 	put_buffers(buffer, buffer2);
 
 	return error;
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 8daad14c47fd..001e40073ff9 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -197,7 +197,7 @@ int aa_path_perm(const char *op, struct aa_label *label,
 		 const struct path *path, int flags, u32 request,
 		 struct path_cond *cond);
 
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
 		 const struct path *new_dir, struct dentry *new_dentry);
 
 int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index d0c5721aa8b3..7a986763b2b7 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -332,8 +332,7 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
 
 	label = begin_current_label_crit_section();
 	if (!unconfined(label))
-		error = aa_path_link(labels_profile(label), old_dentry, new_dir,
-				     new_dentry);
+		error = aa_path_link(label, old_dentry, new_dir, new_dentry);
 	end_current_label_crit_section(label);
 
 	return error;
-- 
2.12.3