linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: jjohansen@suse.de
To: linux-kernel@vger.kernel.org
Cc: linux-security-module@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, John Johansen <jjohansen@suse.de>,
	Andreas Gruenbacher <agruen@suse.de>
Subject: [AppArmor 37/45] AppArmor: Main Part
Date: Mon, 14 May 2007 04:06:44 -0700	[thread overview]
Message-ID: <20070514110620.908918667@suse.de> (raw)
In-Reply-To: 20070514110607.549397248@suse.de

[-- Attachment #1: apparmor-main.diff --]
[-- Type: text/plain, Size: 38356 bytes --]

The underlying functions by which the AppArmor LSM hooks are implemented.

Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

Index: b/security/apparmor/main.c
===================================================================
--- /dev/null
+++ b/security/apparmor/main.c
@@ -0,0 +1,1399 @@
+/*
+ *	Copyright (C) 2002-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 Core
+ */
+
+#include <linux/security.h>
+#include <linux/namei.h>
+#include <linux/audit.h>
+#include <linux/mount.h>
+#include <linux/ptrace.h>
+
+#include "apparmor.h"
+
+#include "inline.h"
+
+/*
+ * Table of capability names: we generate it from capabilities.h.
+ */
+static const char *capability_names[] = {
+#include "capability_names.h"
+};
+
+/* NULL complain profile
+ *
+ * Used when in complain mode, to emit Permitting messages for non-existant
+ * profiles and hats.  This is necessary because of selective mode, in which
+ * case we need a complain null_profile and enforce null_profile
+ *
+ * The null_complain_profile cannot be statically allocated, because it
+ * can be associated to files which keep their reference even if apparmor is
+ * unloaded
+ */
+struct aa_profile *null_complain_profile;
+
+static inline void aa_permerror2result(int perm_result, struct aa_audit *sa)
+{
+	if (perm_result == 0) {	/* success */
+		sa->result = 1;
+		sa->error_code = 0;
+	} else { /* -ve internal error code or +ve mask of denied perms */
+		sa->result = 0;
+		sa->error_code = perm_result;
+	}
+}
+
+/**
+ * aa_file_denied - check for @mask access on a file
+ * @profile: profile to check against
+ * @name: pathname of file
+ * @mask: permission mask requested for file
+ *
+ * Return %0 on success, or else the permissions in @mask that the
+ * profile denies.
+ */
+static int aa_file_denied(struct aa_profile *profile, const char *name,
+			  int mask)
+{
+	return (mask & ~aa_match(profile->file_rules, name));
+}
+
+/**
+ * aa_link_denied - check for permission to link a file
+ * @profile: profile to check against
+ * @link: pathname of link being created
+ * @target: pathname of target to be linked to
+ *
+ * Return %0 on success, or else the permissions that the profile denies.
+ */
+static int aa_link_denied(struct aa_profile *profile, const char *link,
+			  const char *target)
+{
+	int l_mode, t_mode;
+
+	l_mode = aa_match(profile->file_rules, link);
+	t_mode = aa_match(profile->file_rules, target);
+
+	/* Link always requires 'l' on the link, a subset of the
+	 * target's 'r', 'w', 'x', and 'm' permissions on the link, and
+	 * if the link has 'x', an exact match of all the execute flags
+	 * ('i', 'u', 'U', 'p', 'P').
+	 */
+#define RWXM (MAY_READ | MAY_WRITE | MAY_EXEC | AA_EXEC_MMAP)
+	if ((l_mode & AA_MAY_LINK) &&
+	    (l_mode & RWXM) && !(l_mode & ~t_mode & RWXM) &&
+	    (!(l_mode & MAY_EXEC) ||
+	     ((l_mode & AA_EXEC_MODIFIERS) == (t_mode & AA_EXEC_MODIFIERS) &&
+	      (l_mode & AA_EXEC_UNSAFE) == (t_mode & AA_EXEC_UNSAFE))))
+		return 0;
+#undef RWXM
+	/* FIXME: There currenly is no way to report which permissions
+	 * we expect in t_mode, so linking could fail even after learning
+	 * the required l_mode.
+	 */
+	return AA_MAY_LINK;
+}
+
+/**
+ * mangle -- escape special characters in str
+ * @str: string to escape
+ * @buffer: buffer containing str
+ *
+ * Escape special characters in @str, which is contained in @buffer. @str must
+ * be aligned to the end of the buffer, and the space between @buffer and @str
+ * may be used for escaping.
+ *
+ * Returns @str if no escaping was necessary, a pointer to the beginning of the
+ * escaped string, or NULL if there was not enough space in @buffer.  When
+ * called with a NULL buffer, the return value tells whether any escaping is
+ * necessary.
+ */
+static const char *mangle(const char *str, char *buffer)
+{
+	static const char c_escape[] = {
+		['\a'] = 'a',	['\b'] = 'b',
+		['\f'] = 'f',	['\n'] = 'n',
+		['\r'] = 'r',	['\t'] = 't',
+		['\v'] = 'v',
+		[' '] = ' ',	['\\'] = '\\',
+	};
+	const char *s;
+	char *t, c;
+
+#define mangle_escape(c)						\
+	unlikely((unsigned char)(c) < ARRAY_SIZE(c_escape) &&		\
+		 c_escape[(unsigned char)c])
+
+	for (s = (char *)str; (c = *s) != '\0'; s++)
+		if (mangle_escape(c))
+			goto escape;
+	return str;
+
+escape:
+	if (!buffer)
+		return NULL;
+	for (s = str, t = buffer; (c = *s) != '\0'; s++) {
+		if (mangle_escape(c)) {
+			if (t == s)
+				return NULL;
+			*t++ = '\\';
+			*t++ = c_escape[(unsigned char)c];
+		} else
+			*t++ = c;
+	}
+	*t++ = '\0';
+
+#undef mangle_escape
+
+	return buffer;
+}
+
+/**
+ * aa_get_name - compute the pathname of a file
+ * @dentry: dentry of the file
+ * @mnt: vfsmount of the file
+ * @buffer: buffer that aa_get_name() allocated
+ * @check: AA_CHECK_DIR is set if the file is a directory
+ *
+ * Returns a pointer to the beginning of the pathname (which usually differs
+ * from the beginning of the buffer), or an error code.
+ *
+ * We need @check to indicate whether the file is a directory or not because
+ * the file may not yet exist, and so we cannot check the inode's file type.
+ */
+static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt,
+			 char **buffer, int check)
+{
+	char *name;
+	int is_dir, size = 256;
+
+	is_dir = (check & AA_CHECK_DIR) ? 1 : 0;
+
+	for (;;) {
+		char *buf = kmalloc(size, GFP_KERNEL);
+		if (!buf)
+			return ERR_PTR(-ENOMEM);
+
+		name = d_namespace_path(dentry, mnt, buf, size - is_dir);
+
+		/* Make sure we have enough space for name mangling. */
+		if (!IS_ERR(name) &&
+		    (check & AA_CHECK_MANGLE) && name - buf <= size / 2)
+			name = ERR_PTR(-ENAMETOOLONG);
+
+		if (!IS_ERR(name)) {
+			if (name[0] != '/') {
+				/*
+				 * This dentry is not connected to the
+				 * namespace root -- reject access.
+				 */
+				kfree(buf);
+				return ERR_PTR(-ENOENT);
+			}
+			if (is_dir && name[1] != '\0') {
+				/*
+				 * Append "/" to the pathname. The root
+				 * directory is a special case; it already
+				 * ends in slash.
+				 */
+				buf[size - 2] = '/';
+				buf[size - 1] = '\0';
+			}
+
+			*buffer = buf;
+			return name;
+		}
+		if (PTR_ERR(name) != -ENAMETOOLONG)
+			return name;
+
+		kfree(buf);
+		size <<= 1;
+		if (size > apparmor_path_max)
+			return ERR_PTR(-ENAMETOOLONG);
+	}
+}
+
+static inline void aa_put_name_buffer(char *buffer)
+{
+	kfree(buffer);
+}
+
+/**
+ * aa_perm_dentry - check if @profile allows @mask for a file
+ * @profile: profile to check against
+ * @dentry: dentry of the file
+ * @mnt: vfsmount o the file
+ * @sa: audit context
+ * @mask: requested profile permissions
+ * @check: kind of check to perform
+ *
+ * Returns 0 upon success, or else an error code.
+ *
+ * @check indicates the file type, and whether the file was accessed through
+ * an open file descriptor (AA_CHECK_FD) or not.
+ */
+static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry,
+			  struct vfsmount *mnt, struct aa_audit *sa, int mask,
+			  int check)
+{
+	int denied_mask, error;
+
+again:
+	sa->buffer = NULL;
+	sa->name = aa_get_name(dentry, mnt, &sa->buffer, check);
+
+	if (IS_ERR(sa->name)) {
+		/*
+		 * deleted files are given a pass on permission checks when
+		 * accessed through a file descriptor.
+		 */
+		if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
+			denied_mask = 0;
+		else
+			denied_mask = PTR_ERR(sa->name);
+		sa->name = NULL;
+	} else
+		denied_mask = aa_file_denied(profile, sa->name, mask);
+
+	aa_permerror2result(denied_mask, sa);
+
+	error = aa_audit(profile, sa);
+
+	aa_put_name_buffer(sa->buffer);
+	if (error == -ENAMETOOLONG) {
+		BUG_ON(check & AA_CHECK_MANGLE);
+		check |= AA_CHECK_MANGLE;
+		goto again;
+	}
+
+	return error;
+}
+
+/**
+ * attach_nullprofile - allocate and attach a null_profile hat to profile
+ * @profile: profile to attach a null_profile hat to.
+ *
+ * Return %0 (success) or error (-%ENOMEM)
+ */
+int attach_nullprofile(struct aa_profile *profile)
+{
+	struct aa_profile *hat = NULL;
+	char *hatname = NULL;
+
+	hat = alloc_aa_profile();
+	if (!hat)
+		goto fail;
+	if (profile->flags.complain)
+		hatname = kstrdup("null-complain-profile", GFP_KERNEL);
+	else
+		hatname = kstrdup("null-profile", GFP_KERNEL);
+	if (!hatname)
+		goto fail;
+
+	hat->flags.complain = profile->flags.complain;
+	hat->name = hatname;
+	hat->parent = profile;
+
+	profile->null_profile = hat;
+
+	return 0;
+
+fail:
+	kfree(hatname);
+	free_aa_profile(hat);
+
+	return -ENOMEM;
+}
+
+/**
+ * alloc_null_complain_profile - Allocate the global null_complain_profile.
+ *
+ * Return %0 (success) or error (-%ENOMEM)
+ */
+int alloc_null_complain_profile(void)
+{
+	null_complain_profile = alloc_aa_profile();
+	if (!null_complain_profile)
+		goto fail;
+
+	null_complain_profile->name =
+		kstrdup("null-complain-profile", GFP_KERNEL);
+
+	if (!null_complain_profile->name)
+		goto fail;
+
+	null_complain_profile->flags.complain = 1;
+	if (attach_nullprofile(null_complain_profile))
+		goto fail;
+
+	return 0;
+
+fail:
+	/* free_aa_profile is safe for freeing partially constructed objects */
+	free_aa_profile(null_complain_profile);
+	null_complain_profile = NULL;
+
+	return -ENOMEM;
+}
+
+/**
+ * free_null_complain_profile - Free null profiles
+ */
+void free_null_complain_profile(void)
+{
+	aa_put_profile(null_complain_profile);
+	null_complain_profile = NULL;
+}
+
+/**
+ * aa_audit_message - Log a message to the audit subsystem
+ * @profile: profile to check against
+ * @gfp: allocation flags
+ * @flags: audit flags
+ * @fmt: varargs fmt
+ */
+int aa_audit_message(struct aa_profile *profile, gfp_t gfp, const char *fmt,
+		     ...)
+{
+	int ret;
+	struct aa_audit sa;
+
+	sa.type = AA_AUDITTYPE_MSG;
+	sa.name = fmt;
+	va_start(sa.vaval, fmt);
+	sa.flags = 0;
+	sa.gfp_mask = gfp;
+	sa.error_code = 0;
+	sa.result = 0;	/* fake failure: force message to be logged */
+
+	ret = aa_audit(profile, &sa);
+
+	va_end(sa.vaval);
+
+	return ret;
+}
+
+/**
+ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem
+ * @profile: profile to check against
+ * @msg: string describing syscall being rejected
+ * @gfp: memory allocation flags
+ */
+int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
+			   const char *msg)
+{
+	struct aa_audit sa;
+
+	sa.type = AA_AUDITTYPE_SYSCALL;
+	sa.name = msg;
+	sa.flags = 0;
+	sa.gfp_mask = gfp;
+	sa.error_code = 0;
+	sa.result = 0; /* failure */
+
+	return aa_audit(profile, &sa);
+}
+
+/**
+ * aa_audit - Log an audit event to the audit subsystem
+ * @profile: profile to check against
+ * @sa: audit event
+ */
+int aa_audit(struct aa_profile *profile, struct aa_audit *sa)
+{
+	struct audit_buffer *ab = NULL;
+	struct audit_context *audit_cxt;
+
+	const char *logcls;
+	unsigned int flags;
+	int audit = 0,
+	    complain = 0,
+	    error = -EINVAL,
+	    opspec_error = -EACCES;
+
+	const gfp_t gfp_mask = sa->gfp_mask;
+
+	/*
+	 * sa->result:	  1 success, 0 failure
+	 * sa->error_code: success: 0
+	 *		  failure: +ve mask of failed permissions or -ve
+	 *		  system error
+	 */
+
+	if (likely(sa->result)) {
+		if (likely(!PROFILE_AUDIT(profile))) {
+			/* nothing to log */
+			error = 0;
+			goto out;
+		} else {
+			audit = 1;
+			logcls = "AUDITING";
+		}
+	} else if (sa->error_code < 0) {
+		audit_log(current->audit_context, gfp_mask, AUDIT_APPARMOR,
+			"Internal error auditing event type %d (error %d)",
+			sa->type, sa->error_code);
+		AA_ERROR("Internal error auditing event type %d (error %d)\n",
+			sa->type, sa->error_code);
+		error = sa->error_code;
+		goto out;
+	} else if (sa->type == AA_AUDITTYPE_SYSCALL) {
+		/* Currently AA_AUDITTYPE_SYSCALL is for rejects only.
+		 * Values set by aa_audit_syscallreject will get us here.
+		 */
+		logcls = "REJECTING";
+	} else {
+		complain = PROFILE_COMPLAIN(profile);
+		logcls = complain ? "PERMITTING" : "REJECTING";
+	}
+
+	/* In future extend w/ per-profile flags
+	 * (flags |= sa->profile->flags)
+	 */
+	flags = sa->flags;
+	if (apparmor_logsyscall)
+		flags |= AA_AUDITFLAG_AUDITSS_SYSCALL;
+
+
+	/* Force full audit syscall logging regardless of global setting if
+	 * we are rejecting a syscall
+	 */
+	if (sa->type == AA_AUDITTYPE_SYSCALL) {
+		audit_cxt = current->audit_context;
+	} else {
+		audit_cxt = (flags & AA_AUDITFLAG_AUDITSS_SYSCALL) ?
+			current->audit_context : NULL;
+	}
+
+	ab = audit_log_start(audit_cxt, gfp_mask, AUDIT_APPARMOR);
+
+	if (!ab) {
+		AA_ERROR("Unable to log event (%d) to audit subsys\n",
+			sa->type);
+		if (complain)
+			error = 0;
+		goto out;
+	}
+
+	/* messages get special handling */
+	if (sa->type == AA_AUDITTYPE_MSG) {
+		audit_log_vformat(ab, sa->name, sa->vaval);
+		audit_log_end(ab);
+		error = 0;
+		goto out;
+	}
+
+	if (sa->type & AA_MANGLE_NAME) {
+		sa->name = mangle(sa->name, sa->buffer);
+		if (!sa->name)
+			return -ENAMETOOLONG;
+	}
+	if (sa->type & AA_MANGLE_NAME2) {
+		sa->name2 = mangle(sa->name2, sa->buffer2);
+		if (!sa->name2)
+			return -ENAMETOOLONG;
+
+	}
+
+	/* log operation */
+
+	audit_log_format(ab, "%s ", logcls);	/* REJECTING/ALLOWING/etc */
+
+#define NOFLAGS(x) ((x) & ~(AA_MANGLE_NAME | AA_MANGLE_NAME2))
+
+	switch(NOFLAGS(sa->type)) {
+	case NOFLAGS(AA_AUDITTYPE_FILE): {
+		int perm = audit ? sa->mask : sa->error_code;
+
+		audit_log_format(ab, "%s%s%s%s%s access to %s ",
+				 perm & AA_EXEC_MMAP ? "m" : "",
+				 perm & MAY_READ  ? "r" : "",
+				 perm & MAY_WRITE ? "w" : "",
+				 perm & MAY_EXEC  ? "x" : "",
+				 perm & AA_MAY_LINK  ? "l" : "",
+				 sa->name);
+		opspec_error = -EPERM;
+		break;
+	}
+	case NOFLAGS(AA_AUDITTYPE_DIR):
+		audit_log_format(ab, "%s on %s ", sa->name2, sa->name);
+		break;
+	case NOFLAGS(AA_AUDITTYPE_ATTR): {
+		struct iattr *iattr = sa->iattr;
+
+		audit_log_format(ab,
+			"attribute (%s%s%s%s%s%s%s) change to %s ",
+			iattr->ia_valid & ATTR_MODE ? "mode," : "",
+			iattr->ia_valid & ATTR_UID ? "uid," : "",
+			iattr->ia_valid & ATTR_GID ? "gid," : "",
+			iattr->ia_valid & ATTR_SIZE ? "size," : "",
+			((iattr->ia_valid & ATTR_ATIME_SET) ||
+			 (iattr->ia_valid & ATTR_ATIME)) ? "atime," : "",
+			((iattr->ia_valid & ATTR_MTIME_SET) ||
+			 (iattr->ia_valid & ATTR_MTIME)) ? "mtime," : "",
+			iattr->ia_valid & ATTR_CTIME ? "ctime," : "",
+			sa->name);
+		break;
+	}
+	case NOFLAGS(AA_AUDITTYPE_XATTR):
+		audit_log_format(ab, "%s on %s ", sa->name2, sa->name);
+		break;
+	case NOFLAGS(AA_AUDITTYPE_LINK):
+		audit_log_format(ab, "link access from %s to %s ", sa->name,
+				 sa->name2);
+		break;
+	case NOFLAGS(AA_AUDITTYPE_CAP):
+		audit_log_format(ab, "access to capability '%s' ",
+			capability_names[sa->capability]);
+		opspec_error = -EPERM;
+		break;
+	case NOFLAGS(AA_AUDITTYPE_SYSCALL):
+		audit_log_format(ab, "access to syscall '%s' ", sa->name);
+		opspec_error = -EPERM;
+		break;
+	default:
+		WARN_ON(1);
+		return error;
+	}
+
+#undef NOFLAGS
+
+	audit_log_format(ab, "(%d profile %s active %s)",
+			 current->pid, profile->parent->name, profile->name);
+
+	audit_log_end(ab);
+
+	if (complain)
+		error = 0;
+	else
+		error = sa->result ? 0 : opspec_error;
+out:
+	return error;
+}
+
+/**
+ * aa_attr - check if attribute change is allowed
+ * @profile: profile to check against
+ * @dentry: dentry of the file to check
+ * @mnt: vfsmount of the file to check
+ * @iattr: attribute changes requested
+ */
+int aa_attr(struct aa_profile *profile, struct dentry *dentry,
+	    struct vfsmount *mnt, struct iattr *iattr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error, check;
+	struct aa_audit sa;
+
+	sa.type = AA_AUDITTYPE_ATTR;
+	sa.iattr = iattr;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	check = 0;
+	if (inode && S_ISDIR(inode->i_mode))
+		check |= AA_CHECK_DIR;
+	if (iattr->ia_valid & ATTR_FILE)
+		check |= AA_CHECK_FD;
+
+	error = aa_perm_dentry(profile, dentry, mnt, &sa, MAY_WRITE, check);
+
+	return error;
+}
+
+/**
+ * aa_perm_xattr - check if xattr attribute change is allowed
+ * @profile: profile to check against
+ * @dentry: dentry of the file to check
+ * @mnt: vfsmount of the file to check
+ * @operation: xattr operation being done
+ * @mask: access mode requested
+ * @check: kind of check to perform
+ */
+int aa_perm_xattr(struct aa_profile *profile, struct dentry *dentry,
+		  struct vfsmount *mnt, const char *operation,
+		  int mask, int check)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	struct aa_audit sa;
+
+	sa.type = AA_AUDITTYPE_XATTR;
+	sa.name2 = operation;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	if (inode && S_ISDIR(inode->i_mode))
+		check |= AA_CHECK_DIR;
+
+	error = aa_perm_dentry(profile, dentry, mnt, &sa, mask, check);
+
+	return error;
+}
+
+/**
+ * aa_perm - basic apparmor permissions check
+ * @profile: profile to check against
+ * @dentry: dentry of the file to check
+ * @mnt: vfsmount of the file to check
+ * @mask: access mode requested
+ * @check: kind of check to perform
+ *
+ * Determine if access @mask for the file is authorized by @profile.
+ * Returns 0 on success, or else an error code.
+ */
+int aa_perm(struct aa_profile *profile, struct dentry *dentry,
+	    struct vfsmount *mnt, int mask, int check)
+{
+	struct aa_audit sa;
+	int error = 0;
+
+	if (mask == 0)
+		goto out;
+
+	sa.type = AA_AUDITTYPE_FILE;
+	sa.mask = mask;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+	error = aa_perm_dentry(profile, dentry, mnt, &sa, mask, check);
+
+out:
+	return error;
+}
+
+/**
+ * aa_perm_dir
+ * @profile: profile to check against
+ * @dentry: dentry of directory to check
+ * @mnt: vfsmount of directory to check
+ * @operation: directory operation being performed
+ * @mask: access mode requested
+ *
+ * Determine if directory operation (make/remove) for dentry is authorized
+ * by @profile.
+ * Returns 0 on success, or else an error code.
+ */
+int aa_perm_dir(struct aa_profile *profile, struct dentry *dentry,
+		struct vfsmount *mnt, const char *operation, int mask)
+{
+	struct aa_audit sa;
+
+	sa.type = AA_AUDITTYPE_DIR;
+	sa.name2 = operation;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	return aa_perm_dentry(profile, dentry, mnt, &sa, mask, AA_CHECK_DIR);
+}
+
+int aa_perm_path(struct aa_profile *profile, const char *name, int mask)
+{
+	struct aa_audit sa;
+	int denied_mask;
+
+	sa.type = AA_AUDITTYPE_FILE;
+	sa.mask = mask;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+	sa.name = name;
+
+	denied_mask = aa_file_denied(profile, name, mask);
+	aa_permerror2result(denied_mask, &sa);
+
+	return aa_audit(profile, &sa);
+}
+
+/**
+ * aa_capability - test permission to use capability
+ * @cxt: aa_task_context with profile to check against
+ * @cap: capability to be tested
+ *
+ * Look up capability in profile capability set.
+ * Returns 0 on success, or else an error code.
+ */
+int aa_capability(struct aa_task_context *cxt, int cap)
+{
+	int error = cap_raised(cxt->profile->capabilities, cap) ? 0 : -EPERM;
+	struct aa_audit sa;
+
+	/* test if cap has alread been logged */
+	if (cap_raised(cxt->caps_logged, cap)) {
+		if (PROFILE_COMPLAIN(cxt->profile))
+			error = 0;
+		return error;
+	} else
+		/* don't worry about rcu replacement of the cxt here.
+		 * caps_logged is a cache to reduce the occurance of
+		 * duplicate messages in the log.  The worst that can
+		 * happen is duplicate capability messages shows up in
+		 * the audit log
+		 */
+		cap_raise(cxt->caps_logged, cap);
+
+	sa.type = AA_AUDITTYPE_CAP;
+	sa.name = NULL;
+	sa.capability = cap;
+	sa.flags = 0;
+	sa.error_code = 0;
+	sa.result = !error;
+	sa.gfp_mask = GFP_ATOMIC;
+
+	error = aa_audit(cxt->profile, &sa);
+
+	return error;
+}
+
+/* must be used inside rcu_read_lock or task_lock */
+int aa_may_ptrace(struct aa_task_context *cxt, struct aa_profile *tracee)
+{
+	if (!cxt || cxt->profile == tracee)
+		return 0;
+	return aa_capability(cxt, CAP_SYS_PTRACE);
+}
+
+/**
+ * aa_link - hard link check
+ * @profile: profile to check against
+ * @link: dentry of link being created
+ * @link_mnt: vfsmount of link being created
+ * @target: dentry of link target
+ * @target_mnt: vfsmunt of link target
+ *
+ * Returns 0 on success, or else an error code.
+ */
+int aa_link(struct aa_profile *profile,
+	    struct dentry *link, struct vfsmount *link_mnt,
+	    struct dentry *target, struct vfsmount *target_mnt)
+{
+	int denied_mask = -EPERM, error, check = 0;
+	struct aa_audit sa;
+
+again:
+	sa.buffer = NULL;
+	sa.name = aa_get_name(link, link_mnt, &sa.buffer, check);
+	sa.buffer2 = NULL;
+	sa.name2 = aa_get_name(target, target_mnt, &sa.buffer2, check);
+
+	if (IS_ERR(sa.name)) {
+		denied_mask = PTR_ERR(sa.name);
+		sa.name = NULL;
+	}
+	if (IS_ERR(sa.name2)) {
+		denied_mask = PTR_ERR(sa.name2);
+		sa.name2 = NULL;
+	}
+
+	if (sa.name && sa.name2)
+		denied_mask = aa_link_denied(profile, sa.name, sa.name2);
+
+	aa_permerror2result(denied_mask, &sa);
+
+	sa.type = AA_AUDITTYPE_LINK;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	error = aa_audit(profile, &sa);
+
+	aa_put_name_buffer(sa.buffer);
+	aa_put_name_buffer(sa.buffer2);
+	if (error == -ENAMETOOLONG) {
+		BUG_ON(check & AA_CHECK_MANGLE);
+		check |= AA_CHECK_MANGLE;
+		goto again;
+	}
+
+	return error;
+}
+
+/*******************************
+ * Global task related functions
+ *******************************/
+
+/**
+ * aa_clone - initialize the task context for a new task
+ * @child: task that is being created
+ *
+ * Returns 0 on success, or else an error code.
+ */
+int aa_clone(struct task_struct *child)
+{
+	struct aa_task_context *cxt, *child_cxt;
+	struct aa_profile *profile;
+
+	if (!aa_task_context(current))
+		return 0;
+	child_cxt = aa_alloc_task_context(GFP_KERNEL);
+	if (!child_cxt)
+		return -ENOMEM;
+
+repeat:
+	profile = aa_get_profile(current);
+	if (profile) {
+		lock_profile(profile);
+		cxt = aa_task_context(current);
+		if (unlikely(profile->isstale || !cxt ||
+			     cxt->profile != profile)) {
+			/**
+			 * Race with profile replacement or removal, or with
+			 * task context removal.
+			 */
+			unlock_profile(profile);
+			aa_put_profile(profile);
+			goto repeat;
+		}
+
+		/* No need to grab the child's task lock here. */
+		aa_change_task_context(child, child_cxt, profile,
+				       cxt->hat_magic);
+		unlock_profile(profile);
+
+		if (APPARMOR_COMPLAIN(child_cxt) &&
+		    profile == null_complain_profile)
+			aa_audit_message(profile, GFP_KERNEL,
+					 "LOGPROF-HINT fork child=%d "
+					 "(%d profile %s active %s)",
+					 child->pid, current->pid,
+					 profile->parent->name, profile->name);
+		aa_put_profile(profile);
+	} else
+		aa_free_task_context(child_cxt);
+
+	return 0;
+}
+
+static struct aa_profile *
+aa_register_find(struct aa_profile *profile, const char *name, char *buffer,
+		 int mandatory, int complain)
+{
+	struct aa_profile *new_profile;
+
+	/* Locate new profile */
+	new_profile = aa_find_profile(name);
+	if (new_profile) {
+		AA_DEBUG("%s: setting profile %s\n",
+			 __FUNCTION__, new_profile->name);
+	} else if (mandatory && profile) {
+		name = mangle(name, buffer);
+		if (complain) {
+			aa_audit_message(profile, GFP_KERNEL, "LOGPROF-HINT "
+					 "missing_mandatory_profile image '%s' "
+					 "(%d profile %s active %s)",
+					 name, current->pid,
+					 profile->parent->name, profile->name);
+			profile = aa_dup_profile(null_complain_profile);
+		} else {
+			aa_audit_message(profile, GFP_KERNEL, "REJECTING "
+					 "exec(2) of image '%s'. Profile "
+					 "mandatory and not found. "
+					 "(%d profile %s active %s)",
+					 name, current->pid,
+					 profile->parent->name, profile->name);
+			return ERR_PTR(-EPERM);
+		}
+	} else {
+		/* Only way we can get into this code is if task
+		 * is unconfined.
+		 */
+		AA_DEBUG("%s: No profile found for exec image '%s'\n",
+			 __FUNCTION__,
+			 name);
+	}
+	return new_profile;
+}
+
+/**
+ * aa_register - register a new program
+ * @bprm: binprm of program being registered
+ *
+ * Try to register a new program during execve().  This should give the
+ * new program a valid aa_task_context if confined.
+ */
+int aa_register(struct linux_binprm *bprm)
+{
+	const char *filename;
+	char  *buffer = NULL;
+	struct file *filp = bprm->file;
+	struct aa_profile *profile, *old_profile, *new_profile = NULL;
+	int exec_mode = AA_EXEC_UNSAFE, complain = 0;
+
+	AA_DEBUG("%s\n", __FUNCTION__);
+
+	filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer,
+			       AA_CHECK_MANGLE);
+	if (IS_ERR(filename)) {
+		AA_ERROR("%s: Failed to get filename", __FUNCTION__);
+		return -ENOENT;
+	}
+
+repeat:
+	profile = aa_get_profile(current);
+	if (profile) {
+		complain = PROFILE_COMPLAIN(profile);
+
+		/* Confined task, determine what mode inherit, unconfined or
+		 * mandatory to load new profile
+		 */
+		exec_mode = aa_match(profile->file_rules, filename);
+
+		if (exec_mode & (MAY_EXEC | AA_EXEC_MODIFIERS)) {
+			switch (exec_mode & (MAY_EXEC | AA_EXEC_MODIFIERS)) {
+			case MAY_EXEC | AA_EXEC_INHERIT:
+				AA_DEBUG("%s: INHERIT %s\n",
+					 __FUNCTION__,
+					 filename);
+				/* nothing to be done here */
+				goto cleanup;
+
+			case MAY_EXEC | AA_EXEC_UNCONFINED:
+				AA_DEBUG("%s: UNCONFINED %s\n",
+					 __FUNCTION__,
+					 filename);
+
+				/* detach current profile */
+				new_profile = NULL;
+				break;
+
+			case MAY_EXEC | AA_EXEC_PROFILE:
+				AA_DEBUG("%s: PROFILE %s\n",
+					 __FUNCTION__,
+					 filename);
+				new_profile = aa_register_find(profile,
+							       filename,
+							       buffer, 1,
+							       complain);
+				break;
+
+			default:
+				AA_ERROR("Rejecting exec(2) of image '%s'. "
+					 "Unknown exec qualifier %x "
+					 "(%d profile %s active %s)\n",
+					 filename,
+					 exec_mode & AA_EXEC_MODIFIERS,
+					 current->pid,
+					 profile->parent->name,
+					 profile->name);
+				new_profile = ERR_PTR(-EPERM);
+				break;
+			}
+
+		} else if (complain) {
+			/* There was no entry in calling profile
+			 * describing mode to execute image in.
+			 * Drop into null-profile (disabling secure exec).
+			 */
+			new_profile = aa_dup_profile(null_complain_profile);
+			exec_mode |= AA_EXEC_UNSAFE;
+		} else {
+			filename = mangle(filename, buffer);
+			aa_audit_message(profile, GFP_KERNEL, "REJECTING "
+					 "exec(2) of image '%s'. Unable to "
+					 "determine exec qualifier. "
+					 "(%d profile %s active %s)",
+					 filename, current->pid,
+					 profile->parent->name, profile->name);
+			new_profile = ERR_PTR(-EPERM);
+		}
+	} else {
+		/* Unconfined task, load profile if it exists */
+		new_profile = aa_register_find(NULL, filename, buffer, 0, 0);
+		if (new_profile == NULL)
+			goto cleanup;
+	}
+
+	if (IS_ERR(new_profile))
+		goto cleanup;
+
+	old_profile = __aa_replace_profile(current, new_profile, 0);
+	if (IS_ERR(old_profile)) {
+		aa_put_profile(new_profile);
+		aa_put_profile(profile);
+		if (PTR_ERR(old_profile) == -ESTALE)
+			goto repeat;
+		if (PTR_ERR(old_profile) == -EPERM) {
+			filename = mangle(filename, buffer);
+			aa_audit_message(profile, GFP_KERNEL,
+					 "REJECTING exec(2) of image '%s'. "
+					 "Unable to change profile, ptraced by "
+					 "%d. (%d profile %s active %s)",
+					 filename, current->parent->pid,
+					 current->pid,
+					 profile->parent->name, profile->name);
+		}
+		new_profile = old_profile;
+		goto cleanup;
+	}
+	aa_put_profile(old_profile);
+	aa_put_profile(profile);
+
+	/* Handle confined exec.
+	 * Can be at this point for the following reasons:
+	 * 1. unconfined switching to confined
+	 * 2. confined switching to different confinement
+	 * 3. confined switching to unconfined
+	 *
+	 * Cases 2 and 3 are marked as requiring secure exec
+	 * (unless policy specified "unsafe exec")
+	 */
+	if (!(exec_mode & AA_EXEC_UNSAFE)) {
+		unsigned long bprm_flags;
+
+		bprm_flags = AA_SECURE_EXEC_NEEDED;
+		bprm->security = (void*)
+			((unsigned long)bprm->security | bprm_flags);
+	}
+
+	if (complain && new_profile == null_complain_profile)
+		aa_audit_message(new_profile, GFP_ATOMIC,
+				 "LOGPROF-HINT changing_profile "
+				 "(%d profile %s active %s)",
+				 current->pid,
+				 new_profile->parent->name, new_profile->name);
+cleanup:
+	aa_put_name_buffer(buffer);
+	if (IS_ERR(new_profile))
+		return PTR_ERR(new_profile);
+	aa_put_profile(new_profile);
+	return 0;
+}
+
+/**
+ * aa_release - release a task context
+ * @task: task being released
+ *
+ * This is called after a task has exited and the parent has reaped it.
+ */
+void aa_release(struct task_struct *task)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *profile;
+	/*
+	 * While the task context is still on a profile's task context
+	 * list, another process could replace the profile under us,
+	 * leaving us with a locked profile that is no longer attached
+	 * to this task. So after locking the profile, we check that
+	 * the profile is still attached.  The profile lock is
+	 * sufficient to prevent the replacement race so we do not lock
+	 * the task.
+	 *
+	 * lock_dep reports a false 'possible irq lock inversion dependency'
+	 * between the profile lock and the task_lock.
+	 *
+	 * We also avoid taking the task_lock here because lock_dep
+	 * would report another false {softirq-on-W} potential irq_lock
+	 * inversion.
+	 *
+	 * If the task does not have a profile attached we are safe;
+	 * nothing can race with us at this point.
+	 */
+
+repeat:
+	profile = aa_get_profile(task);
+	if (profile) {
+		lock_profile(profile);
+		cxt = aa_task_context(task);
+		if (unlikely(!cxt || cxt->profile != profile)) {
+			unlock_profile(profile);
+			aa_put_profile(profile);
+			goto repeat;
+		}
+		aa_change_task_context(task, NULL, NULL, 0);
+		unlock_profile(profile);
+		aa_put_profile(profile);
+	}
+}
+
+/**
+ * do_change_hat - actually switch hats
+ * @hat_name: name of hat to switch to
+ * @new_cxt: new aa_task_context to use on profile change
+ * @hat_magic: new magic value to use
+ *
+ * Switch to a new hat.  Returns %0 on success, error otherwise.
+ */
+static int do_change_hat(const char *hat_name,
+			 struct aa_task_context *new_cxt, u64 hat_magic)
+{
+	struct aa_task_context *cxt = aa_task_context(current);
+	struct aa_profile *sub;
+	int error = 0;
+
+	/*
+	 * Note: the profile and sub-profiles cannot go away under us here;
+	 * no need to grab an additional reference count.
+	 */
+	sub = __aa_find_profile(hat_name, &cxt->profile->parent->sub);
+
+	if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, sub))
+		return -EPERM;
+
+	if (sub) {
+		/* change hat */
+		aa_change_task_context(current, new_cxt, sub, hat_magic);
+	} else {
+		struct aa_profile *profile = cxt->profile;
+
+		if (APPARMOR_COMPLAIN(cxt)) {
+			aa_audit_message(profile, GFP_ATOMIC,
+					 "LOGPROF-HINT unknown_hat %s "
+					 "(%d profile %s active %s)",
+					 hat_name, current->pid,
+					 profile->parent->name, profile->name);
+		} else {
+			AA_DEBUG("%s: Unknown hatname '%s'. "
+				"Changing to NULL profile "
+				"(%d profile %s active %s)\n",
+				 __FUNCTION__,
+				 hat_name,
+				 current->pid,
+				 profile->parent->name,
+				 profile->name);
+			error = -EACCES;
+		}
+		/*
+		 * Switch to the NULL profile: it grants no accesses, so in
+		 * learning mode all accesses will get logged, and in enforce
+		 * mode all accesses will be denied.
+		 *
+		 * In learning mode, this allows us to learn about new hats.
+		 */
+		aa_change_task_context(current, new_cxt,
+				       cxt->profile->null_profile, hat_magic);
+	}
+
+	return error;
+}
+
+/**
+ * aa_change_hat - change hat to/from subprofile
+ * @hat_name: hat to change to
+ * @hat_magic: magic cookie to validate the hat change
+ *
+ * Change to new @hat_name, and store the @hat_magic in the current task
+ * context.  If the new @hat_name is %NULL and the @hat_magic matches that
+ * stored in the current task context and is not 0, return to the top level
+ * profile.
+ * Returns %0 on success, error otherwise.
+ */
+int aa_change_hat(const char *hat_name, u64 hat_magic)
+{
+	struct aa_task_context *cxt, *new_cxt;
+	struct aa_profile *profile = NULL;
+	int error = 0;
+
+	/* Dump out above debugging in WARN mode if we are in AUDIT mode */
+	if (APPARMOR_AUDIT(aa_task_context(current))) {
+		aa_audit_message(NULL, GFP_KERNEL, "change_hat %s, 0x%llx "
+				 "(pid %d)",
+				 hat_name ? hat_name : "NULL", hat_magic,
+				 current->pid);
+	}
+
+	new_cxt = aa_alloc_task_context(GFP_KERNEL);
+	if (!new_cxt)
+		return -ENOMEM;
+
+	cxt = lock_task_and_profiles(current, NULL);
+	if (!cxt) {
+		/* An unconfined process cannot change_hat(). */
+		error = -EPERM;
+		goto out;
+	}
+
+	/* No need to get reference count: we do not sleep. */
+	profile = cxt->profile;
+
+	/* check to see if the confined process has any hats. */
+	if (list_empty(&profile->parent->sub) && !PROFILE_COMPLAIN(profile)) {
+		error = -ECHILD;
+		goto out;
+	}
+
+	if (profile == profile->parent) {
+		/* We are in the parent profile. */
+		if (hat_name) {
+			AA_DEBUG("%s: switching to %s, 0x%llx\n",
+				 __FUNCTION__,
+				 hat_name,
+				 hat_magic);
+			error = do_change_hat(hat_name, new_cxt, hat_magic);
+		}
+	} else {
+		/*
+		 * We are in a child profile.
+		 *
+		 * Check to make sure magic is same as what was passed when
+		 * we switched into this profile.  Handle special casing of
+		 * NULL magic which confines task to subprofile and prohibits
+		 * further change_hats.
+		 */
+		if (hat_magic && hat_magic == cxt->hat_magic) {
+			if (!hat_name) {
+				/* Return from subprofile back to parent. */
+				aa_change_task_context(current, new_cxt,
+						       profile->parent, 0);
+			} else {
+				/*
+				 * Change to another (sibling) profile, and
+				 * stick with the same hat_magic.
+				 */
+				error = do_change_hat(hat_name, new_cxt,
+						      cxt->hat_magic);
+			}
+		} else if (cxt->hat_magic) {
+			AA_ERROR("KILLING process %d "
+				 "Invalid change_hat() magic# 0x%llx "
+				 "(hatname %s profile %s active %s)\n",
+				 current->pid, hat_magic,
+				 hat_name ? hat_name : "NULL",
+				 profile->parent->name,
+				 profile->name);
+
+			/* terminate current process */
+			(void)send_sig_info(SIGKILL, NULL, current);
+		} else {	/* cxt->hat_magic == 0 */
+			AA_ERROR("KILLING process %d "
+				 "Task was confined to current subprofile "
+				 "(profile %s active %s)\n",
+				 current->pid,
+				 profile->parent->name,
+				 profile->name);
+
+			/* terminate current process */
+			(void)send_sig_info(SIGKILL, NULL, current);
+		}
+
+	}
+
+out:
+	if (aa_task_context(current) != new_cxt)
+		aa_free_task_context(new_cxt);
+	task_unlock(current);
+	unlock_profile(profile);
+	return error;
+}
+
+/**
+ * __aa_replace_profile - replace a task's profile
+ * @task: task to switch the profile of
+ * @profile: profile to switch to
+ * @hat_magic: magic cookie to switch to
+ *
+ * Returns a handle to the previous profile upon success, or else an
+ * error code.
+ */
+struct aa_profile *__aa_replace_profile(struct task_struct *task,
+					struct aa_profile *profile,
+					u32 hat_magic)
+{
+	struct aa_task_context *cxt, *new_cxt = NULL;
+	struct aa_profile *old_profile = NULL;
+
+	if (profile) {
+		new_cxt = aa_alloc_task_context(GFP_KERNEL);
+		if (!new_cxt)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	cxt = lock_task_and_profiles(task, profile);
+	if (unlikely(profile && profile->isstale)) {
+		task_unlock(task);
+		unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+		aa_free_task_context(new_cxt);
+		return ERR_PTR(-ESTALE);
+	}
+
+	if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
+		task_unlock(task);
+		unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+		aa_free_task_context(new_cxt);
+		return ERR_PTR(-EPERM);
+	}
+
+	if (cxt) {
+		old_profile = aa_dup_profile(cxt->profile);
+		aa_change_task_context(task, new_cxt, profile, cxt->hat_magic);
+	} else
+		aa_change_task_context(task, new_cxt, profile, 0);
+
+	task_unlock(task);
+	unlock_both_profiles(profile, old_profile);
+	return old_profile;
+}
+
+/**
+ * lock_task_and_profile - lock the task and confining profiles and @profile
+ * @task - task to lock
+ * @profile - extra profile to lock in addition to the current profile
+ *
+ * Handle the spinning on locking to make sure the task context and
+ * profile are consistent once all locks are aquired.
+ *
+ * return the aa_task_context currently confining the task.  The task lock
+ * will be held whether or not the task is confined.
+ */
+struct aa_task_context *
+lock_task_and_profiles(struct task_struct *task, struct aa_profile *profile)
+{
+	struct aa_task_context *cxt;
+	struct aa_profile *old_profile = NULL;
+
+	rcu_read_lock();
+repeat:
+	cxt = aa_task_context(task);
+	if (cxt)
+		old_profile = cxt->profile;
+	lock_both_profiles(profile, old_profile);
+	task_lock(task);
+
+	/* check for race with profile transition, replacement or removal */
+	if (unlikely(cxt != aa_task_context(task))) {
+		task_unlock(task);
+		unlock_both_profiles(profile, old_profile);
+		goto repeat;
+	}
+	rcu_read_unlock();
+	return cxt;
+}
+
+static void free_aa_task_context_rcu_callback(struct rcu_head *head)
+{
+	struct aa_task_context *cxt;
+
+	cxt = container_of(head, struct aa_task_context, rcu);
+	aa_free_task_context(cxt);
+}
+
+/**
+ * aa_change_task_context - switch a task to use a new context and profile
+ * @task: task that is having its task context changed
+ * @new_cxt: new task context to use after the switch
+ * @profile: new profile to use after the switch
+ * @hat_magic: hat value to switch to (0 for no hat)
+ */
+void aa_change_task_context(struct task_struct *task,
+			    struct aa_task_context *new_cxt,
+			    struct aa_profile *profile, u64 hat_magic)
+{
+	struct aa_task_context *old_cxt = aa_task_context(task);
+
+	if (old_cxt) {
+		list_del_init(&old_cxt->list);
+		call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
+	}
+	if (new_cxt) {
+		/* clear the caps_logged cache, so that new profile/hat has
+		 * chance to emit its own set of cap messages */
+		new_cxt->caps_logged = CAP_EMPTY_SET;
+		new_cxt->hat_magic = hat_magic;
+		new_cxt->task = task;
+		new_cxt->profile = aa_dup_profile(profile);
+		list_move(&new_cxt->list, &profile->parent->task_contexts);
+	}
+	rcu_assign_pointer(task->security, new_cxt);
+}

