]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.apparmor/apparmor-misc.diff
Added missing Xen Kernel Patches which were not commited because
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.apparmor / apparmor-misc.diff
diff --git a/src/patches/suse-2.6.27.31/patches.apparmor/apparmor-misc.diff b/src/patches/suse-2.6.27.31/patches.apparmor/apparmor-misc.diff
new file mode 100644 (file)
index 0000000..abffd1c
--- /dev/null
@@ -0,0 +1,1441 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: all the rest
+
+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      |   42 ++++
+ security/apparmor/Makefile     |   13 +
+ security/apparmor/apparmor.h   |  371 +++++++++++++++++++++++++++++++++++++++++
+ security/apparmor/apparmorfs.c |  281 +++++++++++++++++++++++++++++++
+ security/apparmor/inline.h     |  250 +++++++++++++++++++++++++++
+ security/apparmor/list.c       |  174 +++++++++++++++++++
+ security/apparmor/locking.txt  |   68 +++++++
+ security/apparmor/procattr.c   |  195 +++++++++++++++++++++
+ 8 files changed, 1394 insertions(+)
+
+--- /dev/null
++++ b/security/apparmor/Kconfig
+@@ -0,0 +1,42 @@
++config SECURITY_APPARMOR
++      bool "AppArmor support"
++      depends on SECURITY
++      select AUDIT
++      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.
++
++config SECURITY_APPARMOR_BOOTPARAM_VALUE
++      int "AppArmor boot parameter default value"
++      depends on SECURITY_APPARMOR
++      range 0 1
++      default 1
++      help
++        This option sets the default value for the kernel parameter
++        'apparmor', which allows AppArmor to be enabled or disabled
++          at boot.  If this option is set to 0 (zero), the AppArmor
++        kernel parameter will default to 0, disabling AppArmor at
++        bootup.  If this option is set to 1 (one), the AppArmor
++        kernel parameter will default to 1, enabling AppArmor at
++        bootup.
++
++        If you are unsure how to answer this question, answer 1.
++
++config SECURITY_APPARMOR_DISABLE
++      bool "AppArmor runtime disable"
++      depends on SECURITY_APPARMOR
++      default n
++      help
++        This option enables writing to a apparmorfs node 'disable', which
++        allows AppArmor to be disabled at runtime prior to the policy load.
++        AppArmor will then remain disabled until the next boot.
++        This option is similar to the apparmor.enabled=0 boot parameter,
++        but is to support runtime disabling of AppArmor, e.g. from
++        /sbin/init, for portability across platforms where boot
++        parameters are difficult to employ.
++
++        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,371 @@
++/*
++ *    Copyright (C) 1998-2007 Novell/SUSE
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License as
++ *    published by the Free Software Foundation, version 2 of the
++ *    License.
++ *
++ *    AppArmor internal prototypes
++ */
++
++#ifndef __APPARMOR_H
++#define __APPARMOR_H
++
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/binfmts.h>
++#include <linux/rcupdate.h>
++
++/*
++ * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
++ * for profile permissions
++ */
++#define AA_MAY_LINK                   0x0010
++#define AA_MAY_LOCK                   0x0020
++#define AA_EXEC_MMAP                  0x0040
++#define AA_MAY_MOUNT                  0x0080  /* no direct audit mapping */
++#define AA_EXEC_UNSAFE                        0x0100
++#define AA_EXEC_INHERIT                       0x0200
++#define AA_EXEC_MOD_0                 0x0400
++#define AA_EXEC_MOD_1                 0x0800
++#define AA_EXEC_MOD_2                 0x1000
++#define AA_EXEC_MOD_3                 0x2000
++
++#define AA_BASE_PERMS                 (MAY_READ | MAY_WRITE | MAY_EXEC | \
++                                       MAY_APPEND | AA_MAY_LINK | \
++                                       AA_MAY_LOCK | AA_EXEC_MMAP | \
++                                       AA_MAY_MOUNT | AA_EXEC_UNSAFE | \
++                                       AA_EXEC_INHERIT | AA_EXEC_MOD_0 | \
++                                       AA_EXEC_MOD_1 | AA_EXEC_MOD_2 | \
++                                       AA_EXEC_MOD_3)
++
++#define AA_EXEC_MODIFIERS             (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
++                                       AA_EXEC_MOD_2 | AA_EXEC_MOD_3)
++
++#define AA_EXEC_TYPE                  (AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \
++                                       AA_EXEC_MODIFIERS)
++
++#define AA_EXEC_UNCONFINED            AA_EXEC_MOD_0
++#define AA_EXEC_PROFILE                       AA_EXEC_MOD_1
++#define AA_EXEC_CHILD                 (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
++/* remaining exec modes are index into profile name table */
++#define AA_EXEC_INDEX(mode)           ((mode & AA_EXEC_MODIFIERS) >> 10)
++
++#define AA_USER_SHIFT                 0
++#define AA_OTHER_SHIFT                        14
++
++#define AA_USER_PERMS                 (AA_BASE_PERMS << AA_USER_SHIFT)
++#define AA_OTHER_PERMS                        (AA_BASE_PERMS << AA_OTHER_SHIFT)
++
++#define AA_FILE_PERMS                 (AA_USER_PERMS | AA_OTHER_PERMS)
++
++#define AA_LINK_BITS                  ((AA_MAY_LINK << AA_USER_SHIFT) | \
++                                       (AA_MAY_LINK << AA_OTHER_SHIFT))
++
++#define AA_USER_EXEC                  (MAY_EXEC << AA_USER_SHIFT)
++#define AA_OTHER_EXEC                 (MAY_EXEC << AA_OTHER_SHIFT)
++
++#define AA_USER_EXEC_TYPE             (AA_EXEC_TYPE << AA_USER_SHIFT)
++#define AA_OTHER_EXEC_TYPE            (AA_EXEC_TYPE << AA_OTHER_SHIFT)
++
++#define AA_EXEC_BITS                  (AA_USER_EXEC | AA_OTHER_EXEC)
++
++#define ALL_AA_EXEC_UNSAFE            ((AA_EXEC_UNSAFE << AA_USER_SHIFT) | \
++                                       (AA_EXEC_UNSAFE << AA_OTHER_SHIFT))
++
++#define ALL_AA_EXEC_TYPE              (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE)
++
++/* overloaded permissions for link pairs */
++#define AA_LINK_SUBSET_TEST           0x0020
++
++#define AA_USER_PTRACE                        0x10000000
++#define AA_OTHER_PTRACE                       0x20000000
++#define AA_PTRACE_PERMS                       (AA_USER_PTRACE | AA_OTHER_PTRACE)
++
++/* shared permissions that are not duplicated in user::other */
++#define AA_CHANGE_HAT                 0x40000000
++#define AA_CHANGE_PROFILE             0x80000000
++
++#define AA_SHARED_PERMS                       (AA_CHANGE_HAT | AA_CHANGE_PROFILE)
++
++#define AA_VALID_PERM_MASK            (AA_FILE_PERMS | AA_PTRACE_PERMS | \
++                                       AA_SHARED_PERMS)
++
++/* audit bits for the second accept field */
++#define AUDIT_FILE_MASK 0x1fc07f
++#define AUDIT_QUIET_MASK(mask)                ((mask >> 7) & AUDIT_FILE_MASK)
++#define AA_VALID_PERM2_MASK           0x0fffffff
++
++#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))
++
++#define PROFILE_IS_HAT(_profile) \
++      ((_profile) && (_profile)->flags.hat)
++
++/*
++ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
++ * which is not related to profile accesses.
++ */
++
++#define AA_DEBUG(fmt, args...)                                                \
++      do {                                                            \
++              if (apparmor_debug)                                     \
++                      printk(KERN_DEBUG "AppArmor: " fmt, ##args);    \
++      } while (0)
++
++#define AA_ERROR(fmt, args...)        printk(KERN_ERR "AppArmor: " fmt, ##args)
++
++struct aa_profile;
++
++/* struct aa_namespace - namespace for a set of profiles
++ * @name: the name of the namespace
++ * @list: list the namespace is on
++ * @profiles: list of profile in the namespace
++ * @profile_count: the number of profiles in the namespace
++ * @null_complain_profile: special profile used for learning in this namespace
++ * @count: reference count on the namespace
++ * @lock: lock for adding/removing profile to the namespace
++ */
++struct aa_namespace {
++      char *name;
++      struct list_head list;
++      struct list_head profiles;
++      int profile_count;
++      struct aa_profile *null_complain_profile;
++
++      struct kref count;
++      rwlock_t lock;
++};
++
++/* struct aa_profile - basic confinement data
++ * @name: the profiles name
++ * @list: list this profile is on
++ * @ns: namespace the profile is in
++ * @file_rules: dfa containing the profiles file rules
++ * @flags: flags controlling profile behavior
++ * @isstale: flag indicating if profile is stale
++ * @set_caps: capabilities that are being set
++ * @capabilities: capabilities mask
++ * @audit_caps: caps that are to be audited
++ * @quiet_caps: caps that should not be audited
++ * @capabilities: capabilities granted by the process
++ * @count: reference count of the profile
++ * @task_contexts: list of tasks confined by profile
++ * @lock: lock for the task_contexts list
++ * @network_families: basic network permissions
++ * @audit_network: which network permissions to force audit
++ * @quiet_network: which network permissions to quiet rejects
++ *
++ * The AppArmor profile contains the basic confinement data.  Each profile
++ * has a name, and all nonstale profile are in a profile namespace.
++ *
++ * The task_contexts list and the isstale flag are protected by the
++ * profile lock.
++ *
++ * If a task context is moved between two profiles, we first need to grab
++ * both profile locks. lock_both_profiles() does that in a deadlock-safe
++ * way.
++ */
++struct aa_profile {
++      char *name;
++      struct list_head list;
++      struct aa_namespace *ns;
++
++      int exec_table_size;
++      char **exec_table;
++      struct aa_dfa *file_rules;
++      struct {
++              int hat;
++              int complain;
++              int audit;
++      } flags;
++      int isstale;
++
++      kernel_cap_t set_caps;
++      kernel_cap_t capabilities;
++      kernel_cap_t audit_caps;
++      kernel_cap_t quiet_caps;
++
++      struct kref count;
++      struct list_head task_contexts;
++      spinlock_t lock;
++      unsigned long int_flags;
++};
++
++extern struct list_head profile_ns_list;
++extern rwlock_t profile_ns_list_lock;
++extern struct mutex aa_interface_lock;
++
++/**
++ * struct aa_task_context - primary label for confined tasks
++ * @profile: the current profile
++ * @previous_profile: profile the task may return to
++ * @cookie: magic value the task must know for returning to @previous_profile
++ * @list: list this aa_task_context is on
++ * @task: task that the aa_task_context confines
++ * @rcu: rcu head used when freeing the aa_task_context
++ * @caps_logged: caps that have previously generated log entries
++ *
++ * Contains the task's current profile (which could change due to
++ * change_hat).  Plus the hat_magic needed during change_hat.
++ */
++struct aa_task_context {
++      struct aa_profile *profile;
++      struct aa_profile *previous_profile;
++      u64 cookie;
++      struct list_head list;
++      struct task_struct *task;
++      struct rcu_head rcu;
++      kernel_cap_t caps_logged;
++};
++
++extern struct aa_namespace *default_namespace;
++
++/* aa_audit - AppArmor auditing structure
++ * Structure is populated by access control code and passed to aa_audit which
++ * provides for a single point of logging.
++ */
++
++struct aa_audit {
++      const char *operation;
++      gfp_t gfp_mask;
++      const char *info;
++      const char *name;
++      const char *name2;
++      const char *name3;
++      int request_mask, denied_mask, audit_mask;
++      struct iattr *iattr;
++      pid_t task, parent;
++      int error_code;
++};
++
++/* Flags for the permission check functions */
++#define AA_CHECK_FD   1  /* coming from a file descriptor */
++#define AA_CHECK_DIR  2  /* file type is directory */
++
++/* lock subtypes so lockdep does not raise false dependencies */
++enum aa_lock_class {
++      aa_lock_normal,
++      aa_lock_nested,
++      aa_lock_task_release
++};
++
++/* main.c */
++extern int alloc_default_namespace(void);
++extern void free_default_namespace(void);
++extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
++                          int type);
++void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa);
++void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa);
++int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa);
++extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
++                                const char *);
++extern int aa_audit(struct aa_profile *profile, struct aa_audit *);
++
++extern int aa_attr(struct aa_profile *profile, struct dentry *dentry,
++                 struct vfsmount *mnt, struct iattr *iattr);
++extern int aa_perm_xattr(struct aa_profile *profile, const char *operation,
++                       struct dentry *dentry, struct vfsmount *mnt,
++                       int mask, int check);
++extern int aa_capability(struct aa_task_context *cxt, int cap);
++extern int aa_perm(struct aa_profile *profile, const char *operation,
++                 struct dentry *dentry, struct vfsmount *mnt, int mask,
++                 int check);
++extern int aa_perm_dir(struct aa_profile *profile, const char *operation,
++                     struct dentry *dentry, struct vfsmount *mnt,
++                     int mask);
++extern int aa_perm_path(struct aa_profile *, const char *operation,
++                      const char *name, int mask, uid_t uid);
++extern int aa_link(struct aa_profile *profile,
++                 struct dentry *link, struct vfsmount *link_mnt,
++                 struct dentry *target, struct vfsmount *target_mnt);
++extern int aa_clone(struct task_struct *task);
++extern int aa_register(struct linux_binprm *bprm);
++extern void aa_release(struct task_struct *task);
++extern int aa_change_hat(const char *id, u64 hat_magic);
++extern int aa_change_profile(const char *ns_name, const char *name);
++extern struct aa_profile *__aa_replace_profile(struct task_struct *task,
++                                             struct aa_profile *profile);
++extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task,
++                                                    struct aa_profile *profile);
++extern void unlock_task_and_profiles(struct task_struct *task,
++                                   struct aa_task_context *cxt,
++                                   struct aa_profile *profile);
++extern void aa_change_task_context(struct task_struct *task,
++                                 struct aa_task_context *new_cxt,
++                                 struct aa_profile *profile, u64 cookie,
++                                 struct aa_profile *previous_profile);
++extern int aa_may_ptrace(struct aa_task_context *cxt,
++                       struct aa_profile *tracee);
++
++/* lsm.c */
++extern int apparmor_initialized;
++extern void info_message(const char *str);
++extern void apparmor_disable(void);
++
++/* list.c */
++extern struct aa_namespace *__aa_find_namespace(const char *name,
++                                              struct list_head *list);
++extern struct aa_profile *__aa_find_profile(const char *name,
++                                          struct list_head *list);
++extern void aa_profile_ns_list_release(void);
++
++/* module_interface.c */
++extern ssize_t aa_add_profile(void *, size_t);
++extern ssize_t aa_replace_profile(void *, size_t);
++extern ssize_t aa_remove_profile(char *, size_t);
++extern struct aa_namespace *alloc_aa_namespace(char *name);
++extern void free_aa_namespace(struct aa_namespace *ns);
++extern void free_aa_namespace_kref(struct kref *kref);
++extern struct aa_profile *alloc_aa_profile(void);
++extern void free_aa_profile(struct aa_profile *profile);
++extern void free_aa_profile_kref(struct kref *kref);
++extern void aa_unconfine_tasks(struct aa_profile *profile);
++
++/* procattr.c */
++extern int aa_getprocattr(struct aa_profile *profile, char **string,
++                        unsigned *len);
++extern int aa_setprocattr_changehat(char *args);
++extern int aa_setprocattr_changeprofile(char *args);
++extern int aa_setprocattr_setprofile(struct task_struct *task, char *args);
++
++/* apparmorfs.c */
++extern int create_apparmorfs(void);
++extern void destroy_apparmorfs(void);
++
++/* match.c */
++extern struct aa_dfa *aa_match_alloc(void);
++extern void aa_match_free(struct aa_dfa *dfa);
++extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
++extern int verify_dfa(struct aa_dfa *dfa);
++extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *);
++extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
++                                    const char *str);
++extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
++                                 const char *str, unsigned int *final);
++extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
++                                         unsigned int start);
++
++#endif  /* __APPARMOR_H */
+--- /dev/null
++++ b/security/apparmor/apparmorfs.c
+@@ -0,0 +1,281 @@
++/*
++ *    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 <linux/namei.h>
++
++#include "apparmor.h"
++#include "inline.h"
++
++static char *aa_simple_write_to_buffer(const char __user *userbuf,
++                                     size_t alloc_size, size_t copy_size,
++                                     loff_t *pos, const char *operation)
++{
++      struct aa_profile *profile;
++      char *data;
++
++      if (*pos != 0) {
++              /* only writes from pos 0, that is complete writes */
++              data = ERR_PTR(-ESPIPE);
++              goto out;
++      }
++
++      /*
++       * Don't allow confined processes to load/replace/remove profiles.
++       * No sane person would add rules allowing this to a profile
++       * but we enforce the restriction anyways.
++       */
++      profile = aa_get_profile(current);
++      if (profile) {
++              struct aa_audit sa;
++              memset(&sa, 0, sizeof(sa));
++              sa.operation = operation;
++              sa.gfp_mask = GFP_KERNEL;
++              sa.error_code = -EACCES;
++              data = ERR_PTR(aa_audit_reject(profile, &sa));
++              aa_put_profile(profile);
++              goto out;
++      }
++
++      data = vmalloc(alloc_size);
++      if (data == NULL) {
++              data = ERR_PTR(-ENOMEM);
++              goto out;
++      }
++
++      if (copy_from_user(data, userbuf, copy_size)) {
++              vfree(data);
++              data = ERR_PTR(-EFAULT);
++              goto out;
++      }
++
++out:
++      return data;
++}
++
++/* apparmor/profiles */
++extern struct seq_operations apparmorfs_profiles_op;
++
++static int aa_profiles_open(struct inode *inode, struct file *file)
++{
++      return seq_open(file, &apparmorfs_profiles_op);
++}
++
++
++static int aa_profiles_release(struct inode *inode, struct file *file)
++{
++      return seq_release(inode, file);
++}
++
++static struct file_operations apparmorfs_profiles_fops = {
++      .open =         aa_profiles_open,
++      .read =         seq_read,
++      .llseek =       seq_lseek,
++      .release =      aa_profiles_release,
++};
++
++/* apparmor/matching */
++static ssize_t aa_matching_read(struct file *file, char __user *buf,
++                             size_t size, loff_t *ppos)
++{
++      const char *matching = "pattern=aadfa audit perms=rwxamlk/ user::other";
++
++      return simple_read_from_buffer(buf, size, ppos, matching,
++                                     strlen(matching));
++}
++
++static struct file_operations apparmorfs_matching_fops = {
++      .read =         aa_matching_read,
++};
++
++/* apparmor/features */
++static ssize_t aa_features_read(struct file *file, char __user *buf,
++                              size_t size, loff_t *ppos)
++{
++      const char *features = "file=3.0 capability=2.0 network=1.0 "
++                             "change_hat=1.5 change_profile=1.0 "
++                             "aanamespaces=1.0";
++
++      return simple_read_from_buffer(buf, size, ppos, features,
++                                     strlen(features));
++}
++
++static struct file_operations apparmorfs_features_fops = {
++      .read =         aa_features_read,
++};
++
++/* apparmor/.load */
++static ssize_t aa_profile_load(struct file *f, const char __user *buf,
++                             size_t size, loff_t *pos)
++{
++      char *data;
++      ssize_t error;
++
++      data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load");
++
++      error = PTR_ERR(data);
++      if (!IS_ERR(data)) {
++              error = aa_add_profile(data, size);
++              vfree(data);
++      }
++
++      return error;
++}
++
++
++static struct file_operations apparmorfs_profile_load = {
++      .write = aa_profile_load
++};
++
++/* apparmor/.replace */
++static ssize_t aa_profile_replace(struct file *f, const char __user *buf,
++                                size_t size, loff_t *pos)
++{
++      char *data;
++      ssize_t error;
++
++      data = aa_simple_write_to_buffer(buf, size, size, pos,
++                                       "profile_replace");
++
++      error = PTR_ERR(data);
++      if (!IS_ERR(data)) {
++              error = aa_replace_profile(data, size);
++              vfree(data);
++      }
++
++      return error;
++}
++
++
++static struct file_operations apparmorfs_profile_replace = {
++      .write = aa_profile_replace
++};
++
++/* apparmor/.remove */
++static ssize_t aa_profile_remove(struct file *f, const char __user *buf,
++                                size_t size, loff_t *pos)
++{
++      char *data;
++      ssize_t error;
++
++      /*
++       * aa_remove_profile needs a null terminated string so 1 extra
++       * byte is allocated and the copied data is null terminated.
++       */
++      data = aa_simple_write_to_buffer(buf, size + 1, size, pos,
++                                       "profile_remove");
++
++      error = PTR_ERR(data);
++      if (!IS_ERR(data)) {
++              data[size] = 0;
++              error = aa_remove_profile(data, size);
++              vfree(data);
++      }
++
++      return error;
++}
++
++static struct file_operations apparmorfs_profile_remove = {
++      .write = aa_profile_remove
++};
++
++static struct dentry *apparmor_dentry;
++
++static void aafs_remove(const char *name)
++{
++      struct dentry *dentry;
++
++      dentry = lookup_one_len(name, apparmor_dentry, strlen(name));
++      if (!IS_ERR(dentry)) {
++              securityfs_remove(dentry);
++              dput(dentry);
++      }
++}
++
++static int aafs_create(const char *name, int mask, struct file_operations *fops)
++{
++      struct dentry *dentry;
++
++      dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry,
++                                      NULL, fops);
++
++      return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
++}
++
++void destroy_apparmorfs(void)
++{
++      if (apparmor_dentry) {
++              aafs_remove(".remove");
++              aafs_remove(".replace");
++              aafs_remove(".load");
++              aafs_remove("matching");
++              aafs_remove("features");
++              aafs_remove("profiles");
++              securityfs_remove(apparmor_dentry);
++              apparmor_dentry = NULL;
++      }
++}
++
++int create_apparmorfs(void)
++{
++      int error;
++
++      if (!apparmor_initialized)
++              return 0;
++
++      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("features", 0444, &apparmorfs_features_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;
++
++      /* Report that AppArmor fs is enabled */
++      info_message("AppArmor Filesystem Enabled");
++      return 0;
++
++error:
++      destroy_apparmorfs();
++      AA_ERROR("Error creating AppArmor securityfs\n");
++      apparmor_disable();
++      return error;
++}
++
++fs_initcall(create_apparmorfs);
++
+--- /dev/null
++++ b/security/apparmor/inline.h
+@@ -0,0 +1,250 @@
++/*
++ *    Copyright (C) 1998-2007 Novell/SUSE
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License as
++ *    published by the Free Software Foundation, version 2 of the
++ *    License.
++ */
++
++#ifndef __INLINE_H
++#define __INLINE_H
++
++#include <linux/sched.h>
++
++#include "match.h"
++
++static inline int mediated_filesystem(struct inode *inode)
++{
++      return !(inode->i_sb->s_flags & MS_NOUSER);
++}
++
++static inline struct aa_task_context *aa_task_context(struct task_struct *task)
++{
++      return (struct aa_task_context *) rcu_dereference(task->security);
++}
++
++static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
++{
++      if (ns)
++              kref_get(&(ns->count));
++
++      return ns;
++}
++
++static inline void aa_put_namespace(struct aa_namespace *ns)
++{
++      if (ns)
++              kref_put(&ns->count, free_aa_namespace_kref);
++}
++
++
++static inline struct aa_namespace *aa_find_namespace(const char *name)
++{
++      struct aa_namespace *ns = NULL;
++
++      read_lock(&profile_ns_list_lock);
++      ns = aa_get_namespace(__aa_find_namespace(name, &profile_ns_list));
++      read_unlock(&profile_ns_list_lock);
++
++      return ns;
++}
++
++/**
++ * aa_dup_profile - increment refcount on profile @p
++ * @p: profile
++ */
++static inline struct aa_profile *aa_dup_profile(struct aa_profile *p)
++{
++      if (p)
++              kref_get(&(p->count));
++
++      return p;
++}
++
++/**
++ * aa_put_profile - decrement refcount on profile @p
++ * @p: profile
++ */
++static inline void aa_put_profile(struct aa_profile *p)
++{
++      if (p)
++              kref_put(&p->count, free_aa_profile_kref);
++}
++
++static inline struct aa_profile *aa_get_profile(struct task_struct *task)
++{
++      struct aa_task_context *cxt;
++      struct aa_profile *profile = NULL;
++
++      rcu_read_lock();
++      cxt = aa_task_context(task);
++      if (cxt) {
++              profile = cxt->profile;
++              aa_dup_profile(profile);
++      }
++      rcu_read_unlock();
++
++      return profile;
++}
++
++static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns,
++                                               const char *name)
++{
++      struct aa_profile *profile = NULL;
++
++      read_lock(&ns->lock);
++      profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles));
++      read_unlock(&ns->lock);
++
++      return profile;
++}
++
++static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags)
++{
++      struct aa_task_context *cxt;
++
++      cxt = kzalloc(sizeof(*cxt), flags);
++      if (cxt) {
++              INIT_LIST_HEAD(&cxt->list);
++              INIT_RCU_HEAD(&cxt->rcu);
++      }
++
++      return cxt;
++}
++
++static inline void aa_free_task_context(struct aa_task_context *cxt)
++{
++      if (cxt) {
++              aa_put_profile(cxt->profile);
++              aa_put_profile(cxt->previous_profile);
++              kfree(cxt);
++      }
++}
++
++/**
++ * lock_profile - lock a profile
++ * @profile: the profile to lock
++ *
++ * While the profile is locked, local interrupts are disabled. This also
++ * gives us RCU reader safety.
++ */
++static inline void lock_profile_nested(struct aa_profile *profile,
++                                     enum aa_lock_class lock_class)
++{
++      /*
++       * Lock the profile.
++       *
++       * Need to disable interrupts here because this lock is used in
++       * the task_free_security hook, which may run in RCU context.
++       */
++      if (profile)
++              spin_lock_irqsave_nested(&profile->lock, profile->int_flags,
++                                       lock_class);
++}
++
++static inline void lock_profile(struct aa_profile *profile)
++{
++      lock_profile_nested(profile, aa_lock_normal);
++}
++
++/**
++ * unlock_profile - unlock a profile
++ * @profile: the profile to unlock
++ */
++static inline void unlock_profile(struct aa_profile *profile)
++{
++      /* Unlock the profile. */
++      if (profile)
++              spin_unlock_irqrestore(&profile->lock, profile->int_flags);
++}
++
++/**
++ * lock_both_profiles  -  lock two profiles in a deadlock-free way
++ * @profile1: profile to lock (may be NULL)
++ * @profile2: profile to lock (may be NULL)
++ *
++ * The order in which profiles are passed into lock_both_profiles() /
++ * unlock_both_profiles() does not matter.
++ * While the profile is locked, local interrupts are disabled. This also
++ * gives us RCU reader safety.
++ */
++static inline void lock_both_profiles(struct aa_profile *profile1,
++                                    struct aa_profile *profile2)
++{
++      /*
++       * Lock the two profiles.
++       *
++       * We need to disable interrupts because the profile locks are
++       * used in the task_free_security hook, which may run in RCU
++       * context.
++       *
++       * Do not nest spin_lock_irqsave()/spin_unlock_irqresore():
++       * interrupts only need to be turned off once.
++       */
++      if (!profile1 || profile1 == profile2) {
++              if (profile2)
++                      spin_lock_irqsave_nested(&profile2->lock,
++                                               profile2->int_flags,
++                                               aa_lock_normal);
++      } else if (profile1 > profile2) {
++              /* profile1 cannot be NULL here. */
++              spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags,
++                                       aa_lock_normal);
++              if (profile2)
++                      spin_lock_nested(&profile2->lock, aa_lock_nested);
++
++      } else {
++              /* profile2 cannot be NULL here. */
++              spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags,
++                                       aa_lock_normal);
++              spin_lock_nested(&profile1->lock, aa_lock_nested);
++      }
++}
++
++/**
++ * unlock_both_profiles  -  unlock two profiles in a deadlock-free way
++ * @profile1: profile to unlock (may be NULL)
++ * @profile2: profile to unlock (may be NULL)
++ *
++ * The order in which profiles are passed into lock_both_profiles() /
++ * unlock_both_profiles() does not matter.
++ * While the profile is locked, local interrupts are disabled. This also
++ * gives us RCU reader safety.
++ */
++static inline void unlock_both_profiles(struct aa_profile *profile1,
++                                      struct aa_profile *profile2)
++{
++      /* Unlock the two profiles. */
++      if (!profile1 || profile1 == profile2) {
++              if (profile2)
++                      spin_unlock_irqrestore(&profile2->lock,
++                                             profile2->int_flags);
++      } else if (profile1 > profile2) {
++              /* profile1 cannot be NULL here. */
++              if (profile2)
++                      spin_unlock(&profile2->lock);
++              spin_unlock_irqrestore(&profile1->lock, profile1->int_flags);
++      } else {
++              /* profile2 cannot be NULL here. */
++              spin_unlock(&profile1->lock);
++              spin_unlock_irqrestore(&profile2->lock, profile2->int_flags);
++      }
++}
++
++static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname,
++                                  int *audit_mask)
++{
++      if (dfa)
++              return aa_dfa_match(dfa, pathname, audit_mask);
++      if (audit_mask)
++              *audit_mask = 0;
++      return 0;
++}
++
++static inline int dfa_audit_mask(struct aa_dfa *dfa, unsigned int state)
++{
++      return  ACCEPT_TABLE2(dfa)[state];
++}
++
++#endif /* __INLINE_H__ */
+--- /dev/null
++++ b/security/apparmor/list.c
+@@ -0,0 +1,174 @@
++/*
++ *    Copyright (C) 1998-2007 Novell/SUSE
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License as
++ *    published by the Free Software Foundation, version 2 of the
++ *    License.
++ *
++ *    AppArmor Profile List Management
++ */
++
++#include <linux/seq_file.h>
++#include "apparmor.h"
++#include "inline.h"
++
++/* list of profile namespaces and lock */
++LIST_HEAD(profile_ns_list);
++rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED;
++
++/**
++ * __aa_find_namespace  -  look up a profile namespace on the namespace list
++ * @name: name of namespace to find
++ * @head: list to search
++ *
++ * Returns a pointer to the namespace on the list, or NULL if no namespace
++ * called @name exists. The caller must hold the profile_ns_list_lock.
++ */
++struct aa_namespace *__aa_find_namespace(const char *name,
++                                     struct list_head *head)
++{
++      struct aa_namespace *ns;
++
++      list_for_each_entry(ns, head, list) {
++              if (!strcmp(ns->name, name))
++                      return ns;
++      }
++
++      return NULL;
++}
++
++/**
++ * __aa_find_profile  -  look up a profile on the profile list
++ * @name: name of profile to find
++ * @head: list to search
++ *
++ * Returns a pointer to the profile on the list, or NULL if no profile
++ * called @name exists. The caller must hold the profile_list_lock.
++ */
++struct aa_profile *__aa_find_profile(const char *name, struct list_head *head)
++{
++      struct aa_profile *profile;
++
++      list_for_each_entry(profile, head, list) {
++              if (!strcmp(profile->name, name))
++                      return profile;
++      }
++
++      return NULL;
++}
++
++static void aa_profile_list_release(struct list_head *head)
++{
++      struct aa_profile *profile, *tmp;
++      list_for_each_entry_safe(profile, tmp, head, list) {
++              /* Remove the profile from each task context it is on. */
++              lock_profile(profile);
++              profile->isstale = 1;
++              aa_unconfine_tasks(profile);
++              list_del_init(&profile->list);
++              unlock_profile(profile);
++              aa_put_profile(profile);
++      }
++}
++
++/**
++ * aa_profilelist_release - Remove all profiles from profile_list
++ */
++void aa_profile_ns_list_release(void)
++{
++      struct aa_namespace *ns, *tmp;
++
++      /* Remove and release all the profiles on namespace profile lists. */
++      write_lock(&profile_ns_list_lock);
++      list_for_each_entry_safe(ns, tmp, &profile_ns_list, list) {
++              write_lock(&ns->lock);
++              aa_profile_list_release(&ns->profiles);
++              list_del_init(&ns->list);
++              write_unlock(&ns->lock);
++              aa_put_namespace(ns);
++      }
++      write_unlock(&profile_ns_list_lock);
++}
++
++
++static struct aa_profile *next_profile(struct aa_profile *profile)
++{
++      struct aa_profile *next = profile;
++      struct aa_namespace *ns;
++
++      list_for_each_entry_continue(next, &profile->ns->profiles, list)
++              return next;
++
++      ns = profile->ns;
++      read_unlock(&ns->lock);
++      list_for_each_entry_continue(ns, &profile_ns_list, list) {
++              read_lock(&ns->lock);
++              list_for_each_entry(profile, &ns->profiles, list)
++                      return profile;
++              read_unlock(&ns->lock);
++      }
++      return NULL;
++}
++
++static void *p_start(struct seq_file *f, loff_t *pos)
++{
++      struct aa_namespace *ns;
++      loff_t l = *pos;
++
++      read_lock(&profile_ns_list_lock);
++      if (!list_empty(&profile_ns_list)) {
++              struct aa_profile *profile = NULL;
++              ns = list_first_entry(&profile_ns_list, typeof(*ns), list);
++              read_lock(&ns->lock);
++              if (!list_empty(&ns->profiles))
++                      profile = list_first_entry(&ns->profiles,
++                                                 typeof(*profile), list);
++              else
++                      read_unlock(&ns->lock);
++              for ( ; profile && l > 0; l--)
++                      profile = next_profile(profile);
++              return profile;
++      }
++      return NULL;
++}
++
++static void *p_next(struct seq_file *f, void *p, loff_t *pos)
++{
++      struct aa_profile *profile = (struct aa_profile *) p;
++
++      (*pos)++;
++      profile = next_profile(profile);
++
++      return profile;
++}
++
++static void p_stop(struct seq_file *f, void *p)
++{
++      struct aa_profile *profile = (struct aa_profile *) p;
++
++      if (profile)
++              read_unlock(&profile->ns->lock);
++      read_unlock(&profile_ns_list_lock);
++}
++
++static int seq_show_profile(struct seq_file *f, void *p)
++{
++      struct aa_profile *profile = (struct aa_profile *)p;
++
++      if (profile->ns == default_namespace)
++          seq_printf(f, "%s (%s)\n", profile->name,
++                     PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
++      else
++          seq_printf(f, ":%s:%s (%s)\n", profile->ns->name, profile->name,
++                     PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
++      return 0;
++}
++
++/* Used in apparmorfs.c */
++struct seq_operations apparmorfs_profiles_op = {
++      .start =        p_start,
++      .next =         p_next,
++      .stop =         p_stop,
++      .show =         seq_show_profile,
++};
+--- /dev/null
++++ b/security/apparmor/locking.txt
+@@ -0,0 +1,68 @@
++Locking in AppArmor
++===================
++
++Lock hierarchy:
++
++      aa_interface_lock
++        profile_list_lock
++          aa_profile->lock
++            task_lock()
++
++
++Which lock protects what?
++
++      /-----------------------+-------------------------------\
++      | Variable              | Lock                          |
++      >-----------------------+-------------------------------<
++      | profile_list          | profile_list_lock             |
++      +-----------------------+-------------------------------+
++      | aa_profile            | (reference count)             |
++      +-----------------------+-------------------------------+
++      | aa_profile->          | aa_profile->lock              |
++      |   isstale,            |                               |
++      |   task_contexts       |                               |
++      +-----------------------+-------------------------------+
++      | task_struct->security | read: RCU                     |
++      |                       | write: task_lock()            |
++      +-----------------------+-------------------------------+
++      | aa_profile->sub       | handle on the profile (list   |
++      |                       | is never modified)            |
++      \-----------------------+-------------------------------/
++
++(Obviously, the list_heads embedded in data structures are always
++protected with the lock that also protects the list.)
++
++When moving a task context from one profile to another, we grab both
++profile locks with lock_both_profiles(). This ensures that both locks
++are always taken in the same order, and so we won't deadlock.
++
++Since task_struct->security is RCU protected the aa_task_struct it
++references is only guarenteed to exist for the rcu cycle.  Where
++aa_task_context->profile is needed in blocking operations the
++profile's reference count is incremented and the profile reference
++is used.
++
++Profiles on profile_list are never stale: when a profile becomes stale,
++it is removed from profile_list at the same time (under profile_list_lock
++and aa_profile->lock).
++
++The aa_interface_lock is taken whenever user-space modifies the profile
++list, and can sleep. This ensures that profile loading/replacement/removal
++won't race with itself. We release the profile_list_lock as soon as
++possible to avoid stalling exec during profile loading/replacement/removal.
++
++AppArmor uses lock subtyping to avoid false positives from lockdep.  The
++profile lock is often taken nested, but it is guaranteed to be in a lock
++safe order and not the same lock when done, so it is safe.
++
++A third lock type (aa_lock_task_release) is given to the profile lock
++when it is taken in soft irq context during task release (aa_release).
++This is to avoid a false positive between the task lock and the profile
++lock.  In task context the profile lock wraps the task lock with irqs
++off, but the kernel takes the task lock with irqs enabled.  This won't
++result in a deadlock because for a deadlock to occur the kernel must
++take dead task A's lock (irqs on), the rcu callback hook freeing
++dead task A must be run and AppArmor must be changing the profile on
++dead task A.  The kernel should not be taking a dead task's task_lock
++at the same time the task is being freed by task rcu cleanup other wise
++the task would not be out of its quiescent period.
+--- /dev/null
++++ b/security/apparmor/procattr.c
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (C) 1998-2007 Novell/SUSE
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License as
++ *    published by the Free Software Foundation, version 2 of the
++ *    License.
++ *
++ *    AppArmor /proc/pid/attr handling
++ */
++
++#include "apparmor.h"
++#include "inline.h"
++
++int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len)
++{
++      char *str;
++
++      if (profile) {
++              const char *mode_str = PROFILE_COMPLAIN(profile) ?
++                      " (complain)" : " (enforce)";
++              int mode_len, name_len, ns_len = 0;
++
++              mode_len = strlen(mode_str);
++              name_len = strlen(profile->name);
++              if (profile->ns != default_namespace)
++                      ns_len = strlen(profile->ns->name) + 2;
++              *len = mode_len + ns_len + name_len + 1;
++              str = kmalloc(*len, GFP_ATOMIC);
++              if (!str)
++                      return -ENOMEM;
++
++              if (ns_len) {
++                      *str++ = ':';
++                      memcpy(str, profile->ns->name, ns_len - 2);
++                      str += ns_len - 2;
++                      *str++ = ':';
++              }
++              memcpy(str, profile->name, name_len);
++              str += name_len;
++              memcpy(str, mode_str, mode_len);
++              str += mode_len;
++              *str++ = '\n';
++              str -= *len;
++      } else {
++              const char *unconfined_str = "unconfined\n";
++
++              *len = strlen(unconfined_str);
++              str = kmalloc(*len, GFP_ATOMIC);
++              if (!str)
++                      return -ENOMEM;
++
++              memcpy(str, unconfined_str, *len);
++      }
++      *string = str;
++
++      return 0;
++}
++
++static char *split_token_from_name(const char *op, char *args, u64 *cookie)
++{
++      char *name;
++
++      *cookie = simple_strtoull(args, &name, 16);
++      if ((name == args) || *name != '^') {
++              AA_ERROR("%s: Invalid input '%s'", op, args);
++              return ERR_PTR(-EINVAL);
++      }
++
++      name++;  /* skip ^ */
++      if (!*name)
++              name = NULL;
++      return name;
++}
++
++int aa_setprocattr_changehat(char *args)
++{
++      char *hat;
++      u64 cookie;
++
++      hat = split_token_from_name("change_hat", args, &cookie);
++      if (IS_ERR(hat))
++              return PTR_ERR(hat);
++
++      if (!hat && !cookie) {
++              AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic");
++              return -EINVAL;
++      }
++
++      AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
++               __FUNCTION__, cookie, hat ? hat : NULL);
++
++      return aa_change_hat(hat, cookie);
++}
++
++int aa_setprocattr_changeprofile(char *args)
++{
++      char *name = args, *ns_name = NULL;
++
++      if (name[0] == ':') {
++              char *split = strchr(&name[1], ':');
++              if (split) {
++                      *split = 0;
++                      ns_name = &name[1];
++                      name = split + 1;
++              }
++      }
++
++      return aa_change_profile(ns_name, name);
++}
++
++int aa_setprocattr_setprofile(struct task_struct *task, char *args)
++{
++      struct aa_profile *old_profile, *new_profile;
++      struct aa_namespace *ns;
++      struct aa_audit sa;
++      char *name, *ns_name = NULL;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "profile_set";
++      sa.gfp_mask = GFP_KERNEL;
++      sa.task = task->pid;
++
++      AA_DEBUG("%s: current %d\n",
++               __FUNCTION__, current->pid);
++
++      name = args;
++      if (args[0] != '/') {
++              char *split = strchr(args, ':');
++              if (split) {
++                      *split = 0;
++                      ns_name = args;
++                      name = split + 1;
++              }
++      }
++      if (ns_name)
++              ns = aa_find_namespace(ns_name);
++      else
++              ns = aa_get_namespace(default_namespace);
++      if (!ns) {
++              sa.name = ns_name;
++              sa.info = "unknown namespace";
++              aa_audit_reject(NULL, &sa);
++              aa_put_namespace(ns);
++              return -EINVAL;
++      }
++
++repeat:
++      if (strcmp(name, "unconfined") == 0)
++              new_profile = NULL;
++      else {
++              new_profile = aa_find_profile(ns, name);
++              if (!new_profile) {
++                      sa.name = ns_name;
++                      sa.name2 = name;
++                      sa.info = "unknown profile";
++                      aa_audit_reject(NULL, &sa);
++                      aa_put_namespace(ns);
++                      return -EINVAL;
++              }
++      }
++
++      old_profile = __aa_replace_profile(task, new_profile);
++      if (IS_ERR(old_profile)) {
++              int error;
++
++              aa_put_profile(new_profile);
++              error = PTR_ERR(old_profile);
++              if (error == -ESTALE)
++                      goto repeat;
++              aa_put_namespace(ns);
++              return error;
++      }
++
++      if (new_profile) {
++              sa.name = ns_name;
++              sa.name2 = name;
++              sa.name3 = old_profile ? old_profile->name :
++                      "unconfined";
++              aa_audit_status(NULL, &sa);
++      } else {
++              if (old_profile) {
++                      sa.name = "unconfined";
++                      sa.name2 = old_profile->name;
++                      aa_audit_status(NULL, &sa);
++              } else {
++                      sa.info = "task is unconfined";
++                      aa_audit_status(NULL, &sa);
++              }
++      }
++      aa_put_namespace(ns);
++      aa_put_profile(old_profile);
++      aa_put_profile(new_profile);
++      return 0;
++}