[AppArmor 44/47] AppArmor: all the rest

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



All the things that didn't nicely fit in a category on their own: kbuild
code, declararions and inline functions, /sys/kernel/security/apparmor
filesystem for controlling apparmor from user space, profile list
functions, locking documentation, /proc/$pid/task/$tid/attr/current
access.

Signed-off-by: John Johansen <[email protected]>
Signed-off-by: Andreas Gruenbacher <[email protected]>

---
 security/apparmor/Kconfig      |    9 +
 security/apparmor/Makefile     |   13 +
 security/apparmor/apparmor.h   |  323 +++++++++++++++++++++++++++++++++++++++++
 security/apparmor/apparmorfs.c |  252 +++++++++++++++++++++++++++++++
 security/apparmor/inline.h     |  240 ++++++++++++++++++++++++++++++
 security/apparmor/list.c       |  156 +++++++++++++++++++
 security/apparmor/locking.txt  |   68 ++++++++
 security/apparmor/procattr.c   |  194 ++++++++++++++++++++++++
 8 files changed, 1255 insertions(+)

--- /dev/null
+++ b/security/apparmor/Kconfig
@@ -0,0 +1,9 @@
+config SECURITY_APPARMOR
+	tristate "AppArmor support"
+	depends on SECURITY!=n
+	help
+	  This enables the AppArmor security module.
+	  Required userspace tools (if they are not included in your
+	  distribution) and further information may be found at
+	  <http://forge.novell.com/modules/xfmod/project/?apparmor>
+	  If you are unsure how to answer this question, answer N.
--- /dev/null
+++ b/security/apparmor/Makefile
@@ -0,0 +1,13 @@
+# Makefile for AppArmor Linux Security Module
+#
+obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
+
+apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \
+	      module_interface.o match.o
+
+quiet_cmd_make-caps = GEN     $@
+cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
+
+$(obj)/main.o : $(obj)/capability_names.h
+$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
+	$(call cmd,make-caps)
--- /dev/null
+++ b/security/apparmor/apparmor.h
@@ -0,0 +1,323 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2 of the
+ *	License.
+ *
+ *	AppArmor internal prototypes
+ */
+
+#ifndef __APPARMOR_H
+#define __APPARMOR_H
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/rcupdate.h>
+
+/*
+ * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
+ * for profile permissions
+ */
+#define AA_MAY_LINK			0x0010
+#define AA_MAY_LOCK			0x0020
+#define AA_EXEC_MMAP			0x0040
+#define AA_EXEC_UNSAFE			0x0080
+#define AA_EXEC_MOD_0			0x0100
+#define AA_EXEC_MOD_1			0x0200
+#define AA_BASE_PERMS			(MAY_READ | MAY_WRITE | MAY_EXEC | \
+					 MAY_APPEND | AA_MAY_LINK | \
+					 AA_MAY_LOCK | AA_EXEC_MMAP | \
+					 AA_EXEC_UNSAFE | AA_EXEC_MOD_0 | \
+					 AA_EXEC_MOD_1)
+#define AA_LINK_SUBSET_TEST		0x0020
+
+#define AA_EXEC_UNCONFINED		0
+#define AA_EXEC_INHERIT			AA_EXEC_MOD_0
+#define AA_EXEC_PROFILE			AA_EXEC_MOD_1
+#define AA_EXEC_PIX			(AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+
+#define AA_EXEC_MODIFIERS		(AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+
+#define AA_USER_SHIFT			0
+#define AA_OTHER_SHIFT			10
+
+#define AA_USER_PERMS			(AA_BASE_PERMS << AA_USER_SHIFT)
+#define AA_OTHER_PERMS			(AA_BASE_PERMS << AA_OTHER_SHIFT)
+
+#define AA_FILE_PERMS			(AA_USER_PERMS | AA_OTHER_PERMS)
+
+#define AA_LINK_BITS			((AA_MAY_LINK << AA_USER_SHIFT) | \
+					 (AA_MAY_LINK << AA_OTHER_SHIFT))
+
+#define AA_USER_EXEC			(MAY_EXEC << AA_USER_SHIFT)
+#define AA_OTHER_EXEC			(MAY_EXEC << AA_OTHER_SHIFT)
+
+#define AA_USER_EXEC_MODS		(AA_EXEC_MODIFIERS << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_MODS		(AA_EXEC_MODIFIERS << AA_OTHER_SHIFT)
+
+#define AA_USER_EXEC_UNSAFE		(AA_EXEC_UNSAFE << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_UNSAFE		(AA_EXEC_UNSAFE << AA_OTHER_SHIFT)
+
+#define AA_EXEC_BITS			(AA_USER_EXEC | AA_OTHER_EXEC)
+
+#define AA_ALL_EXEC_MODS		(AA_USER_EXEC_MODS | \
+					 AA_OTHER_EXEC_MODS)
+
+/* shared permissions that are not duplicated in user:group:other */
+#define AA_CHANGE_PROFILE		0x40000000
+
+#define AA_SHARED_PERMS			(AA_CHANGE_PROFILE)
+
+#define AA_VALID_PERM_MASK		(AA_FILE_PERMS | AA_SHARED_PERMS)
+
+#define AA_SECURE_EXEC_NEEDED		1
+
+/* Control parameters (0 or 1), settable thru module/boot flags or
+ * via /sys/kernel/security/apparmor/control */
+extern int apparmor_complain;
+extern int apparmor_debug;
+extern int apparmor_audit;
+extern int apparmor_logsyscall;
+extern unsigned int apparmor_path_max;
+
+#define PROFILE_COMPLAIN(_profile) \
+	(apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain))
+
+#define APPARMOR_COMPLAIN(_cxt) \
+	(apparmor_complain == 1 || \
+	 ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.complain))
+
+#define PROFILE_AUDIT(_profile) \
+	(apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit))
+
+#define APPARMOR_AUDIT(_cxt) \
+	(apparmor_audit == 1 || \
+	 ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.audit))
+
+/*
+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
+ * which is not related to profile accesses.
+ */
+
+#define AA_DEBUG(fmt, args...)						\
+	do {								\
+		if (apparmor_debug)					\
+			printk(KERN_DEBUG "AppArmor: " fmt, ##args);	\
+	} while (0)
+
+#define AA_ERROR(fmt, args...)	printk(KERN_ERR "AppArmor: " fmt, ##args)
+
+struct aa_profile;
+
+/* struct aa_namespace - namespace for a set of profiles
+ * @name: the name of the namespace
+ * @list: list the namespace is on
+ * @profiles: list of profile in the namespace
+ * @profile_count: the number of profiles in the namespace
+ * @null_complain_profile: special profile used for learning in this namespace
+ * @count: reference count on the namespace
+ * @lock: lock for adding/removing profile to the namespace
+ */
+struct aa_namespace {
+	char *name;
+	struct list_head list;
+	struct list_head profiles;
+	int profile_count;
+	struct aa_profile *null_complain_profile;
+
+	struct kref count;
+	rwlock_t lock;
+};
+
+/* struct aa_profile - basic confinement data
+ * @name: the profiles name
+ * @list: list this profile is on
+ * @ns: namespace the profile is in
+ * @file_rules: dfa containing the profiles file rules
+ * @flags: flags controlling profile behavior
+ * @isstale: flag indicating if profile is stale
+ * @capabilities: capabilities granted by the process
+ * @count: reference count of the profile
+ *
+ * The AppArmor profile contains the basic confinement data.  Each profile
+ * has a name, and all nonstale profile are in a profile namespace.
+ *
+ * The task_contexts list and the isstale flag are protected by the
+ * profile lock.
+ *
+ * If a task context is moved between two profiles, we first need to grab
+ * both profile locks. lock_both_profiles() does that in a deadlock-safe
+ * way.
+ */
+struct aa_profile {
+	char *name;
+	struct list_head list;
+	struct aa_namespace *ns;
+
+	struct aa_dfa *file_rules;
+	struct {
+		int complain;
+		int audit;
+	} flags;
+	int isstale;
+
+	kernel_cap_t capabilities;
+	struct kref count;
+	struct list_head task_contexts;
+	spinlock_t lock;
+	unsigned long int_flags;
+};
+
+extern struct list_head profile_ns_list;
+extern rwlock_t profile_ns_list_lock;
+extern struct mutex aa_interface_lock;
+
+/**
+ * struct aa_task_context - primary label for confined tasks
+ * @profile: the current profile
+ * @previous_profile: profile the task may return to
+ * @cookie: magic value the task must know for returning to @previous_profile
+ * @list: list this aa_task_context is on
+ * @task: task that the aa_task_context confines
+ * @rcu: rcu head used when freeing the aa_task_context
+ * @caps_logged: caps that have previously generated log entries
+ *
+ * Contains the task's current profile (which could change due to
+ * change_hat).  Plus the hat_magic needed during change_hat.
+ */
+struct aa_task_context {
+	struct aa_profile *profile;
+	struct aa_profile *previous_profile;
+	u64 cookie;
+	struct list_head list;
+	struct task_struct *task;
+	struct rcu_head rcu;
+	kernel_cap_t caps_logged;
+};
+
+extern struct aa_namespace *default_namespace;
+
+/* aa_audit - AppArmor auditing structure
+ * Structure is populated by access control code and passed to aa_audit which
+ * provides for a single point of logging.
+ */
+
+struct aa_audit {
+	const char *operation;
+	gfp_t gfp_mask;
+	const char *info;
+	const char *name;
+	const char *name2;
+	const char *name3;
+	int request_mask, denied_mask;
+	struct iattr *iattr;
+	pid_t task, parent;
+	int error_code;
+};
+
+/* Flags for the permission check functions */
+#define AA_CHECK_FD	1  /* coming from a file descriptor */
+#define AA_CHECK_DIR	2  /* file type is directory */
+
+/* lock subtypes so lockdep does not raise false dependencies */
+enum aa_lock_class {
+	aa_lock_normal,
+	aa_lock_nested,
+	aa_lock_task_release
+};
+
+/* main.c */
+extern int alloc_default_namespace(void);
+extern void free_default_namespace(void);
+extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
+			    int type);
+void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa);
+void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa);
+int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa);
+extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
+				  const char *);
+extern int aa_audit(struct aa_profile *profile, struct aa_audit *);
+
+extern int aa_attr(struct aa_profile *profile, struct dentry *dentry,
+		   struct vfsmount *mnt, struct iattr *iattr);
+extern int aa_perm_xattr(struct aa_profile *profile, const char *operation,
+			 struct dentry *dentry, struct vfsmount *mnt,
+			 int mask, int check);
+extern int aa_capability(struct aa_task_context *cxt, int cap);
+extern int aa_perm(struct aa_profile *profile, const char *operation,
+		   struct dentry *dentry, struct vfsmount *mnt, int mask,
+		   int check);
+extern int aa_perm_dir(struct aa_profile *profile, const char *operation,
+		       struct dentry *dentry, struct vfsmount *mnt,
+		       int mask);
+extern int aa_perm_path(struct aa_profile *, const char *operation,
+			const char *name, int mask, uid_t uid);
+extern int aa_link(struct aa_profile *profile,
+		   struct dentry *link, struct vfsmount *link_mnt,
+		   struct dentry *target, struct vfsmount *target_mnt);
+extern int aa_clone(struct task_struct *task);
+extern int aa_register(struct linux_binprm *bprm);
+extern void aa_release(struct task_struct *task);
+extern int aa_change_hat(const char *id, u64 hat_magic);
+extern int aa_change_profile(const char *ns_name, const char *name);
+extern struct aa_profile *__aa_replace_profile(struct task_struct *task,
+					       struct aa_profile *profile);
+extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task,
+						      struct aa_profile *profile);
+extern void unlock_task_and_profiles(struct task_struct *task,
+				     struct aa_task_context *cxt,
+				     struct aa_profile *profile);
+extern void aa_change_task_context(struct task_struct *task,
+				   struct aa_task_context *new_cxt,
+				   struct aa_profile *profile, u64 cookie,
+				   struct aa_profile *previous_profile);
+extern int aa_may_ptrace(struct aa_task_context *cxt,
+			 struct aa_profile *tracee);
+
+/* list.c */
+extern struct aa_namespace *__aa_find_namespace(const char *name,
+						struct list_head *list);
+extern struct aa_profile *__aa_find_profile(const char *name,
+					    struct list_head *list);
+extern void aa_profile_ns_list_release(void);
+
+/* module_interface.c */
+extern ssize_t aa_add_profile(void *, size_t);
+extern ssize_t aa_replace_profile(void *, size_t);
+extern ssize_t aa_remove_profile(char *, size_t);
+extern struct aa_namespace *alloc_aa_namespace(char *name);
+extern void free_aa_namespace(struct aa_namespace *ns);
+extern void free_aa_namespace_kref(struct kref *kref);
+extern struct aa_profile *alloc_aa_profile(void);
+extern void free_aa_profile(struct aa_profile *profile);
+extern void free_aa_profile_kref(struct kref *kref);
+extern void aa_unconfine_tasks(struct aa_profile *profile);
+
+/* procattr.c */
+extern int aa_getprocattr(struct aa_profile *profile, char **string,
+			  unsigned *len);
+extern int aa_setprocattr_changehat(char *args);
+extern int aa_setprocattr_changeprofile(char *args);
+extern int aa_setprocattr_setprofile(struct task_struct *task, char *args);
+
+/* apparmorfs.c */
+extern int create_apparmorfs(void);
+extern void destroy_apparmorfs(void);
+
+/* match.c */
+extern struct aa_dfa *aa_match_alloc(void);
+extern void aa_match_free(struct aa_dfa *dfa);
+extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
+extern int verify_dfa(struct aa_dfa *dfa);
+extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str);
+extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
+				      const char *str);
+extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
+				   const char *str, unsigned int *final);
+extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
+					   unsigned int start);
+
+#endif  /* __APPARMOR_H */
--- /dev/null
+++ b/security/apparmor/apparmorfs.c
@@ -0,0 +1,252 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2 of the
+ *	License.
+ *
+ *	AppArmor filesystem (part of securityfs)
+ */
+
+#include <linux/security.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+static char *aa_simple_write_to_buffer(const char __user *userbuf,
+				       size_t alloc_size, size_t copy_size,
+				       loff_t *pos, const char *operation)
+{
+	struct aa_profile *profile;
+	char *data;
+
+	if (*pos != 0) {
+		/* only writes from pos 0, that is complete writes */
+		data = ERR_PTR(-ESPIPE);
+		goto out;
+	}
+
+	/*
+	 * Don't allow confined processes to load/replace/remove profiles.
+	 * No sane person would add rules allowing this to a profile
+	 * but we enforce the restriction anyways.
+	 */
+	profile = aa_get_profile(current);
+	if (profile) {
+		struct aa_audit sa;
+		memset(&sa, 0, sizeof(sa));
+		sa.operation = operation;
+		sa.gfp_mask = GFP_KERNEL;
+		sa.error_code = -EACCES;
+		data = ERR_PTR(aa_audit_reject(profile, &sa));
+		aa_put_profile(profile);
+		goto out;
+	}
+
+	data = vmalloc(alloc_size);
+	if (data == NULL) {
+		data = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+
+	if (copy_from_user(data, userbuf, copy_size)) {
+		vfree(data);
+		data = ERR_PTR(-EFAULT);
+		goto out;
+	}
+
+out:
+	return data;
+}
+
+/* apparmor/profiles */
+extern struct seq_operations apparmorfs_profiles_op;
+
+static int aa_profiles_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &apparmorfs_profiles_op);
+}
+
+
+static int aa_profiles_release(struct inode *inode, struct file *file)
+{
+	return seq_release(inode, file);
+}
+
+static struct file_operations apparmorfs_profiles_fops = {
+	.open =		aa_profiles_open,
+	.read =		seq_read,
+	.llseek =	seq_lseek,
+	.release =	aa_profiles_release,
+};
+
+/* apparmor/matching */
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
+			       size_t size, loff_t *ppos)
+{
+	const char *matching = "pattern=aadfa perms=rwxamlz user:other";
+
+	return simple_read_from_buffer(buf, size, ppos, matching,
+				       strlen(matching));
+}
+
+static struct file_operations apparmorfs_matching_fops = {
+	.read = 	aa_matching_read,
+};
+
+/* apparmor/.load */
+static ssize_t aa_profile_load(struct file *f, const char __user *buf,
+			       size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load");
+
+	error = PTR_ERR(data);
+	if (!IS_ERR(data)) {
+		error = aa_add_profile(data, size);
+		vfree(data);
+	}
+
+	return error;
+}
+
+
+static struct file_operations apparmorfs_profile_load = {
+	.write = aa_profile_load
+};
+
+/* apparmor/.replace */
+static ssize_t aa_profile_replace(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	data = aa_simple_write_to_buffer(buf, size, size, pos,
+					 "profile_replace");
+
+	error = PTR_ERR(data);
+	if (!IS_ERR(data)) {
+		error = aa_replace_profile(data, size);
+		vfree(data);
+	}
+
+	return error;
+}
+
+
+static struct file_operations apparmorfs_profile_replace = {
+	.write = aa_profile_replace
+};
+
+/* apparmor/.remove */
+static ssize_t aa_profile_remove(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	/*
+	 * aa_remove_profile needs a null terminated string so 1 extra
+	 * byte is allocated and the copied data is null terminated.
+	 */
+	data = aa_simple_write_to_buffer(buf, size + 1, size, pos,
+					 "profile_remove");
+
+	error = PTR_ERR(data);
+	if (!IS_ERR(data)) {
+		data[size] = 0;
+		error = aa_remove_profile(data, size);
+		vfree(data);
+	}
+
+	return error;
+}
+
+static struct file_operations apparmorfs_profile_remove = {
+	.write = aa_profile_remove
+};
+
+static struct dentry *apparmor_dentry;
+
+static void aafs_remove(const char *name)
+{
+	struct dentry *dentry;
+
+	dentry = lookup_one_len(name, apparmor_dentry, strlen(name));
+	if (!IS_ERR(dentry)) {
+		securityfs_remove(dentry);
+		dput(dentry);
+	}
+}
+
+static int aafs_create(const char *name, int mask, struct file_operations *fops)
+{
+	struct dentry *dentry;
+
+	dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry,
+					NULL, fops);
+
+	return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
+}
+
+void destroy_apparmorfs(void)
+{
+	if (apparmor_dentry) {
+		aafs_remove(".remove");
+		aafs_remove(".replace");
+		aafs_remove(".load");
+		aafs_remove("matching");
+		aafs_remove("profiles");
+		securityfs_remove(apparmor_dentry);
+		apparmor_dentry = NULL;
+	}
+}
+
+int create_apparmorfs(void)
+{
+	int error;
+
+	if (apparmor_dentry) {
+		AA_ERROR("%s: AppArmor securityfs already exists\n",
+			__FUNCTION__);
+		return -EEXIST;
+	}
+
+	apparmor_dentry = securityfs_create_dir("apparmor", NULL);
+	if (IS_ERR(apparmor_dentry)) {
+		error = PTR_ERR(apparmor_dentry);
+		apparmor_dentry = NULL;
+ 		goto error;
+	}
+	error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops);
+	if (error)
+		goto error;
+	error = aafs_create("matching", 0444, &apparmorfs_matching_fops);
+	if (error)
+		goto error;
+	error = aafs_create(".load", 0640, &apparmorfs_profile_load);
+	if (error)
+		goto error;
+	error = aafs_create(".replace", 0640, &apparmorfs_profile_replace);
+	if (error)
+		goto error;
+	error = aafs_create(".remove", 0640, &apparmorfs_profile_remove);
+	if (error)
+		goto error;
+
+	return 0;
+
+error:
+	destroy_apparmorfs();
+	AA_ERROR("Error creating AppArmor securityfs\n");
+	return error;
+}
+
--- /dev/null
+++ b/security/apparmor/inline.h
@@ -0,0 +1,240 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2 of the
+ *	License.
+ */
+
+#ifndef __INLINE_H
+#define __INLINE_H
+
+#include <linux/sched.h>
+
+#include "match.h"
+
+static inline int mediated_filesystem(struct inode *inode)
+{
+	return !(inode->i_sb->s_flags & MS_NOUSER);
+}
+
+static inline struct aa_task_context *aa_task_context(struct task_struct *task)
+{
+	return (struct aa_task_context *) rcu_dereference(task->security);
+}
+
+static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
+{
+	if (ns)
+		kref_get(&(ns->count));
+
+	return ns;
+}
+
+static inline void aa_put_namespace(struct aa_namespace *ns)
+{
+	if (ns)
+		kref_put(&ns->count, free_aa_namespace_kref);
+}
+
+
+static inline struct aa_namespace *aa_find_namespace(const char *name)
+{
+	struct aa_namespace *ns = NULL;
+
+	read_lock(&profile_ns_list_lock);
+	ns = aa_get_namespace(__aa_find_namespace(name, &profile_ns_list));
+	read_unlock(&profile_ns_list_lock);
+
+	return ns;
+}
+
+/**
+ * aa_dup_profile - increment refcount on profile @p
+ * @p: profile
+ */
+static inline struct aa_profile *aa_dup_profile(struct aa_profile *p)
+{
+	if (p)
+		kref_get(&(p->count));
+
+	return p;
+}
+
+/**
+ * aa_put_profile - decrement refcount on profile @p
+ * @p: profile
+ */
+static inline void aa_put_profile(struct aa_profile *p)
+{
+	if (p)
+		kref_put(&p->count, free_aa_profile_kref);
+}
+
+static inline struct aa_profile *aa_get_profile(struct task_struct *task)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *profile = NULL;
+
+	rcu_read_lock();
+	cxt = aa_task_context(task);
+	if (cxt) {
+		profile = cxt->profile;
+		aa_dup_profile(profile);
+	}
+	rcu_read_unlock();
+
+	return profile;
+}
+
+static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns,
+						 const char *name)
+{
+	struct aa_profile *profile = NULL;
+
+	read_lock(&ns->lock);
+	profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles));
+	read_unlock(&ns->lock);
+
+	return profile;
+}
+
+static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags)
+{
+	struct aa_task_context *cxt;
+
+	cxt = kzalloc(sizeof(*cxt), flags);
+	if (cxt) {
+		INIT_LIST_HEAD(&cxt->list);
+		INIT_RCU_HEAD(&cxt->rcu);
+	}
+
+	return cxt;
+}
+
+static inline void aa_free_task_context(struct aa_task_context *cxt)
+{
+	if (cxt) {
+		aa_put_profile(cxt->profile);
+		aa_put_profile(cxt->previous_profile);
+		kfree(cxt);
+	}
+}
+
+/**
+ * lock_profile - lock a profile
+ * @profile: the profile to lock
+ *
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void lock_profile_nested(struct aa_profile *profile,
+				       enum aa_lock_class lock_class)
+{
+	/*
+	 * Lock the profile.
+	 *
+	 * Need to disable interrupts here because this lock is used in
+	 * the task_free_security hook, which may run in RCU context.
+	 */
+	if (profile)
+		spin_lock_irqsave_nested(&profile->lock, profile->int_flags,
+					 lock_class);
+}
+
+static inline void lock_profile(struct aa_profile *profile)
+{
+	lock_profile_nested(profile, aa_lock_normal);
+}
+
+/**
+ * unlock_profile - unlock a profile
+ * @profile: the profile to unlock
+ */
+static inline void unlock_profile(struct aa_profile *profile)
+{
+	/* Unlock the profile. */
+	if (profile)
+		spin_unlock_irqrestore(&profile->lock, profile->int_flags);
+}
+
+/**
+ * lock_both_profiles  -  lock two profiles in a deadlock-free way
+ * @profile1:	profile to lock (may be NULL)
+ * @profile2:	profile to lock (may be NULL)
+ *
+ * The order in which profiles are passed into lock_both_profiles() /
+ * unlock_both_profiles() does not matter.
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void lock_both_profiles(struct aa_profile *profile1,
+				      struct aa_profile *profile2)
+{
+	/*
+	 * Lock the two profiles.
+	 *
+	 * We need to disable interrupts because the profile locks are
+	 * used in the task_free_security hook, which may run in RCU
+	 * context.
+	 *
+	 * Do not nest spin_lock_irqsave()/spin_unlock_irqresore():
+	 * interrupts only need to be turned off once.
+	 */
+	if (!profile1 || profile1 == profile2) {
+		if (profile2)
+			spin_lock_irqsave_nested(&profile2->lock,
+						 profile2->int_flags,
+						 aa_lock_normal);
+	} else if (profile1 > profile2) {
+		/* profile1 cannot be NULL here. */
+		spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags,
+					 aa_lock_normal);
+		if (profile2)
+			spin_lock_nested(&profile2->lock, aa_lock_nested);
+
+	} else {
+		/* profile2 cannot be NULL here. */
+		spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags,
+					 aa_lock_normal);
+		spin_lock_nested(&profile1->lock, aa_lock_nested);
+	}
+}
+
+/**
+ * unlock_both_profiles  -  unlock two profiles in a deadlock-free way
+ * @profile1:	profile to unlock (may be NULL)
+ * @profile2:	profile to unlock (may be NULL)
+ *
+ * The order in which profiles are passed into lock_both_profiles() /
+ * unlock_both_profiles() does not matter.
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void unlock_both_profiles(struct aa_profile *profile1,
+				        struct aa_profile *profile2)
+{
+	/* Unlock the two profiles. */
+	if (!profile1 || profile1 == profile2) {
+		if (profile2)
+			spin_unlock_irqrestore(&profile2->lock,
+					       profile2->int_flags);
+	} else if (profile1 > profile2) {
+		/* profile1 cannot be NULL here. */
+		if (profile2)
+			spin_unlock(&profile2->lock);
+		spin_unlock_irqrestore(&profile1->lock, profile1->int_flags);
+	} else {
+		/* profile2 cannot be NULL here. */
+		spin_unlock(&profile1->lock);
+		spin_unlock_irqrestore(&profile2->lock, profile2->int_flags);
+	}
+}
+
+static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname)
+{
+	        return dfa ? aa_dfa_match(dfa, pathname) : 0;
+}
+
+#endif /* __INLINE_H__ */
--- /dev/null
+++ b/security/apparmor/list.c
@@ -0,0 +1,156 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2 of the
+ *	License.
+ *
+ *	AppArmor Profile List Management
+ */
+
+#include <linux/seq_file.h>
+#include "apparmor.h"
+#include "inline.h"
+
+/* list of profile namespaces and lock */
+LIST_HEAD(profile_ns_list);
+rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED;
+
+/**
+ * __aa_find_namespace  -  look up a profile namespace on the namespace list
+ * @name: name of namespace to find
+ * @head: list to search
+ *
+ * Returns a pointer to the namespace on the list, or NULL if no namespace
+ * called @name exists. The caller must hold the profile_ns_list_lock.
+ */
+struct aa_namespace *__aa_find_namespace(const char *name,
+				       struct list_head *head)
+{
+	struct aa_namespace *ns;
+
+	list_for_each_entry(ns, head, list) {
+		if (!strcmp(ns->name, name))
+			return ns;
+	}
+
+	return NULL;
+}
+
+/**
+ * __aa_find_profile  -  look up a profile on the profile list
+ * @name: name of profile to find
+ * @head: list to search
+ *
+ * Returns a pointer to the profile on the list, or NULL if no profile
+ * called @name exists. The caller must hold the profile_list_lock.
+ */
+struct aa_profile *__aa_find_profile(const char *name, struct list_head *head)
+{
+	struct aa_profile *profile;
+
+	list_for_each_entry(profile, head, list) {
+		if (!strcmp(profile->name, name))
+			return profile;
+	}
+
+	return NULL;
+}
+
+static void aa_profile_list_release(struct list_head *head)
+{
+	struct aa_profile *profile, *tmp;
+	list_for_each_entry_safe(profile, tmp, head, list) {
+		/* Remove the profile from each task context it is on. */
+		lock_profile(profile);
+		profile->isstale = 1;
+		aa_unconfine_tasks(profile);
+		list_del_init(&profile->list);
+		unlock_profile(profile);
+		aa_put_profile(profile);
+	}
+}
+
+/**
+ * aa_profilelist_release - Remove all profiles from profile_list
+ */
+void aa_profile_ns_list_release(void)
+{
+	struct aa_namespace *ns, *tmp;
+
+	/* Remove and release all the profiles on namespace profile lists. */
+	write_lock(&profile_ns_list_lock);
+	list_for_each_entry_safe(ns, tmp, &profile_ns_list, list) {
+		write_lock(&ns->lock);
+		aa_profile_list_release(&ns->profiles);
+		list_del_init(&ns->list);
+		write_unlock(&ns->lock);
+		aa_put_namespace(ns);
+	}
+	write_unlock(&profile_ns_list_lock);
+}
+
+static void *p_start(struct seq_file *f, loff_t *pos)
+{
+	struct aa_namespace *ns;
+	struct aa_profile *profile;
+	loff_t l = *pos;
+	read_lock(&profile_ns_list_lock);
+	if (l--)
+		return NULL;
+	list_for_each_entry(ns, &profile_ns_list, list) {
+		read_lock(&ns->lock);
+		list_for_each_entry(profile, &ns->profiles, list)
+			return profile;
+		read_unlock(&ns->lock);
+	}
+	return NULL;
+}
+
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
+{
+	struct aa_profile *profile = (struct aa_profile *) p;
+	struct list_head *lh = profile->list.next;
+	struct aa_namespace *ns;
+	(*pos)++;
+	if (lh != &profile->ns->profiles)
+		return list_entry(lh, struct aa_profile, list);
+
+	lh = profile->ns->list.next;
+	read_unlock(&profile->ns->lock);
+	while (lh != &profile_ns_list) {
+		ns = list_entry(lh, struct aa_namespace, list);
+		read_lock(&ns->lock);
+		list_for_each_entry(profile, &ns->profiles, list)
+			return profile;
+		read_unlock(&ns->lock);
+		lh = ns->list.next;
+	}
+	return NULL;
+}
+
+static void p_stop(struct seq_file *f, void *v)
+{
+	read_unlock(&profile_ns_list_lock);
+}
+
+static int seq_show_profile(struct seq_file *f, void *v)
+{
+	struct aa_profile *profile = (struct aa_profile *)v;
+	if (profile->ns == default_namespace)
+	    seq_printf(f, "%s (%s)\n", profile->name,
+		       PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
+	else
+	    seq_printf(f, "%s:%s (%s)\n", profile->ns->name, profile->name,
+		       PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
+	return 0;
+}
+
+/* Used in apparmorfs.c */
+struct seq_operations apparmorfs_profiles_op = {
+	.start =	p_start,
+	.next =		p_next,
+	.stop =		p_stop,
+	.show =		seq_show_profile,
+};
--- /dev/null
+++ b/security/apparmor/locking.txt
@@ -0,0 +1,68 @@
+Locking in AppArmor
+===================
+
+Lock hierarchy:
+
+	aa_interface_lock
+	  profile_list_lock
+	    aa_profile->lock
+	      task_lock()
+
+
+Which lock protects what?
+
+	/-----------------------+-------------------------------\
+	| Variable		| Lock				|
+	>-----------------------+-------------------------------<
+	| profile_list		| profile_list_lock		|
+	+-----------------------+-------------------------------+
+	| aa_profile		| (reference count)		|
+	+-----------------------+-------------------------------+
+	| aa_profile->		| aa_profile->lock		|
+	|   isstale,		|				|
+	|   task_contexts	|				|
+	+-----------------------+-------------------------------+
+	| task_struct->security	| read: RCU			|
+	|			| write: task_lock()		|
+	+-----------------------+-------------------------------+
+	| aa_profile->sub	| handle on the profile (list	|
+	|			| is never modified)		|
+	\-----------------------+-------------------------------/
+
+(Obviously, the list_heads embedded in data structures are always
+protected with the lock that also protects the list.)
+
+When moving a task context from one profile to another, we grab both
+profile locks with lock_both_profiles(). This ensures that both locks
+are always taken in the same order, and so we won't deadlock.
+
+Since task_struct->security is RCU protected the aa_task_struct it
+references is only guarenteed to exist for the rcu cycle.  Where
+aa_task_context->profile is needed in blocking operations the
+profile's reference count is incremented and the profile reference
+is used.
+
+Profiles on profile_list are never stale: when a profile becomes stale,
+it is removed from profile_list at the same time (under profile_list_lock
+and aa_profile->lock).
+
+The aa_interface_lock is taken whenever user-space modifies the profile
+list, and can sleep. This ensures that profile loading/replacement/removal
+won't race with itself. We release the profile_list_lock as soon as
+possible to avoid stalling exec during profile loading/replacement/removal.
+
+AppArmor uses lock subtyping to avoid false positives from lockdep.  The
+profile lock is often taken nested, but it is guaranteed to be in a lock
+safe order and not the same lock when done, so it is safe.
+
+A third lock type (aa_lock_task_release) is given to the profile lock
+when it is taken in soft irq context during task release (aa_release).
+This is to avoid a false positive between the task lock and the profile
+lock.  In task context the profile lock wraps the task lock with irqs
+off, but the kernel takes the task lock with irqs enabled.  This won't
+result in a deadlock because for a deadlock to occur the kernel must
+take dead task A's lock (irqs on), the rcu callback hook freeing
+dead task A must be run and AppArmor must be changing the profile on
+dead task A.  The kernel should not be taking a dead task's task_lock
+at the same time the task is being freed by task rcu cleanup other wise
+the task would not be out of its quiescent period.
--- /dev/null
+++ b/security/apparmor/procattr.c
@@ -0,0 +1,194 @@
+/*
+ *	Copyright (C) 1998-2007 Novell/SUSE
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2 of the
+ *	License.
+ *
+ *	AppArmor /proc/pid/attr handling
+ */
+
+#include "apparmor.h"
+#include "inline.h"
+
+int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len)
+{
+	char *str;
+
+	if (profile) {
+		const char *mode_str = PROFILE_COMPLAIN(profile) ?
+			" (complain)" : " (enforce)";
+		int mode_len, name_len, ns_len = 0;
+
+		mode_len = strlen(mode_str);
+		name_len = strlen(profile->name);
+		if (profile->ns != default_namespace)
+			ns_len = strlen(profile->ns->name) + 1;
+		*len = mode_len + ns_len + name_len + 1;
+		str = kmalloc(*len, GFP_ATOMIC);
+		if (!str)
+			return -ENOMEM;
+
+		if (ns_len) {
+			memcpy(str, profile->ns->name, ns_len - 1);
+			str += ns_len - 1;
+			*str++ = ':';
+		}
+		memcpy(str, profile->name, name_len);
+		str += name_len;
+		memcpy(str, mode_str, mode_len);
+		str += mode_len;
+		*str++ = '\n';
+		str -= *len;
+	} else {
+		const char *unconfined_str = "unconfined\n";
+
+		*len = strlen(unconfined_str);
+		str = kmalloc(*len, GFP_ATOMIC);
+		if (!str)
+			return -ENOMEM;
+
+		memcpy(str, unconfined_str, *len);
+	}
+	*string = str;
+
+	return 0;
+}
+
+static char *split_token_from_name(const char *op, char *args, u64 *cookie)
+{
+	char *name;
+
+	*cookie = simple_strtoull(args, &name, 16);
+	if ((name == args) || *name != '^') {
+		AA_ERROR("%s: Invalid input '%s'", op, args);
+		return ERR_PTR(-EINVAL);
+	}
+
+	name++;  /* skip ^ */
+	if (!*name)
+		name = NULL;
+	return name;
+}
+
+int aa_setprocattr_changehat(char *args)
+{
+	char *hat;
+	u64 cookie;
+
+	hat = split_token_from_name("change_hat", args, &cookie);
+	if (IS_ERR(hat))
+		return PTR_ERR(hat);
+
+	if (!hat && !cookie) {
+		AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic");
+		return -EINVAL;
+	}
+
+	AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
+		 __FUNCTION__, cookie, hat ? hat : NULL);
+
+	return aa_change_hat(hat, cookie);
+}
+
+int aa_setprocattr_changeprofile(char *args)
+{
+	char *name = args, *ns_name = NULL;
+
+	if (name[0] != '/') {
+		char *split = strchr(name, ':');
+		if (split) {
+			*split = 0;
+			ns_name = name;
+			name = split + 1;
+		}
+	}
+
+	return aa_change_profile(ns_name, name);
+}
+
+int aa_setprocattr_setprofile(struct task_struct *task, char *args)
+{
+	struct aa_profile *old_profile, *new_profile;
+	struct aa_namespace *ns;
+	struct aa_audit sa;
+	char *name, *ns_name = NULL;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.operation = "profile_set";
+	sa.gfp_mask = GFP_KERNEL;
+	sa.task = task->pid;
+
+	AA_DEBUG("%s: current %d\n",
+		 __FUNCTION__, current->pid);
+
+	name = args;
+	if (args[0] != '/') {
+		char *split = strchr(args, ':');
+		if (split) {
+			*split = 0;
+			ns_name = args;
+			name = split + 1;
+		}
+	}
+	if (ns_name)
+		ns = aa_find_namespace(ns_name);
+	else
+		ns = aa_get_namespace(default_namespace);
+	if (!ns) {
+		sa.name = ns_name;
+		sa.info = "unknown namespace";
+		aa_audit_reject(NULL, &sa);
+		aa_put_namespace(ns);
+		return -EINVAL;
+	}
+
+repeat:
+	if (strcmp(name, "unconfined") == 0)
+		new_profile = NULL;
+	else {
+		new_profile = aa_find_profile(ns, name);
+		if (!new_profile) {
+			sa.name = ns_name;
+			sa.name2 = name;
+			sa.info = "unknown profile";
+			aa_audit_reject(NULL, &sa);
+			aa_put_namespace(ns);
+			return -EINVAL;
+		}
+	}
+
+	old_profile = __aa_replace_profile(task, new_profile);
+	if (IS_ERR(old_profile)) {
+		int error;
+
+		aa_put_profile(new_profile);
+		error = PTR_ERR(old_profile);
+		if (error == -ESTALE)
+			goto repeat;
+		aa_put_namespace(ns);
+		return error;
+	}
+
+	if (new_profile) {
+		sa.name = ns_name;
+		sa.name2 = name;
+		sa.name3 = old_profile ? old_profile->name :
+			"unconfined";
+		aa_audit_status(NULL, &sa);
+	} else {
+		if (old_profile) {
+			sa.name = "unconfined";
+			sa.name2 = old_profile->name;
+			aa_audit_status(NULL, &sa);
+		} else {
+			sa.info = "task is unconfined";
+			aa_audit_status(NULL, &sa);
+		}
+	}
+	aa_put_namespace(ns);
+	aa_put_profile(old_profile);
+	aa_put_profile(new_profile);
+	return 0;
+}

-- 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux