[apparmor] [PATCH 23/24] apparmor: treat each task as if the label can have mutiple entries

John Johansen john.johansen at canonical.com
Wed Feb 27 18:14:22 UTC 2013


next baby step to labels. Update most code to walk labels as if there
is multiple entries in a label, even though atm there can only be
one.

This does not update the domain transitions, exec, change_hat, change_profile
(separate patch).

Also it bails on first error, where for learning purposes it might be
desireable to check permission, and log against all profiles before failing.

Signed-off-by: John Johansen <john.johansen at canonical.com>
---
 security/apparmor/file.c         |  167 +++++++++++++++++++++++---------------
 security/apparmor/include/file.h |    7 +-
 security/apparmor/ipc.c          |   22 +++--
 security/apparmor/lsm.c          |   48 +++++++----
 security/apparmor/resource.c     |   62 ++++++++------
 5 files changed, 193 insertions(+), 113 deletions(-)

diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index cd21ec5..15847fb 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -267,7 +267,7 @@ static inline bool is_deleted(struct dentry *dentry)
 /**
  * aa_path_perm - do permissions check & audit for @path
  * @op: operation being checked
- * @profile: profile being enforced  (NOT NULL)
+ * @label: profile being enforced  (NOT NULL)
  * @path: path to check permissions of  (NOT NULL)
  * @flags: any additional path flags beyond what the profile specifies
  * @request: requested permissions
@@ -275,15 +275,17 @@ static inline bool is_deleted(struct dentry *dentry)
  *
  * Returns: %0 else error if access denied or other error
  */
-int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
+int aa_path_perm(int op, struct aa_label *label, struct path *path,
 		 int flags, u32 request, struct path_cond *cond)
 {
 	char *buffer = NULL;
 	struct file_perms perms = {};
 	const char *name, *info = NULL;
-	int error;
+	struct aa_profile *profile;
+	int i, error;
 
-	flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
+	/* TODO: fix path lookup flags */
+	flags |= labels_profile(label)->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
 	error = aa_path_name(path, flags, &buffer, &name, &info);
 	if (error) {
 		if (error == -ENOENT && is_deleted(path->dentry)) {
@@ -294,14 +296,29 @@ int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
 			info = NULL;
 			perms.allow = request;
 		}
-	} else {
+
+		label_for_each_confined(i, label, profile) {
+			error = aa_audit_file(profile, &perms, GFP_KERNEL,
+					      op, request, name, NULL,
+					      cond->uid, info, error);
+			if (error)
+				break;
+		}
+		goto out;
+	}
+
+	label_for_each_confined(i, label, profile) {
 		aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
 			     &perms);
 		if (request & ~perms.allow)
 			error = -EACCES;
+		error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
+				      name, NULL, cond->uid, info, error);
+		if (error)
+			break;
 	}
-	error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name,
-			      NULL, cond->uid, info, error);
+
+out:
 	kfree(buffer);
 
 	return error;
@@ -329,7 +346,7 @@ static inline bool xindex_is_subset(u32 link, u32 target)
 
 /**
  * aa_path_link - Handle hard link permission check
- * @profile: the profile being enforced  (NOT NULL)
+ * @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)
@@ -345,7 +362,7 @@ static inline bool xindex_is_subset(u32 link, u32 target)
  *
  * Returns: %0 if allowed else error
  */
-int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
+int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
 		 struct path *new_dir, struct dentry *new_dentry)
 {
 	struct path link = { new_dir->mnt, new_dentry };
@@ -359,93 +376,115 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 	struct file_perms lperms, perms;
 	u32 request = AA_MAY_LINK;
 	unsigned int state;
-	int error;
+	struct aa_profile *profile;
+	int i, error;
 
 	lperms = nullperms;
 
+	/* TODO: fix path lookup flags, auditing of failed path for profile */
+	profile = labels_profile(label);
 	/* buffer freed below, lname is pointer in buffer */
-	error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
+	error = aa_path_name(&link, labels_profile(label)->path_flags, &buffer, &lname,
 			     &info);
 	if (error)
-		goto audit;
+		goto err;
 
 	/* buffer2 freed below, tname is pointer in buffer2 */
-	error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
+	error = aa_path_name(&target, labels_profile(label)->path_flags, &buffer2, &tname,
 			     &info);
 	if (error)
-		goto audit;
+		goto err;
 
-	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);
 
-	if (!(lperms.allow & AA_MAY_LINK))
-		goto audit;
+	label_for_each_confined(i, label, profile) {
+		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);
 
