From: jjohansen@suse.de
To: linux-kernel@vger.kernel.org
Cc: linux-security-module@vger.kernel.org,
linux-fsdevel@vger.kernel.org, chrisw@sous-sol.org,
John Johansen <jjohansen@suse.de>,
Andreas Gruenbacher <agruen@suse.de>
Subject: [AppArmor 40/41] AppArmor: all the rest
Date: Thu, 12 Apr 2007 02:08:49 -0700 [thread overview]
Message-ID: <20070412090849.824835000@suse.de> (raw)
In-Reply-To: 20070412090809.917795000@suse.de
[-- Attachment #1: apparmor-misc.diff --]
[-- Type: text/plain, Size: 31490 bytes --]
All the things that didn't nicely fit in a category on their own: kbuild
code, declararions and inline functions, /sys/kernel/security/apparmor
filesystem for controlling apparmor from user space, profile list
functions, locking documentation, /proc/$pid/task/$tid/attr/current
access.
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
security/apparmor/Kconfig | 9 +
security/apparmor/Makefile | 13 +
security/apparmor/apparmor.h | 282 +++++++++++++++++++++++++++++++++++++++++
security/apparmor/apparmorfs.c | 248 ++++++++++++++++++++++++++++++++++++
security/apparmor/inline.h | 219 +++++++++++++++++++++++++++++++
security/apparmor/list.c | 94 +++++++++++++
security/apparmor/locking.txt | 59 ++++++++
security/apparmor/procattr.c | 143 ++++++++++++++++++++
8 files changed, 1067 insertions(+)
--- /dev/null
+++ b/security/apparmor/Kconfig
@@ -0,0 +1,9 @@
+config SECURITY_APPARMOR
+ tristate "AppArmor support"
+ depends on SECURITY!=n
+ help
+ This enables the AppArmor security module.
+ Required userspace tools (if they are not included in your
+ distribution) and further information may be found at
+ <http://forge.novell.com/modules/xfmod/project/?apparmor>
+ If you are unsure how to answer this question, answer N.
--- /dev/null
+++ b/security/apparmor/Makefile
@@ -0,0 +1,13 @@
+# Makefile for AppArmor Linux Security Module
+#
+obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
+
+apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \
+ module_interface.o match.o
+
+quiet_cmd_make-caps = GEN $@
+cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
+
+$(obj)/main.o : $(obj)/capability_names.h
+$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
+ $(call cmd,make-caps)
--- /dev/null
+++ b/security/apparmor/apparmor.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * AppArmor internal prototypes
+ */
+
+#ifndef __APPARMOR_H
+#define __APPARMOR_H
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/rcupdate.h>
+
+/*
+ * We use MAY_READ, MAY_WRITE, MAY_EXEC, and the following flags for
+ * profile permissions (we don't use MAY_APPEND):
+ */
+#define AA_MAY_LINK 0x0010
+#define AA_EXEC_INHERIT 0x0020
+#define AA_EXEC_UNCONFINED 0x0040
+#define AA_EXEC_PROFILE 0x0080
+#define AA_EXEC_MMAP 0x0100
+#define AA_EXEC_UNSAFE 0x0200
+
+#define AA_EXEC_MODIFIERS (AA_EXEC_INHERIT | \
+ AA_EXEC_UNCONFINED | \
+ AA_EXEC_PROFILE)
+
+#define AA_SECURE_EXEC_NEEDED 1
+
+/* Control parameters (0 or 1), settable thru module/boot flags or
+ * via /sys/kernel/security/apparmor/control */
+extern int apparmor_complain;
+extern int apparmor_debug;
+extern int apparmor_audit;
+extern int apparmor_logsyscall;
+extern unsigned int apparmor_path_max;
+
+#define PROFILE_COMPLAIN(_profile) \
+ (apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain))
+
+#define APPARMOR_COMPLAIN(_cxt) \
+ (apparmor_complain == 1 || \
+ ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.complain))
+
+#define PROFILE_AUDIT(_profile) \
+ (apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit))
+
+#define APPARMOR_AUDIT(_cxt) \
+ (apparmor_audit == 1 || \
+ ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.audit))
+
+/*
+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
+ * which is not related to profile accesses.
+ */
+
+#define AA_DEBUG(fmt, args...) \
+ do { \
+ if (apparmor_debug) \
+ printk(KERN_DEBUG "AppArmor: " fmt, ##args); \
+ } while (0)
+#define AA_INFO(gfp, fmt, args...) \
+ do { \
+ printk(KERN_INFO "AppArmor: " fmt "\n", ##args); \
+ aa_audit_message(NULL, (gfp), 0, fmt, ##args); \
+ } while (0)
+#define AA_WARN(gfp, fmt, args...) \
+ aa_audit_message(NULL, (gfp), 0, fmt, ##args);
+
+#define AA_REJECT_MSG(p, gfp, fmt, args...) \
+ aa_audit_message((p), (gfp), 0, \
+ "REJECTING " fmt \
+ " (%s(%d) profile %s active %s)", ##args, \
+ current->comm, current->pid, \
+ (p)->parent->name, (p)->name)
+
+#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
+
+/* struct aa_profile - basic confinement data
+ * @parent: non refcounted pointer to parent profile
+ * @name: the profiles name
+ * @file_rules: dfa containing the profiles file rules
+ * @list: list this profile is on
+ * @sub: profiles list of subprofiles (HATS)
+ * @flags: flags controlling profile behavior
+ * @null_profile: if needed per profile learning and null confinement profile
+ * @isstale: flag indicating if profile is stale
+ * @capabilities: capabilities granted by the process
+ * @count: reference count of the profile
+ *
+ * The AppArmor profile contains the basic confinement data. Each profile
+ * has a name and potentially a list of profile entries. All profiles are
+ * on the profile_list.
+ *
+ * The task_contexts list and the isstale flag are protected by the
+ * profile lock.
+ *
+ * If a task context is moved between two profiles, we first need to grab
+ * both profile locks. lock_both_profiles() does that in a deadlock-safe
+ * way.
+ */
+struct aa_profile {
+ struct aa_profile *parent;
+ char *name;
+ struct aa_dfa *file_rules;
+ struct list_head list;
+ struct list_head sub;
+ struct {
+ int complain;
+ int audit;
+ } flags;
+ struct aa_profile *null_profile;
+ int isstale;
+
+ kernel_cap_t capabilities;
+ struct kref count;
+ struct list_head task_contexts;
+ spinlock_t lock;
+ unsigned long int_flags;
+};
+
+extern struct list_head profile_list;
+extern rwlock_t profile_list_lock;
+extern struct mutex aa_interface_lock;
+
+/**
+ * struct aa_task_context - primary label for confined tasks
+ * @profile: the current profile
+ * @hat_magic: the magic token controling the ability to leave a hat
+ * @list: list this aa_task_context is on
+ * @task: task that the aa_task_context confines
+ * @rcu: rcu head used when freeing the aa_task_context
+ * @caps_logged: caps that have previously generated log entries
+ *
+ * Contains the task's current profile (which could change due to
+ * change_hat). Plus the hat_magic needed during change_hat.
+ */
+struct aa_task_context {
+ struct aa_profile *profile; /* The current profile */
+ u64 hat_magic; /* used with change_hat */
+ struct list_head list;
+ struct task_struct *task;
+ struct rcu_head rcu;
+ kernel_cap_t caps_logged;
+};
+
+extern struct aa_profile *null_complain_profile;
+
+/* aa_audit - AppArmor auditing structure
+ * Structure is populated by access control code and passed to aa_audit which
+ * provides for a single point of logging.
+ */
+
+struct aa_audit {
+ unsigned short type, flags;
+ unsigned int result;
+ gfp_t gfp_mask;
+ int error_code;
+
+ const char *operation;
+ const char *name;
+ union {
+ int capability;
+ int mask;
+ };
+ union {
+ const void *pval;
+ va_list vaval;
+ };
+};
+
+/* audit types */
+#define AA_AUDITTYPE_FILE 1
+#define AA_AUDITTYPE_DIR 2
+#define AA_AUDITTYPE_ATTR 3
+#define AA_AUDITTYPE_XATTR 4
+#define AA_AUDITTYPE_LINK 5
+#define AA_AUDITTYPE_CAP 6
+#define AA_AUDITTYPE_MSG 7
+#define AA_AUDITTYPE_SYSCALL 8
+#define AA_AUDITTYPE__END 9
+
+/* audit flags */
+#define AA_AUDITFLAG_AUDITSS_SYSCALL 1 /* log syscall context */
+#define AA_AUDITFLAG_LOGERR 2 /* log operations that failed due to
+ non permission errors */
+
+#define HINT_UNKNOWN_HAT "unknown_hat"
+#define HINT_FORK "fork"
+#define HINT_MANDPROF "missing_mandatory_profile"
+#define HINT_CHGPROF "changing_profile"
+#define HINT_PTRACE "ptrace"
+
+#define LOG_HINT(p, gfp, hint, fmt, args...) \
+ aa_audit_message((p), (gfp), 0, \
+ "LOGPROF-HINT " hint " " fmt \
+ " (%s(%d) profile %s active %s)", ##args, \
+ current->comm, current->pid, \
+ (p)->parent->name, (p)->name)
+
+/* Flags for the permission check functions */
+#define AA_CHECK_LEAF 1 /* this is the leaf lookup component */
+#define AA_CHECK_FD 2 /* coming from a file descriptor */
+#define AA_CHECK_DIR 4 /* file type is directory */
+
+/* main.c */
+extern int alloc_null_complain_profile(void);
+extern void free_null_complain_profile(void);
+extern int attach_nullprofile(struct aa_profile *profile);
+extern int aa_audit_message(struct aa_profile *profile, gfp_t gfp, int,
+ const char *, ...);
+extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
+ const char *);
+extern int aa_audit(struct aa_profile *profile, const struct aa_audit *);
+
+extern int aa_attr(struct aa_profile *profile, struct dentry *dentry,
+ struct vfsmount *mnt, struct iattr *iattr);
+extern int aa_perm_xattr(struct aa_profile *profile, struct dentry *dentry,
+ struct vfsmount *mnt, const char *operation,
+ const char *xattr_xattr, int mask, int check);
+extern int aa_capability(struct aa_task_context *cxt, int cap);
+extern int aa_perm(struct aa_profile *profile, struct dentry *dentry,
+ struct vfsmount *mnt, int mask, int check);
+extern int aa_perm_dir(struct aa_profile *profile, struct dentry *dentry,
+ struct vfsmount *mnt, const char *operation, int mask);
+extern int aa_link(struct aa_profile *profile,
+ struct dentry *link, struct vfsmount *link_mnt,
+ struct dentry *target, struct vfsmount *target_mnt);
+extern int aa_clone(struct task_struct *task);
+extern int aa_register(struct linux_binprm *bprm);
+extern void aa_release(struct task_struct *task);
+extern int aa_change_hat(const char *id, u64 hat_magic);
+extern struct aa_profile *__aa_find_profile(const char *name,
+ struct list_head *list);
+extern struct aa_profile *__aa_replace_profile(struct task_struct *task,
+ struct aa_profile *profile,
+ u32 hat_magic);
+extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task,
+ struct aa_profile *profile);
+extern void aa_change_task_context(struct task_struct *task,
+ struct aa_task_context *new_cxt,
+ struct aa_profile *profile, u64 hat_magic);
+extern int aa_may_ptrace(struct aa_task_context *cxt,
+ struct aa_profile *tracee);
+
+/* list.c */
+extern void aa_profilelist_release(void);
+
+/* module_interface.c */
+extern ssize_t aa_add_profile(void *, size_t);
+extern ssize_t aa_replace_profile(void *, size_t);
+extern ssize_t aa_remove_profile(const char *, size_t);
+extern struct aa_profile *alloc_aa_profile(void);
+extern void free_aa_profile(struct aa_profile *profile);
+extern void free_aa_profile_kref(struct kref *kref);
+extern void aa_unconfine_tasks(struct aa_profile *profile);
+
+/* procattr.c */
+extern int aa_getprocattr(struct aa_profile *profile, char **string,
+ unsigned *len);
+extern int aa_setprocattr_changehat(char *args);
+extern int aa_setprocattr_setprofile(struct task_struct *task, char *args);
+
+/* apparmorfs.c */
+extern int create_apparmorfs(void);
+extern void destroy_apparmorfs(void);
+
+/* match.c */
+extern struct aa_dfa *aa_match_alloc(void);
+extern void aa_match_free(struct aa_dfa *dfa);
+extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
+extern int verify_dfa(struct aa_dfa *dfa);
+extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str);
+
+#endif /* __APPARMOR_H */
--- /dev/null
+++ b/security/apparmor/apparmorfs.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * AppArmor filesystem (part of securityfs)
+ */
+
+#include <linux/security.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+static char *aa_simple_write_to_buffer(const char __user *userbuf,
+ size_t alloc_size, size_t copy_size,
+ loff_t *pos, const char *msg)
+{
+ struct aa_profile *profile;
+ char *data;
+
+ if (*pos != 0) {
+ /* only writes from pos 0, that is complete writes */
+ data = ERR_PTR(-ESPIPE);
+ goto out;
+ }
+
+ /*
+ * Don't allow confined processes to load/replace/remove profiles.
+ * No sane person would add rules allowing this to a profile
+ * but we enforce the restriction anyways.
+ */
+ profile = aa_get_profile(current);
+ if (profile) {
+ AA_WARN(GFP_KERNEL, "REJECTING access to profile %s (%s(%d) "
+ "profile %s active %s)",
+ msg, current->comm, current->pid,
+ profile->parent->name, profile->name);
+ aa_put_profile(profile);
+
+ data = ERR_PTR(-EPERM);
+ goto out;
+ }
+
+ data = vmalloc(alloc_size);
+ if (data == NULL) {
+ data = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ if (copy_from_user(data, userbuf, copy_size)) {
+ vfree(data);
+ data = ERR_PTR(-EFAULT);
+ goto out;
+ }
+
+out:
+ return data;
+}
+
+/* apparmor/profiles */
+extern struct seq_operations apparmorfs_profiles_op;
+
+static int aa_profiles_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &apparmorfs_profiles_op);
+}
+
+
+static int aa_profiles_release(struct inode *inode, struct file *file)
+{
+ return seq_release(inode, file);
+}
+
+static struct file_operations apparmorfs_profiles_fops = {
+ .open = aa_profiles_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = aa_profiles_release,
+};
+
+/* apparmor/matching */
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char *matching = "pattern=aadfa";
+
+ return simple_read_from_buffer(buf, size, ppos, matching,
+ strlen(matching));
+}
+
+static struct file_operations apparmorfs_matching_fops = {
+ .read = aa_matching_read,
+};
+
+/* apparmor/.load */
+static ssize_t aa_profile_load(struct file *f, const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ char *data;
+ ssize_t error;
+
+ data = aa_simple_write_to_buffer(buf, size, size, pos, "load");
+
+ error = PTR_ERR(data);
+ if (!IS_ERR(data)) {
+ error = aa_add_profile(data, size);
+ vfree(data);
+ }
+
+ return error;
+}
+
+
+static struct file_operations apparmorfs_profile_load = {
+ .write = aa_profile_load
+};
+
+/* apparmor/.replace */
+static ssize_t aa_profile_replace(struct file *f, const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ char *data;
+ ssize_t error;
+
+ data = aa_simple_write_to_buffer(buf, size, size, pos, "replacement");
+
+ error = PTR_ERR(data);
+ if (!IS_ERR(data)) {
+ error = aa_replace_profile(data, size);
+ vfree(data);
+ }
+
+ return error;
+}
+
+
+static struct file_operations apparmorfs_profile_replace = {
+ .write = aa_profile_replace
+};
+
+/* apparmor/.remove */
+static ssize_t aa_profile_remove(struct file *f, const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ char *data;
+ ssize_t error;
+
+ /*
+ * aa_remove_profile needs a null terminated string so 1 extra
+ * byte is allocated and the copied data is null terminated.
+ */
+ data = aa_simple_write_to_buffer(buf, size + 1, size, pos, "removal");
+
+ error = PTR_ERR(data);
+ if (!IS_ERR(data)) {
+ data[size] = 0;
+ error = aa_remove_profile(data, size);
+ vfree(data);
+ }
+
+ return error;
+}
+
+static struct file_operations apparmorfs_profile_remove = {
+ .write = aa_profile_remove
+};
+
+static struct dentry *apparmor_dentry;
+
+static void aafs_remove(const char *name)
+{
+ struct dentry *dentry;
+
+ dentry = lookup_one_len(name, apparmor_dentry, strlen(name));
+ if (dentry && !IS_ERR(dentry))
+ securityfs_remove(dentry);
+}
+
+static int aafs_create(const char *name, int mask, struct file_operations *fops)
+{
+ struct dentry *dentry;
+
+ dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry,
+ NULL, fops);
+
+ return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
+}
+
+void destroy_apparmorfs(void)
+{
+ if (apparmor_dentry) {
+ aafs_remove(".remove");
+ aafs_remove(".replace");
+ aafs_remove(".load");
+ aafs_remove("matching");
+ aafs_remove("profiles");
+ securityfs_remove(apparmor_dentry);
+ apparmor_dentry = NULL;
+ }
+}
+
+int create_apparmorfs(void)
+{
+ int error;
+
+ if (apparmor_dentry) {
+ AA_ERROR("%s: AppArmor securityfs already exists\n",
+ __FUNCTION__);
+ return -EEXIST;
+ }
+
+ apparmor_dentry = securityfs_create_dir("apparmor", NULL);
+ if (IS_ERR(apparmor_dentry)) {
+ error = PTR_ERR(apparmor_dentry);
+ apparmor_dentry = NULL;
+ goto error;
+ }
+ error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops);
+ if (error)
+ goto error;
+ error = aafs_create("matching", 0444, &apparmorfs_matching_fops);
+ if (error)
+ goto error;
+ error = aafs_create(".load", 0640, &apparmorfs_profile_load);
+ if (error)
+ goto error;
+ error = aafs_create(".replace", 0640, &apparmorfs_profile_replace);
+ if (error)
+ goto error;
+ error = aafs_create(".remove", 0640, &apparmorfs_profile_remove);
+ if (error)
+ goto error;
+
+ return 0;
+
+error:
+ destroy_apparmorfs();
+ AA_ERROR("Error creating AppArmor securityfs\n");
+ return error;
+}
+
--- /dev/null
+++ b/security/apparmor/inline.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#ifndef __INLINE_H
+#define __INLINE_H
+
+#include <linux/sched.h>
+
+static inline int mediated_filesystem(struct inode *inode)
+{
+ return !(inode->i_sb->s_flags & MS_NOUSER);
+}
+
+static inline struct aa_task_context *aa_task_context(struct task_struct *task)
+{
+ return rcu_dereference((struct aa_task_context *)task->security);
+}
+
+/**
+ * aa_dup_profile - increment refcount on profile @p
+ * @p: profile
+ */
+static inline struct aa_profile *aa_dup_profile(struct aa_profile *p)
+{
+ if (p)
+ kref_get(&(p->parent->count));
+
+ return p;
+}
+
+/**
+ * aa_put_profile - decrement refcount on profile @p
+ * @p: profile
+ */
+static inline void aa_put_profile(struct aa_profile *p)
+{
+ if (p)
+ kref_put(&p->parent->count, free_aa_profile_kref);
+}
+
+static inline struct aa_profile *aa_get_profile(struct task_struct *task)
+{
+ struct aa_task_context *cxt;
+ struct aa_profile *profile = NULL;
+
+ rcu_read_lock();
+ cxt = aa_task_context(task);
+ if (cxt) {
+ profile = cxt->profile;
+ aa_dup_profile(profile);
+ }
+ rcu_read_unlock();
+
+ return profile;
+}
+
+static inline struct aa_profile *aa_find_profile(const char *name)
+{
+ struct aa_profile *profile = NULL;
+
+ read_lock(&profile_list_lock);
+ profile = aa_dup_profile(__aa_find_profile(name, &profile_list));
+ read_unlock(&profile_list_lock);
+
+ return profile;
+}
+
+static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags)
+{
+ struct aa_task_context *cxt;
+
+ cxt = kzalloc(sizeof(*cxt), flags);
+ if (cxt) {
+ INIT_LIST_HEAD(&cxt->list);
+ INIT_RCU_HEAD(&cxt->rcu);
+ }
+
+ return cxt;
+}
+
+static inline void aa_free_task_context(struct aa_task_context *cxt)
+{
+ if (cxt) {
+ aa_put_profile(cxt->profile);
+ kfree(cxt);
+ }
+}
+
+/**
+ * lock_profile - lock a profile
+ * @profile: the profile to lock
+ *
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void lock_profile(struct aa_profile *profile)
+{
+ /* We always lock top-level profiles instead of children. */
+ if (profile)
+ profile = profile->parent;
+
+ /*
+ * Lock the profile.
+ *
+ * Need to disable interrupts here because this lock is used in
+ * the task_free_security hook, which may run in RCU context.
+ */
+ if (profile)
+ spin_lock_irqsave(&profile->lock, profile->int_flags);
+}
+
+/**
+ * unlock_profile - unlock a profile
+ * @profile: the profile to unlock
+ */
+static inline void unlock_profile(struct aa_profile *profile)
+{
+ /* We always lock top-level profiles instead of children. */
+ if (profile)
+ profile = profile->parent;
+
+ /* Unlock the profile. */
+ if (profile)
+ spin_unlock_irqrestore(&profile->lock, profile->int_flags);
+}
+
+/**
+ * lock_both_profiles - lock two profiles in a deadlock-free way
+ * @profile1: profile to lock (may be NULL)
+ * @profile2: profile to lock (may be NULL)
+ *
+ * The order in which profiles are passed into lock_both_profiles() /
+ * unlock_both_profiles() does not matter.
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void lock_both_profiles(struct aa_profile *profile1,
+ struct aa_profile *profile2)
+{
+ /* We always lock top-level profiles instead of children. */
+ if (profile1)
+ profile1 = profile1->parent;
+ if (profile2)
+ profile2 = profile2->parent;
+
+ /*
+ * Lock the two profiles.
+ *
+ * We need to disable interrupts because the profile locks are
+ * used in the task_free_security hook, which may run in RCU
+ * context.
+ *
+ * Do not nest spin_lock_irqsave()/spin_unlock_irqresore():
+ * interrupts only need to be turned off once.
+ */
+ if (!profile1 || profile1 == profile2) {
+ if (profile2)
+ spin_lock_irqsave(&profile2->lock, profile2->int_flags);
+ } else if (profile1 > profile2) {
+ /* profile1 cannot be NULL here. */
+ spin_lock_irqsave(&profile1->lock, profile1->int_flags);
+ if (profile2)
+ spin_lock(&profile2->lock);
+
+ } else {
+ /* profile2 cannot be NULL here. */
+ spin_lock_irqsave(&profile2->lock, profile2->int_flags);
+ spin_lock(&profile1->lock);
+ }
+}
+
+/**
+ * unlock_both_profiles - unlock two profiles in a deadlock-free way
+ * @profile1: profile to unlock (may be NULL)
+ * @profile2: profile to unlock (may be NULL)
+ *
+ * The order in which profiles are passed into lock_both_profiles() /
+ * unlock_both_profiles() does not matter.
+ * While the profile is locked, local interrupts are disabled. This also
+ * gives us RCU reader safety.
+ */
+static inline void unlock_both_profiles(struct aa_profile *profile1,
+ struct aa_profile *profile2)
+{
+ /* We always lock top-level profiles instead of children. */
+ if (profile1)
+ profile1 = profile1->parent;
+ if (profile2)
+ profile2 = profile2->parent;
+
+ /* Unlock the two profiles. */
+ if (!profile1 || profile1 == profile2) {
+ if (profile2)
+ spin_unlock_irqrestore(&profile2->lock,
+ profile2->int_flags);
+ } else if (profile1 > profile2) {
+ /* profile1 cannot be NULL here. */
+ if (profile2)
+ spin_unlock(&profile2->lock);
+ spin_unlock_irqrestore(&profile1->lock, profile1->int_flags);
+ } else {
+ /* profile2 cannot be NULL here. */
+ spin_unlock(&profile1->lock);
+ spin_unlock_irqrestore(&profile2->lock, profile2->int_flags);
+ }
+}
+
+static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname)
+{
+ return dfa ? aa_dfa_match(dfa, pathname) : 0;
+}
+
+#endif /* __INLINE_H__ */
--- /dev/null
+++ b/security/apparmor/list.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * AppArmor Profile List Management
+ */
+
+#include <linux/seq_file.h>
+#include "apparmor.h"
+#include "inline.h"
+
+/* list of all profiles and lock */
+LIST_HEAD(profile_list);
+rwlock_t profile_list_lock = RW_LOCK_UNLOCKED;
+
+/**
+ * __aa_find_profile - look up a profile on the profile list
+ * @name: name of profile to find
+ * @head: list to search
+ *
+ * Returns a pointer to the profile on the list, or NULL if no profile
+ * called @name exists. The caller must hold the profile_list_lock.
+ */
+struct aa_profile *__aa_find_profile(const char *name, struct list_head *head)
+{
+ struct aa_profile *profile;
+
+ list_for_each_entry(profile, head, list) {
+ if (!strcmp(profile->name, name))
+ return profile;
+ }
+
+ return NULL;
+}
+
+/**
+ * aa_profilelist_release - Remove all profiles from profile_list
+ */
+void aa_profilelist_release(void)
+{
+ struct aa_profile *p, *tmp;
+
+ write_lock(&profile_list_lock);
+ list_for_each_entry_safe(p, tmp, &profile_list, list) {
+ list_del_init(&p->list);
+ aa_put_profile(p);
+ }
+ write_unlock(&profile_list_lock);
+}
+
+static void *p_start(struct seq_file *f, loff_t *pos)
+{
+ struct aa_profile *node;
+ loff_t l = *pos;
+
+ read_lock(&profile_list_lock);
+ list_for_each_entry(node, &profile_list, list)
+ if (!l--)
+ return node;
+ return NULL;
+}
+
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
+{
+ struct list_head *lh = ((struct aa_profile *)p)->list.next;
+ (*pos)++;
+ return lh == &profile_list ?
+ NULL : list_entry(lh, struct aa_profile, list);
+}
+
+static void p_stop(struct seq_file *f, void *v)
+{
+ read_unlock(&profile_list_lock);
+}
+
+static int seq_show_profile(struct seq_file *f, void *v)
+{
+ struct aa_profile *profile = (struct aa_profile *)v;
+ seq_printf(f, "%s (%s)\n", profile->name,
+ PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
+ return 0;
+}
+
+/* Used in apparmorfs.c */
+struct seq_operations apparmorfs_profiles_op = {
+ .start = p_start,
+ .next = p_next,
+ .stop = p_stop,
+ .show = seq_show_profile,
+};
--- /dev/null
+++ b/security/apparmor/locking.txt
@@ -0,0 +1,59 @@
+Locking in AppArmor
+===================
+
+Lock hierarchy:
+
+ aa_interface_lock
+ profile_list_lock
+ aa_profile->lock
+ task_lock()
+
+
+Which lock protects what?
+
+ /-----------------------+-------------------------------\
+ | Variable | Lock |
+ >-----------------------+-------------------------------<
+ | profile_list | profile_list_lock |
+ +-----------------------+-------------------------------+
+ | aa_profile | (reference count) |
+ +-----------------------+-------------------------------+
+ | aa_profile-> | aa_profile->lock |
+ | isstale, | |
+ | task_contexts | |
+ +-----------------------+-------------------------------+
+ | task_struct->security | read: RCU |
+ | | write: task_lock() |
+ +-----------------------+-------------------------------+
+ | aa_profile->sub | handle on the profile (list |
+ | | is never modified) |
+ \-----------------------+-------------------------------/
+
+(Obviously, the list_heads embedded in data structures are always
+protected with the lock that also protects the list.)
+
+When moving a task context from one profile to another, we grab both
+profile locks with lock_both_profiles(). This ensures that both locks
+are always taken in the same order, and so we won't deadlock.
+
+Since task_struct->security is RCU protected the aa_task_struct it
+references is only guarenteed to exist for the rcu cycle. Where
+aa_task_context->profile is needed in blocking operations the
+profile's reference count is incremented and the profile reference
+is used.
+
+Profiles on profile_list are never stale: when a profile becomes stale,
+it is removed from profile_list at the same time (under profile_list_lock
+and aa_profile->lock).
+
+The aa_interface_lock is taken whenever user-space modifies the profile
+list, and can sleep. This ensures that profile loading/replacement/removal
+won't race with itself. We release the profile_list_lock as soon as
+possible to avoid stalling exec during profile loading/replacement/removal.
+
+lock_dep reports a false 'possible irq lock inversion dependency detected'
+when the profile lock is taken in aa_release. This is due to that the
+task_lock is often taken inside the profile lock but other kernel code
+takes the task_lock with interrupts enabled. A deadlock will not actually
+occur because apparmor does not take the task_lock in hard_irq or soft_irq
+context.
--- /dev/null
+++ b/security/apparmor/procattr.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * AppArmor /proc/pid/attr handling
+ */
+
+#include "apparmor.h"
+#include "inline.h"
+
+int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len)
+{
+ char *str;
+
+ if (profile) {
+ const char *mode_str = PROFILE_COMPLAIN(profile) ?
+ " (complain)" : " (enforce)";
+
+ *len = ((profile != profile->parent) ?
+ strlen(profile->parent->name) + 1 : 0) +
+ strlen(mode_str) + strlen(profile->name) + 1;
+ str = kmalloc(*len, GFP_ATOMIC);
+ if (!str)
+ return -ENOMEM;
+
+ if (profile != profile->parent) {
+ memcpy(str, profile->parent->name,
+ strlen(profile->parent->name));
+ str += strlen(profile->parent->name);
+ *str++ = '^';
+ }
+ memcpy(str, profile->name, strlen(profile->name));
+ str += strlen(profile->name);
+ memcpy(str, mode_str, strlen(mode_str));
+ str += strlen(mode_str);
+ *str++ = '\n';
+ str -= *len;
+ } else {
+ const char *unconfined_str = "unconfined\n";
+
+ *len = strlen(unconfined_str);
+ str = kmalloc(*len, GFP_ATOMIC);
+ if (!str)
+ return -ENOMEM;
+
+ memcpy(str, unconfined_str, *len);
+ }
+ *string = str;
+
+ return 0;
+}
+
+int aa_setprocattr_changehat(char *args)
+{
+ char *hat;
+ u64 magic;
+
+ magic = simple_strtoull(args, &hat, 16);
+ if (hat == args || *hat != '^') {
+ AA_ERROR("change_hat: Invalid input '%s'", args);
+ return -EINVAL;
+ }
+ hat++; /* skip ^ */
+ if (!*hat)
+ hat = NULL;
+ if (!hat && !magic) {
+ AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic");
+ return -EINVAL;
+ }
+
+ AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
+ __FUNCTION__, magic, hat ? hat : NULL);
+
+ return aa_change_hat(hat, magic);
+}
+
+int aa_setprocattr_setprofile(struct task_struct *task, char *args)
+{
+ struct aa_profile *old_profile, *new_profile;
+
+ AA_DEBUG("%s: current %s(%d)\n",
+ __FUNCTION__, current->comm, current->pid);
+
+repeat:
+ if (strcmp(args, "unconfined") == 0)
+ new_profile = NULL;
+ else {
+ new_profile = aa_find_profile(args);
+ if (!new_profile) {
+ AA_WARN(GFP_KERNEL,
+ "Unable to switch task %s(%d) to profile"
+ "'%s'. No such profile.",
+ task->comm, task->pid,
+ args);
+
+ return -EINVAL;
+ }
+ }
+
+ old_profile = __aa_replace_profile(task, new_profile, 0);
+ if (IS_ERR(old_profile)) {
+ int error;
+
+ aa_put_profile(new_profile);
+ error = PTR_ERR(old_profile);
+ if (error == -ESTALE)
+ goto repeat;
+ return error;
+ }
+
+ if (new_profile) {
+ AA_WARN(GFP_KERNEL,
+ "Switching task %s(%d) "
+ "profile %s active %s to new profile %s",
+ task->comm, task->pid,
+ old_profile ? old_profile->parent->name :
+ "unconfined",
+ old_profile ? old_profile->name : "unconfined",
+ args);
+ } else {
+ if (old_profile) {
+ AA_WARN(GFP_KERNEL,
+ "Unconfining task %s(%d) "
+ "profile %s active %s",
+ task->comm, task->pid,
+ old_profile->parent->name,
+ old_profile->name);
+ } else {
+ AA_WARN(GFP_KERNEL,
+ "task %s(%d) is already unconfined",
+ task->comm, task->pid);
+ }
+ }
+
+ aa_put_profile(old_profile);
+ aa_put_profile(new_profile);
+
+ return 0;
+}
--
next prev parent reply other threads:[~2007-04-12 9:40 UTC|newest]
Thread overview: 159+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-04-12 9:08 [AppArmor 00/41] AppArmor security module overview jjohansen
2007-04-12 9:08 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook jjohansen
2007-04-12 10:06 ` Christoph Hellwig
2007-04-16 16:11 ` [nameidata 1/2] Don't pass NULL nameidata to vfs_create Andreas Gruenbacher
2007-04-16 16:21 ` Christoph Hellwig
2007-04-16 16:40 ` Andreas Gruenbacher
2007-04-16 16:45 ` Christoph Hellwig
2007-04-17 12:09 ` Andreas Gruenbacher
2007-05-11 15:59 ` Andreas Gruenbacher
2007-04-16 16:25 ` Matthew Wilcox
2007-04-16 16:29 ` [nameidata 2/2] Pass no useless nameidata to the create, lookup, and permission IOPs Andreas Gruenbacher
2007-04-16 16:39 ` Christoph Hellwig
2007-04-16 16:42 ` Randy Dunlap
2007-04-16 16:44 ` Andreas Gruenbacher
2007-04-16 16:50 ` Randy Dunlap
2007-04-12 10:12 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook Al Viro
2007-05-23 19:06 ` Andreas Gruenbacher
2007-05-24 1:28 ` James Morris
2007-05-24 9:16 ` Andreas Gruenbacher
2007-05-24 12:51 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook Tetsuo Handa
[not found] ` <200705241112.41101.agruen@suse.de>
2007-05-24 13:19 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook James Morris
2007-05-24 18:10 ` Andreas Gruenbacher
2007-05-24 18:40 ` Al Viro
2007-05-24 21:56 ` Andreas Gruenbacher
2007-05-24 18:58 ` Casey Schaufler
2007-05-25 4:14 ` Andreas Gruenbacher
2007-05-25 5:17 ` Jeremy Maitin-Shepard
2007-05-25 17:43 ` Casey Schaufler
2007-05-25 18:10 ` Jeremy Maitin-Shepard
2007-05-25 18:13 ` Jeremy Maitin-Shepard
2007-05-25 19:06 ` Casey Schaufler
2007-05-26 1:40 ` Tetsuo Handa
2007-05-26 12:10 ` Andreas Gruenbacher
2007-05-26 22:58 ` Casey Schaufler
2007-05-27 1:33 ` Valdis.Kletnieks
2007-05-25 20:00 ` Andreas Gruenbacher
2007-05-25 20:27 ` Casey Schaufler
2007-05-26 5:27 ` Crispin Cowan
2007-05-26 13:34 ` Alan Cox
2007-05-26 14:05 ` Andreas Gruenbacher
2007-05-26 18:41 ` James Morris
2007-05-26 5:20 ` Kyle Moffett
2007-05-26 11:46 ` Andreas Gruenbacher
2007-05-26 12:09 ` Tetsuo Handa
2007-05-26 13:41 ` Andreas Gruenbacher
2007-05-26 14:44 ` Tetsuo Handa
2007-05-26 16:52 ` Andreas Gruenbacher
2007-05-26 18:16 ` Kyle Moffett
2007-05-26 18:45 ` [AppArmor 01/41] " James Morris
2007-05-26 23:08 ` Toshiharu Harada
2007-05-27 2:10 ` Kyle Moffett
2007-05-27 2:37 ` Valdis.Kletnieks
2007-05-27 5:32 ` Kyle Moffett
2007-05-28 20:38 ` Pavel Machek
2007-05-29 2:00 ` Kyle Moffett
2007-05-27 7:25 ` Toshiharu Harada
2007-05-27 13:35 ` Kyle Moffett
2007-05-28 10:41 ` Toshiharu Harada
2007-05-29 1:54 ` Kyle Moffett
2007-05-29 21:17 ` Valdis.Kletnieks
2007-05-30 5:52 ` Crispin Cowan
2007-05-24 14:40 ` Pavel Machek
2007-05-30 10:06 ` Alan Cox
2007-05-30 2:38 ` Toshiharu Harada
2007-05-27 8:34 ` Cliffe
2007-05-27 13:07 ` Kyle Moffett
2007-05-27 16:12 ` Casey Schaufler
2007-05-25 8:01 ` Toshiharu Harada
2007-04-12 9:08 ` [AppArmor 02/41] Remove redundant check from proc_setattr() jjohansen
2007-04-12 9:08 ` [AppArmor 03/41] Remove redundant check from proc_sys_setattr() jjohansen
2007-04-12 10:10 ` Alan Cox
2007-04-12 9:08 ` [AppArmor 04/41] Pass struct file down to remove_suid and children jjohansen
2007-04-12 9:08 ` [AppArmor 05/41] Add a vfsmount parameter to notify_change() jjohansen
2007-04-12 9:08 ` [AppArmor 06/41] Pass struct vfsmount to the inode_setattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 07/41] Add struct vfsmount parameter to vfs_mkdir() jjohansen
2007-04-12 9:08 ` [AppArmor 08/41] Pass struct vfsmount to the inode_mkdir LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 09/41] Add a struct vfsmount parameter to vfs_mknod() jjohansen
2007-04-12 9:08 ` [AppArmor 10/41] Pass struct vfsmount to the inode_mknod LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 11/41] Add a struct vfsmount parameter to vfs_symlink() jjohansen
2007-04-12 9:08 ` [AppArmor 12/41] Pass struct vfsmount to the inode_symlink LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 13/41] Pass struct vfsmount to the inode_readlink " jjohansen
2007-04-12 9:08 ` [AppArmor 14/41] Add struct vfsmount parameters to vfs_link() jjohansen
2007-04-12 9:08 ` [AppArmor 15/41] Pass the struct vfsmounts to the inode_link LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 16/41] Add a struct vfsmount parameter to vfs_rmdir() jjohansen
2007-04-12 9:08 ` [AppArmor 17/41] Pass struct vfsmount to the inode_rmdir LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 18/41] call lsm hook before unhashing dentry in vfs_rmdir() jjohansen
2007-04-12 9:08 ` [AppArmor 19/41] Add a struct vfsmount parameter to vfs_unlink() jjohansen
2007-04-12 9:08 ` [AppArmor 20/41] Pass struct vfsmount to the inode_unlink LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 21/41] Add struct vfsmount parameters to vfs_rename() jjohansen
2007-04-12 9:08 ` [AppArmor 22/41] Pass struct vfsmount to the inode_rename LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 23/41] Add a struct vfsmount parameter to vfs_setxattr() jjohansen
2007-04-12 9:08 ` [AppArmor 24/41] Pass struct vfsmount to the inode_setxattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 25/41] Add a struct vfsmount parameter to vfs_getxattr() jjohansen
2007-04-12 9:08 ` [AppArmor 26/41] Pass struct vfsmount to the inode_getxattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 27/41] Add a struct vfsmount parameter to vfs_listxattr() jjohansen
2007-04-12 9:08 ` [AppArmor 28/41] Pass struct vfsmount to the inode_listxattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 29/41] Add a struct vfsmount parameter to vfs_removexattr() jjohansen
2007-04-12 9:08 ` [AppArmor 30/41] Pass struct vfsmount to the inode_removexattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts jjohansen
2007-04-12 9:58 ` Alan Cox
2007-04-15 17:40 ` Andreas Gruenbacher
2007-04-16 21:57 ` Alan Cox
2007-04-17 1:35 ` Andreas Gruenbacher
2007-04-17 17:21 ` Alan Cox
2007-04-19 23:23 ` [d_path 0/7] Fixes to d_path: Respin Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 1/7] Fix __d_path() for lazy unmounts and make it unambiguous Andreas Gruenbacher
2007-04-20 9:32 ` Alan Cox
2007-04-19 23:23 ` [d_path 2/7] Make d_path() consistent across mount operations Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 3/7] Add d_namespace_path() to compute namespace relative pathnames Andreas Gruenbacher
2007-04-21 12:57 ` Tetsuo Handa
2007-04-21 16:16 ` Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 4/7] Make getcwd() only return valid paths Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 5/7] Remove duplicate proc code Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 6/7] Filter out disconnected paths from /proc/mounts Andreas Gruenbacher
2007-04-20 9:34 ` Alan Cox
2007-04-19 23:23 ` [d_path 7/7] Distinguish between connected and disconnected paths in d_path() Andreas Gruenbacher
2007-04-20 9:30 ` [d_path 0/7] Fixes to d_path: Respin Alan Cox
2007-04-20 11:45 ` Andreas Gruenbacher
2007-04-20 15:15 ` Ulrich Drepper
2007-04-20 15:21 ` Andreas Gruenbacher
2007-04-20 15:24 ` Ulrich Drepper
2007-04-20 16:40 ` Andreas Gruenbacher
2007-04-20 19:17 ` Ulrich Drepper
2007-04-20 20:44 ` Miklos Szeredi
2007-04-21 19:04 ` Andreas Gruenbacher
2007-04-21 19:46 ` Ulrich Drepper
2007-04-22 9:10 ` Christoph Hellwig
2007-04-22 15:48 ` Andreas Gruenbacher
2007-04-17 6:30 ` [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts Rob Meijer
2007-04-12 9:08 ` [AppArmor 32/41] Make d_path() consistent across mount operations jjohansen
2007-04-12 9:08 ` [AppArmor 33/41] Add d_namespace_path() to obtain namespace relative pathnames jjohansen
2007-04-12 10:49 ` Al Viro
2007-04-12 9:08 ` [AppArmor 34/41] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames jjohansen
2007-04-12 9:08 ` [AppArmor 35/41] Pass struct file down the inode_*xattr security LSM hooks jjohansen
2007-04-12 9:08 ` [AppArmor 36/41] Export audit subsystem for use by modules jjohansen
2007-04-12 9:08 ` [AppArmor 37/41] AppArmor: Main Part jjohansen
2007-04-12 10:37 ` Alan Cox
2007-04-13 8:17 ` Andreas Gruenbacher
2007-04-13 8:48 ` Andreas Gruenbacher
2007-04-13 8:52 ` Nick Piggin
2007-04-12 9:08 ` [AppArmor 38/41] AppArmor: Module and LSM hooks jjohansen
2007-04-12 10:21 ` Alan Cox
2007-04-16 21:37 ` John Johansen
2007-04-12 9:08 ` [AppArmor 39/41] AppArmor: Profile loading and manipulation, pathname matching jjohansen
2007-04-12 10:28 ` Alan Cox
2007-04-12 13:46 ` Andi Kleen
2007-04-15 14:21 ` Andreas Gruenbacher
2007-04-16 6:27 ` Andi Kleen
2007-04-16 20:56 ` John Johansen
2007-04-16 7:39 ` Pavel Machek
2007-04-16 22:00 ` Alan Cox
2007-04-16 22:11 ` John Johansen
2007-04-12 9:08 ` jjohansen [this message]
2007-04-12 10:32 ` [AppArmor 40/41] AppArmor: all the rest Al Viro
2007-04-12 11:32 ` Al Viro
2007-04-12 9:08 ` [AppArmor 41/41] Add AppArmor LSM to security/Makefile jjohansen
2007-04-12 10:33 ` [AppArmor 00/41] AppArmor security module overview Shaya Potter
2007-04-12 13:50 ` Pavel Machek
2007-04-13 8:04 ` Rob Meijer
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=20070412090849.824835000@suse.de \
--to=jjohansen@suse.de \
--cc=agruen@suse.de \
--cc=chrisw@sous-sol.org \
--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).