-- 

  parent reply	other threads:[~2007-05-14 11:32 UTC|newest]

Thread overview: 213+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-05-14 11:06 [AppArmor 00/45] AppArmor security module overview jjohansen
2007-05-14 11:06 ` [AppArmor 01/45] Pass struct vfsmount to the inode_create LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 02/45] Pass struct path down to remove_suid and children jjohansen
2007-05-14 11:06 ` [AppArmor 03/45] Add a vfsmount parameter to notify_change() jjohansen
2007-05-14 11:06 ` [AppArmor 04/45] Pass struct vfsmount to the inode_setattr LSM hook jjohansen
2007-05-14 11:06 ` jjohansen
2007-05-14 11:06 ` [AppArmor 05/45] Add struct vfsmount parameter to vfs_mkdir() jjohansen
2007-05-14 11:06 ` [AppArmor 06/45] Pass struct vfsmount to the inode_mkdir LSM hook jjohansen
2007-05-14 11:06 ` jjohansen
2007-05-14 11:06 ` [AppArmor 07/45] Add a struct vfsmount parameter to vfs_mknod() jjohansen
2007-05-14 11:06 ` jjohansen
2007-05-14 11:06 ` [AppArmor 08/45] Pass struct vfsmount to the inode_mknod LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 09/45] Add a struct vfsmount parameter to vfs_symlink() jjohansen
2007-05-14 11:06 ` [AppArmor 10/45] Pass struct vfsmount to the inode_symlink LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 11/45] Pass struct vfsmount to the inode_readlink " jjohansen
2007-05-14 11:06 ` [AppArmor 12/45] Add struct vfsmount parameters to vfs_link() jjohansen
2007-05-14 11:06 ` [AppArmor 13/45] Pass the struct vfsmounts to the inode_link LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 14/45] Add a struct vfsmount parameter to vfs_rmdir() jjohansen
2007-05-14 11:06 ` [AppArmor 15/45] Pass struct vfsmount to the inode_rmdir LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 16/45] Call lsm hook before unhashing dentry in vfs_rmdir() jjohansen
2007-05-14 11:06 ` [AppArmor 17/45] Add a struct vfsmount parameter to vfs_unlink() jjohansen
2007-05-14 11:06 ` [AppArmor 18/45] Pass struct vfsmount to the inode_unlink LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 19/45] Add struct vfsmount parameters to vfs_rename() jjohansen
2007-05-14 11:06 ` [AppArmor 20/45] Pass struct vfsmount to the inode_rename LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 21/45] Add a struct vfsmount parameter to vfs_setxattr() jjohansen
2007-05-14 11:06 ` [AppArmor 22/45] Pass struct vfsmount to the inode_setxattr LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 23/45] Add a struct vfsmount parameter to vfs_getxattr() jjohansen
2007-05-14 11:06 ` [AppArmor 24/45] Pass struct vfsmount to the inode_getxattr LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 25/45] Add a struct vfsmount parameter to vfs_listxattr() jjohansen
2007-05-14 11:06 ` jjohansen
2007-05-14 11:06 ` [AppArmor 26/45] Pass struct vfsmount to the inode_listxattr LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 27/45] Add a struct vfsmount parameter to vfs_removexattr() jjohansen
2007-05-14 11:06 ` [AppArmor 28/45] Pass struct vfsmount to the inode_removexattr LSM hook jjohansen
2007-05-14 11:06 ` [AppArmor 29/45] Fix __d_path() for lazy unmounts and make it unambiguous jjohansen
2007-05-14 11:06 ` [AppArmor 30/45] Make d_path() consistent across mount operations jjohansen
2007-05-14 11:06 ` [AppArmor 31/45] Add d_namespace_path() to compute namespace relative pathnames jjohansen
2007-05-14 11:06 ` [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames jjohansen
2007-05-14 11:06 ` [AppArmor 33/45] Pass struct file down the inode_*xattr security LSM hooks jjohansen
2007-05-14 11:06 ` [AppArmor 34/45] Factor out sysctl pathname code jjohansen
2007-05-14 11:06 ` [AppArmor 35/45] Allow permission functions to tell between parent and leaf checks jjohansen
2007-05-15  9:08   ` Pavel Machek
2007-05-14 11:06 ` [AppArmor 36/45] Export audit subsystem for use by modules jjohansen
2007-05-14 11:06 ` jjohansen [this message]
2007-05-15  9:12   ` [AppArmor 37/45] AppArmor: Main Part Pavel Machek
2007-05-14 11:06 ` [AppArmor 38/45] AppArmor: Module and LSM hooks jjohansen
2007-05-15  9:14   ` Pavel Machek
2007-05-23 16:16     ` Andreas Gruenbacher
2007-06-04 10:55       ` Pavel Machek
2007-06-04 11:25         ` Andreas Gruenbacher
2007-06-04 11:35           ` Pavel Machek
2007-06-04 11:42             ` Andreas Gruenbacher
2007-06-04 13:12               ` Pavel Machek
2007-06-04 14:30                 ` Andreas Gruenbacher
2007-06-06 13:09                   ` Stephen Smalley
2007-06-10 23:10                     ` Andreas Gruenbacher
2007-06-11 14:33                       ` Stephen Smalley
2007-06-11 15:55                         ` Andreas Gruenbacher
2007-06-11 19:02                           ` Serge E. Hallyn
2007-06-12 13:00                             ` Stephen Smalley
2007-06-12 15:34                               ` Serge E. Hallyn
2007-06-12  5:17                                 ` Karl MacMillan
2007-06-12 19:00                                   ` Serge E. Hallyn
2007-06-12 13:13                           ` Stephen Smalley
2007-06-12 23:50                         ` Andreas Gruenbacher
2007-06-09 12:58                   ` Pavel Machek
2007-06-09 13:44                     ` Andreas Gruenbacher
2007-06-12 13:06                       ` Pavel Machek
2007-05-14 11:06 ` [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching jjohansen
2007-05-15  9:20   ` Pavel Machek
2007-06-04 21:03     ` Andreas Gruenbacher
2007-06-06 13:26       ` Stephen Smalley
2007-06-06 17:32         ` Greg KH
2007-06-09 23:47           ` Pavel Machek
2007-06-08 22:03         ` Andreas Gruenbacher
2007-06-09  0:17           ` Greg KH
2007-06-09  1:06             ` david
2007-06-10  8:34               ` Pavel Machek
2007-06-10  9:04                 ` david
2007-06-10 20:04                   ` Casey Schaufler
2007-06-10 20:51                     ` Crispin Cowan
2007-06-11  6:45                       ` david
2007-06-11  8:29                         ` Sean
2007-06-11  9:33                           ` david
2007-06-11 11:34                             ` Sean
2007-06-11 11:00                         ` Pavel Machek
2007-06-10 21:05                   ` Pavel Machek
2007-06-11  6:27                     ` david
2007-06-14 19:16                       ` Jack Stone
2007-06-15  0:18                         ` david
2007-06-15 17:01                           ` Greg KH
2007-06-12 17:03                     ` Lars Marowsky-Bree
2007-06-09  5:18             ` david
2007-06-09  5:46               ` Sean
2007-06-09  7:13                 ` david
2007-06-09  7:36                   ` Sean
2007-06-09  8:06                     ` david
2007-06-09  8:10                       ` Sean
2007-06-09 15:17                         ` Andreas Gruenbacher
2007-06-09 16:36                           ` Sean
2007-06-09 15:33                   ` Joshua Brindle
2007-06-09 16:18               ` Kyle Moffett
2007-06-09 16:46                 ` david
2007-06-09 17:06                   ` Kyle Moffett
2007-06-09 17:32                     ` david
2007-06-09 19:50                       ` Kyle Moffett
2007-06-09 20:43                         ` david
2007-06-10 20:54               ` Crispin Cowan
2007-06-10 21:17                 ` Joshua Brindle
2007-06-09 15:05             ` Andreas Gruenbacher
2007-06-10 17:09             ` Crispin Cowan
2007-06-15 16:50               ` Greg KH
2007-06-15 18:01                 ` Casey Schaufler
2007-06-15 18:15                   ` Stephen Smalley
2007-06-15 20:43                     ` Casey Schaufler
2007-06-15 21:14                       ` Greg KH
2007-06-15 21:28                         ` Karl MacMillan
2007-06-15 21:44                           ` Greg KH
2007-06-15 22:24                             ` Karl MacMillan
2007-06-18 13:33                               ` Stephen Smalley
2007-06-21 15:54                                 ` Andreas Gruenbacher
2007-06-15 22:37                         ` Casey Schaufler
2007-06-18 12:47                       ` Stephen Smalley
2007-06-15 20:06                 ` Pavel Machek
2007-06-15 21:11                   ` Greg KH
2007-06-15 21:42                     ` James Morris
2007-06-15 23:50                       ` Greg KH
2007-06-16  1:21                         ` James Morris
2007-06-16  2:57                           ` Casey Schaufler
2007-06-16  3:39                             ` James Morris
2007-06-18  1:51                               ` Casey Schaufler
2007-06-18 11:29                                 ` Joshua Brindle
2007-06-16  4:23                           ` Greg KH
2007-06-15 23:30                     ` Crispin Cowan
2007-06-15 23:49                       ` Greg KH
2007-06-16  0:01                         ` david
2007-06-16  0:20                           ` Pavel Machek
2007-06-22  9:59                             ` Andreas Gruenbacher
2007-06-16  0:31                           ` Greg KH
2007-06-16  8:09                             ` david
2007-06-16 16:24                               ` Greg KH
2007-06-16  1:41                           ` James Morris
2007-06-16  0:18                         ` Seth Arnold
2007-06-16  0:29                           ` Greg KH
2007-06-16  1:46                           ` James Morris
2007-06-16  2:19                           ` James Morris
2007-06-18 18:48                         ` Crispin Cowan
2007-06-21 16:01                         ` Andreas Gruenbacher
2007-06-21 17:59                           ` Pavel Machek
2007-06-16  0:02                       ` Pavel Machek
2007-06-21 16:08                         ` Lars Marowsky-Bree
2007-06-21 18:33                           ` Pavel Machek
2007-06-21 19:24                             ` Lars Marowsky-Bree
2007-06-21 19:42                               ` James Morris
2007-06-21 19:54                                 ` Lars Marowsky-Bree
2007-06-21 20:59                                   ` Stephen Smalley
2007-06-21 21:17                                     ` Lars Marowsky-Bree
2007-06-22  0:16                                       ` Joshua Brindle
2007-06-22  0:19                                         ` Lars Marowsky-Bree
2007-06-22  0:28                                         ` david
2007-06-22  3:45                                           ` Joshua Brindle
2007-06-22  5:07                                             ` david
2007-06-22 10:49                                             ` Lars Marowsky-Bree
2007-06-22 11:19                                       ` Stephen Smalley
2007-06-22 11:34                                         ` Neil Brown
2007-06-22 11:48                                           ` Stephen Smalley
2007-06-22 11:37                                         ` Lars Marowsky-Bree
2007-06-22 12:41                                           ` Stephen Smalley
2007-06-22 12:54                                             ` Lars Marowsky-Bree
2007-06-22 13:22                                               ` Stephen Smalley
2007-06-22 14:49                                                 ` Stephen Smalley
2007-06-22 16:06                                             ` Casey Schaufler
2007-06-22  0:34                                     ` Chris Mason
2007-06-22  1:06                                       ` James Morris
2007-06-22  4:17                                         ` Crispin Cowan
2007-06-22 12:20                                           ` Stephen Smalley
2007-06-22  7:40                                         ` John Johansen
2007-06-22 12:17                                         ` Chris Mason
2007-06-22 13:48                                           ` James Morris
2007-06-22 14:02                                             ` Chris Mason
2007-06-22 14:23                                               ` James Morris
2007-06-22 17:30                                                 ` Chris Mason
2007-06-23  0:11                                                   ` Chris Wright
2007-06-24  0:10                                                     ` Toshiharu Harada
2007-06-24  0:40                                                       ` Toshiharu Harada
2007-06-26 21:01                                                     ` Crispin Cowan
2007-06-24 20:43                                                   ` Pavel Machek
2007-06-22 18:12                                                 ` david
2007-06-25 15:14                                               ` Pavel Machek
2007-06-25 21:02                                                 ` david
2007-06-26  8:50                                                 ` Lars Marowsky-Bree
2007-06-22  8:06                                     ` John Johansen
2007-06-22 11:53                                       ` Stephen Smalley
2007-06-22 12:42                                         ` Lars Marowsky-Bree
2007-06-22 12:46                                           ` Stephen Smalley
2007-06-22 18:35                                         ` david
2007-06-21 20:07                               ` Pavel Machek
2007-06-21 20:21                                 ` Lars Marowsky-Bree
2007-06-21 23:25                                   ` John Johansen
2007-06-21 19:30                             ` david
2007-06-21 19:35                               ` Lars Marowsky-Bree
2007-06-21 19:52                               ` Pavel Machek
2007-06-15 23:33                   ` Seth Arnold
2007-06-15 23:39                     ` Pavel Machek
2007-06-16  0:07                       ` Seth Arnold
2007-06-11 15:16           ` Stephen Smalley
2007-05-14 11:06 ` [AppArmor 40/45] AppArmor: all the rest jjohansen
2007-05-14 11:06 ` [AppArmor 41/45] Add AppArmor LSM to security/Makefile jjohansen
2007-05-14 11:06 ` [AppArmor 42/45] AppArmor: add lock subtyping so lockdep does not report false dependencies jjohansen
2007-05-14 11:06 ` [AppArmor 43/45] Switch to vfs_permission() in do_path_lookup() jjohansen
2007-05-14 11:06 ` [AppArmor 44/45] Switch to vfs_permission() in sys_fchdir() jjohansen
2007-05-14 11:06 ` jjohansen
2007-05-14 11:06 ` [AppArmor 45/45] Fix file_permission() jjohansen
2007-05-14 13:50 ` [AppArmor 00/45] AppArmor security module overview John Johansen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070514110620.908918667@suse.de \
    --to=jjohansen@suse.de \
    --cc=agruen@suse.de \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).