-	/* 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);
+		if (!(lperms.allow & AA_MAY_LINK))
+			goto audit;
 
-	/* force audit/quiet masks for link are stored in the second entry
-	 * in the link pair.
-	 */
-	lperms.audit = perms.audit;
-	lperms.quiet = perms.quiet;
-	lperms.kill = perms.kill;
+		/* 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);
 
-	if (!(perms.allow & AA_MAY_LINK)) {
-		info = "target restricted";
-		goto audit;
-	}
+		/* force audit/quiet masks for link are stored in the second
+		 * entry in the link pair.
+		 */
+		lperms.audit = perms.audit;
+		lperms.quiet = perms.quiet;
+		lperms.kill = perms.kill;
 
-	/* done if link subset test is not required */
-	if (!(perms.allow & AA_LINK_SUBSET))
-		goto done_tests;
+		if (!(perms.allow & AA_MAY_LINK)) {
+			info = "target restricted";
+			goto audit;
+		}
 
-	/* 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,
-		     &perms);
-
-	/* AA_MAY_LINK is not considered in the subset test */
-	request = lperms.allow & ~AA_MAY_LINK;
-	lperms.allow &= perms.allow | AA_MAY_LINK;
-
-	request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow);
-	if (request & ~lperms.allow) {
-		goto audit;
-	} else if ((lperms.allow & MAY_EXEC) &&
-		   !xindex_is_subset(lperms.xindex, perms.xindex)) {
-		lperms.allow &= ~MAY_EXEC;
-		request |= MAY_EXEC;
-		info = "link not subset of target";
-		goto audit;
-	}
+		/* done if link subset test is not required */
+		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.
+		 */
+		aa_str_perms(profile->file.dfa, profile->file.start, tname,
+			     &cond, &perms);
+
+		/* AA_MAY_LINK is not considered in the subset test */
+		request = lperms.allow & ~AA_MAY_LINK;
+		lperms.allow &= perms.allow | AA_MAY_LINK;
+
+		request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow);
+		if (request & ~lperms.allow) {
+			goto audit;
+		} else if ((lperms.allow & MAY_EXEC) &&
+			   !xindex_is_subset(lperms.xindex, perms.xindex)) {
+			lperms.allow &= ~MAY_EXEC;
+			request |= MAY_EXEC;
+			info = "link not subset of target";
+			goto audit;
+		}
 
-done_tests:
-	error = 0;
+	done_tests:
+		error = 0;
 
-audit:
-	error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request,
-			      lname, tname, cond.uid, info, error);
+	audit:
+		error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK,
+				      request, lname, tname, cond.uid, info,
+				      error);
+		if (error)
+			break;
+	}
+
+out:
 	kfree(buffer);
 	kfree(buffer2);
 
 	return error;
+
+err:
+	label_for_each_confined(i, label, profile) {
+		error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK,
+				      request, lname, tname, cond.uid, info,
+				      error);
+		if (error)
+			break;
+	}
+
+	goto out;
 }
 
 /**
  * aa_file_perm - do permission revalidation check & audit for @file
  * @op: operation being checked
- * @profile: profile being enforced   (NOT NULL)
+ * @label: profile being enforced   (NOT NULL)
  * @file: file to revalidate access permissions on  (NOT NULL)
  * @request: requested permissions
  *
  * Returns: %0 if access allowed else error
  */
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(int op, struct aa_label *label, struct file *file,
 		 u32 request)
 {
 	struct path_cond cond = {
@@ -453,6 +492,6 @@ int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
 		.mode = file->f_path.dentry->d_inode->i_mode
 	};
 
-	return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED,
+	return aa_path_perm(op, label, &file->f_path, PATH_DELEGATE_DELETED,
 			    request, &cond);
 }
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 2c922b8..0b0c1ab 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -17,6 +17,7 @@
 
 #include "domain.h"
 #include "match.h"
+#include "label.h"
 
 struct aa_profile;
 struct path;
@@ -171,13 +172,13 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
 			  const char *name, struct path_cond *cond,
 			  struct file_perms *perms);
 
-int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
+int aa_path_perm(int op, struct aa_label *label, 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,
 		 struct path *new_dir, struct dentry *new_dentry);
 
