From: jjohansen@suse.de
To: akpm@linux-foundation.org
Cc: linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org,
John Johansen <jjohansen@suse.de>,
Andreas Gruenbacher <agruen@suse.de>
Subject: [AppArmor 37/45] AppArmor: Main Part
Date: Thu, 25 Oct 2007 23:41:01 -0700 [thread overview]
Message-ID: <20071026064052.099542126@suse.de> (raw)
In-Reply-To: 20071026064024.243943043@suse.de
[-- Attachment #1: apparmor-main.diff --]
[-- Type: text/plain, Size: 37311 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>
---
security/apparmor/main.c | 1361 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1361 insertions(+)
--- /dev/null
+++ b/security/apparmor/main.c
@@ -0,0 +1,1361 @@
+/*
+ * 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"
+};
+
+struct aa_namespace *default_namespace;
+
+static int aa_inode_mode(struct inode *inode)
+{
+ /* if the inode doesn't exist the user is creating it */
+ if (!inode || current->fsuid == inode->i_uid)
+ return AA_USER_SHIFT;
+ if (in_group_p(inode->i_gid))
+ return AA_GROUP_SHIFT;
+ return AA_OTHER_SHIFT;
+}
+
+/**
+ * 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
+ * @target_mode: UGO shift for target inode
+ * @request_mask: the permissions subset valid only if link succeeds
+ * 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 target_mode,
+ int *request_mask)
+{
+ unsigned int state;
+ int l_mode, t_mode, denied_mask = 0;
+ int link_mask = AA_MAY_LINK << target_mode;
+
+ *request_mask = link_mask;
+
+ l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
+ if (l_mode & link_mask) {
+ int mode;
+ /* test to see if target can be paired with link */
+ state = aa_dfa_null_transition(profile->file_rules, state);
+ mode = aa_match_state(profile->file_rules, state, target,
+ NULL);
+
+ if (!(mode & link_mask))
+ denied_mask |= link_mask;
+ if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
+ return denied_mask;
+ }
+
+ /* do link perm subset test */
+ l_mode = aa_match(profile->file_rules, link);
+ t_mode = aa_match(profile->file_rules, target);
+
+ /* Ignore valid-profile-transition flags. */
+ l_mode &= ~AA_SHARED_PERMS;
+ t_mode &= ~AA_SHARED_PERMS;
+
+ *request_mask |= l_mode;
+
+ /* Link always requires 'l' on the link for both parts of the pair.
+ * If a subset test is required a permission subset test of the
+ * perms for the link are done against the user:group:other of the
+ * target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions.
+ *
+ * If the link has 'x', an exact match of all the execute flags
+ * ('i', 'u', 'U', 'p', 'P').
+ */
+#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS)
+ denied_mask |= ~l_mode & link_mask;
+ if (l_mode & SUBSET_PERMS) {
+ denied_mask |= (l_mode & SUBSET_PERMS) & ~t_mode;
+ if (denied_mask & AA_EXEC_BITS)
+ denied_mask |= l_mode & AA_ALL_EXEC_MODS;
+ else if (l_mode & AA_EXEC_BITS) {
+ if (l_mode & AA_USER_EXEC &&
+ (l_mode & AA_USER_EXEC_MODS) !=
+ (t_mode & AA_USER_EXEC_MODS))
+ denied_mask |= AA_USER_EXEC |
+ (l_mode & AA_USER_EXEC_MODS);
+ if (l_mode & AA_GROUP_EXEC &&
+ (l_mode & AA_GROUP_EXEC_MODS) !=
+ (t_mode & AA_GROUP_EXEC_MODS))
+ denied_mask |= AA_GROUP_EXEC |
+ (l_mode & AA_GROUP_EXEC_MODS);
+ if (l_mode & AA_OTHER_EXEC &&
+ (l_mode & AA_OTHER_EXEC_MODS) !=
+ (t_mode & AA_OTHER_EXEC_MODS))
+ denied_mask |= AA_OTHER_EXEC |
+ (l_mode & AA_OTHER_EXEC_MODS);
+ }
+ } else
+ denied_mask |= t_mode | link_mask;
+#undef SUBSET_PERMS
+
+ return denied_mask;
+}
+
+/**
+ * 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);
+ 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 check)
+{
+ int error;
+ char *buffer = NULL;
+
+ sa->name = aa_get_name(dentry, mnt, &buffer, check);
+ sa->request_mask <<= aa_inode_mode(dentry->d_inode);
+ 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))
+ sa->denied_mask = 0;
+ else {
+ sa->denied_mask = sa->request_mask;
+ sa->error_code = PTR_ERR(sa->name);
+ }
+ sa->name = NULL;
+ } else
+ sa->denied_mask = aa_file_denied(profile, sa->name,
+ sa->request_mask);
+
+ if (!sa->denied_mask)
+ sa->error_code = 0;
+
+ error = aa_audit(profile, sa);
+ aa_put_name_buffer(buffer);
+
+ return error;
+}
+
+int alloc_default_namespace(void)
+{
+ struct aa_namespace *ns;
+ char *name = kstrdup("default", GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ ns = alloc_aa_namespace(name);
+ if (!ns) {
+ kfree(name);
+ return -ENOMEM;
+ }
+
+ write_lock(&profile_ns_list_lock);
+ default_namespace = ns;
+ aa_get_namespace(ns);
+ list_add(&ns->list, &profile_ns_list);
+ write_unlock(&profile_ns_list_lock);
+
+ return 0;
+}
+
+void free_default_namespace(void)
+{
+ write_lock(&profile_ns_list_lock);
+ list_del_init(&default_namespace->list);
+ write_unlock(&profile_ns_list_lock);
+ aa_put_namespace(default_namespace);
+ default_namespace = NULL;
+}
+
+static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer,
+ int mask)
+{
+ char *m = buffer;
+
+ if (mask & AA_EXEC_MMAP)
+ *m++ = 'm';
+ if (mask & MAY_READ)
+ *m++ = 'r';
+ if (mask & MAY_WRITE)
+ *m++ = 'w';
+ else if (mask & MAY_APPEND)
+ *m++ = 'a';
+ if (mask & MAY_EXEC) {
+ if (mask & AA_EXEC_UNSAFE) {
+ switch(mask & AA_EXEC_MODIFIERS) {
+ case AA_EXEC_UNCONFINED:
+ *m++ = 'u';
+ break;
+ case AA_EXEC_PIX:
+ *m++ = 'p';
+ /* fall through */
+ case AA_EXEC_INHERIT:
+ *m++ = 'i';
+ break;
+ case AA_EXEC_PROFILE:
+ *m++ = 'p';
+ break;
+ }
+ } else {
+ switch(mask & AA_EXEC_MODIFIERS) {
+ case AA_EXEC_UNCONFINED:
+ *m++ = 'U';
+ break;
+ case AA_EXEC_PIX:
+ *m++ = 'P';
+ /* fall through */
+ case AA_EXEC_INHERIT:
+ *m++ = 'I';
+ break;
+ case AA_EXEC_PROFILE:
+ *m++ = 'P';
+ break;
+ }
+ }
+ *m++ = 'x';
+ }
+ if (mask & AA_MAY_LINK)
+ *m++ = 'l';
+ if (mask & AA_MAY_LOCK)
+ *m++ = 'k';
+ *m++ = '\0';
+}
+
+static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
+ int mask)
+{
+ char user[10], group[10], other[10];
+
+ aa_audit_file_sub_mask(ab, user,
+ (mask & AA_USER_PERMS) >> AA_USER_SHIFT);
+ aa_audit_file_sub_mask(ab, group,
+ (mask & AA_GROUP_PERMS) >> AA_GROUP_SHIFT);
+ aa_audit_file_sub_mask(ab, other,
+ (mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT);
+
+ audit_log_format(ab, " %s=\"%s:%s:%s\"", name, user, group, other);
+}
+
+/**
+ * aa_audit - Log an audit event to the audit subsystem
+ * @profile: profile to check against
+ * @sa: audit event
+ * @audit_cxt: audit context to log message to
+ * @type: audit event number
+ */
+static int aa_audit_base(struct aa_profile *profile, struct aa_audit *sa,
+ struct audit_context *audit_cxt, int type)
+{
+ struct audit_buffer *ab = NULL;
+
+ ab = audit_log_start(audit_cxt, sa->gfp_mask, type);
+
+ if (!ab) {
+ AA_ERROR("Unable to log event (%d) to audit subsys\n",
+ type);
+ /* don't fail operations in complain mode even if logging
+ * fails */
+ return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM;
+ }
+
+ if (sa->operation)
+ audit_log_format(ab, "operation=\"%s\"", sa->operation);
+
+ if (sa->info)
+ audit_log_format(ab, " info=\"%s\"", sa->info);
+
+ if (sa->request_mask)
+ aa_audit_file_mask(ab, "request_mask", sa->request_mask);
+
+ if (sa->denied_mask)
+ aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);
+
+ if (sa->iattr) {
+ struct iattr *iattr = sa->iattr;
+
+ audit_log_format(ab, " attribute=\"%s%s%s%s%s%s%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 | ATTR_ATIME_SET) ?
+ "atime," : "",
+ iattr->ia_valid & (ATTR_MTIME | ATTR_MTIME_SET) ?
+ "mtime," : "",
+ iattr->ia_valid & ATTR_CTIME ? "ctime," : "");
+ }
+
+ if (sa->task)
+ audit_log_format(ab, " task=%d", sa->task);
+
+ if (sa->parent)
+ audit_log_format(ab, " parent=%d", sa->parent);
+
+ if (sa->name) {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, sa->name);
+ }
+
+ if (sa->name2) {
+ audit_log_format(ab, " name2=");
+ audit_log_untrustedstring(ab, sa->name2);
+ }
+
+ audit_log_format(ab, " pid=%d", current->pid);
+
+ if (profile) {
+ audit_log_format(ab, " profile=");
+ audit_log_untrustedstring(ab, profile->name);
+
+ audit_log_format(ab, " namespace=");
+ audit_log_untrustedstring(ab, profile->ns->name);
+ }
+
+ audit_log_end(ab);
+
+ return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error_code;
+}
+
+/**
+ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem
+ * @profile: profile to check against
+ * @gfp: memory allocation flags
+ * @msg: string describing syscall being rejected
+ */
+int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
+ const char *msg)
+{
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "syscall";
+ sa.name = msg;
+ sa.gfp_mask = gfp;
+ sa.error_code = -EPERM;
+
+ return aa_audit_base(profile, &sa, current->audit_context,
+ AUDIT_APPARMOR_DENIED);
+}
+
+int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
+ int type)
+{
+ struct audit_context *audit_cxt;
+
+ audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
+ return aa_audit_base(profile, sa, audit_cxt, type);
+}
+
+void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa)
+{
+ aa_audit_message(profile, sa, AUDIT_APPARMOR_HINT);
+}
+
+void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa)
+{
+ aa_audit_message(profile, sa, AUDIT_APPARMOR_STATUS);
+}
+
+int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa)
+{
+ return aa_audit_message(profile, sa, AUDIT_APPARMOR_DENIED);
+}
+
+/**
+ * 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)
+{
+ int type = AUDIT_APPARMOR_DENIED;
+ struct audit_context *audit_cxt;
+
+ if (likely(!sa->error_code)) {
+ if (likely(!PROFILE_AUDIT(profile)))
+ /* nothing to log */
+ return 0;
+ else
+ type = AUDIT_APPARMOR_AUDIT;
+ } else if (PROFILE_COMPLAIN(profile)) {
+ type = AUDIT_APPARMOR_ALLOWED;
+ }
+
+ audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
+ return aa_audit_base(profile, sa, audit_cxt, type);
+}
+
+/**
+ * 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;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "setattr";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.iattr = iattr;
+ sa.request_mask = MAY_WRITE;
+ sa.error_code = -EACCES;
+
+ 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, 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, const char *operation,
+ struct dentry *dentry, struct vfsmount *mnt, int mask,
+ int check)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ struct aa_audit sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = operation;
+ sa.gfp_mask = GFP_KERNEL;
+ sa.request_mask = mask;
+ sa.error_code = -EACCES;
+
+ if (inode && S_ISDIR(inode->i_mode))
+ check |= AA_CHECK_DIR;
+
+ error = aa_perm_dentry(profile, dentry, mnt, &sa, 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, const char *operation,
+ struct dentry *dentry, struct vfsmount *mnt, int mask, int check)
+{
+ struct aa_audit sa;
+ int error = 0;
+
+ if (mask == 0)
+ goto out;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = operation;
+ sa.gfp_mask = GFP_KERNEL;
+ sa.request_mask = mask;
+ sa.error_code = -EACCES;
+
+ error = aa_perm_dentry(profile, dentry, mnt, &sa, 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, const char *operation,
+ struct dentry *dentry, struct vfsmount *mnt, int mask)
+{
+ struct aa_audit sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = operation;
+ sa.gfp_mask = GFP_KERNEL;
+ sa.request_mask = mask;
+ sa.error_code = -EACCES;
+
+ return aa_perm_dentry(profile, dentry, mnt, &sa, AA_CHECK_DIR);
+}
+
+int aa_perm_path(struct aa_profile *profile, const char *operation,
+ const char *name, int mask, uid_t owner_uid, uid_t group_uid)
+{
+ struct aa_audit sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = operation;
+ sa.gfp_mask = GFP_KERNEL;
+ sa.request_mask = mask;
+ sa.name = name;
+ if (current->fsuid == owner_uid)
+ sa.request_mask = mask << AA_USER_SHIFT;
+ else if (in_group_p(group_uid))
+ sa.request_mask = mask << AA_GROUP_SHIFT;
+ else
+ sa.request_mask = mask << AA_OTHER_SHIFT;
+
+ sa.denied_mask = aa_file_denied(profile, name, sa.request_mask) ;
+ sa.error_code = sa.denied_mask ? -EACCES : 0;
+
+ 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 occurence 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);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "capable";
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.name = capability_names[cap];
+ sa.error_code = error;
+
+ 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 error;
+ struct aa_audit sa;
+ char *buffer = NULL, *buffer2 = NULL;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "inode_link";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.name = aa_get_name(link, link_mnt, &buffer, 0);
+ sa.name2 = aa_get_name(target, target_mnt, &buffer2, 0);
+
+ if (IS_ERR(sa.name)) {
+ sa.error_code = PTR_ERR(sa.name);
+ sa.name = NULL;
+ }
+ if (IS_ERR(sa.name2)) {
+ sa.error_code = PTR_ERR(sa.name2);
+ sa.name2 = NULL;
+ }
+
+ if (sa.name && sa.name2) {
+ sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
+ aa_inode_mode(target->d_inode),
+ &sa.request_mask);
+ sa.error_code = sa.denied_mask ? -EACCES : 0;
+ }
+
+ error = aa_audit(profile, &sa);
+
+ aa_put_name_buffer(buffer);
+ aa_put_name_buffer(buffer2);
+
+ 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->cookie, cxt->previous_profile);
+ unlock_profile(profile);
+
+ if (APPARMOR_COMPLAIN(child_cxt) &&
+ profile == profile->ns->null_complain_profile) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "clone";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.task = child->pid;
+ aa_audit_hint(profile, &sa);
+ }
+ 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, int mandatory,
+ int complain, struct aa_audit *sa)
+{
+ struct aa_profile *new_profile;
+
+ /* Locate new profile */
+ if (profile)
+ new_profile = aa_find_profile(profile->ns, name);
+ else
+ new_profile = aa_find_profile(default_namespace, name);
+
+ if (new_profile) {
+ AA_DEBUG("%s: setting profile %s\n",
+ __FUNCTION__, new_profile->name);
+ } else if (mandatory && profile) {
+ sa->info = "mandatory profile missing";
+ sa->denied_mask = sa->request_mask; /* shifted MAY_EXEC */
+ if (complain) {
+ aa_audit_hint(profile, sa);
+ new_profile =
+ aa_dup_profile(profile->ns->null_complain_profile);
+ } else {
+ aa_audit_reject(profile, sa);
+ return ERR_PTR(-EACCES); /* was -EPERM */
+ }
+ } else {
+ /* Only way we can get into this code is if task
+ * is unconfined, or pix.
+ */
+ 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, complain = 0, shift;
+ struct aa_audit sa;
+
+ AA_DEBUG("%s\n", __FUNCTION__);
+
+ filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0);
+ if (IS_ERR(filename)) {
+ AA_ERROR("%s: Failed to get filename", __FUNCTION__);
+ return -ENOENT;
+ }
+
+ shift = aa_inode_mode(filp->f_dentry->d_inode);
+ exec_mode = AA_EXEC_UNSAFE << shift;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "exec";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.name = filename;
+ sa.request_mask = MAY_EXEC << shift;
+
+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 & sa.request_mask) {
+ switch ((exec_mode >> shift) & AA_EXEC_MODIFIERS) {
+ case AA_EXEC_INHERIT:
+ AA_DEBUG("%s: INHERIT %s\n",
+ __FUNCTION__,
+ filename);
+ /* nothing to be done here */
+ goto cleanup;
+
+ case AA_EXEC_UNCONFINED:
+ AA_DEBUG("%s: UNCONFINED %s\n",
+ __FUNCTION__,
+ filename);
+
+ /* detach current profile */
+ new_profile = NULL;
+ break;
+
+ case AA_EXEC_PROFILE:
+ AA_DEBUG("%s: PROFILE %s\n",
+ __FUNCTION__,
+ filename);
+ new_profile = aa_register_find(profile,
+ filename,
+ 1, complain,
+ &sa);
+ break;
+ case AA_EXEC_PIX:
+ AA_DEBUG("%s: PROFILE %s\n",
+ __FUNCTION__,
+ filename);
+ new_profile = aa_register_find(profile,
+ filename,
+ 0, complain,
+ &sa);
+ if (!new_profile)
+ /* inherit - nothing to be done here */
+ goto cleanup;
+ 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(profile->ns->null_complain_profile);
+ exec_mode |= AA_EXEC_UNSAFE << shift;
+ } else {
+ sa.denied_mask = sa.request_mask;
+ aa_audit_reject(profile, &sa);
+ new_profile = ERR_PTR(-EPERM);
+ }
+ } else {
+ /* Unconfined task, load profile if it exists */
+ new_profile = aa_register_find(NULL, filename, 0, 0, &sa);
+ if (new_profile == NULL)
+ goto cleanup;
+ }
+
+ if (IS_ERR(new_profile))
+ goto cleanup;
+
+ old_profile = __aa_replace_profile(current, new_profile);
+ 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) {
+ sa.denied_mask = sa.request_mask;
+ sa.info = "unable to set profile due to ptrace";
+ sa.task = current->parent->pid;
+ aa_audit_reject(profile, &sa);
+ }
+ 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 << shift))) {
+ unsigned long bprm_flags;
+
+ bprm_flags = AA_SECURE_EXEC_NEEDED;
+ bprm->security = (void*)
+ ((unsigned long)bprm->security | bprm_flags);
+ }
+
+ if (complain && new_profile &&
+ new_profile == new_profile->ns->null_complain_profile) {
+ sa.request_mask = 0;
+ sa.name = NULL;
+ sa.info = "set profile";
+ aa_audit_hint(new_profile, &sa);
+ }
+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.
+ *
+ * Use lock subtyping to avoid lockdep reporting a false irq
+ * possible inversion between the task_lock and profile_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_nested(profile, aa_lock_task_release);
+ 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, NULL);
+ unlock_profile(profile);
+ aa_put_profile(profile);
+ }
+}
+
+static int do_change_profile(struct aa_profile *expected,
+ struct aa_namespace *ns, const char *name,
+ u64 cookie, int restore, struct aa_audit *sa)
+{
+ struct aa_profile *new_profile = NULL, *old_profile = NULL,
+ *previous_profile = NULL;
+ struct aa_task_context *new_cxt, *cxt;
+ int error = 0;
+
+ sa->name = name;
+
+ new_cxt = aa_alloc_task_context(GFP_KERNEL);
+ if (!new_cxt)
+ return -ENOMEM;
+
+ new_profile = aa_find_profile(ns, name);
+ if (!new_profile && !restore) {
+ if (!PROFILE_COMPLAIN(expected))
+ return -ENOENT;
+ new_profile = aa_dup_profile(ns->null_complain_profile);
+ }
+
+ cxt = lock_task_and_profiles(current, new_profile);
+ if (!cxt) {
+ error = -EPERM;
+ goto out;
+ }
+ old_profile = cxt->profile;
+
+ if (cxt->profile != expected || (new_profile && new_profile->isstale)) {
+ error = -ESTALE;
+ goto out;
+ }
+
+ if (cxt->previous_profile) {
+ if (cxt->cookie != cookie) {
+ error = -EACCES;
+ sa->info = "killing process";
+ aa_audit_reject(cxt->profile, sa);
+ /* terminate process */
+ (void)send_sig_info(SIGKILL, NULL, current);
+ goto out;
+ }
+
+ if (!restore)
+ previous_profile = cxt->previous_profile;
+ } else
+ previous_profile = cxt->profile;
+
+ if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, new_profile)) {
+ error = -EACCES;
+ goto out;
+ }
+
+ if (new_profile == ns->null_complain_profile)
+ aa_audit_hint(cxt->profile, sa);
+
+ if (APPARMOR_AUDIT(cxt))
+ aa_audit_message(cxt->profile, sa, AUDIT_APPARMOR_AUDIT);
+
+ if (!restore && cookie)
+ aa_change_task_context(current, new_cxt, new_profile, cookie,
+ previous_profile);
+ else
+ /* either return to previous_profile, or a permanent change */
+ aa_change_task_context(current, new_cxt, new_profile, 0, NULL);
+
+out:
+ if (aa_task_context(current) != new_cxt)
+ aa_free_task_context(new_cxt);
+ task_unlock(current);
+ unlock_both_profiles(old_profile, new_profile);
+ aa_put_profile(new_profile);
+ return error;
+}
+
+/**
+ * aa_change_profile - perform a one-way profile transition
+ * @ns_name: name of the profile namespace to change to
+ * @name: name of profile to change to
+ * Change to new profile @name. Unlike with hats, there is no way
+ * to change back.
+ *
+ * Returns %0 on success, error otherwise.
+ */
+int aa_change_profile(const char *ns_name, const char *name)
+{
+ struct aa_task_context *cxt;
+ struct aa_profile *profile;
+ struct aa_namespace *ns = NULL;
+ struct aa_audit sa;
+ unsigned int state;
+ int error = -EINVAL;
+
+ if (!name)
+ return -EINVAL;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.operation = "change_profile";
+
+repeat:
+ task_lock(current);
+ cxt = aa_task_context(current);
+ if (!cxt) {
+ task_unlock(current);
+ return -EPERM;
+ }
+ profile = aa_dup_profile(cxt->profile);
+ task_unlock(current);
+
+ if (ns_name)
+ ns = aa_find_namespace(ns_name);
+ else
+ ns = aa_get_namespace(profile->ns);
+ if (!ns) {
+ aa_put_profile(profile);
+ return -ENOENT;
+ }
+
+ if (PROFILE_COMPLAIN(profile) ||
+ (ns == profile->ns &&
+ (aa_match(profile->file_rules, name) & AA_CHANGE_PROFILE)))
+ error = do_change_profile(profile, ns, name, 0, 0, &sa);
+ else {
+ /* check for a rule with a namespace prepended */
+ aa_match_state(profile->file_rules, DFA_START, ns->name,
+ &state);
+ state = aa_dfa_null_transition(profile->file_rules, state);
+ if ((aa_match_state(profile->file_rules, state, name, NULL) &
+ AA_CHANGE_PROFILE))
+ error = do_change_profile(profile, ns, name, 0, 0,
+ &sa);
+ else
+ /* no permission to transition to profile @name */
+ error = -EACCES;
+ }
+
+ aa_put_namespace(ns);
+ aa_put_profile(profile);
+ if (error == -ESTALE)
+ goto repeat;
+
+ return error;
+}
+
+/**
+ * aa_change_hat - change hat to/from subprofile
+ * @hat_name: hat to change to
+ * @cookie: magic value 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 @cookie 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 cookie)
+{
+ struct aa_task_context *cxt;
+ struct aa_profile *profile, *previous_profile;
+ struct aa_audit sa;
+ int error = 0;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.operation = "change_hat";
+
+repeat:
+ task_lock(current);
+ cxt = aa_task_context(current);
+ if (!cxt) {
+ task_unlock(current);
+ return -EPERM;
+ }
+ profile = aa_dup_profile(cxt->profile);
+ previous_profile = aa_dup_profile(cxt->previous_profile);
+ task_unlock(current);
+
+ if (hat_name) {
+ char *name, *profile_name;
+ if (previous_profile)
+ profile_name = previous_profile->name;
+ else
+ profile_name = profile->name;
+
+ name = kmalloc(strlen(hat_name) + 3 + strlen(profile_name),
+ GFP_KERNEL);
+ if (!name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ sprintf(name, "%s//%s", profile_name, hat_name);
+ error = do_change_profile(profile, profile->ns, name, cookie,
+ 0, &sa);
+ kfree(name);
+ } else if (previous_profile)
+ error = do_change_profile(profile, profile->ns,
+ previous_profile->name, cookie, 1,
+ &sa);
+ /* else ignore restores when there is no saved profile */
+
+out:
+ aa_put_profile(previous_profile);
+ aa_put_profile(profile);
+ if (error == -ESTALE)
+ goto repeat;
+
+ return error;
+}
+
+/**
+ * __aa_replace_profile - replace a task's profile
+ * @task: task to switch the profile of
+ * @profile: profile 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)
+{
+ 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, 0, NULL);
+
+ task_unlock(task);
+ unlock_both_profiles(profile, old_profile);
+ return old_profile;
+}
+
+/**
+ * lock_task_and_profiles - 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);
+ old_profile = NULL;
+ 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
+ * @cookie: magic value to switch to
+ * @previous_profile: profile the task can return to
+ */
+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)
+{
+ 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->cookie = cookie;
+ new_cxt->task = task;
+ new_cxt->profile = aa_dup_profile(profile);
+ new_cxt->previous_profile = aa_dup_profile(previous_profile);
+ list_move(&new_cxt->list, &profile->task_contexts);
+ }
+ rcu_assign_pointer(task->security, new_cxt);
+}
--
next prev parent reply other threads:[~2007-10-26 7:12 UTC|newest]
Thread overview: 71+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-10-26 6:40 [AppArmor 00/45] AppArmor security module overview jjohansen
2007-10-26 6:40 ` [AppArmor 01/45] Pass struct vfsmount to the inode_create LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 02/45] Pass struct path down to remove_suid and children jjohansen
2007-10-26 6:40 ` [AppArmor 03/45] Add a vfsmount parameter to notify_change() jjohansen
2007-10-26 6:40 ` [AppArmor 04/45] Pass struct vfsmount to the inode_setattr LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 05/45] Add struct vfsmount parameter to vfs_mkdir() jjohansen
2007-10-26 6:40 ` [AppArmor 06/45] Pass struct vfsmount to the inode_mkdir LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 07/45] Add a struct vfsmount parameter to vfs_mknod() jjohansen
2007-10-26 6:40 ` [AppArmor 08/45] Pass struct vfsmount to the inode_mknod LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 09/45] Add a struct vfsmount parameter to vfs_symlink() jjohansen
2007-10-26 6:40 ` [AppArmor 10/45] Pass struct vfsmount to the inode_symlink LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 11/45] Pass struct vfsmount to the inode_readlink " jjohansen
2007-10-26 6:40 ` [AppArmor 12/45] Add struct vfsmount parameters to vfs_link() jjohansen
2007-10-26 6:40 ` [AppArmor 13/45] Pass the struct vfsmounts to the inode_link LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 14/45] Add a struct vfsmount parameter to vfs_rmdir() jjohansen
2007-10-26 6:40 ` [AppArmor 15/45] Pass struct vfsmount to the inode_rmdir LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 16/45] Call lsm hook before unhashing dentry in vfs_rmdir() jjohansen
2007-10-26 6:40 ` [AppArmor 17/45] Add a struct vfsmount parameter to vfs_unlink() jjohansen
2007-10-26 6:40 ` [AppArmor 18/45] Pass struct vfsmount to the inode_unlink LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 19/45] Add struct vfsmount parameters to vfs_rename() jjohansen
2007-10-26 7:37 ` Al Viro
2007-10-26 18:23 ` John Johansen
2007-10-26 20:33 ` Al Viro
2007-10-26 6:40 ` [AppArmor 20/45] Pass struct vfsmount to the inode_rename LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 21/45] Add a struct vfsmount parameter to vfs_setxattr() jjohansen
2007-10-26 6:40 ` [AppArmor 22/45] Pass struct vfsmount to the inode_setxattr LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 23/45] Add a struct vfsmount parameter to vfs_getxattr() jjohansen
2007-10-26 6:40 ` [AppArmor 24/45] Pass struct vfsmount to the inode_getxattr LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 25/45] Add a struct vfsmount parameter to vfs_listxattr() jjohansen
2007-10-26 6:40 ` [AppArmor 26/45] Pass struct vfsmount to the inode_listxattr LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 27/45] Add a struct vfsmount parameter to vfs_removexattr() jjohansen
2007-10-26 6:40 ` [AppArmor 28/45] Pass struct vfsmount to the inode_removexattr LSM hook jjohansen
2007-10-26 6:40 ` [AppArmor 29/45] Fix __d_path() for lazy unmounts and make it unambiguous jjohansen
2007-10-26 6:40 ` [AppArmor 30/45] Make d_path() consistent across mount operations jjohansen
2007-10-26 6:40 ` [AppArmor 31/45] Add d_namespace_path() to compute namespace relative pathnames jjohansen
2007-10-26 6:40 ` [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames jjohansen
2007-10-26 11:30 ` Miklos Szeredi
2007-10-26 11:45 ` Miklos Szeredi
2007-10-26 18:49 ` John Johansen
2007-10-26 20:24 ` Andreas Gruenbacher
2007-10-26 20:58 ` Miklos Szeredi
2007-10-26 21:56 ` Andreas Gruenbacher
2007-10-26 6:40 ` [AppArmor 33/45] Pass struct file down the inode_*xattr security LSM hooks jjohansen
2007-10-26 6:40 ` [AppArmor 34/45] Factor out sysctl pathname code jjohansen
2007-10-26 9:24 ` James Morris
2007-10-26 6:40 ` [AppArmor 35/45] Allow permission functions to tell between parent and leaf checks jjohansen
2007-10-26 12:32 ` Stephen Smalley
2007-10-26 18:26 ` John Johansen
2007-10-26 6:41 ` [AppArmor 36/45] Export audit subsystem for use by modules jjohansen
2007-10-26 6:41 ` jjohansen [this message]
2007-10-26 6:41 ` [AppArmor 38/45] AppArmor: Module and LSM hooks jjohansen
2007-10-26 6:41 ` [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching jjohansen
2007-10-26 6:41 ` [AppArmor 40/45] AppArmor: all the rest jjohansen
2007-10-26 6:41 ` [AppArmor 41/45] add simple network toggles to apparmor jjohansen
2007-10-26 6:41 ` [AppArmor 42/45] Add AppArmor LSM to security/Makefile jjohansen
2007-10-26 6:41 ` [AppArmor 43/45] Switch to vfs_permission() in do_path_lookup() jjohansen
2007-10-26 6:41 ` [AppArmor 44/45] Switch to vfs_permission() in sys_fchdir() jjohansen
2007-10-26 6:41 ` [AppArmor 45/45] Fix file_permission() jjohansen
2007-10-26 7:04 ` [AppArmor 00/45] AppArmor security module overview John Johansen
2007-10-26 14:37 ` Arjan van de Ven
2007-10-26 18:34 ` John Johansen
2007-10-26 20:15 ` Arjan van de Ven
2007-10-26 20:44 ` Andreas Gruenbacher
2007-10-26 21:13 ` Arjan van de Ven
2007-10-26 21:24 ` Andreas Gruenbacher
2007-10-26 22:16 ` Crispin Cowan
2007-10-26 22:23 ` Arjan van de Ven
2007-10-27 20:47 ` Christoph Hellwig
2007-10-28 14:25 ` Andreas Gruenbacher
-- strict thread matches above, loose matches on Subject: below --
2007-05-14 11:06 jjohansen
2007-05-14 11:06 ` [AppArmor 37/45] AppArmor: Main Part jjohansen
2007-05-15 9:12 ` Pavel Machek
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=20071026064052.099542126@suse.de \
--to=jjohansen@suse.de \
--cc=agruen@suse.de \
--cc=akpm@linux-foundation.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.