-int aa_file_perm(int op, struct aa_profile *profile, struct file *file,
+int aa_file_perm(int op, struct aa_label *label, struct file *file,
 		 u32 request);
 
 static inline void aa_free_file_rules(struct aa_file_rules *rules)
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index 364a76b..a780917 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -64,15 +64,25 @@ static int aa_audit_ptrace(struct aa_profile *profile,
 int aa_may_ptrace(struct task_struct *tracer_task, struct aa_label *tracer,
 		  struct aa_label *tracee, unsigned int mode)
 {
-	/* TODO: currently only based on capability, not extended ptrace
-	 *       rules,
-	 *       Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH
-	 */
+	struct aa_profile *profile;
+	int i, error = 0;
 
 	if (unconfined(tracer) || tracer == tracee)
 		return 0;
-	/* log this capability request */
-	return aa_capable(tracer_task, labels_profile(tracer), CAP_SYS_PTRACE, 1);
+
+	label_for_each_confined(i, tracer, profile) {
+		/* TODO: currently only based on capability, not extended ptrace
+		 *       rules,
+		 *       Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH
+		 */
+
+		/* log this capability request */
+		error = aa_capable(tracer_task, profile, CAP_SYS_PTRACE, 1);
+		if (error)
+			break;
+	}
+
+	return error;
 }
 
 /**
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index c4eb445..eb7bf2a 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -127,9 +127,17 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
 	*inheritable = cred->cap_inheritable;
 	*permitted = cred->cap_permitted;
 
-	if (!unconfined(label) && !COMPLAIN_MODE(labels_profile(label))) {
-		*effective = cap_intersect(*effective, labels_profile(label)->caps.allow);
-		*permitted = cap_intersect(*permitted, labels_profile(label)->caps.allow);
+	if (!unconfined(label)) {
+		struct aa_profile *profile;
+		int i;
+		label_for_each_confined(i, label, profile) {
+			if (COMPLAIN_MODE(profile))
+				continue;
+			*effective = cap_intersect(*effective,
+						   profile->caps.allow);
+			*permitted = cap_intersect(*permitted,
+						   profile->caps.allow);
+		}
 	}
 	rcu_read_unlock();
 
@@ -139,14 +147,23 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
 static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
 			    int cap, int audit)
 {
+	struct aa_profile *profile;
 	struct aa_label *label;
 	/* cap_capable returns 0 on success, else -EPERM */
-	int error = cap_capable(cred, ns, cap, audit);
-	if (!error) {
-		label = aa_cred_label(cred);
-		if (!unconfined(label))
-			error = aa_capable(current, labels_profile(label), cap, audit);
+	int i, error = cap_capable(cred, ns, cap, audit);
+	if (error)
+		return error;
+
+	label = aa_cred_label(cred);
+	if (unconfined(label))
+		return 0;
+
+	label_for_each_confined(i, label, profile) {
+		error = aa_capable(current, profile, cap, audit);
+		if (error)
+			break;
 	}
+
 	return error;
 }
 
@@ -167,7 +184,7 @@ static int common_perm(int op, struct path *path, u32 mask,
 
 	label = __aa_current_label();
 	if (!unconfined(label))
-		error = aa_path_perm(op, labels_profile(label), path, 0, mask, cond);
+		error = aa_path_perm(op, label, path, 0, mask, cond);
 
 	return error;
 }
@@ -310,7 +327,7 @@ static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir,
 
 	label = aa_current_label();
 	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);
 	return error;
 }
 
@@ -331,12 +348,12 @@ static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
 					  old_dentry->d_inode->i_mode
 		};
 
-		error = aa_path_perm(OP_RENAME_SRC, labels_profile(label), &old_path, 0,
+		error = aa_path_perm(OP_RENAME_SRC, label, &old_path, 0,
 				     MAY_READ | AA_MAY_META_READ | MAY_WRITE |
 				     AA_MAY_META_WRITE | AA_MAY_DELETE,
 				     &cond);
 		if (!error)
-			error = aa_path_perm(OP_RENAME_DEST, labels_profile(label), &new_path,
+			error = aa_path_perm(OP_RENAME_DEST, label, &new_path,
 					     0, MAY_WRITE | AA_MAY_META_WRITE |
 					     AA_MAY_CREATE, &cond);
 
@@ -349,7 +366,8 @@ static int apparmor_path_chmod(struct path *path, umode_t mode)
 	if (!mediated_filesystem(path->dentry->d_inode))
 		return 0;
 
-	return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD);
+	return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry,
+				      AA_MAY_CHMOD);
 }
 
 static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
@@ -397,7 +415,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
 		struct inode *inode = file->f_path.dentry->d_inode;
 		struct path_cond cond = { inode->i_uid, inode->i_mode };
 
-		error = aa_path_perm(OP_OPEN, labels_profile(label), &file->f_path, 0,
+		error = aa_path_perm(OP_OPEN, label, &file->f_path, 0,
 				     aa_map_file_to_perms(file), &cond);
 		/* todo cache full allowed permissions set and state */
 		fcxt->allow = aa_map_file_to_perms(file);
@@ -446,7 +464,7 @@ static int common_file_perm(int op, struct file *file, u32 mask)
 	 */
 	if (!unconfined(label) && !unconfined(flabel) &&
 	    ((flabel != label) || (mask & ~fcxt->allow)))
-		error = aa_file_perm(op, labels_profile(label), file, mask);
+		error = aa_file_perm(op, label, file, mask);
 
 	return error;
 }
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c
index e0a1d39..b9489c5 100644
--- a/security/apparmor/resource.c
+++ b/security/apparmor/resource.c
@@ -91,8 +91,9 @@ int aa_map_resource(int resource)
 int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
 		      unsigned int resource, struct rlimit *new_rlim)
 {
+	struct aa_profile *profile;
 	struct aa_label *task_label;
-	int error = 0;
+	int i, error = 0;
 
 	rcu_read_lock();
 	task_label = aa_get_label(aa_cred_label(__task_cred(task)));
@@ -103,14 +104,20 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
 	 * that the task is setting the resource of a task confined with
 	 * the same profile.
 	 */
-	if (label != task_label ||
-	    (labels_profile(label)->rlimits.mask & (1 << resource) &&
-	     new_rlim->rlim_max > labels_profile(label)->rlimits.limits[resource].rlim_max))
-		error = -EACCES;
 
+	label_for_each_confined(i, label, profile) {
+		if (label != task_label ||
+		    (profile->rlimits.mask & (1 << resource) &&
+		     new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max))
+			error = -EACCES;
+		error = audit_resource(labels_profile(label), resource,
+				       new_rlim->rlim_max, error);
+		if (error)
+			break;
+	}
 	aa_put_label(task_label);
 
-	return audit_resource(labels_profile(label), resource, new_rlim->rlim_max, error);
+	return error;
 }
 
 /**
@@ -128,31 +135,36 @@ void __aa_transition_rlimits(struct aa_label *old_l, struct aa_label *new_l)
 	old = labels_profile(old_l);
 	new = labels_profile(new_l);
 
-	/* for any rlimits the profile controlled reset the soft limit
-	 * to the less of the tasks hard limit and the init tasks soft limit
+	/* for any rlimits the profile controlled, reset the soft limit
+	 * to the lesser of the tasks hard limit and the init tasks soft limit
 	 */
-	if (old->rlimits.mask) {
-		for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
-			if (old->rlimits.mask & mask) {
-				rlim = current->signal->rlim + i;
-				initrlim = init_task.signal->rlim + i;
-				rlim->rlim_cur = min(rlim->rlim_max,
-						     initrlim->rlim_cur);
+	label_for_each_confined(i, old_l, old) {
+		if (old->rlimits.mask) {
+			for (i = 0, mask = 1; i < RLIM_NLIMITS; i++,
+				     mask <<= 1) {
+				if (old->rlimits.mask & mask) {
+					rlim = current->signal->rlim + i;
+					initrlim = init_task.signal->rlim + i;
+					rlim->rlim_cur = min(rlim->rlim_max,
+							     initrlim->rlim_cur);
+				}
 			}
 		}
 	}
 
 	/* set any new hard limits as dictated by the new profile */
-	if (!new->rlimits.mask)
-		return;
-	for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
-		if (!(new->rlimits.mask & mask))
+	label_for_each_confined(i, new_l, new) {
+		if (!new->rlimits.mask)
 			continue;
-
-		rlim = current->signal->rlim + i;
-		rlim->rlim_max = min(rlim->rlim_max,
-				     new->rlimits.limits[i].rlim_max);
-		/* soft limit should not exceed hard limit */
-		rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
+		for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) {
+			if (!(new->rlimits.mask & mask))
+				continue;
+
+			rlim = current->signal->rlim + i;
+			rlim->rlim_max = min(rlim->rlim_max,
+					     new->rlimits.limits[i].rlim_max);
+			/* soft limit should not exceed hard limit */
+			rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max);
+		}
 	}
 }
-- 
1.7.10.4




More information about the AppArmor mailing list