]> git.pld-linux.org Git - packages/kernel.git/commitdiff
This commit was manufactured by cvs2git to create branch 'LINUX_2_6'.
authorcvs2git <feedback@pld-linux.org>
Sun, 25 Mar 2007 22:22:00 +0000 (22:22 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Cherrypick from master 2007-03-25 22:22:00 UTC Arkadiusz Miƛkiewicz <arekm@maven.pl> '- new; apparmor for 2.6.20':
    apparmor-2.6.20.3-v405-fullseries.diff -> 1.1

apparmor-2.6.20.3-v405-fullseries.diff [new file with mode: 0644]

diff --git a/apparmor-2.6.20.3-v405-fullseries.diff b/apparmor-2.6.20.3-v405-fullseries.diff
new file mode 100644 (file)
index 0000000..ed62dce
--- /dev/null
@@ -0,0 +1,8464 @@
+ fs/namespace.c                          |    3 
+ include/linux/audit.h                   |    5 
+ include/linux/mnt_namespace.h           |    3 
+ kernel/audit.c                          |    6 
+ security/Kconfig                        |    1 
+ security/Makefile                       |    1 
+ security/apparmor/Kbuild                |   10 
+ security/apparmor/Kconfig               |    9 
+ security/apparmor/Makefile              |   28 
+ security/apparmor/apparmor.h            |  356 +++++
+ security/apparmor/apparmorfs.c          |  432 +++++++
+ security/apparmor/capabilities.c        |   71 +
+ security/apparmor/inline.h              |  393 ++++++
+ security/apparmor/list.c                |  268 ++++
+ security/apparmor/lsm.c                 |  894 ++++++++++++++
+ security/apparmor/main.c                | 1702 ++++++++++++++++++++++++++++
+ security/apparmor/match/Kbuild          |    6 
+ security/apparmor/match/Makefile        |    5 
+ security/apparmor/match/match.h         |  132 ++
+ security/apparmor/match/match_default.c |   57 
+ security/apparmor/match/match_pcre.c    |  169 ++
+ security/apparmor/match/pcre_exec.c     | 1945 ++++++++++++++++++++++++++++++++
+ security/apparmor/match/pcre_exec.h     |  308 +++++
+ security/apparmor/match/pcre_tables.h   |  184 +++
+ security/apparmor/module_interface.c    |  845 +++++++++++++
+ security/apparmor/module_interface.h    |   37 
+ security/apparmor/procattr.c            |  332 +++++
+ security/apparmor/shared.h              |   46 
+ 28 files changed, 8245 insertions(+), 3 deletions(-)
+Index: b/include/linux/audit.h
+===================================================================
+--- a/include/linux/audit.h
++++ b/include/linux/audit.h
+@@ -110,6 +110,8 @@
+ #define AUDIT_LAST_KERN_ANOM_MSG    1799
+ #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
++#define AUDIT_SD              1500    /* AppArmor (SubDomain) audit */
++
+ #define AUDIT_KERNEL          2000    /* Asynchronous audit record. NOT A REQUEST. */
+ /* Rule flags */
+@@ -478,6 +480,9 @@ extern void                    audit_log(struct audit_
+                                     __attribute__((format(printf,4,5)));
+ extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
++extern void               audit_log_vformat(struct audit_buffer *ab,
++                                            const char *fmt, va_list args)
++                          __attribute__((format(printf,2,0)));
+ extern void               audit_log_format(struct audit_buffer *ab,
+                                            const char *fmt, ...)
+                           __attribute__((format(printf,2,3)));
+Index: b/kernel/audit.c
+===================================================================
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -956,8 +956,7 @@ static inline int audit_expand(struct au
+  * will be called a second time.  Currently, we assume that a printk
+  * can't format message larger than 1024 bytes, so we don't either.
+  */
+-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
+-                            va_list args)
++void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
+ {
+       int len, avail;
+       struct sk_buff *skb;
+@@ -1213,3 +1212,6 @@ EXPORT_SYMBOL(audit_log_start);
+ EXPORT_SYMBOL(audit_log_end);
+ EXPORT_SYMBOL(audit_log_format);
+ EXPORT_SYMBOL(audit_log);
++EXPORT_SYMBOL_GPL(audit_log_vformat);
++EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
++EXPORT_SYMBOL_GPL(audit_log_d_path);
+Index: b/fs/namespace.c
+===================================================================
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -37,7 +37,8 @@ static int event;
+ static struct list_head *mount_hashtable __read_mostly;
+ static int hash_mask __read_mostly, hash_bits __read_mostly;
+ static struct kmem_cache *mnt_cache __read_mostly;
+-static struct rw_semaphore namespace_sem;
++struct rw_semaphore namespace_sem;
++EXPORT_SYMBOL_GPL(namespace_sem);
+ /* /sys/fs */
+ decl_subsys(fs, NULL, NULL);
+Index: b/include/linux/mnt_namespace.h
+===================================================================
+--- a/include/linux/mnt_namespace.h
++++ b/include/linux/mnt_namespace.h
+@@ -6,6 +6,9 @@
+ #include <linux/sched.h>
+ #include <linux/nsproxy.h>
++/* exported for AppArmor (SubDomain) */
++extern struct rw_semaphore namespace_sem;
++
+ struct mnt_namespace {
+       atomic_t                count;
+       struct vfsmount *       root;
+Index: b/security/Makefile
+===================================================================
+--- a/security/Makefile
++++ b/security/Makefile
+@@ -4,6 +4,7 @@
+ obj-$(CONFIG_KEYS)                    += keys/
+ subdir-$(CONFIG_SECURITY_SELINUX)     += selinux
++obj-$(CONFIG_SECURITY_APPARMOR)               += commoncap.o apparmor/
+ # if we don't select a security model, use the default capabilities
+ ifneq ($(CONFIG_SECURITY),y)
+Index: b/security/Kconfig
+===================================================================
+--- a/security/Kconfig
++++ b/security/Kconfig
+@@ -94,6 +94,7 @@ config SECURITY_ROOTPLUG
+         If you are unsure how to answer this question, answer N.
+ source security/selinux/Kconfig
++source security/apparmor/Kconfig
+ endmenu
+Index: b/security/apparmor/Kbuild
+===================================================================
+--- /dev/null
++++ b/security/apparmor/Kbuild
+@@ -0,0 +1,10 @@
++# Makefile for AppArmor Linux Security Module
++#
++EXTRA_CFLAGS += -DAPPARMOR_VERSION=\"${APPARMOR_VER}\"
++
++obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
++
++apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o capabilities.o \
++            module_interface.o
++
++obj-$(CONFIG_SECURITY_APPARMOR) += match/
+Index: b/security/apparmor/Kconfig
+===================================================================
+--- /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.
+Index: b/security/apparmor/Makefile
+===================================================================
+--- /dev/null
++++ b/security/apparmor/Makefile
+@@ -0,0 +1,28 @@
++# Makefile for AppArmor Linux Security Module
++#
++# kernel build Makefile is the Kbuild file
++
++REPO_VERSION := $(shell if [ -x /usr/bin/svn ] ; then \
++       /usr/bin/svn info . 2> /dev/null | grep "^Last Changed Rev:" | sed "s/^Last Changed Rev: //" ; \
++       fi)
++
++ifeq ("${REPO_VERSION}", "")
++REPO_VERSION := "unknown"
++endif
++
++KERNELVER := $(shell uname -r)
++
++KERNELDIR := /lib/modules/${KERNELVER}/build
++
++all:
++      $(MAKE) -C $(KERNELDIR) M=`pwd` modules CONFIG_SECURITY_APPARMOR=m \
++                                      APPARMOR_VER=${REPO_VERSION}
++      mv apparmor.ko apparmor-${KERNELVER}.ko
++      mv match/aamatch_pcre.ko aamatch_pcre-${KERNELVER}.ko
++
++clean:
++      rm -f *~ *.o *.ko *.mod.c .*.cmd Module{s,}.symvers \
++              match/*~ match/*.o match/*.ko match/.*.cmd match/*.mod.c
++      rm -rf .tmp_versions
++
++
+Index: b/security/apparmor/apparmor.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/apparmor.h
+@@ -0,0 +1,356 @@
++/*
++ *    Copyright (C) 1998-2005 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/fs.h> /* Include for defn of iattr */
++#include <linux/binfmts.h>    /* defn of linux_binprm */
++#include <linux/rcupdate.h>
++
++#include "shared.h"
++
++/* 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;
++
++/* PIPEFS_MAGIC */
++#include <linux/pipe_fs_i.h>
++/* from net/socket.c */
++#define SOCKFS_MAGIC 0x534F434B
++/* from inotify.c  */
++#define INOTIFYFS_MAGIC 0xBAD1DEA
++
++#define VALID_FSTYPE(inode) ((inode)->i_sb->s_magic != PIPEFS_MAGIC && \
++                             (inode)->i_sb->s_magic != SOCKFS_MAGIC && \
++                             (inode)->i_sb->s_magic != INOTIFYFS_MAGIC)
++
++#define PROFILE_COMPLAIN(_profile) \
++      (apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain))
++
++#define SUBDOMAIN_COMPLAIN(_sd) \
++      (apparmor_complain == 1 || \
++       ((_sd) && (_sd)->active && (_sd)->active->flags.complain))
++
++#define PROFILE_AUDIT(_profile) \
++      (apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit))
++
++#define SUBDOMAIN_AUDIT(_sd) \
++      (apparmor_audit == 1 || \
++       ((_sd) && (_sd)->active && (_sd)->active->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(fmt, args...) printk(KERN_INFO "AppArmor: " fmt, ##args)
++#define AA_WARN(fmt, args...) printk(KERN_WARNING "AppArmor: " fmt, ##args)
++#define AA_ERROR(fmt, args...)        printk(KERN_ERR "AppArmor: " fmt, ##args)
++
++
++/* apparmor logged syscall reject caching */
++enum aasyscall {
++      AA_SYSCALL_PTRACE,
++      AA_SYSCALL_SYSCTL_WRITE,
++      AA_SYSCALL_MOUNT,
++      AA_SYSCALL_UMOUNT
++};
++
++#define AA_SYSCALL_TO_MASK(X) (1 << (X))
++
++
++/* basic AppArmor data structures */
++
++struct flagval {
++      int debug;
++      int complain;
++      int audit;
++};
++
++enum entry_match_type {
++      aa_entry_literal,
++      aa_entry_tailglob,
++      aa_entry_pattern,
++      aa_entry_invalid
++};
++
++/* struct aa_entry - file ACL *
++ * @filename: filename controlled by this ACL
++ * @mode: permissions granted by ACL
++ * @type: type of match to perform against @filename
++ * @extradata: any extra data needed by an extended matching type
++ * @list: list the ACL is on
++ * @listp: permission partitioned lists this ACL is on.
++ *
++ * Each entry describes a file and an allowed access mode.
++ */
++struct aa_entry {
++      char *filename;
++      int mode;               /* mode is 'or' of READ, WRITE, EXECUTE,
++                               * INHERIT, UNCONSTRAINED, and LIBRARY
++                               * (meaning don't prefetch). */
++
++      enum entry_match_type type;
++      void *extradata;
++
++      struct list_head list;
++      struct list_head listp[POS_AA_FILE_MAX + 1];
++};
++
++#define AA_SECURE_EXEC_NEEDED 0x00000001
++
++#define AA_EXEC_MODIFIER_MASK(mask) ((mask) & AA_EXEC_MODIFIERS)
++#define AA_EXEC_MASK(mask) ((mask) & (AA_MAY_EXEC | AA_EXEC_MODIFIERS))
++#define AA_EXEC_UNSAFE_MASK(mask) ((mask) & (AA_MAY_EXEC | AA_EXEC_MODIFIERS |\
++                                           AA_EXEC_UNSAFE))
++
++/* struct aaprofile - basic confinement data
++ * @parent: non refcounted pointer to parent profile
++ * @name: the profiles name
++ * @file_entry: file ACL
++ * @file_entryp: vector of file ACL by permission granted
++ * @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 to indicate the profile is stale
++ * @num_file_entries: number of file entries the profile contains
++ * @num_file_pentries: number of file entries for each partitioned list
++ * @capabilities: capabilities granted by the process
++ * @rcu: rcu head used when freeing the profile
++ * @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. The profiles are
++ * connected in a list
++ */
++struct aaprofile {
++      struct aaprofile *parent;
++      char *name;
++
++      struct list_head file_entry;
++      struct list_head file_entryp[POS_AA_FILE_MAX + 1];
++      struct list_head list;
++      struct list_head sub;
++      struct flagval flags;
++      struct aaprofile *null_profile;
++      int isstale;
++
++      int num_file_entries;
++      int num_file_pentries[POS_AA_FILE_MAX + 1];
++
++      kernel_cap_t capabilities;
++
++      struct rcu_head rcu;
++
++      struct kref count;
++};
++
++enum aafile_type {
++      aa_file_default,
++      aa_file_shmem
++};
++
++/**
++ * aafile - file pointer confinement data
++ *
++ * Data structure assigned to each open file (by apparmor_file_alloc_security)
++ */
++struct aafile {
++      enum aafile_type type;
++      struct aaprofile *profile;
++};
++
++/**
++ * struct subdomain - primary label for confined tasks
++ * @active: the current active profile
++ * @hat_magic: the magic token controling the ability to leave a hat
++ * @list: list this subdomain is on
++ * @task: task that the subdomain confines
++ * @cached_caps: caps that have previously generated log entries
++ * @cached_syscalls: mediated syscalls that have previously been logged
++ *
++ * Contains the tasks current active profile (which could change due to
++ * change_hat).  Plus the hat_magic needed during change_hat.
++ *
++ * N.B AppArmor's previous product name SubDomain was derived from the name
++ * of this structure/concept (changehat reducing a task into a sub-domain).
++ */
++struct subdomain {
++      struct aaprofile *active;       /* The current active profile */
++      u32 hat_magic;                  /* used with change_hat */
++      struct list_head list;          /* list of subdomains */
++      struct task_struct *task;
++
++      kernel_cap_t cached_caps;
++      unsigned int cached_syscalls;
++};
++
++typedef int (*aa_iter) (struct subdomain *, void *);
++
++/* aa_path_data
++ * temp (cookie) data used by aa_path_* functions, see inline.h
++ */
++struct aa_path_data {
++      struct dentry *root, *dentry;
++      struct mnt_namespace *mnt_namespace;
++      struct list_head *head, *pos;
++      int errno;
++};
++
++#define AA_SUBDOMAIN(sec)     ((struct subdomain*)(sec))
++#define AA_PROFILE(sec)               ((struct aaprofile*)(sec))
++
++/* Lock protecting access to 'struct subdomain' accesses */
++extern spinlock_t sd_lock;
++
++extern struct aaprofile *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 *name;
++      unsigned int ival;
++      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 LOG_HINT(p, gfp, hint, fmt, args...) \
++      do {\
++              aa_audit_message(p, gfp, 0, \
++                      "LOGPROF-HINT " hint " " fmt, ##args);\
++      } while(0)
++
++/* directory op type, for aa_perm_dir */
++enum aa_diroptype {
++      aa_dir_mkdir,
++      aa_dir_rmdir
++};
++
++/* xattr op type, for aa_xattr */
++enum aa_xattroptype {
++      aa_xattr_get,
++      aa_xattr_set,
++      aa_xattr_list,
++      aa_xattr_remove
++};
++
++#define BASE_PROFILE(p) ((p)->parent ? (p)->parent : (p))
++#define IN_SUBPROFILE(p) ((p)->parent)
++
++/* main.c */
++extern int alloc_null_complain_profile(void);
++extern void free_null_complain_profile(void);
++extern int attach_nullprofile(struct aaprofile *profile);
++extern int aa_audit_message(struct aaprofile *active, gfp_t gfp, int,
++                          const char *, ...);
++extern int aa_audit_syscallreject(struct aaprofile *active, gfp_t gfp,
++                                enum aasyscall call);
++extern int aa_audit(struct aaprofile *active, const struct aa_audit *);
++extern char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt);
++
++extern int aa_attr(struct aaprofile *active, struct dentry *dentry,
++                 struct iattr *iattr);
++extern int aa_xattr(struct aaprofile *active, struct dentry *dentry,
++                  const char *xattr, enum aa_xattroptype xattroptype);
++extern int aa_capability(struct aaprofile *active, int cap);
++extern int aa_perm(struct aaprofile *active, struct dentry *dentry,
++                 struct vfsmount *mnt, int mask);
++extern int aa_perm_nameidata(struct aaprofile *active, struct nameidata *nd,
++                           int mask);
++extern int aa_perm_dentry(struct aaprofile *active, struct dentry *dentry,
++                        int mask);
++extern int aa_perm_dir(struct aaprofile *active, struct dentry *dentry,
++                     enum aa_diroptype diroptype);
++extern int aa_link(struct aaprofile *active,
++                 struct dentry *link, struct dentry *target);
++extern int aa_fork(struct task_struct *p);
++extern int aa_register(struct linux_binprm *bprm);
++extern void aa_release(struct task_struct *p);
++extern int aa_change_hat(const char *id, u32 hat_magic);
++extern int aa_associate_filp(struct file *filp);
++
++/* list.c */
++extern struct aaprofile *aa_profilelist_find(const char *name);
++extern int aa_profilelist_add(struct aaprofile *profile);
++extern struct aaprofile *aa_profilelist_remove(const char *name);
++extern void aa_profilelist_release(void);
++extern struct aaprofile *aa_profilelist_replace(struct aaprofile *profile);
++extern void aa_profile_dump(struct aaprofile *);
++extern void aa_profilelist_dump(void);
++extern void aa_subdomainlist_add(struct subdomain *);
++extern void aa_subdomainlist_remove(struct subdomain *);
++extern void aa_subdomainlist_iterate(aa_iter, void *);
++extern void aa_subdomainlist_iterateremove(aa_iter, void *);
++extern void aa_subdomainlist_release(void);
++
++/* module_interface.c */
++extern ssize_t aa_file_prof_add(void *, size_t);
++extern ssize_t aa_file_prof_repl(void *, size_t);
++extern ssize_t aa_file_prof_remove(const char *, size_t);
++extern void free_aaprofile(struct aaprofile *profile);
++extern void free_aaprofile_kref(struct kref *kref);
++
++/* procattr.c */
++extern size_t aa_getprocattr(struct aaprofile *active, char *str, size_t size);
++extern int aa_setprocattr_changehat(char *hatinfo, size_t infosize);
++extern int aa_setprocattr_setprofile(struct task_struct *p, char *profilename,
++                                   size_t profilesize);
++
++/* apparmorfs.c */
++extern int create_apparmorfs(void);
++extern void destroy_apparmorfs(void);
++
++/* capabilities.c */
++extern const char *capability_to_name(unsigned int cap);
++extern const char *syscall_to_name(enum aasyscall call);
++
++#endif                                /* __APPARMOR_H */
+Index: b/security/apparmor/apparmorfs.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/apparmorfs.c
+@@ -0,0 +1,432 @@
++/*
++ *    Copyright (C) 2005 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"
++#include "match/match.h"
++
++#define SECFS_AA "apparmor"
++static struct dentry *aafs_dentry = NULL;
++
++/* profile */
++extern struct seq_operations apparmorfs_profiles_op;
++static int aa_prof_open(struct inode *inode, struct file *file);
++static int aa_prof_release(struct inode *inode, struct file *file);
++
++static struct file_operations apparmorfs_profiles_fops = {
++      .open =         aa_prof_open,
++      .read =         seq_read,
++      .llseek =       seq_lseek,
++      .release =      aa_prof_release,
++};
++
++/* matching */
++static ssize_t aa_matching_read(struct file *file, char __user *buf,
++                             size_t size, loff_t *ppos);
++
++static struct file_operations apparmorfs_matching_fops = {
++      .read =         aa_matching_read,
++};
++
++
++/* interface */
++static ssize_t aa_profile_load(struct file *f, const char __user *buf,
++                             size_t size, loff_t *pos);
++static ssize_t aa_profile_replace(struct file *f, const char __user *buf,
++                                size_t size, loff_t *pos);
++static ssize_t aa_profile_remove(struct file *f, const char __user *buf,
++                               size_t size, loff_t *pos);
++
++static struct file_operations apparmorfs_profile_load = {
++      .write = aa_profile_load
++};
++
++static struct file_operations apparmorfs_profile_replace = {
++      .write = aa_profile_replace
++};
++
++static struct file_operations apparmorfs_profile_remove = {
++      .write = aa_profile_remove
++};
++
++
++/* control */
++static u64 aa_control_get(void *data);
++static void aa_control_set(void *data, u64 val);
++
++DEFINE_SIMPLE_ATTRIBUTE(apparmorfs_control_fops, aa_control_get,
++                      aa_control_set, "%lld\n");
++
++
++
++/* table of static entries */
++
++static struct root_entry {
++      const char *name;
++      int mode;
++      int access;
++      struct file_operations *fops;
++      void *data;
++
++      /* internal fields */
++      struct dentry *dentry;
++      int parent_index;
++} root_entries[] = {
++      /* our root, normally /sys/kernel/security/apparmor */
++      {SECFS_AA,      S_IFDIR, 0550}, /* DO NOT EDIT/MOVE */
++
++      /* interface for obtaining list of profiles currently loaded */
++      {"profiles",    S_IFREG, 0440, &apparmorfs_profiles_fops,
++                                     NULL},
++
++      /* interface for obtaining matching features supported */
++      {"matching",    S_IFREG, 0440, &apparmorfs_matching_fops,
++                                     NULL},
++
++      /* interface for loading/removing/replacing profiles */
++      {".load",       S_IFREG, 0640, &apparmorfs_profile_load,
++                                     NULL},
++      {".replace",    S_IFREG, 0640, &apparmorfs_profile_replace,
++                                     NULL},
++      {".remove",     S_IFREG, 0640, &apparmorfs_profile_remove,
++                                     NULL},
++
++      /* interface for setting binary config values */
++      {"control",     S_IFDIR, 0550},
++      {"complain",    S_IFREG, 0640, &apparmorfs_control_fops,
++                                     &apparmor_complain},
++      {"audit",       S_IFREG, 0640, &apparmorfs_control_fops,
++                                     &apparmor_audit},
++      {"debug",       S_IFREG, 0640, &apparmorfs_control_fops,
++                                     &apparmor_debug},
++      {"logsyscall",  S_IFREG, 0640, &apparmorfs_control_fops,
++                                     &apparmor_logsyscall},
++      {NULL,          S_IFDIR, 0},
++
++      /* root end */
++      {NULL,          S_IFDIR, 0}
++};
++
++#define AAFS_DENTRY root_entries[0].dentry
++
++static const unsigned int num_entries =
++      sizeof(root_entries) / sizeof(struct root_entry);
++
++
++
++static int aa_prof_open(struct inode *inode, struct file *file)
++{
++      return seq_open(file, &apparmorfs_profiles_op);
++}
++
++
++static int aa_prof_release(struct inode *inode, struct file *file)
++{
++      return seq_release(inode, file);
++}
++
++static ssize_t aa_matching_read(struct file *file, char __user *buf,
++                             size_t size, loff_t *ppos)
++{
++      const char *matching = aamatch_features();
++
++      return simple_read_from_buffer(buf, size, ppos, matching,
++                                     strlen(matching));
++}
++
++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 aaprofile *active;
++      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.
++       */
++      rcu_read_lock();
++      active = get_activeptr_rcu();
++      if (active) {
++              AA_WARN("REJECTING access to profile %s (%s(%d) "
++                      "profile %s active %s)\n",
++                      msg, current->comm, current->pid,
++                      BASE_PROFILE(active)->name, active->name);
++
++              data = ERR_PTR(-EPERM);
++              goto out;
++      }
++      rcu_read_unlock();
++
++      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;
++}
++
++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");
++
++      if (!IS_ERR(data)) {
++              error = aa_file_prof_add(data, size);
++              vfree(data);
++      } else {
++              error = PTR_ERR(data);
++      }
++
++      return error;
++}
++
++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");
++
++      if (!IS_ERR(data)) {
++              error = aa_file_prof_repl(data, size);
++              vfree(data);
++      } else {
++              error = PTR_ERR(data);
++      }
++
++      return error;
++}
++
++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_file_prof_remove needs a null terminated string so 1 extra
++       * byte is allocated and null the copied data is then null terminated
++       */
++      data = aa_simple_write_to_buffer(buf, size+1, size, pos, "removal");
++
++      if (!IS_ERR(data)) {
++              data[size] = 0;
++              error = aa_file_prof_remove(data, size);
++              vfree(data);
++      } else {
++              error = PTR_ERR(data);
++      }
++
++      return error;
++}
++
++static u64 aa_control_get(void *data)
++{
++      return *(int *)data;
++}
++
++static void aa_control_set(void *data, u64 val)
++{
++      if (val > 1)
++              val = 1;
++
++      *(int*)data = (int)val;
++}
++
++static void clear_apparmorfs(void)
++{
++      unsigned int i;
++
++      for (i=0; i < num_entries;i++) {
++              unsigned int index;
++
++              if (root_entries[i].mode == S_IFDIR) {
++                      if (root_entries[i].name)
++                              /* defer dir free till all sub-entries freed */
++                              continue;
++                      else
++                              /* cleanup parent */
++                              index = root_entries[i].parent_index;
++              } else {
++                      index = i;
++              }
++
++              if (root_entries[index].dentry) {
++                      securityfs_remove(root_entries[index].dentry);
++
++                      AA_DEBUG("%s: deleted apparmorfs entry name=%s "
++                               "dentry=%p\n",
++                              __FUNCTION__,
++                              root_entries[index].name,
++                              root_entries[index].dentry);
++
++                      root_entries[index].dentry = NULL;
++                      root_entries[index].parent_index = 0;
++              }
++      }
++}
++
++static int populate_apparmorfs(struct dentry *root)
++{
++      unsigned int i, parent_index, depth;
++
++      for (i = 0; i < num_entries; i++) {
++              root_entries[i].dentry = NULL;
++              root_entries[i].parent_index = 0;
++      }
++
++      /* 1. Verify entry 0 is valid [sanity check] */
++      if (num_entries == 0 ||
++          !root_entries[0].name ||
++          strcmp(root_entries[0].name, SECFS_AA) != 0 ||
++          root_entries[0].mode != S_IFDIR) {
++              AA_ERROR("%s: root entry 0 is not SECFS_AA/dir\n",
++                      __FUNCTION__);
++              goto error;
++      }
++
++      /* 2. Build back pointers */
++      parent_index = 0;
++      depth = 1;
++
++      for (i = 1; i < num_entries; i++) {
++              root_entries[i].parent_index = parent_index;
++
++              if (root_entries[i].name &&
++                  root_entries[i].mode == S_IFDIR) {
++                      depth++;
++                      parent_index = i;
++              } else if (!root_entries[i].name) {
++                      if (root_entries[i].mode != S_IFDIR || depth == 0) {
++                              AA_ERROR("%s: root_entry %d invalid (%u %d)",
++                                       __FUNCTION__, i,
++                                       root_entries[i].mode,
++                                       root_entries[i].parent_index);
++                              goto error;
++                      }
++
++                      depth--;
++                      parent_index = root_entries[parent_index].parent_index;
++              }
++      }
++
++      if (depth != 0) {
++              AA_ERROR("%s: root_entry table not correctly terminated\n",
++                      __FUNCTION__);
++              goto error;
++      }
++
++      /* 3. Create root (parent=NULL) */
++      root_entries[0].dentry = securityfs_create_file(
++                                      root_entries[0].name,
++                                      root_entries[0].mode |
++                                              root_entries[0].access,
++                                      NULL, NULL, NULL);
++
++      if (IS_ERR(root_entries[0].dentry))
++              goto error;
++      else
++              AA_DEBUG("%s: created securityfs/apparmor [dentry=%p]\n",
++                      __FUNCTION__, root_entries[0].dentry);
++
++
++      /* 4. create remaining nodes */
++      for (i = 1; i < num_entries; i++) {
++              struct dentry *parent;
++              void *data = NULL;
++              struct file_operations *fops = NULL;
++
++              /* end of directory ? */
++              if (!root_entries[i].name)
++                      continue;
++
++              parent = root_entries[root_entries[i].parent_index].dentry;
++
++              if (root_entries[i].mode != S_IFDIR) {
++                      data = root_entries[i].data;
++                      fops = root_entries[i].fops;
++              }
++
++              root_entries[i].dentry = securityfs_create_file(
++                                              root_entries[i].name,
++                                              root_entries[i].mode |
++                                                      root_entries[i].access,
++                                              parent,
++                                              data,
++                                              fops);
++
++              if (IS_ERR(root_entries[i].dentry))
++                      goto cleanup_error;
++
++              AA_DEBUG("%s: added apparmorfs entry "
++                       "name=%s mode=%x dentry=%p [parent %p]\n",
++                      __FUNCTION__, root_entries[i].name,
++                      root_entries[i].mode|root_entries[i].access,
++                      root_entries[i].dentry, parent);
++      }
++
++      return 0;
++
++cleanup_error:
++      clear_apparmorfs();
++
++error:
++      return -EINVAL;
++}
++
++int create_apparmorfs(void)
++{
++      int error = 0;
++
++      if (AAFS_DENTRY) {
++              error = -EEXIST;
++              AA_ERROR("%s: AppArmor securityfs already exists\n",
++                      __FUNCTION__);
++      } else {
++              error = populate_apparmorfs(aafs_dentry);
++              if (error != 0) {
++                      AA_ERROR("%s: Error populating AppArmor securityfs\n",
++                              __FUNCTION__);
++              }
++      }
++
++      return error;
++}
++
++void destroy_apparmorfs(void)
++{
++      if (AAFS_DENTRY)
++              clear_apparmorfs();
++}
+Index: b/security/apparmor/capabilities.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/capabilities.c
+@@ -0,0 +1,71 @@
++/*
++ *    Copyright (C) 2005 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 capability definitions
++ */
++
++#include "apparmor.h"
++
++static const char *cap_names[] = {
++      "chown",
++      "dac_override",
++      "dac_read_search",
++      "fowner",
++      "fsetid",
++      "kill",
++      "setgid",
++      "setuid",
++      "setpcap",
++      "linux_immutable",
++      "net_bind_service",
++      "net_broadcast",
++      "net_admin",
++      "net_raw",
++      "ipc_lock",
++      "ipc_owner",
++      "sys_module",
++      "sys_rawio",
++      "sys_chroot",
++      "sys_ptrace",
++      "sys_pacct",
++      "sys_admin",
++      "sys_boot",
++      "sys_nice",
++      "sys_resource",
++      "sys_time",
++      "sys_tty_config",
++      "mknod",
++      "lease",
++      "audit_write",
++      "audit_control"
++};
++
++const char *capability_to_name(unsigned int cap)
++{
++      const char *name;
++
++      name = (cap < (sizeof(cap_names) / sizeof(char *))
++                 ? cap_names[cap] : "invalid-capability");
++
++      return name;
++}
++
++static const char *syscall_names[] = {
++      "ptrace",
++      "sysctl (write)",
++      "mount",
++      "umount"
++};
++
++const char *syscall_to_name(enum aasyscall call)
++{
++      const char *name;
++      name = (call < (sizeof(syscall_names) / sizeof(char *))
++              ? syscall_names[call] : "invalid-syscall");
++      return name;
++}
+Index: b/security/apparmor/inline.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/inline.h
+@@ -0,0 +1,393 @@
++/*
++ *    Copyright (C) 2005 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/mnt_namespace.h>
++
++static inline int __aa_is_confined(struct subdomain *sd)
++{
++      return (sd && sd->active);
++}
++
++/**
++ *  aa_is_confined
++ *  Determine whether current task contains a valid profile (confined).
++ *  Return %1 if confined, %0 otherwise.
++ */
++static inline int aa_is_confined(void)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      return __aa_is_confined(sd);
++}
++
++static inline int __aa_sub_defined(struct subdomain *sd)
++{
++    return __aa_is_confined(sd) && !list_empty(&BASE_PROFILE(sd->active)->sub);
++}
++
++/**
++ * aa_sub_defined - check to see if current task has any subprofiles
++ * Return 1 if true, 0 otherwise
++ */
++static inline int aa_sub_defined(void)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      return __aa_sub_defined(sd);
++}
++
++/**
++ * get_aaprofile - increment refcount on profile @p
++ * @p: profile
++ */
++static inline struct aaprofile *get_aaprofile(struct aaprofile *p)
++{
++      if (p)
++              kref_get(&(BASE_PROFILE(p)->count));
++
++      return p;
++}
++
++/**
++ * put_aaprofile - decrement refcount on profile @p
++ * @p: profile
++ */
++static inline void put_aaprofile(struct aaprofile *p)
++{
++      if (p)
++              kref_put(&BASE_PROFILE(p)->count, free_aaprofile_kref);
++}
++
++/**
++ * get_task_activeptr_rcu - get pointer to @tsk's active profile.
++ * @tsk: task to get active profile from
++ *
++ * Requires rcu_read_lock is held
++ */
++static inline struct aaprofile *get_task_activeptr_rcu(struct task_struct *tsk)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(tsk->security);
++      struct aaprofile *active = NULL;
++
++      if (sd)
++              active = (struct aaprofile *) rcu_dereference(sd->active);
++
++      return active;
++}
++
++/**
++ * get_activeptr_rcu - get pointer to current task's active profile
++ * Requires rcu_read_lock is held
++ */
++static inline struct aaprofile *get_activeptr_rcu(void)
++{
++      return get_task_activeptr_rcu(current);
++}
++
++/**
++ * get_task_active_aaprofile - get a reference to tsk's active profile.
++ * @tsk: the task to get the active profile reference for
++ */
++static inline struct aaprofile *get_task_active_aaprofile(struct task_struct *tsk)
++{
++      struct aaprofile *active;
++
++      rcu_read_lock();
++      active = get_aaprofile(get_task_activeptr_rcu(tsk));
++      rcu_read_unlock();
++
++      return active;
++}
++
++/**
++ * get_active_aaprofile - get a reference to the current tasks active profile
++ */
++static inline struct aaprofile *get_active_aaprofile(void)
++{
++      return get_task_active_aaprofile(current);
++}
++
++/**
++ * cap_is_cached - check if @cap access has already been logged for current
++ * @cap: capability to test if cached
++ */
++static inline int cap_is_cached(int cap)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      return cap_raised(sd->cached_caps, cap);
++}
++
++/**
++ * add_to_cached_caps - add a capability to the tasks logged capabilities cache
++ * @cap: the capability to add
++ */
++static inline void add_to_cached_caps(int cap)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      sd->cached_caps = cap_combine(sd->cached_caps, CAP_TO_MASK(cap));
++}
++
++/**
++ * clear_cached_caps - clear the tasks logged capabilities cache
++ */
++static inline void clear_cached_caps(struct subdomain *sd)
++{
++      sd->cached_caps = CAP_EMPTY_SET;
++}
++
++/**
++ * syscall_is_cached - check if @call access has already been logged
++ * @call: syscall to test if cached
++ */
++static inline int syscall_is_cached(enum aasyscall call)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      return sd->cached_syscalls & AA_SYSCALL_TO_MASK(call);
++}
++
++/**
++ * add_to_cached_syscalls - add a syscall to the tasks logged syscalls cache
++ * @call: the syscall to add
++ */
++static inline void add_to_cached_syscalls(enum aasyscall call)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      sd->cached_syscalls |= AA_SYSCALL_TO_MASK(call);
++}
++
++/**
++ * clear_cached_syscalls - clear the tasks logged syscalls cache
++ */
++static inline void clear_cached_syscalls(struct subdomain *sd)
++{
++      sd->cached_syscalls = 0;
++}
++
++/**
++ * aa_switch - change subdomain to use a new profile
++ * @sd: subdomain to switch the active profile on
++ * @newactive: new active profile
++ *
++ * aa_switch handles the changing of a subdomain's active profile.  The
++ * sd_lock must be held to ensure consistency against other writers.
++ * Some write paths (ex. aa_register) require sd->active not to change
++ * over several operations, so the calling function is responsible
++ * for grabing the sd_lock to meet its consistency constraints before
++ * calling aa_switch
++ */
++static inline void aa_switch(struct subdomain *sd, struct aaprofile *newactive)
++{
++      struct aaprofile *oldactive = sd->active;
++
++      /* noop if NULL */
++      rcu_assign_pointer(sd->active, get_aaprofile(newactive));
++      clear_cached_caps(sd);
++      clear_cached_syscalls(sd);
++      put_aaprofile(oldactive);
++}
++
++/**
++ * aa_switch_unconfined - change subdomain to be unconfined (no profile)
++ * @sd: subdomain to switch
++ *
++ * aa_switch_unconfined handles the removal of a subdomain's active profile.
++ * The sd_lock must be held to ensure consistency against other writers.
++ * Like aa_switch the sd_lock is used to maintain consistency.
++ */
++static inline void aa_switch_unconfined(struct subdomain *sd)
++{
++      aa_switch(sd, NULL);
++
++      /* reset magic in case we were in a subhat before */
++      sd->hat_magic = 0;
++}
++
++/**
++ * alloc_subdomain - allocate a new subdomain
++ * @tsk: task struct
++ *
++ * Allocate a new subdomain including a backpointer to it's referring task.
++ */
++static inline struct subdomain *alloc_subdomain(struct task_struct *tsk)
++{
++      struct subdomain *sd;
++
++      sd = kzalloc(sizeof(struct subdomain), GFP_KERNEL);
++      if (!sd)
++              goto out;
++
++      /* back pointer to task */
++      sd->task = tsk;
++
++      /* any readers of the list must make sure that they can handle
++       * case where sd->active is not yet set (null)
++       */
++      aa_subdomainlist_add(sd);
++
++out:
++      return sd;
++}
++
++/**
++ * free_subdomain - Free a subdomain previously allocated by alloc_subdomain
++ * @sd: subdomain
++ */
++static inline void free_subdomain(struct subdomain *sd)
++{
++      aa_subdomainlist_remove(sd);
++      kfree(sd);
++}
++
++/**
++ * alloc_aaprofile - Allocate, initialize and return a new zeroed profile.
++ * Returns NULL on failure.
++ */
++static inline struct aaprofile *alloc_aaprofile(void)
++{
++      struct aaprofile *profile;
++
++      profile = (struct aaprofile *)kzalloc(sizeof(struct aaprofile),
++                                            GFP_KERNEL);
++      AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
++      if (profile) {
++              int i;
++
++              INIT_LIST_HEAD(&profile->list);
++              INIT_LIST_HEAD(&profile->sub);
++              INIT_LIST_HEAD(&profile->file_entry);
++              for (i = 0; i <= POS_AA_FILE_MAX; i++) {
++                      INIT_LIST_HEAD(&profile->file_entryp[i]);
++              }
++              INIT_RCU_HEAD(&profile->rcu);
++              kref_init(&profile->count);
++      }
++      return profile;
++}
++
++/**
++ * aa_put_name
++ * @name: name to release.
++ *
++ * Release space (free_page) allocated to hold pathname
++ * name may be NULL (checked for by free_page)
++ */
++static inline void aa_put_name(const char *name)
++{
++      free_page((unsigned long)name);
++}
++
++/** __aa_find_profile
++ * @name: name of profile to find
++ * @head: list to search
++ *
++ * Return reference counted copy of profile. NULL if not found
++ * Caller must hold any necessary locks
++ */
++static inline struct aaprofile *__aa_find_profile(const char *name,
++                                                struct list_head *head)
++{
++      struct aaprofile *p;
++
++      if (!name || !head)
++              return NULL;
++
++      AA_DEBUG("%s: finding profile %s\n", __FUNCTION__, name);
++      list_for_each_entry(p, head, list) {
++              if (!strcmp(p->name, name)) {
++                      /* return refcounted object */
++                      p = get_aaprofile(p);
++                      return p;
++              } else {
++                      AA_DEBUG("%s: skipping %s\n", __FUNCTION__, p->name);
++              }
++      }
++      return NULL;
++}
++
++/** __aa_path_begin
++ * @rdentry: filesystem root dentry (searching for vfsmnts matching this)
++ * @dentry: dentry object to obtain pathname from (relative to matched vfsmnt)
++ *
++ * Setup data for iterating over vfsmounts (in current tasks namespace).
++ */
++static inline void __aa_path_begin(struct dentry *rdentry,
++                                 struct dentry *dentry,
++                                 struct aa_path_data *data)
++{
++      data->dentry = dentry;
++      data->root = dget(rdentry->d_sb->s_root);
++      data->mnt_namespace = current->nsproxy->mnt_ns;
++      data->head = &data->mnt_namespace->list;
++      data->pos = data->head->next;
++      prefetch(data->pos->next);
++      data->errno = 0;
++
++      down_read(&namespace_sem);
++}
++
++/** aa_path_begin
++ * @dentry: filesystem root dentry and object to obtain pathname from
++ *
++ * Utility function for calling _aa_path_begin for when the dentry we are
++ * looking for and the root are the same (this is the usual case).
++ */
++static inline void aa_path_begin(struct dentry *dentry,
++                                   struct aa_path_data *data)
++{
++      __aa_path_begin(dentry, dentry, data);
++}
++
++/** aa_path_end
++ * @data: data object previously initialized by aa_path_begin
++ *
++ * End iterating over vfsmounts.
++ * If an error occured in begin or get, it is returned. Otherwise 0.
++ */
++static inline int aa_path_end(struct aa_path_data *data)
++{
++      up_read(&namespace_sem);
++      dput(data->root);
++
++      return data->errno;
++}
++
++/** aa_path_getname
++ * @data: data object previously initialized by aa_path_begin
++ *
++ * Return the next mountpoint which has the same root dentry as data->root.
++ * If no more mount points exist (or in case of error) NULL is returned
++ * (caller should call aa_path_end() and inspect return code to differentiate)
++ */
++static inline char *aa_path_getname(struct aa_path_data *data)
++{
++      char *name = NULL;
++      struct vfsmount *mnt;
++
++      while (data->pos != data->head) {
++              mnt = list_entry(data->pos, struct vfsmount, mnt_list);
++
++              /* advance to next -- so that it is done before we break */
++              data->pos = data->pos->next;
++              prefetch(data->pos->next);
++
++              if (mnt->mnt_root == data->root) {
++                      name = aa_get_name(data->dentry, mnt);
++                      if (IS_ERR(name)) {
++                              data->errno = PTR_ERR(name);
++                              name = NULL;
++                      }
++                      break;
++              }
++      }
++
++      return name;
++}
++
++#endif /* __INLINE_H__ */
+Index: b/security/apparmor/list.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/list.c
+@@ -0,0 +1,268 @@
++/*
++ *    Copyright (C) 1998-2005 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 */
++static LIST_HEAD(profile_list);
++static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
++
++/* list of all subdomains and lock */
++static LIST_HEAD(subdomain_list);
++static rwlock_t subdomain_lock = RW_LOCK_UNLOCKED;
++
++/**
++ * aa_profilelist_find
++ * @name: profile name (program name)
++ *
++ * Search the profile list for profile @name.  Return refcounted profile on
++ * success, NULL on failure.
++ */
++struct aaprofile *aa_profilelist_find(const char *name)
++{
++      struct aaprofile *p = NULL;
++      if (name) {
++              read_lock(&profile_lock);
++              p = __aa_find_profile(name, &profile_list);
++              read_unlock(&profile_lock);
++      }
++      return p;
++}
++
++/**
++ * aa_profilelist_add - add new profile to list
++ * @profile: new profile to add to list
++ *
++ * NOTE: Caller must allocate necessary reference count that will be used
++ * by the profile_list.  This is because profile allocation alloc_aaprofile()
++ * returns an unreferenced object with a initial count of %1.
++ *
++ * Return %1 on success, %0 on failure (already exists)
++ */
++int aa_profilelist_add(struct aaprofile *profile)
++{
++      struct aaprofile *old_profile;
++      int ret = 0;
++
++      if (!profile)
++              goto out;
++
++      write_lock(&profile_lock);
++      old_profile = __aa_find_profile(profile->name, &profile_list);
++      if (old_profile) {
++              put_aaprofile(old_profile);
++              goto out;
++      }
++
++      list_add(&profile->list, &profile_list);
++      ret = 1;
++ out:
++      write_unlock(&profile_lock);
++      return ret;
++}
++
++/**
++ * aa_profilelist_remove - remove a profile from the list by name
++ * @name: name of profile to be removed
++ *
++ * If the profile exists remove profile from list and return its reference.
++ * The reference count on profile is not decremented and should be decremented
++ * when the profile is no longer needed
++ */
++struct aaprofile *aa_profilelist_remove(const char *name)
++{
++      struct aaprofile *profile = NULL;
++      struct aaprofile *p, *tmp;
++
++      if (!name)
++              goto out;
++
++      write_lock(&profile_lock);
++      list_for_each_entry_safe(p, tmp, &profile_list, list) {
++              if (!strcmp(p->name, name)) {
++                      list_del_init(&p->list);
++                      /* mark old profile as stale */
++                      p->isstale = 1;
++                      profile = p;
++                      break;
++              }
++      }
++      write_unlock(&profile_lock);
++
++out:
++      return profile;
++}
++
++/**
++ * aa_profilelist_replace - replace a profile on the list
++ * @profile: new profile
++ *
++ * Replace a profile on the profile list.  Find the old profile by name in
++ * the list, and replace it with the new profile.   NOTE: Caller must allocate
++ * necessary initial reference count for new profile as aa_profilelist_add().
++ *
++ * This is an atomic list operation.  Returns the old profile (which is still
++ * refcounted) if there was one, or NULL.
++ */
++struct aaprofile *aa_profilelist_replace(struct aaprofile *profile)
++{
++      struct aaprofile *oldprofile;
++
++      write_lock(&profile_lock);
++      oldprofile = __aa_find_profile(profile->name, &profile_list);
++      if (oldprofile) {
++              list_del_init(&oldprofile->list);
++              /* mark old profile as stale */
++              oldprofile->isstale = 1;
++
++              /* __aa_find_profile incremented count, so adjust down */
++              put_aaprofile(oldprofile);
++      }
++
++      list_add(&profile->list, &profile_list);
++      write_unlock(&profile_lock);
++
++      return oldprofile;
++}
++
++/**
++ * aa_profilelist_release - Remove all profiles from profile_list
++ */
++void aa_profilelist_release(void)
++{
++      struct aaprofile *p, *tmp;
++
++      write_lock(&profile_lock);
++      list_for_each_entry_safe(p, tmp, &profile_list, list) {
++              list_del_init(&p->list);
++              put_aaprofile(p);
++      }
++      write_unlock(&profile_lock);
++}
++
++/**
++ * aa_subdomainlist_add - Add subdomain to subdomain_list
++ * @sd: new subdomain
++ */
++void aa_subdomainlist_add(struct subdomain *sd)
++{
++      unsigned long flags;
++
++      if (!sd) {
++              AA_INFO("%s: bad subdomain\n", __FUNCTION__);
++              return;
++      }
++
++      write_lock_irqsave(&subdomain_lock, flags);
++      /* new subdomains must be added to the end of the list due to a
++       * subtle interaction between fork and profile replacement.
++       */
++      list_add_tail(&sd->list, &subdomain_list);
++      write_unlock_irqrestore(&subdomain_lock, flags);
++}
++
++/**
++ * aa_subdomainlist_remove - Remove subdomain from subdomain_list
++ * @sd: subdomain to be removed
++ */
++void aa_subdomainlist_remove(struct subdomain *sd)
++{
++      unsigned long flags;
++
++      if (sd) {
++              write_lock_irqsave(&subdomain_lock, flags);
++              list_del_init(&sd->list);
++              write_unlock_irqrestore(&subdomain_lock, flags);
++      }
++}
++
++/**
++ * aa_subdomainlist_iterate - iterate over the subdomain list applying @func
++ * @func: method to be called for each element
++ * @cookie: user passed data
++ *
++ * Iterate over subdomain list applying @func, stop when @func returns
++ * non zero
++ */
++void aa_subdomainlist_iterate(aa_iter func, void *cookie)
++{
++      struct subdomain *node;
++      int ret = 0;
++      unsigned long flags;
++
++      read_lock_irqsave(&subdomain_lock, flags);
++      list_for_each_entry(node, &subdomain_list, list) {
++              ret = (*func) (node, cookie);
++              if (ret != 0)
++                      break;
++      }
++      read_unlock_irqrestore(&subdomain_lock, flags);
++}
++
++/**
++ * aa_subdomainlist_release - Remove all subdomains from subdomain_list
++ */
++void aa_subdomainlist_release(void)
++{
++      struct subdomain *node, *tmp;
++      unsigned long flags;
++
++      write_lock_irqsave(&subdomain_lock, flags);
++      list_for_each_entry_safe(node, tmp, &subdomain_list, list) {
++              list_del_init(&node->list);
++      }
++      write_unlock_irqrestore(&subdomain_lock, flags);
++}
++
++/* seq_file helper routines
++ * Used by apparmorfs.c to iterate over profile_list
++ */
++static void *p_start(struct seq_file *f, loff_t *pos)
++{
++      struct aaprofile *node;
++      loff_t l = *pos;
++
++      read_lock(&profile_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 aaprofile *)p)->list.next;
++      (*pos)++;
++      return lh == &profile_list ?
++                      NULL : list_entry(lh, struct aaprofile, list);
++}
++
++static void p_stop(struct seq_file *f, void *v)
++{
++      read_unlock(&profile_lock);
++}
++
++static int seq_show_profile(struct seq_file *f, void *v)
++{
++      struct aaprofile *profile = (struct aaprofile *)v;
++      seq_printf(f, "%s (%s)\n", profile->name,
++                 PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
++      return 0;
++}
++
++struct seq_operations apparmorfs_profiles_op = {
++      .start =        p_start,
++      .next =         p_next,
++      .stop =         p_stop,
++      .show =         seq_show_profile,
++};
+Index: b/security/apparmor/lsm.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/lsm.c
+@@ -0,0 +1,894 @@
++/*
++ *    Copyright (C) 2002-2005 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.
++ *
++ *    http://forge.novell.com/modules/xfmod/project/?apparmor
++ *
++ *    Immunix AppArmor LSM interface
++ */
++
++#include <linux/security.h>
++#include <linux/module.h>
++#include <linux/mm.h>
++#include <linux/mman.h>
++
++#include "apparmor.h"
++#include "inline.h"
++
++/* struct subdomain write update lock (read side is RCU). */
++spinlock_t sd_lock = SPIN_LOCK_UNLOCKED;
++
++/* Flag values, also controllable via apparmorfs/control.
++ * We explicitly do not allow these to be modifiable when exported via
++ * /sys/modules/parameters, as we want to do additional mediation and
++ * don't want to add special path code. */
++
++/* Complain mode -- in complain mode access failures result in auditing only
++ * and task is allowed access.  audit events are processed by userspace to
++ * generate policy.  Default is 'enforce' (0).
++ * Value is also togglable per profile and referenced when global value is
++ * enforce.
++ */
++int apparmor_complain = 0;
++module_param_named(complain, apparmor_complain, int, S_IRUSR);
++MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode");
++
++/* Debug mode */
++int apparmor_debug = 0;
++module_param_named(debug, apparmor_debug, int, S_IRUSR);
++MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode");
++
++/* Audit mode */
++int apparmor_audit = 0;
++module_param_named(audit, apparmor_audit, int, S_IRUSR);
++MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode");
++
++/* Syscall logging mode */
++int apparmor_logsyscall = 0;
++module_param_named(logsyscall, apparmor_logsyscall, int, S_IRUSR);
++MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode");
++
++#ifndef MODULE
++static int __init aa_getopt_complain(char *str)
++{
++      get_option(&str, &apparmor_complain);
++      return 1;
++}
++__setup("apparmor_complain=", aa_getopt_complain);
++
++static int __init aa_getopt_debug(char *str)
++{
++      get_option(&str, &apparmor_debug);
++      return 1;
++}
++__setup("apparmor_debug=", aa_getopt_debug);
++
++static int __init aa_getopt_audit(char *str)
++{
++      get_option(&str, &apparmor_audit);
++      return 1;
++}
++__setup("apparmor_audit=", aa_getopt_audit);
++
++static int __init aa_getopt_logsyscall(char *str)
++{
++      get_option(&str, &apparmor_logsyscall);
++      return 1;
++}
++__setup("apparmor_logsyscall=", aa_getopt_logsyscall);
++#endif
++
++static int apparmor_ptrace(struct task_struct *parent,
++                          struct task_struct *child)
++{
++      int error;
++      struct aaprofile *active;
++
++      error = cap_ptrace(parent, child);
++
++      active = get_task_active_aaprofile(parent);
++
++      if (!error && active)
++              error = aa_audit_syscallreject(active, GFP_ATOMIC,
++                                             AA_SYSCALL_PTRACE);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_capget(struct task_struct *target,
++                          kernel_cap_t *effective,
++                          kernel_cap_t *inheritable,
++                          kernel_cap_t *permitted)
++{
++      return cap_capget(target, effective, inheritable, permitted);
++}
++
++static int apparmor_capset_check(struct task_struct *target,
++                                kernel_cap_t *effective,
++                                kernel_cap_t *inheritable,
++                                kernel_cap_t *permitted)
++{
++      return cap_capset_check(target, effective, inheritable, permitted);
++}
++
++static void apparmor_capset_set(struct task_struct *target,
++                               kernel_cap_t *effective,
++                               kernel_cap_t *inheritable,
++                               kernel_cap_t *permitted)
++{
++      cap_capset_set(target, effective, inheritable, permitted);
++      return;
++}
++
++static int apparmor_capable(struct task_struct *tsk, int cap)
++{
++      int error;
++
++      /* cap_capable returns 0 on success, else -EPERM */
++      error = cap_capable(tsk, cap);
++
++      if (error == 0) {
++              struct aaprofile *active;
++
++              active = get_task_active_aaprofile(tsk);
++
++              if (active)
++                      error = aa_capability(active, cap);
++
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++
++static int apparmor_sysctl(struct ctl_table *table, int op)
++{
++      int error = 0;
++      struct aaprofile *active;
++
++      active = get_active_aaprofile();
++
++      if ((op & 002) && active && !capable(CAP_SYS_ADMIN))
++              error = aa_audit_syscallreject(active, GFP_ATOMIC,
++                                             AA_SYSCALL_SYSCTL_WRITE);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_syslog(int type)
++{
++      return cap_syslog(type);
++}
++
++static int apparmor_netlink_send(struct sock *sk, struct sk_buff *skb)
++{
++      return cap_netlink_send(sk, skb);
++}
++
++static int apparmor_netlink_recv(struct sk_buff *skb, int cap)
++{
++      return cap_netlink_recv(skb, cap);
++}
++
++static void apparmor_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
++{
++      cap_bprm_apply_creds(bprm, unsafe);
++      return;
++}
++
++static int apparmor_bprm_set_security(struct linux_binprm *bprm)
++{
++      /* handle capability bits with setuid, etc */
++      cap_bprm_set_security(bprm);
++      /* already set based on script name */
++      if (bprm->sh_bang)
++              return 0;
++      return aa_register(bprm);
++}
++
++static int apparmor_bprm_secureexec(struct linux_binprm *bprm)
++{
++      int ret = cap_bprm_secureexec(bprm);
++
++      if (ret == 0 &&
++          (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) {
++              AA_DEBUG("%s: secureexec required for %s\n",
++                       __FUNCTION__, bprm->filename);
++              ret = 1;
++      }
++
++      return ret;
++}
++
++static int apparmor_sb_mount(char *dev_name, struct nameidata *nd, char *type,
++                            unsigned long flags, void *data)
++{
++      int error = 0;
++      struct aaprofile *active;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_audit_syscallreject(active, GFP_ATOMIC,
++                                             AA_SYSCALL_MOUNT);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_umount(struct vfsmount *mnt, int flags)
++{
++      int error = 0;
++      struct aaprofile *active;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_audit_syscallreject(active, GFP_ATOMIC,
++                                             AA_SYSCALL_UMOUNT);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_mkdir(struct inode *inode, struct dentry *dentry,
++                               int mask)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_perm_dir(active, dentry, aa_dir_mkdir);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_rmdir(struct inode *inode, struct dentry *dentry)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_perm_dir(active, dentry, aa_dir_rmdir);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_create(struct inode *inode, struct dentry *dentry,
++                                int mask)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++
++      /* At a minimum, need write perm to create */
++      if (active)
++              error = aa_perm_dentry(active, dentry, MAY_WRITE);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_link(struct dentry *old_dentry, struct inode *inode,
++                              struct dentry *new_dentry)
++{
++      int error = 0;
++      struct aaprofile *active;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_link(active, new_dentry, old_dentry);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_unlink(struct inode *inode, struct dentry *dentry)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_perm_dentry(active, dentry, MAY_WRITE);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_mknod(struct inode *inode, struct dentry *dentry,
++                               int mode, dev_t dev)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++
++      if (active)
++              error = aa_perm_dentry(active, dentry, MAY_WRITE);
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_rename(struct inode *old_inode,
++                                struct dentry *old_dentry,
++                                struct inode *new_inode,
++                                struct dentry *new_dentry)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++
++      if (active) {
++              error = aa_perm_dentry(active, old_dentry, MAY_READ |
++                                     MAY_WRITE);
++
++              if (!error)
++                      error = aa_perm_dentry(active, new_dentry,
++                                             MAY_WRITE);
++      }
++
++      put_aaprofile(active);
++
++      return error;
++}
++
++static int apparmor_inode_permission(struct inode *inode, int mask,
++                                    struct nameidata *nd)
++{
++      int error = 0;
++
++      /* Do not perform check on pipes or sockets
++       * Same as apparmor_file_permission
++       */
++      if (VALID_FSTYPE(inode)) {
++              struct aaprofile *active;
++
++              active = get_active_aaprofile();
++              if (active)
++                      error = aa_perm_nameidata(active, nd, mask);
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++
++static int apparmor_inode_setattr(struct dentry *dentry, struct iattr *iattr)
++{
++      int error = 0;
++
++      if (VALID_FSTYPE(dentry->d_inode)) {
++              struct aaprofile *active;
++
++              active = get_active_aaprofile();
++              /*
++               * Mediate any attempt to change attributes of a file
++               * (chmod, chown, chgrp, etc)
++               */
++              if (active)
++                      error = aa_attr(active, dentry, iattr);
++
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++
++static int apparmor_inode_setxattr(struct dentry *dentry, char *name,
++                                  void *value, size_t size, int flags)
++{
++      int error = 0;
++
++      if (VALID_FSTYPE(dentry->d_inode)) {
++              struct aaprofile *active;
++
++              active = get_active_aaprofile();
++              if (active)
++                      error = aa_xattr(active, dentry, name, aa_xattr_set);
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++
++static int apparmor_inode_getxattr(struct dentry *dentry, char *name)
++{
++      int error = 0;
++
++      if (VALID_FSTYPE(dentry->d_inode)) {
++              struct aaprofile *active;
++
++              active = get_active_aaprofile();
++              if (active)
++                      error = aa_xattr(active, dentry, name, aa_xattr_get);
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++static int apparmor_inode_listxattr(struct dentry *dentry)
++{
++      int error = 0;
++
++      if (VALID_FSTYPE(dentry->d_inode)) {
++              struct aaprofile *active;
++
++              active = get_active_aaprofile();
++              if (active)
++                      error = aa_xattr(active, dentry, NULL, aa_xattr_list);
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++
++static int apparmor_inode_removexattr(struct dentry *dentry, char *name)
++{
++      int error = 0;
++
++      if (VALID_FSTYPE(dentry->d_inode)) {
++              struct aaprofile *active;
++
++              active = get_active_aaprofile();
++              if (active)
++                      error = aa_xattr(active, dentry, name,
++                                       aa_xattr_remove);
++              put_aaprofile(active);
++      }
++
++      return error;
++}
++
++static int apparmor_file_permission(struct file *file, int mask)
++{
++      struct aaprofile *active;
++      struct aafile *aaf;
++      int error = 0;
++
++      aaf = (struct aafile *)file->f_security;
++      /* bail out early if this isn't a mediated file */
++      if (!aaf || !VALID_FSTYPE(file->f_dentry->d_inode))
++              goto out;
++
++      active = get_active_aaprofile();
++      if (active && aaf->profile != active)
++              error = aa_perm(active, file->f_dentry, file->f_vfsmnt,
++                              mask & (MAY_EXEC | MAY_WRITE | MAY_READ));
++      put_aaprofile(active);
++
++out:
++      return error;
++}
++
++static int apparmor_file_alloc_security(struct file *file)
++{
++      struct aaprofile *active;
++      int error = 0;
++
++      active = get_active_aaprofile();
++      if (active) {
++              struct aafile *aaf;
++              aaf = kmalloc(sizeof(struct aafile), GFP_KERNEL);
++
++              if (aaf) {
++                      aaf->type = aa_file_default;
++                      aaf->profile = get_aaprofile(active);
++              } else {
++                      error = -ENOMEM;
++              }
++              file->f_security = aaf;
++      }
++      put_aaprofile(active);
++
++      return error;
++}
++
++static void apparmor_file_free_security(struct file *file)
++{
++      struct aafile *aaf = (struct aafile *)file->f_security;
++
++      if (aaf) {
++              put_aaprofile(aaf->profile);
++              kfree(aaf);
++      }
++}
++
++static inline int aa_mmap(struct file *file, unsigned long prot,
++                        unsigned long flags)
++{
++      int error = 0, mask = 0;
++      struct aaprofile *active;
++      struct aafile *aaf;
++
++      active = get_active_aaprofile();
++      if (!active || !file ||
++          !(aaf = (struct aafile *)file->f_security) ||
++          aaf->type == aa_file_shmem)
++              goto out;
++
++      if (prot & PROT_READ)
++              mask |= MAY_READ;
++
++      /* Private mappings don't require write perms since they don't
++       * write back to the files */
++      if (prot & PROT_WRITE && !(flags & MAP_PRIVATE))
++              mask |= MAY_WRITE;
++      if (prot & PROT_EXEC)
++              mask |= AA_EXEC_MMAP;
++
++      AA_DEBUG("%s: 0x%x\n", __FUNCTION__, mask);
++
++      if (mask)
++              error = aa_perm(active, file->f_dentry, file->f_vfsmnt, mask);
++
++      put_aaprofile(active);
++
++out:
++      return error;
++}
++
++static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
++                             unsigned long prot, unsigned long flags)
++{
++      return aa_mmap(file, prot, flags);
++}
++
++static int apparmor_file_mprotect(struct vm_area_struct *vma,
++                                unsigned long reqprot, unsigned long prot)
++{
++      return aa_mmap(vma->vm_file, prot,
++                     !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
++}
++
++static int apparmor_task_alloc_security(struct task_struct *p)
++{
++      return aa_fork(p);
++}
++
++static void apparmor_task_free_security(struct task_struct *p)
++{
++      aa_release(p);
++}
++
++static int apparmor_task_post_setuid(uid_t id0, uid_t id1, uid_t id2,
++                                   int flags)
++{
++      return cap_task_post_setuid(id0, id1, id2, flags);
++}
++
++static void apparmor_task_reparent_to_init(struct task_struct *p)
++{
++      cap_task_reparent_to_init(p);
++      return;
++}
++
++static int apparmor_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
++                            int shmflg)
++{
++      struct aafile *aaf = (struct aafile *)shp->shm_file->f_security;
++
++      if (aaf)
++              aaf->type = aa_file_shmem;
++
++      return 0;
++}
++
++static int apparmor_getprocattr(struct task_struct *p, char *name, void *value,
++                              size_t size)
++{
++      int error;
++      struct aaprofile *active;
++      char *str = value;
++
++      /* AppArmor only supports the "current" process attribute */
++      if (strcmp(name, "current") != 0) {
++              error = -EINVAL;
++              goto out;
++      }
++
++      /* must be task querying itself or admin */
++      if (current != p && !capable(CAP_SYS_ADMIN)) {
++              error = -EPERM;
++              goto out;
++      }
++
++      active = get_task_active_aaprofile(p);
++      error = aa_getprocattr(active, str, size);
++      put_aaprofile(active);
++
++out:
++      return error;
++}
++
++static int apparmor_setprocattr(struct task_struct *p, char *name, void *value,
++                               size_t size)
++{
++      const char *cmd_changehat = "changehat ",
++                 *cmd_setprofile = "setprofile ";
++
++      int error = -EACCES;    /* default to a perm denied */
++      char *cmd = (char *)value;
++
++      /* only support messages to current */
++      if (strcmp(name, "current") != 0) {
++              error = -EINVAL;
++              goto out;
++      }
++
++      if (!size) {
++              error = -ERANGE;
++              goto out;
++      }
++
++      /* CHANGE HAT -- switch task into a subhat (subprofile) if defined */
++      if (size > strlen(cmd_changehat) &&
++          strncmp(cmd, cmd_changehat, strlen(cmd_changehat)) == 0) {
++              char *hatinfo = cmd + strlen(cmd_changehat);
++              size_t infosize = size - strlen(cmd_changehat);
++
++              /* Only the current process may change it's hat */
++              if (current != p) {
++                      AA_WARN("%s: Attempt by foreign task %s(%d) "
++                              "[user %d] to changehat of task %s(%d)\n",
++                              __FUNCTION__,
++                              current->comm,
++                              current->pid,
++                              current->uid,
++                              p->comm,
++                              p->pid);
++
++                      error = -EACCES;
++                      goto out;
++              }
++
++              error = aa_setprocattr_changehat(hatinfo, infosize);
++              if (error == 0)
++                      /* success, set return to #bytes in orig request */
++                      error = size;
++
++      /* SET NEW PROFILE */
++      } else if (size > strlen(cmd_setprofile) &&
++                 strncmp(cmd, cmd_setprofile, strlen(cmd_setprofile)) == 0) {
++              struct aaprofile *active;
++
++              /* only an unconfined process with admin capabilities
++               * may change the profile of another task
++               */
++
++              if (!capable(CAP_SYS_ADMIN)) {
++                      AA_WARN("%s: Unprivileged attempt by task %s(%d) "
++                              "[user %d] to assign profile to task %s(%d)\n",
++                              __FUNCTION__,
++                              current->comm,
++                              current->pid,
++                              current->uid,
++                              p->comm,
++                              p->pid);
++                      error = -EACCES;
++                      goto out;
++              }
++
++              active = get_active_aaprofile();
++              if (!active) {
++                      char *profile = cmd + strlen(cmd_setprofile);
++                      size_t profilesize = size - strlen(cmd_setprofile);
++
++                      error = aa_setprocattr_setprofile(p, profile, profilesize);
++                      if (error == 0)
++                              /* success,
++                               * set return to #bytes in orig request
++                               */
++                              error = size;
++              } else {
++                      AA_WARN("%s: Attempt by confined task %s(%d) "
++                              "[user %d] to assign profile to task %s(%d)\n",
++                              __FUNCTION__,
++                              current->comm,
++                              current->pid,
++                              current->uid,
++                              p->comm,
++                              p->pid);
++
++                      error = -EACCES;
++              }
++              put_aaprofile(active);
++      } else {
++              /* unknown operation */
++              AA_WARN("%s: Unknown setprocattr command '%.*s' by task %s(%d) "
++                      "[user %d] for task %s(%d)\n",
++                      __FUNCTION__,
++                      size < 16 ? (int)size : 16,
++                      cmd,
++                      current->comm,
++                      current->pid,
++                      current->uid,
++                      p->comm,
++                      p->pid);
++
++              error = -EINVAL;
++      }
++
++out:
++      return error;
++}
++
++struct security_operations apparmor_ops = {
++      .ptrace =                       apparmor_ptrace,
++      .capget =                       apparmor_capget,
++      .capset_check =                 apparmor_capset_check,
++      .capset_set =                   apparmor_capset_set,
++      .sysctl =                       apparmor_sysctl,
++      .capable =                      apparmor_capable,
++      .syslog =                       apparmor_syslog,
++
++      .netlink_send =                 apparmor_netlink_send,
++      .netlink_recv =                 apparmor_netlink_recv,
++
++      .bprm_apply_creds =             apparmor_bprm_apply_creds,
++      .bprm_set_security =            apparmor_bprm_set_security,
++      .bprm_secureexec =              apparmor_bprm_secureexec,
++
++      .sb_mount =                     apparmor_sb_mount,
++      .sb_umount =                    apparmor_umount,
++
++      .inode_mkdir =                  apparmor_inode_mkdir,
++      .inode_rmdir =                  apparmor_inode_rmdir,
++      .inode_create =                 apparmor_inode_create,
++      .inode_link =                   apparmor_inode_link,
++      .inode_unlink =                 apparmor_inode_unlink,
++      .inode_mknod =                  apparmor_inode_mknod,
++      .inode_rename =                 apparmor_inode_rename,
++      .inode_permission =             apparmor_inode_permission,
++      .inode_setattr =                apparmor_inode_setattr,
++      .inode_setxattr =               apparmor_inode_setxattr,
++      .inode_getxattr =               apparmor_inode_getxattr,
++      .inode_listxattr =              apparmor_inode_listxattr,
++      .inode_removexattr =            apparmor_inode_removexattr,
++      .file_permission =              apparmor_file_permission,
++      .file_alloc_security =          apparmor_file_alloc_security,
++      .file_free_security =           apparmor_file_free_security,
++      .file_mmap =                    apparmor_file_mmap,
++      .file_mprotect =                apparmor_file_mprotect,
++
++      .task_alloc_security =          apparmor_task_alloc_security,
++      .task_free_security =           apparmor_task_free_security,
++      .task_post_setuid =             apparmor_task_post_setuid,
++      .task_reparent_to_init =        apparmor_task_reparent_to_init,
++
++      .shm_shmat =                    apparmor_shm_shmat,
++
++      .getprocattr =                  apparmor_getprocattr,
++      .setprocattr =                  apparmor_setprocattr,
++};
++
++static int __init apparmor_init(void)
++{
++      int error;
++      const char *complainmsg = ": complainmode enabled";
++
++      if ((error = create_apparmorfs())) {
++              AA_ERROR("Unable to activate AppArmor filesystem\n");
++              goto createfs_out;
++      }
++
++      if ((error = alloc_null_complain_profile())){
++              AA_ERROR("Unable to allocate null complain profile\n");
++              goto alloc_out;
++      }
++
++      if ((error = register_security(&apparmor_ops))) {
++              AA_ERROR("Unable to load AppArmor\n");
++              goto register_security_out;
++      }
++
++      AA_INFO("AppArmor initialized%s\n",
++              apparmor_complain ? complainmsg : "");
++      aa_audit_message(NULL, GFP_KERNEL, 0,
++              "AppArmor initialized%s\n",
++              apparmor_complain ? complainmsg : "");
++
++      return error;
++
++register_security_out:
++      free_null_complain_profile();
++
++alloc_out:
++      (void)destroy_apparmorfs();
++
++createfs_out:
++      return error;
++
++}
++
++static int apparmor_exit_removeall_iter(struct subdomain *sd, void *cookie)
++{
++      /* spin_lock(&sd_lock) held here */
++
++      if (__aa_is_confined(sd)) {
++              AA_DEBUG("%s: Dropping profiles %s(%d) "
++                       "profile %s(%p) active %s(%p)\n",
++                       __FUNCTION__,
++                       sd->task->comm, sd->task->pid,
++                       BASE_PROFILE(sd->active)->name,
++                       BASE_PROFILE(sd->active),
++                       sd->active->name, sd->active);
++              aa_switch_unconfined(sd);
++      }
++
++      return 0;
++}
++
++static void __exit apparmor_exit(void)
++{
++      unsigned long flags;
++
++      /* Remove profiles from the global profile list.
++       * This is just for tidyness as there is no way to reference this
++       * list once the AppArmor lsm hooks are detached (below)
++       */
++      aa_profilelist_release();
++
++      /* Remove profiles from active tasks
++       * If this is not done,  if module is reloaded after being removed,
++       * old profiles (still refcounted in memory) will become 'magically'
++       * reattached
++       */
++
++      spin_lock_irqsave(&sd_lock, flags);
++      aa_subdomainlist_iterate(apparmor_exit_removeall_iter, NULL);
++      spin_unlock_irqrestore(&sd_lock, flags);
++
++      /* Free up list of active subdomain */
++      aa_subdomainlist_release();
++
++      free_null_complain_profile();
++
++      destroy_apparmorfs();
++
++      if (unregister_security(&apparmor_ops))
++              AA_WARN("Unable to properly unregister AppArmor\n");
++
++      /* delay for an rcu cycle to make ensure that profiles pending
++       * destruction in the rcu callback are freed.
++       */
++      synchronize_rcu();
++
++      AA_INFO("AppArmor protection removed\n");
++      aa_audit_message(NULL, GFP_KERNEL, 0,
++              "AppArmor protection removed\n");
++}
++
++module_init(apparmor_init);
++module_exit(apparmor_exit);
++
++MODULE_VERSION(APPARMOR_VERSION);
++MODULE_DESCRIPTION("AppArmor process confinement");
++MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
++MODULE_LICENSE("GPL");
+Index: b/security/apparmor/main.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/main.c
+@@ -0,0 +1,1702 @@
++/*
++ *    Copyright (C) 2002-2005 Novell/SUSE
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License as
++ *    published by the Free Software Foundation, version 2 of the
++ *    License.
++ *
++ *    AppArmor Core
++ */
++
++#include <linux/security.h>
++#include <linux/namei.h>
++#include <linux/audit.h>
++
++#include "apparmor.h"
++#include "match/match.h"
++
++#include "inline.h"
++
++/* NULL complain profile
++ *
++ * Used when in complain mode, to emit Permitting messages for non-existant
++ * profiles and hats.  This is necessary because of selective mode, in which
++ * case we need a complain null_profile and enforce null_profile
++ *
++ * The null_complain_profile cannot be statically allocated, because it
++ * can be associated to files which keep their reference even if apparmor is
++ * unloaded
++ */
++struct aaprofile *null_complain_profile;
++
++/***************************
++ * Private utility functions
++ **************************/
++
++/**
++ * dentry_xlate_error
++ * @dentry: pointer to dentry
++ * @error: error number
++ * @dtype: type of dentry
++ *
++ * Display error message when a dentry translation error occured
++ */
++static void dentry_xlate_error(struct dentry *dentry, int error, char *dtype)
++{
++      const unsigned int len = 16;
++      char buf[len];
++
++      if (dentry->d_inode) {
++              snprintf(buf, len, "%lu", dentry->d_inode->i_ino);
++      } else {
++              strncpy(buf, "<negative>", len);
++              buf[len-1]=0;
++      }
++
++      AA_ERROR("An error occured while translating %s %p "
++               "inode# %s to a pathname. Error %d\n",
++               dtype,
++               dentry,
++               buf,
++               error);
++}
++
++/**
++ * aa_taskattr_access
++ * @procrelname: name of file to check permission
++ *
++ * Determine if request is for write access to /proc/self/attr/current
++ * This file is the usermode iterface for changing it's hat.
++ */
++static inline int aa_taskattr_access(const char *procrelname)
++{
++      char buf[sizeof("/attr/current") + 10];
++      const int maxbuflen = sizeof(buf);
++      /* assumption, 32bit pid (10 decimal digits incl \0) */
++
++      snprintf(buf, maxbuflen, "%d/attr/current", current->pid);
++      buf[maxbuflen - 1] = 0;
++
++      return strcmp(buf, procrelname) == 0;
++}
++
++/**
++ * aa_file_mode - get full mode for file entry from profile
++ * @profile: profile
++ * @name: filename
++ */
++static inline int aa_file_mode(struct aaprofile *profile, const char *name)
++{
++      struct aa_entry *entry;
++      int mode = 0;
++
++      AA_DEBUG("%s: %s\n", __FUNCTION__, name);
++      if (!name) {
++              AA_DEBUG("%s: no name\n", __FUNCTION__);
++              goto out;
++      }
++
++      if (!profile) {
++              AA_DEBUG("%s: no profile\n", __FUNCTION__);
++              goto out;
++      }
++      list_for_each_entry(entry, &profile->file_entry, list) {
++              if (aamatch_match(name, entry->filename,
++                                entry->type, entry->extradata))
++                      mode |= entry->mode;
++      }
++out:
++      return mode;
++}
++
++/**
++ * aa_get_execmode - calculate what qualifier to apply to an exec
++ * @active: profile to search
++ * @name: name of file to exec
++ * @xmod: pointer to a execution mode bit for the rule that was matched
++ *         if the rule has no execuition qualifier {pui} then
++ *         %AA_MAY_EXEC is returned indicating a naked x
++ *         if the has an exec qualifier then only the qualifier bit {pui}
++ *         is returned (%AA_MAY_EXEC) is not set.
++ * @unsafe: true if secure_exec should be overriden
++ * Returns %0 (false):
++ *    if unable to find profile or there are conflicting pattern matches.
++ *       *xmod - is not modified
++ *       *unsafe - is not modified
++ *
++ * Returns %1 (true):
++ *    if exec rule matched
++ *       if the rule has an execution mode qualifier {pui} then
++ *          *xmod = the execution qualifier of the rule {pui}
++ *       else
++ *          *xmod = %AA_MAY_EXEC
++ *       unsafe = presence of unsage flag
++ */
++static inline int aa_get_execmode(struct aaprofile *active, const char *name,
++                                int *xmod, int *unsafe)
++{
++      struct aa_entry *entry;
++      struct aa_entry *match = NULL;
++
++      int pattern_match_invalid = 0, rc = 0;
++
++      /* search list of profiles with 'x' permission
++       * this will also include entries with 'p', 'u' and 'i'
++       * qualifiers.
++       *
++       * If we find a pattern match we will keep looking for an exact match
++       * If we find conflicting pattern matches we will flag (while still
++       * looking for an exact match).  If all we have is a conflict, FALSE
++       * is returned.
++       */
++
++      list_for_each_entry(entry, &active->file_entryp[POS_AA_MAY_EXEC],
++                          listp[POS_AA_MAY_EXEC]) {
++              if (!pattern_match_invalid &&
++                  entry->type == aa_entry_pattern &&
++                  aamatch_match(name, entry->filename,
++                                entry->type, entry->extradata)) {
++                      if (match &&
++                          AA_EXEC_UNSAFE_MASK(entry->mode) !=
++                          AA_EXEC_UNSAFE_MASK(match->mode))
++                              pattern_match_invalid = 1;
++                      else
++                              /* keep searching for an exact match */
++                              match = entry;
++              } else if ((entry->type == aa_entry_literal ||
++                          (!pattern_match_invalid &&
++                           entry->type == aa_entry_tailglob)) &&
++                          aamatch_match(name, entry->filename,
++                                        entry->type,
++                                        entry->extradata)) {
++                      if (entry->type == aa_entry_literal) {
++                              /* got an exact match -- there can be only
++                               * one, asserted at profile load time
++                               */
++                              match = entry;
++                              pattern_match_invalid = 0;
++                              break;
++                      } else {
++                              if (match &&
++                                  AA_EXEC_UNSAFE_MASK(entry->mode) !=
++                                  AA_EXEC_UNSAFE_MASK(match->mode))
++                                      pattern_match_invalid = 1;
++                              else
++                                      /* got a tailglob match, keep searching
++                                       * for an exact match
++                                       */
++                                      match = entry;
++                      }
++              }
++
++      }
++
++      rc = match && !pattern_match_invalid;
++
++      if (rc) {
++              int mode = AA_EXEC_MASK(match->mode);
++
++              /* check for qualifiers, if present
++               * we just return the qualifier
++               */
++              if (mode & ~AA_MAY_EXEC)
++                      mode = mode & ~AA_MAY_EXEC;
++
++              *xmod = mode;
++              *unsafe = (match->mode & AA_EXEC_UNSAFE);
++      } else if (!match) {
++              AA_DEBUG("%s: Unable to find execute entry in profile "
++                       "for image '%s'\n",
++                       __FUNCTION__,
++                       name);
++      } else if (pattern_match_invalid) {
++              AA_WARN("%s: Inconsistency in profile %s. "
++                      "Two (or more) patterns specify conflicting exec "
++                      "qualifiers ('u', 'i' or 'p') for image %s\n",
++                      __FUNCTION__,
++                      active->name,
++                      name);
++      }
++
++      return rc;
++}
++
++/**
++ * aa_filter_mask
++ * @mask: requested mask
++ * @inode: potential directory inode
++ *
++ * This fn performs pre-verification of the requested mask
++ * We ignore append. Previously we required 'w' on a dir to add a file.
++ * No longer. Now we require 'w' on just the file itself. Traversal 'x' is
++ * also ignored for directories.
++ *
++ * Returned value of %0 indicates no need to perform a perm check.
++ */
++static inline int aa_filter_mask(int mask, struct inode *inode)
++{
++      if (mask) {
++              int elim = MAY_APPEND;
++
++              if (inode && S_ISDIR(inode->i_mode))
++                      elim |= (MAY_EXEC | MAY_WRITE);
++
++              mask &= ~elim;
++      }
++
++      return mask;
++}
++
++static inline void aa_permerror2result(int perm_result, struct aa_audit *sa)
++{
++      if (perm_result == 0) { /* success */
++              sa->result = 1;
++              sa->error_code = 0;
++      } else { /* -ve internal error code or +ve mask of denied perms */
++              sa->result = 0;
++              sa->error_code = perm_result;
++      }
++}
++
++/*************************
++ * Main internal functions
++ ************************/
++
++/**
++ * aa_file_perm - calculate access mode for file
++ * @active: profile to check against
++ * @name: name of file to calculate mode for
++ * @mask: permission mask requested for file
++ *
++ * Search the aa_entry list in @active.
++ * Search looking to verify all permissions passed in mask.
++ * Perform the search by looking at the partitioned list of entries, one
++ * partition per permission bit.
++ *
++ * Return %0 on success, else mask of non-allowed permissions
++ */
++static unsigned int aa_file_perm(struct aaprofile *active, const char *name,
++                               int mask)
++{
++      int i, error = 0, mode;
++
++#define PROCPFX "/proc/"
++#define PROCLEN sizeof(PROCPFX) - 1
++
++      AA_DEBUG("%s: %s 0x%x\n", __FUNCTION__, name, mask);
++
++      /* should not enter with other than R/W/M/X/L */
++      WARN_ON(mask &
++             ~(AA_MAY_READ | AA_MAY_WRITE | AA_MAY_EXEC | AA_EXEC_MMAP |
++               AA_MAY_LINK));
++
++      /* Special case access to /proc/self/attr/current
++       * Currently we only allow access if opened O_WRONLY
++       */
++      if (mask == MAY_WRITE && strncmp(PROCPFX, name, PROCLEN) == 0 &&
++          aa_taskattr_access(name + PROCLEN))
++              goto done;
++
++      mode = 0;
++
++      /* iterate over partition, one permission bit at a time */
++      for (i = 0; i <= POS_AA_FILE_MAX; i++) {
++              struct aa_entry *entry;
++
++              /* do we have to accumulate this bit?
++               * or have we already accumulated it (shortcut below)? */
++              if (!(mask & (1 << i)) || mode & (1 << i))
++                      continue;
++
++              list_for_each_entry(entry, &active->file_entryp[i],
++                                  listp[i]) {
++                      if (aamatch_match(name, entry->filename,
++                              entry->type, entry->extradata)) {
++                              /* Shortcut, accumulate all bits present */
++                              mode |= entry->mode;
++
++                              /* Mask bits are overloaded
++                               * MAY_{EXEC,WRITE,READ,APPEND} are used by
++                               * kernel, other values are used locally only.
++                               */
++                              if ((mode & mask) == mask) {
++                                      AA_DEBUG("MATCH! %s=0x%x [total mode=0x%x]\n",
++                                               name, mask, mode);
++
++                                      goto done;
++                              }
++                      }
++              }
++      }
++
++      /* return permissions not satisfied */
++      error = mask & ~mode;
++
++done:
++      return error;
++}
++
++/**
++ * aa_link_perm - test permission to link to a file
++ * @active: profile to check against
++ * @link: name of link being created
++ * @target: name of target to be linked to
++ *
++ * Look up permission mode on both @link and @target.  @link must have same
++ * permission mode as @target.  At least @link must have the link bit enabled.
++ * Return %0 on success, error otherwise.
++ */
++static int aa_link_perm(struct aaprofile *active,
++                      const char *link, const char *target)
++{
++      int l_mode, t_mode, ret;
++
++      l_mode = aa_file_mode(active, link);
++      if (l_mode & AA_MAY_LINK) {
++              /* mask off link bit */
++              l_mode &= ~AA_MAY_LINK;
++
++              t_mode = aa_file_mode(active, target);
++              t_mode &= ~AA_MAY_LINK;
++
++              ret = (l_mode == t_mode);
++      } else {
++              ret = 0;
++      }
++
++      return ret;
++}
++
++/**
++ * _aa_perm_dentry
++ * @active: profile to check against
++ * @dentry: requested dentry
++ * @mask: mask of requested operations
++ * @pname: pointer to hold matched pathname (if any)
++ *
++ * Helper function.  Obtain pathname for specified dentry. Verify if profile
++ * authorizes mask operations on pathname (due to lack of vfsmnt it is sadly
++ * necessary to search mountpoints in namespace -- when nameidata is passed
++ * more fully, this code can go away).  If more than one mountpoint matches
++ * but none satisfy the profile, only the first pathname (mountpoint) is
++ * returned for subsequent logging.
++ *
++ * Return %0 (success), +ve (mask of permissions not satisfied) or -ve (system
++ * error, most likely -%ENOMEM).
++ */
++static int _aa_perm_dentry(struct aaprofile *active, struct dentry *dentry,
++                         int mask, const char **pname)
++{
++      char *name = NULL, *failed_name = NULL;
++      struct aa_path_data data;
++      int error = 0, failed_error = 0, path_error,
++          complain = PROFILE_COMPLAIN(active);
++
++      /* search all paths to dentry */
++
++      aa_path_begin(dentry, &data);
++      do {
++              name = aa_path_getname(&data);
++              if (name) {
++                      /* error here is 0 (success) or +ve (mask of perms) */
++                      error = aa_file_perm(active, name, mask);
++
++                      /* access via any path is enough */
++                      if (complain || error == 0)
++                              break; /* Caller must free name */
++
++                      /* Already have an path that failed? */
++                      if (failed_name) {
++                              aa_put_name(name);
++                      } else {
++                              failed_name = name;
++                              failed_error = error;
++                      }
++              }
++      } while (name);
++
++      if ((path_error = aa_path_end(&data)) != 0) {
++              dentry_xlate_error(dentry, path_error, "dentry");
++              WARN_ON(name);  /* name should not be set if error */
++              error = path_error;
++              name = NULL;
++      } else if (name) {
++              if (failed_name)
++                      aa_put_name(failed_name);
++      } else {
++              name = failed_name;
++              error = failed_error;
++      }
++
++      *pname = name;
++
++      return error;
++}
++
++/**************************
++ * Global utility functions
++ *************************/
++
++/**
++ * attach_nullprofile - allocate and attach a null_profile hat to profile
++ * @profile: profile to attach a null_profile hat to.
++ *
++ * Return %0 (success) or error (-%ENOMEM)
++ */
++int attach_nullprofile(struct aaprofile *profile)
++{
++      struct aaprofile *hat = NULL;
++      char *hatname = NULL;
++
++      hat = alloc_aaprofile();
++      if (!hat)
++              goto fail;
++      if (profile->flags.complain)
++              hatname = kstrdup("null-complain-profile", GFP_KERNEL);
++      else
++              hatname = kstrdup("null-profile", GFP_KERNEL);
++      if (!hatname)
++              goto fail;
++
++      hat->flags.complain = profile->flags.complain;
++      hat->name = hatname;
++      hat->parent = profile;
++
++      profile->null_profile = hat;
++
++      return 0;
++
++fail:
++      kfree(hatname);
++      free_aaprofile(hat);
++
++      return -ENOMEM;
++}
++
++
++/**
++ * alloc_null_complain_profile - Allocate the global null_complain_profile.
++ *
++ * Return %0 (success) or error (-%ENOMEM)
++ */
++int alloc_null_complain_profile(void)
++{
++      null_complain_profile = alloc_aaprofile();
++      if (!null_complain_profile)
++              goto fail;
++
++      null_complain_profile->name =
++              kstrdup("null-complain-profile", GFP_KERNEL);
++
++      if (!null_complain_profile->name)
++              goto fail;
++
++      null_complain_profile->flags.complain = 1;
++      if (attach_nullprofile(null_complain_profile))
++              goto fail;
++
++      return 0;
++
++fail:
++      /* free_aaprofile is safe for freeing partially constructed objects */
++      free_aaprofile(null_complain_profile);
++      null_complain_profile = NULL;
++
++      return -ENOMEM;
++}
++
++/**
++ * free_null_complain_profile - Free null profiles
++ */
++void free_null_complain_profile(void)
++{
++      put_aaprofile(null_complain_profile);
++      null_complain_profile = NULL;
++}
++
++/**
++ * aa_audit_message - Log a message to the audit subsystem
++ * @active: profile to check against
++ * @gfp: allocation flags
++ * @flags: audit flags
++ * @fmt: varargs fmt
++ */
++int aa_audit_message(struct aaprofile *active, gfp_t gfp, int flags,
++                   const char *fmt, ...)
++{
++      int ret;
++      struct aa_audit sa;
++
++      sa.type = AA_AUDITTYPE_MSG;
++      sa.name = fmt;
++      va_start(sa.vaval, fmt);
++      sa.flags = flags;
++      sa.gfp_mask = gfp;
++      sa.error_code = 0;
++      sa.result = 0;  /* fake failure: force message to be logged */
++
++      ret = aa_audit(active, &sa);
++
++      va_end(sa.vaval);
++
++      return ret;
++}
++
++/**
++ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem
++ * @active: profile to check against
++ * @gfp: memory allocation flags
++ * @call: aa syscall cache bit number
++ */
++int aa_audit_syscallreject(struct aaprofile *active, gfp_t gfp,
++                         enum aasyscall call)
++{
++      struct aa_audit sa;
++      int error = -EPERM;
++
++      if (!syscall_is_cached(call)) {
++              sa.type = AA_AUDITTYPE_SYSCALL;
++              sa.name = syscall_to_name(call);
++              sa.flags = 0;
++              sa.gfp_mask = gfp;
++              sa.error_code = 0;
++              sa.result = 0; /* failure */
++
++              error = aa_audit(active, &sa);
++              if (error == -EPERM)
++                      add_to_cached_syscalls(call);
++      }
++      return error;
++}
++
++/**
++ * aa_audit - Log an audit event to the audit subsystem
++ * @active: profile to check against
++ * @sa: audit event
++ */
++int aa_audit(struct aaprofile *active, const struct aa_audit *sa)
++{
++      struct audit_buffer *ab = NULL;
++      struct audit_context *ctx;
++
++      const char *logcls;
++      unsigned int flags;
++      int audit = 0,
++          complain = 0,
++          error = -EINVAL,
++          opspec_error = -EACCES;
++
++      const gfp_t gfp_mask = sa->gfp_mask;
++
++      WARN_ON(sa->type >= AA_AUDITTYPE__END);
++
++      /*
++       * sa->result:    1 success, 0 failure
++       * sa->error_code: success: 0
++       *                failure: +ve mask of failed permissions or -ve
++       *                system error
++       */
++
++      if (likely(sa->result)) {
++              if (likely(!PROFILE_AUDIT(active))) {
++                      /* nothing to log */
++                      error = 0;
++                      goto out;
++              } else {
++                      audit = 1;
++                      logcls = "AUDITING";
++              }
++      } else if (sa->error_code < 0) {
++              audit_log(current->audit_context, gfp_mask, AUDIT_SD,
++                      "Internal error auditing event type %d (error %d)",
++                      sa->type, sa->error_code);
++              AA_ERROR("Internal error auditing event type %d (error %d)\n",
++                      sa->type, sa->error_code);
++              error = sa->error_code;
++              goto out;
++      } else if (sa->type == AA_AUDITTYPE_SYSCALL) {
++              /* Currently AA_AUDITTYPE_SYSCALL is for rejects only.
++               * Values set by aa_audit_syscallreject will get us here.
++               */
++              logcls = "REJECTING";
++      } else {
++              complain = PROFILE_COMPLAIN(active);
++              logcls = complain ? "PERMITTING" : "REJECTING";
++      }
++
++      /* test if event has already been logged and cached used to log
++       * only first time event occurs.
++       */
++      if (sa->type == AA_AUDITTYPE_CAP) {
++              if (cap_is_cached(sa->ival)) {
++                      opspec_error = -EPERM;
++                      goto skip_logging;
++              }
++      }
++
++      /* In future extend w/ per-profile flags
++       * (flags |= sa->active->flags)
++       */
++      flags = sa->flags;
++      if (apparmor_logsyscall)
++              flags |= AA_AUDITFLAG_AUDITSS_SYSCALL;
++
++
++      /* Force full audit syscall logging regardless of global setting if
++       * we are rejecting a syscall
++       */
++      if (sa->type == AA_AUDITTYPE_SYSCALL) {
++              ctx = current->audit_context;
++      } else {
++              ctx = (flags & AA_AUDITFLAG_AUDITSS_SYSCALL) ?
++                      current->audit_context : NULL;
++      }
++
++      ab = audit_log_start(ctx, gfp_mask, AUDIT_SD);
++
++      if (!ab) {
++              AA_ERROR("Unable to log event (%d) to audit subsys\n",
++                      sa->type);
++              if (complain)
++                      error = 0;
++              goto out;
++      }
++
++      /* messages get special handling */
++      if (sa->type == AA_AUDITTYPE_MSG) {
++              audit_log_vformat(ab, sa->name, sa->vaval);
++              audit_log_end(ab);
++              error = 0;
++              goto out;
++      }
++
++      /* log operation */
++
++      audit_log_format(ab, "%s ", logcls);    /* REJECTING/ALLOWING/etc */
++
++      if (sa->type == AA_AUDITTYPE_FILE) {
++              int perm = audit ? sa->ival : sa->error_code;
++
++              audit_log_format(ab, "%s%s%s%s%s access to %s ",
++                               perm & AA_EXEC_MMAP ? "m" : "",
++                               perm & AA_MAY_READ  ? "r" : "",
++                               perm & AA_MAY_WRITE ? "w" : "",
++                               perm & AA_MAY_EXEC  ? "x" : "",
++                               perm & AA_MAY_LINK  ? "l" : "",
++                               sa->name);
++
++              opspec_error = -EPERM;
++
++      } else if (sa->type == AA_AUDITTYPE_DIR) {
++              audit_log_format(ab, "%s on %s ",
++                      sa->ival == aa_dir_mkdir ? "mkdir" : "rmdir",
++                      sa->name);
++
++      } else if (sa->type == AA_AUDITTYPE_ATTR) {
++              struct iattr *iattr = (struct iattr*)sa->pval;
++
++              audit_log_format(ab,
++                      "attribute (%s%s%s%s%s%s%s) change to %s ",
++                      iattr->ia_valid & ATTR_MODE ? "mode," : "",
++                      iattr->ia_valid & ATTR_UID ? "uid," : "",
++                      iattr->ia_valid & ATTR_GID ? "gid," : "",
++                      iattr->ia_valid & ATTR_SIZE ? "size," : "",
++                      ((iattr->ia_valid & ATTR_ATIME_SET) ||
++                       (iattr->ia_valid & ATTR_ATIME)) ? "atime," : "",
++                      ((iattr->ia_valid & ATTR_MTIME_SET) ||
++                       (iattr->ia_valid & ATTR_MTIME)) ? "mtime," : "",
++                      iattr->ia_valid & ATTR_CTIME ? "ctime," : "",
++                      sa->name);
++
++      } else if (sa->type == AA_AUDITTYPE_XATTR) {
++              const char *fmt;
++              switch (sa->ival) {
++                      case aa_xattr_get:
++                              fmt = "xattr get";
++                              break;
++                      case aa_xattr_set:
++                              fmt = "xattr set";
++                              break;
++                      case aa_xattr_list:
++                              fmt = "xattr list";
++                              break;
++                      case aa_xattr_remove:
++                              fmt = "xattr remove";
++                              break;
++                      default:
++                              fmt = "xattr <unknown>";
++                              break;
++              }
++
++              audit_log_format(ab, "%s on %s ", fmt, sa->name);
++
++      } else if (sa->type == AA_AUDITTYPE_LINK) {
++              audit_log_format(ab,
++                      "link access from %s to %s ",
++                      sa->name,
++                      (char*)sa->pval);
++
++      } else if (sa->type == AA_AUDITTYPE_CAP) {
++              audit_log_format(ab,
++                      "access to capability '%s' ",
++                      capability_to_name(sa->ival));
++              add_to_cached_caps(sa->ival);
++              opspec_error = -EPERM;
++      } else if (sa->type == AA_AUDITTYPE_SYSCALL) {
++              audit_log_format(ab, "access to syscall '%s' ", sa->name);
++
++              opspec_error = -EPERM;
++      } else {
++              /* -EINVAL -- will WARN_ON above */
++              goto out;
++      }
++
++      audit_log_format(ab, "(%s(%d) profile %s active %s)",
++                       current->comm, current->pid,
++                       BASE_PROFILE(active)->name, active->name);
++
++      audit_log_end(ab);
++
++skip_logging:
++      if (complain)
++              error = 0;
++      else
++              error = sa->result ? 0 : opspec_error;
++
++out:
++      return error;
++}
++
++/**
++ * aa_get_name - retrieve fully qualified path name
++ * @dentry: relative path element
++ * @mnt: where in tree
++ *
++ * Returns fully qualified path name on sucess, NULL on failure.
++ * aa_put_name must be used to free allocated buffer.
++ */
++char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt)
++{
++      char *page, *name;
++
++      page = (char *)__get_free_page(GFP_KERNEL);
++      if (!page) {
++              name = ERR_PTR(-ENOMEM);
++              goto out;
++      }
++
++      name = d_path(dentry, mnt, page, PAGE_SIZE);
++      /* check for (deleted) that d_path appends to pathnames if the dentry
++       * has been removed from the cache.
++       * The size > deleted_size and strcmp checks are redundant safe guards.
++       */
++      if (IS_ERR(name)) {
++              free_page((unsigned long)page);
++      } else {
++              const char deleted_str[] = " (deleted)";
++              const size_t deleted_size = sizeof(deleted_str) - 1;
++              size_t size;
++              size = strlen(name);
++              if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
++                  size > deleted_size &&
++                  strcmp(name + size - deleted_size, deleted_str) == 0)
++                      name[size - deleted_size] = '\0';
++              AA_DEBUG("%s: full_path=%s\n", __FUNCTION__, name);
++      }
++
++out:
++      return name;
++}
++
++/***********************************
++ * Global permission check functions
++ ***********************************/
++
++/**
++ * aa_attr - check whether attribute change allowed
++ * @active: profile to check against
++ * @dentry: file to check
++ * @iattr: attribute changes requested
++ */
++int aa_attr(struct aaprofile *active, struct dentry *dentry,
++          struct iattr *iattr)
++{
++      int error = 0, permerror;
++      struct aa_audit sa;
++
++      sa.type = AA_AUDITTYPE_ATTR;
++      sa.pval = iattr;
++      sa.flags = 0;
++      sa.gfp_mask = GFP_KERNEL;
++
++      permerror = _aa_perm_dentry(active, dentry, MAY_WRITE, &sa.name);
++      aa_permerror2result(permerror, &sa);
++
++      error = aa_audit(active, &sa);
++
++      aa_put_name(sa.name);
++
++      return error;
++}
++
++/**
++ * aa_xattr - check whether xattr attribute change allowed
++ * @active: profile to check against
++ * @dentry: file to check
++ * @xattr: xattr to check
++ * @xattroptype: type of xattr operation
++ */
++int aa_xattr(struct aaprofile *active, struct dentry *dentry,
++           const char *xattr, enum aa_xattroptype xattroptype)
++{
++      int error = 0, permerror, mask = 0;
++      struct aa_audit sa;
++
++      /* if not confined or empty mask permission granted */
++      if (!active)
++              goto out;
++
++      if (xattroptype == aa_xattr_get || xattroptype == aa_xattr_list)
++              mask = MAY_READ;
++      else if (xattroptype == aa_xattr_set || xattroptype == aa_xattr_remove)
++              mask = MAY_WRITE;
++
++      sa.type = AA_AUDITTYPE_XATTR;
++      sa.ival = xattroptype;
++      sa.pval = xattr;
++      sa.flags = 0;
++      sa.gfp_mask = GFP_KERNEL;
++
++      permerror = _aa_perm_dentry(active, dentry, mask, &sa.name);
++      aa_permerror2result(permerror, &sa);
++
++      error = aa_audit(active, &sa);
++
++      aa_put_name(sa.name);
++
++out:
++      return error;
++}
++
++/**
++ * aa_perm - basic apparmor permissions check
++ * @active: profile to check against
++ * @dentry: dentry
++ * @mnt: mountpoint
++ * @mask: access mode requested
++ *
++ * Determine if access (mask) for dentry is authorized by active
++ * profile.  Result, %0 (success), -ve (error)
++ */
++int aa_perm(struct aaprofile *active, struct dentry *dentry,
++          struct vfsmount *mnt, int mask)
++{
++      int error = 0, permerror;
++      struct aa_audit sa;
++
++      if (!active)
++              goto out;
++
++      if ((mask = aa_filter_mask(mask, dentry->d_inode)) == 0)
++              goto out;
++
++      sa.type = AA_AUDITTYPE_FILE;
++      sa.name = aa_get_name(dentry, mnt);
++      sa.ival = mask;
++      sa.flags = 0;
++      sa.gfp_mask = GFP_KERNEL;
++
++      if (IS_ERR(sa.name)) {
++              permerror = PTR_ERR(sa.name);
++              sa.name = NULL;
++      } else {
++              permerror = aa_file_perm(active, sa.name, mask);
++      }
++
++      aa_permerror2result(permerror, &sa);
++
++      error = aa_audit(active, &sa);
++
++      aa_put_name(sa.name);
++
++out:
++      return error;
++}
++
++/**
++ * aa_perm_nameidata: interface to sd_perm accepting nameidata
++ * @active: profile to check against
++ * @nd: namespace data (for vfsmnt and dentry)
++ * @mask: access mode requested
++ */
++int aa_perm_nameidata(struct aaprofile *active, struct nameidata *nd, int mask)
++{
++      int error = 0;
++
++      if (nd)
++              error = aa_perm(active, nd->dentry, nd->mnt, mask);
++
++      return error;
++}
++
++/**
++ * aa_perm_dentry - file permissions interface when no vfsmnt available
++ * @active: profile to check against
++ * @dentry: requested dentry
++ * @mask: access mode requested
++ *
++ * Determine if access (mask) for dentry is authorized by active profile.
++ * Result, %0 (success), -ve (error)
++ */
++int aa_perm_dentry(struct aaprofile *active, struct dentry *dentry, int mask)
++{
++      int error = 0, permerror;
++      struct aa_audit sa;
++
++      if (!active)
++              goto out;
++
++      if ((mask = aa_filter_mask(mask, dentry->d_inode)) == 0)
++              goto out;
++
++      sa.type = AA_AUDITTYPE_FILE;
++      sa.ival = mask;
++      sa.flags = 0;
++      sa.gfp_mask = GFP_KERNEL;
++
++      permerror = _aa_perm_dentry(active, dentry, mask, &sa.name);
++      aa_permerror2result(permerror, &sa);
++
++      error = aa_audit(active, &sa);
++
++      aa_put_name(sa.name);
++
++out:
++      return error;
++}
++
++/**
++ * aa_perm_dir
++ * @active: profile to check against
++ * @dentry: requested dentry
++ * @diroptype: aa_dir_mkdir or aa_dir_rmdir
++ *
++ * Determine if directory operation (make/remove) for dentry is authorized
++ * by @active profile.
++ * Result, %0 (success), -ve (error)
++ */
++int aa_perm_dir(struct aaprofile *active, struct dentry *dentry,
++              enum aa_diroptype diroptype)
++{
++      int error = 0, permerror, mask;
++      struct aa_audit sa;
++
++      WARN_ON(diroptype != aa_dir_mkdir && diroptype != aa_dir_rmdir);
++
++      if (!active)
++              goto out;
++
++      mask = MAY_WRITE;
++
++      sa.type = AA_AUDITTYPE_DIR;
++      sa.ival = diroptype;
++      sa.flags = 0;
++      sa.gfp_mask = GFP_KERNEL;
++
++      permerror = _aa_perm_dentry(active, dentry, mask, &sa.name);
++      aa_permerror2result(permerror, &sa);
++
++      error = aa_audit(active, &sa);
++
++      aa_put_name(sa.name);
++
++out:
++      return error;
++}
++
++/**
++ * aa_capability - test permission to use capability
++ * @active: profile to check against
++ * @cap: capability to be tested
++ *
++ * Look up capability in active profile capability set.
++ * Return %0 (success), -%EPERM (error)
++ */
++int aa_capability(struct aaprofile *active, int cap)
++{
++      int error = 0;
++
++      struct aa_audit sa;
++
++      sa.type = AA_AUDITTYPE_CAP;
++      sa.name = NULL;
++      sa.ival = cap;
++      sa.flags = 0;
++      sa.error_code = 0;
++      sa.result = cap_raised(active->capabilities, cap);
++      sa.gfp_mask = GFP_ATOMIC;
++
++      error = aa_audit(active, &sa);
++
++      return error;
++}
++
++/**
++ * aa_link - hard link check
++ * @active: profile to check against
++ * @link: dentry for link being created
++ * @target: dentry for link target
++ *
++ * Checks link permissions for all possible name combinations.  This is
++ * particularly ugly.  Returns %0 on sucess, error otherwise.
++ */
++int aa_link(struct aaprofile *active, struct dentry *link,
++          struct dentry *target)
++{
++      char *iname = NULL, *oname = NULL,
++           *failed_iname = NULL, *failed_oname = NULL;
++      unsigned int result = 0;
++      int error, path_error, error_code = 0, match = 0,
++          complain = PROFILE_COMPLAIN(active);
++      struct aa_path_data idata, odata;
++      struct aa_audit sa;
++
++      if (!active)
++              return 0;
++
++      /* Perform nested lookup for names.
++       * This is necessary in the case where /dev/block is mounted
++       * multiple times,  i.e /dev/block->/a and /dev/block->/b
++       * This allows us to detect links where src/dest are on different
++       * mounts.   N.B no support yet for links across bind mounts of
++       * the form mount -bind /mnt/subpath /mnt2
++       *
++       * Getting direct access to vfsmounts (via nameidata) for link and
++       * target would allow all this uglyness to go away.
++       *
++       * If more than one mountpoint matches but none satisfy the profile,
++       * only the first pathname (mountpoint) is logged.
++       */
++
++      __aa_path_begin(target, link, &odata);
++      do {
++              oname = aa_path_getname(&odata);
++              if (oname) {
++                      aa_path_begin(target, &idata);
++                      do {
++                              iname = aa_path_getname(&idata);
++                              if (iname) {
++                                      result = aa_link_perm(active, oname,
++                                                            iname);
++
++                                      /* access via any path is enough */
++                                      if (result || complain) {
++                                              match = 1;
++                                              break;
++                                      }
++
++                                      /* Already have an path that failed? */
++                                      if (failed_iname) {
++                                              aa_put_name(iname);
++                                      } else {
++                                              failed_iname = iname;
++                                              failed_oname = oname;
++                                      }
++                              }
++                      } while (iname && !match);
++
++                      /* should not be possible if we matched */
++                      if ((path_error = aa_path_end(&idata)) != 0) {
++                              dentry_xlate_error(target, path_error,
++                                                 "inner dentry [link]");
++
++                              /* name should not be set if error */
++                              WARN_ON(iname);
++
++                              error_code = path_error;
++                      }
++
++                      /* don't release if we're saving it */
++                      if (!match && failed_oname != oname)
++                              aa_put_name(oname);
++              }
++      } while (oname && !match);
++
++      if (error_code != 0) {
++              /* inner error */
++              (void)aa_path_end(&odata);
++      } else if ((path_error = aa_path_end(&odata)) != 0) {
++              dentry_xlate_error(link, path_error, "outer dentry [link]");
++              error_code = path_error;
++      }
++
++      if (error_code != 0) {
++              /* inner or outer error */
++              result = 0;
++      } else if (!match) {
++              /* failed to match */
++              WARN_ON(iname);
++              WARN_ON(oname);
++
++              result = 0;
++              iname = failed_iname;
++              oname = failed_oname;
++      }
++
++      sa.type = AA_AUDITTYPE_LINK;
++      sa.name = oname;        /* link */
++      sa.pval = iname;        /* target */
++      sa.flags = 0;
++      sa.error_code = error_code;
++      sa.result = result;
++      sa.gfp_mask = GFP_KERNEL;
++
++      error = aa_audit(active, &sa);
++
++      if (failed_oname != oname)
++              aa_put_name(failed_oname);
++      if (failed_iname != iname)
++              aa_put_name(failed_iname);
++
++      aa_put_name(oname);
++      aa_put_name(iname);
++
++      return error;
++}
++
++/*******************************
++ * Global task related functions
++ *******************************/
++
++/**
++ * aa_fork - create a new subdomain
++ * @p: new process
++ *
++ * Create a new subdomain for newly created process @p if it's parent
++ * is already confined.  Otherwise a subdomain will be lazily allocated
++ * will get one with NULL values.  Return 0 on sucess.
++ * for the child if it subsequently execs (in aa_register).
++ * Return 0 on sucess.
++ *
++ * The sd_lock is used to maintain consistency against profile
++ * replacement/removal.
++ */
++
++int aa_fork(struct task_struct *p)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      struct subdomain *newsd = NULL;
++
++      AA_DEBUG("%s\n", __FUNCTION__);
++
++      if (__aa_is_confined(sd)) {
++              unsigned long flags;
++
++              newsd = alloc_subdomain(p);
++
++              if (!newsd)
++                      return -ENOMEM;
++
++              /* Use locking here instead of getting the reference
++               * because we need both the old reference and the
++               * new reference to be consistent.
++               */
++              spin_lock_irqsave(&sd_lock, flags);
++              aa_switch(newsd, sd->active);
++              newsd->hat_magic = sd->hat_magic;
++              spin_unlock_irqrestore(&sd_lock, flags);
++
++              if (SUBDOMAIN_COMPLAIN(sd) &&
++                  sd->active == null_complain_profile)
++                      LOG_HINT(sd->active, GFP_KERNEL, HINT_FORK,
++                              "pid=%d child=%d\n",
++                              current->pid, p->pid);
++      }
++      p->security = newsd;
++      return 0;
++}
++
++/**
++ * aa_register - register a new program
++ * @bprm: binprm of program being registered
++ *
++ * Try to register a new program during execve().  This should give the
++ * new program a valid subdomain.
++ */
++int aa_register(struct linux_binprm *bprm)
++{
++      char *filename;
++      struct file *filp = bprm->file;
++      struct aaprofile *active;
++      struct aaprofile *newprofile = NULL, unconstrained_flag;
++      int     error = -ENOMEM,
++              exec_mode = 0,
++              find_profile = 0,
++              find_profile_mandatory = 0,
++              unsafe_exec = 0,
++              complain = 0;
++
++      AA_DEBUG("%s\n", __FUNCTION__);
++
++      filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt);
++      if (IS_ERR(filename)) {
++              AA_WARN("%s: Failed to get filename\n", __FUNCTION__);
++              goto out;
++      }
++
++      error = 0;
++
++      active = get_active_aaprofile();
++
++      if (!active) {
++              /* Unconfined task, load profile if it exists */
++              find_profile = 1;
++              goto find_profile;
++      }
++
++      complain = PROFILE_COMPLAIN(active);
++
++      /* Confined task, determine what mode inherit, unconstrained or
++       * mandatory to load new profile
++       */
++      if (aa_get_execmode(active, filename, &exec_mode, &unsafe_exec)) {
++              switch (exec_mode) {
++              case AA_EXEC_INHERIT:
++                      /* do nothing - setting of profile
++                       * already handed in aa_fork
++                       */
++                      AA_DEBUG("%s: INHERIT %s\n",
++                               __FUNCTION__,
++                               filename);
++                      break;
++
++              case AA_EXEC_UNCONSTRAINED:
++                      AA_DEBUG("%s: UNCONSTRAINED %s\n",
++                               __FUNCTION__,
++                               filename);
++
++                      /* unload profile */
++                      newprofile = &unconstrained_flag;
++                      break;
++
++              case AA_EXEC_PROFILE:
++                      AA_DEBUG("%s: PROFILE %s\n",
++                               __FUNCTION__,
++                               filename);
++
++                      find_profile = 1;
++                      find_profile_mandatory = 1;
++                      break;
++
++              case AA_MAY_EXEC:
++                      /* this should not happen, entries
++                       * with just EXEC only should be
++                       * rejected at profile load time
++                       */
++                      AA_ERROR("%s: Rejecting exec(2) of image '%s'. "
++                              "AA_MAY_EXEC without exec qualifier invalid "
++                              "(%s(%d) profile %s active %s\n",
++                               __FUNCTION__,
++                               filename,
++                               current->comm, current->pid,
++                               BASE_PROFILE(active)->name, active->name);
++                      error = -EPERM;
++                      break;
++
++              default:
++                      AA_ERROR("%s: Rejecting exec(2) of image '%s'. "
++                               "Unknown exec qualifier %x "
++                               "(%s (pid %d) profile %s active %s)\n",
++                               __FUNCTION__,
++                               filename,
++                               exec_mode,
++                               current->comm, current->pid,
++                               BASE_PROFILE(active)->name, active->name);
++                      error = -EPERM;
++                      break;
++              }
++
++      } else if (complain) {
++              /* There was no entry in calling profile
++               * describing mode to execute image in.
++               * Drop into null-profile (disabling secure exec).
++               */
++              newprofile = get_aaprofile(null_complain_profile);
++              unsafe_exec = 1;
++      } else {
++              AA_WARN("%s: Rejecting exec(2) of image '%s'. "
++                      "Unable to determine exec qualifier "
++                      "(%s (pid %d) profile %s active %s)\n",
++                      __FUNCTION__,
++                      filename,
++                      current->comm, current->pid,
++                      BASE_PROFILE(active)->name, active->name);
++              error = -EPERM;
++      }
++
++
++find_profile:
++      if (!find_profile)
++              goto apply_profile;
++
++      /* Locate new profile */
++      newprofile = aa_profilelist_find(filename);
++      if (newprofile) {
++              AA_DEBUG("%s: setting profile %s\n",
++                       __FUNCTION__, newprofile->name);
++      } else if (find_profile_mandatory) {
++              /* Profile (mandatory) could not be found */
++
++              if (complain) {
++                      LOG_HINT(active, GFP_KERNEL, HINT_MANDPROF,
++                              "image=%s pid=%d profile=%s active=%s\n",
++                              filename,
++                              current->pid,
++                              BASE_PROFILE(active)->name, active->name);
++
++                      newprofile = get_aaprofile(null_complain_profile);
++              } else {
++                      AA_WARN("REJECTING exec(2) of image '%s'. "
++                              "Profile mandatory and not found "
++                              "(%s(%d) profile %s active %s)\n",
++                              filename,
++                              current->comm, current->pid,
++                              BASE_PROFILE(active)->name, active->name);
++                      error = -EPERM;
++              }
++      } else {
++              /* Profile (non-mandatory) could not be found */
++
++              /* Only way we can get into this code is if task
++               * is unconstrained.
++               */
++
++              WARN_ON(active);
++
++              AA_DEBUG("%s: No profile found for exec image %s\n",
++                       __FUNCTION__,
++                       filename);
++      } /* newprofile */
++
++
++apply_profile:
++      /* Apply profile if necessary */
++      if (newprofile) {
++              struct subdomain *sd, *lazy_sd = NULL;
++              unsigned long flags;
++
++              if (newprofile == &unconstrained_flag)
++                      newprofile = NULL;
++
++              /* grab a lock - this is to guarentee consistency against
++               * other writers of subdomain (replacement/removal)
++               *
++               * Several things may have changed since the code above
++               *
++               * - Task may be presently unconfined (have no sd). In which
++               *   case we have to lazily allocate one.  Note we may be raced
++               *   to this allocation by a setprofile.
++               *
++               * - If we are a confined process, active is a refcounted copy
++               *   of the profile that was on the subdomain at entry.
++               *   This allows us to not have to hold a lock around
++               *   all this code.   If profile replacement has taken place
++               *   our active may not equal sd->active any more.
++               *   This is okay since the operation is treated as if
++               *   the transition occured before replacement.
++               *
++               * - If newprofile points to an actual profile (result of
++               *   aa_profilelist_find above), this profile may have been
++               *   replaced.  We need to fix it up.  Doing this to avoid
++               *   having to hold a lock around all this code.
++               */
++
++              if (!active && !(sd = AA_SUBDOMAIN(current->security))) {
++                      lazy_sd = alloc_subdomain(current);
++                      if (!lazy_sd) {
++                              AA_ERROR("%s: Failed to allocate subdomain\n",
++                                       __FUNCTION__);
++                              error = -ENOMEM;
++                              goto cleanup;
++                      }
++              }
++
++              spin_lock_irqsave(&sd_lock, flags);
++
++              sd = AA_SUBDOMAIN(current->security);
++              if (lazy_sd) {
++                      if (sd) {
++                              /* raced by setprofile - created sd */
++                              free_subdomain(lazy_sd);
++                              lazy_sd = NULL;
++                      } else {
++                              /* Not rcu used to get the write barrier
++                               * correct */
++                              rcu_assign_pointer(current->security, lazy_sd);
++                              sd = lazy_sd;
++                      }
++              }
++
++              /* Determine if profile we found earlier is stale.
++               * If so, reobtain it.  N.B stale flag should never be
++               * set on null_complain profile.
++               */
++              if (newprofile && unlikely(newprofile->isstale)) {
++                      WARN_ON(newprofile == null_complain_profile);
++
++                      /* drop refcnt obtained from earlier get_aaprofile */
++                      put_aaprofile(newprofile);
++
++                      newprofile = aa_profilelist_find(filename);
++
++                      if (!newprofile) {
++                              /* Race, profile was removed, not replaced.
++                               * Redo with error checking
++                               */
++                              spin_unlock_irqrestore(&sd_lock, flags);
++                              goto find_profile;
++                      }
++              }
++
++              /* Handle confined exec.
++               * Can be at this point for the following reasons:
++               * 1. unconfined switching to confined
++               * 2. confined switching to different confinement
++               * 3. confined switching to unconfined
++               *
++               * Cases 2 and 3 are marked as requiring secure exec
++               * (unless policy specified "unsafe exec")
++               */
++              if (__aa_is_confined(sd) && !unsafe_exec) {
++                      unsigned long bprm_flags;
++
++                      bprm_flags = AA_SECURE_EXEC_NEEDED;
++                      bprm->security = (void*)
++                              ((unsigned long)bprm->security | bprm_flags);
++              }
++
++              aa_switch(sd, newprofile);
++              put_aaprofile(newprofile);
++
++              if (complain && newprofile == null_complain_profile)
++                      LOG_HINT(newprofile, GFP_ATOMIC, HINT_CHGPROF,
++                              "pid=%d\n",
++                              current->pid);
++
++              spin_unlock_irqrestore(&sd_lock, flags);
++      }
++
++cleanup:
++      aa_put_name(filename);
++
++      put_aaprofile(active);
++
++out:
++      return error;
++}
++
++/**
++ * aa_release - release the task's subdomain
++ * @p: task being released
++ *
++ * This is called after a task has exited and the parent has reaped it.
++ * @p->security blob is freed.
++ *
++ * This is the one case where we don't need to hold the sd_lock before
++ * removing a profile from a subdomain.  Once the subdomain has been
++ * removed from the subdomain_list, we are no longer racing other writers.
++ * There may still be other readers so we must still use aa_switch
++ * to put the subdomain's reference safely.
++ */
++void aa_release(struct task_struct *p)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(p->security);
++      if (sd) {
++              p->security = NULL;
++
++              aa_subdomainlist_remove(sd);
++              aa_switch_unconfined(sd);
++
++              kfree(sd);
++      }
++}
++
++/*****************************
++ * global subprofile functions
++ ****************************/
++
++/**
++ * do_change_hat - actually switch hats
++ * @hat_name: name of hat to swtich to
++ * @sd: current subdomain
++ *
++ * Switch to a new hat.  Return %0 on success, error otherwise.
++ */
++static inline int do_change_hat(const char *hat_name, struct subdomain *sd)
++{
++      struct aaprofile *sub;
++      int error = 0;
++
++      sub = __aa_find_profile(hat_name, &BASE_PROFILE(sd->active)->sub);
++
++      if (sub) {
++              /* change hat */
++              aa_switch(sd, sub);
++              put_aaprofile(sub);
++      } else {
++              /* There is no such subprofile change to a NULL profile.
++               * The NULL profile grants no file access.
++               *
++               * This feature is used by changehat_apache.
++               *
++               * N.B from the null-profile the task can still changehat back
++               * out to the parent profile (assuming magic != NULL)
++               */
++              if (SUBDOMAIN_COMPLAIN(sd)) {
++                      LOG_HINT(sd->active, GFP_ATOMIC, HINT_UNKNOWN_HAT,
++                              "%s pid=%d "
++                              "profile=%s active=%s\n",
++                              hat_name,
++                              current->pid,
++                              BASE_PROFILE(sd->active)->name,
++                              sd->active->name);
++              } else {
++                      AA_DEBUG("%s: Unknown hatname '%s'. "
++                              "Changing to NULL profile "
++                              "(%s(%d) profile %s active %s)\n",
++                               __FUNCTION__,
++                               hat_name,
++                               current->comm, current->pid,
++                               BASE_PROFILE(sd->active)->name,
++                               sd->active->name);
++                      error = -EACCES;
++              }
++              aa_switch(sd, sd->active->null_profile);
++      }
++
++      return error;
++}
++
++/**
++ * aa_change_hat - change hat to/from subprofile
++ * @hat_name: specifies hat to change to
++ * @hat_magic: token to validate hat change
++ *
++ * Change to new @hat_name when current hat is top level profile, and store
++ * the @hat_magic in the current subdomain.  If the new @hat_name is
++ * %NULL, and the @hat_magic matches that stored in the current subdomain
++ * return to original top level profile.  Returns %0 on success, error
++ * otherwise.
++ */
++int aa_change_hat(const char *hat_name, u32 hat_magic)
++{
++      struct subdomain *sd = AA_SUBDOMAIN(current->security);
++      int error = 0;
++
++      AA_DEBUG("%s: %p, 0x%x (pid %d)\n",
++               __FUNCTION__,
++               hat_name, hat_magic,
++               current->pid);
++
++      /* Dump out above debugging in WARN mode if we are in AUDIT mode */
++      if (SUBDOMAIN_AUDIT(sd)) {
++              AA_WARN("%s: %s, 0x%x (pid %d)\n",
++                      __FUNCTION__, hat_name ? hat_name : "NULL",
++                      hat_magic, current->pid);
++      }
++
++      /* check to see if an unconfined process is doing a changehat. */
++      if (!__aa_is_confined(sd)) {
++              error = -EPERM;
++              goto out;
++      }
++
++      /* check to see if the confined process has any hats. */
++      if (list_empty(&BASE_PROFILE(sd->active)->sub) &&
++          !PROFILE_COMPLAIN(sd->active)) {
++              error = -ECHILD;
++              goto out;
++      }
++
++      /* Check whether current domain is parent
++       * or one of the sibling children
++       */
++      if (!IN_SUBPROFILE(sd->active)) {
++              /*
++               * parent
++               */
++              if (hat_name) {
++                      AA_DEBUG("%s: switching to %s, 0x%x\n",
++                               __FUNCTION__,
++                               hat_name,
++                               hat_magic);
++
++                      /*
++                       * N.B hat_magic == 0 has a special meaning
++                       * this indicates that the task may never changehat
++                       * back to it's parent, it will stay in this subhat
++                       * (or null-profile, if the hat doesn't exist) until
++                       * the task terminates
++                       */
++                      sd->hat_magic = hat_magic;
++                      error = do_change_hat(hat_name, sd);
++              } else {
++                      /* Got here via changehat(NULL, magic)
++                       *
++                       * We used to simply update the magic cookie.
++                       * That's an odd behaviour, so just do nothing.
++                       */
++              }
++      } else {
++              /*
++               * child -- check to make sure magic is same as what was
++               * passed when we switched into this profile,
++               * Handle special casing of NULL magic which confines task
++               * to subprofile and prohibits further changehats
++               */
++              if (hat_magic == sd->hat_magic && sd->hat_magic) {
++                      if (!hat_name) {
++                              /*
++                               * Got here via changehat(NULL, magic)
++                               * Return from subprofile, back to parent
++                               */
++                              aa_switch(sd, sd->active->parent);
++
++                              /* Reset hat_magic to zero.
++                               * New value will be passed on next changehat
++                               */
++                              sd->hat_magic = 0;
++                      } else {
++                              /* change to another (sibling) profile */
++                              error = do_change_hat(hat_name, sd);
++                      }
++              } else if (sd->hat_magic) {
++                      AA_ERROR("KILLING process %s(%d) "
++                               "Invalid change_hat() magic# 0x%x "
++                               "(hatname %s profile %s active %s)\n",
++                               current->comm, current->pid,
++                               hat_magic,
++                               hat_name ? hat_name : "NULL",
++                               BASE_PROFILE(sd->active)->name,
++                               sd->active->name);
++
++                      /* terminate current process */
++                      (void)send_sig_info(SIGKILL, NULL, current);
++              } else {        /* sd->hat_magic == NULL */
++                      AA_ERROR("KILLING process %s(%d) "
++                               "Task was confined to current subprofile "
++                               "(profile %s active %s)\n",
++                               current->comm, current->pid,
++                               BASE_PROFILE(sd->active)->name,
++                               sd->active->name);
++
++                      /* terminate current process */
++                      (void)send_sig_info(SIGKILL, NULL, current);
++              }
++
++      }
++
++out:
++      return error;
++}
+Index: b/security/apparmor/match/Kbuild
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/Kbuild
+@@ -0,0 +1,6 @@
++# Makefile for AppArmor aamatch submodule
++#
++
++obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_pcre.o
++
++aamatch_pcre-y := match_pcre.o pcre_exec.o
+Index: b/security/apparmor/match/Makefile
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/Makefile
+@@ -0,0 +1,5 @@
++# Makefile for AppArmor aamatch submodule
++#
++obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_pcre.o
++
++aamatch_pcre-y := match_pcre.o pcre_exec.o
+Index: b/security/apparmor/match/match.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/match.h
+@@ -0,0 +1,132 @@
++/*
++ *    Copyright (C) 2002-2005 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 submodule (match) prototypes
++ */
++
++#ifndef __MATCH_H
++#define __MATCH_H
++
++#include "../module_interface.h"
++#include "../apparmor.h"
++
++/* The following functions implement an interface used by the primary
++ * AppArmor module to perform name matching (n.b. "AppArmor" was previously
++ * called "SubDomain").
++
++ * aamatch_alloc
++ * aamatch_free
++ * aamatch_features
++ * aamatch_serialize
++ * aamatch_match
++ *
++ * The intent is for the primary module to export (via virtual fs entries)
++ * the features provided by the submodule (aamatch_features) so that the
++ * parser may only load policy that can be supported.
++ *
++ * The primary module will call aamatch_serialize to allow the submodule
++ * to consume submodule specific data from parser data stream and will call
++ * aamatch_match to determine if a pathname matches an aa_entry.
++ */
++
++typedef int (*aamatch_serializecb)
++      (struct aa_ext *, enum aa_code, void *, const char *);
++
++/**
++ * aamatch_alloc: allocate extradata (if necessary)
++ * @type: type of entry being allocated
++ * Return value: NULL indicates no data was allocated (ERR_PTR(x) on error)
++ */
++extern void* aamatch_alloc(enum entry_match_type type);
++
++/**
++ * aamatch_free: release data allocated by aamatch_alloc
++ * @entry_extradata: data previously allocated by aamatch_alloc
++ */
++extern void aamatch_free(void *entry_extradata);
++
++/**
++ * aamatch_features: return match types supported
++ * Return value: space seperated string (of types supported - use type=value
++ * to indicate variants of a type)
++ */
++extern const char* aamatch_features(void);
++
++/**
++ * aamatch_serialize: serialize extradata
++ * @entry_extradata: data previously allocated by aamatch_alloc
++ * @e: input stream
++ * @cb: callback fn (consume incoming data stream)
++ * Return value: 0 success, -ve error
++ */
++extern int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
++                           aamatch_serializecb cb);
++
++/**
++ * aamatch_match: determine if pathname matches entry
++ * @pathname: pathname to verify
++ * @entry_name: entry name
++ * @type: type of entry
++ * @entry_extradata: data previously allocated by aamatch_alloc
++ * Return value: 1 match, 0 othersise
++ */
++extern unsigned int aamatch_match(const char *pathname, const char *entry_name,
++                                enum entry_match_type type,
++                                void *entry_extradata);
++
++
++/**
++ * sd_getmatch_type - return string representation of entry_match_type
++ * @type: entry match type
++ */
++static inline const char *sd_getmatch_type(enum entry_match_type type)
++{
++      const char *names[] = {
++              "aa_entry_literal",
++              "aa_entry_tailglob",
++              "aa_entry_pattern",
++              "aa_entry_invalid"
++      };
++
++      if (type >= aa_entry_invalid) {
++              type = aa_entry_invalid;
++      }
++
++      return names[type];
++}
++
++/**
++ * aamatch_match_common - helper function to check if a pathname matches
++ * a literal/tailglob
++ * @path: path requested to search for
++ * @entry_name: name from aa_entry
++ * @type: type of entry
++ */
++static inline int aamatch_match_common(const char *path,
++                                         const char *entry_name,
++                                         enum entry_match_type type)
++{
++      int retval;
++
++      /* literal, no pattern matching characters */
++      if (type == aa_entry_literal) {
++              retval = (strcmp(entry_name, path) == 0);
++      /* trailing ** glob pattern */
++      } else if (type == aa_entry_tailglob) {
++              retval = (strncmp(entry_name, path,
++                                strlen(entry_name) - 2) == 0);
++      } else {
++              AA_WARN("%s: Invalid entry_match_type %d\n",
++                      __FUNCTION__, type);
++              retval = 0;
++      }
++
++      return retval;
++}
++
++#endif /* __MATCH_H */
+Index: b/security/apparmor/match/match_default.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/match_default.c
+@@ -0,0 +1,57 @@
++/*
++ *    Copyright (C) 2002-2005 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.
++ *
++ *    http://forge.novell.com/modules/xfmod/project/?apparmor
++ *
++ *    AppArmor default match submodule (literal and tailglob)
++ */
++
++#include <linux/module.h>
++#include "match.h"
++
++static const char *features="literal tailglob";
++
++void* aamatch_alloc(enum entry_match_type type)
++{
++      return NULL;
++}
++
++void aamatch_free(void *ptr)
++{
++}
++
++const char *aamatch_features(void)
++{
++      return features;
++}
++
++int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
++                    aamatch_serializecb cb)
++{
++      return 0;
++}
++
++unsigned int aamatch_match(const char *pathname, const char *entry_name,
++                         enum entry_match_type type, void *entry_extradata)
++{
++      int ret;
++
++      ret = aamatch_match_common(pathname, entry_name, type);
++
++      return ret;
++}
++
++EXPORT_SYMBOL_GPL(aamatch_alloc);
++EXPORT_SYMBOL_GPL(aamatch_free);
++EXPORT_SYMBOL_GPL(aamatch_features);
++EXPORT_SYMBOL_GPL(aamatch_serialize);
++EXPORT_SYMBOL_GPL(aamatch_match);
++
++MODULE_DESCRIPTION("AppArmor match module (aamatch) [default]");
++MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
++MODULE_LICENSE("GPL");
+Index: b/security/apparmor/match/match_pcre.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/match_pcre.c
+@@ -0,0 +1,169 @@
++/*
++ *    Copyright (C) 2002-2005 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.
++ *
++ *    http://forge.novell.com/modules/xfmod/project/?apparmor
++ *
++ *    AppArmor aamatch submodule (w/ pattern expansion).
++ *
++ *    This module makes use of a slightly modified version of the PCRE
++ *    library developed by Philip Hazel <ph10@cam.ac.uk>.  See the files
++ *    pcre_* in this directory.
++ */
++
++#include <linux/module.h>
++#include "match.h"
++#include "pcre_exec.h"
++#include "pcre_tables.h"
++
++static const char *features="literal tailglob pattern=pcre";
++
++struct aamatch_entry
++{
++      char *pattern;
++      pcre *compiled;
++};
++
++void* aamatch_alloc(enum entry_match_type entry_type)
++{
++void *ptr=NULL;
++
++      if (entry_type == aa_entry_pattern) {
++              ptr = kmalloc(sizeof(struct aamatch_entry), GFP_KERNEL);
++              if (ptr)
++                      memset(ptr, 0, sizeof(struct aamatch_entry));
++              else
++                      ptr=ERR_PTR(-ENOMEM);
++      } else if (entry_type != aa_entry_literal &&
++                 entry_type != aa_entry_tailglob) {
++              ptr = ERR_PTR(-EINVAL);
++      }
++
++      return ptr;
++}
++
++void aamatch_free(void *ptr)
++{
++      if (ptr) {
++              struct aamatch_entry *ed = (struct aamatch_entry *) ptr;
++              kfree(ed->pattern);
++              kfree(ed->compiled);    /* allocated by AA_READ_X */
++      }
++      kfree(ptr);
++}
++
++const char *aamatch_features(void)
++{
++      return features;
++}
++
++int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
++                    aamatch_serializecb cb)
++{
++#define AA_READ_X(E, C, D, N) \
++      do { \
++              if (!cb((E), (C), (D), (N))) { \
++                      error = -EINVAL; \
++                      goto done; \
++              }\
++      } while (0)
++
++      int error = 0;
++      u32 size, magic, opts;
++      u8 t_char;
++      struct aamatch_entry *ed = (struct aamatch_entry *) entry_extradata;
++
++      if (ed == NULL)
++              goto done;
++
++      AA_READ_X(e, AA_DYN_STRING, &ed->pattern, NULL);
++
++      /* size determines the real size of the pcre struct,
++         it is size_t - sizeof(pcre) on user side.
++         uschar must be the same in user and kernel space */
++      /* check that we are processing the correct structure */
++      AA_READ_X(e, AA_STRUCT, NULL, "pcre");
++      AA_READ_X(e, AA_U32, &size, NULL);
++      AA_READ_X(e, AA_U32, &magic, NULL);
++
++      /* the allocation of pcre is delayed because it depends on the size
++       * of the pattern */
++      ed->compiled = (pcre *) kmalloc(size + sizeof(pcre), GFP_KERNEL);
++      if (!ed->compiled) {
++              error = -ENOMEM;
++              goto done;
++      }
++
++      memset(ed->compiled, 0, size + sizeof(pcre));
++      ed->compiled->magic_number = magic;
++      ed->compiled->size = size + sizeof(pcre);
++
++      AA_READ_X(e, AA_U32, &opts, NULL);
++      ed->compiled->options = opts;
++      AA_READ_X(e, AA_U16, &ed->compiled->top_bracket, NULL);
++      AA_READ_X(e, AA_U16, &ed->compiled->top_backref, NULL);
++      AA_READ_X(e, AA_U8, &t_char, NULL);
++      ed->compiled->first_char = t_char;
++      AA_READ_X(e, AA_U8, &t_char, NULL);
++      ed->compiled->req_char = t_char;
++      AA_READ_X(e, AA_U8, &t_char, NULL);
++      ed->compiled->code[0] = t_char;
++
++      AA_READ_X(e, AA_STATIC_BLOB, &ed->compiled->code[1], NULL);
++
++      AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
++
++      /* stitch in pcre patterns, it was NULLed out by parser
++       * pcre_default_tables defined in pcre_tables.h */
++      ed->compiled->tables = pcre_default_tables;
++
++done:
++      if (error != 0 && ed) {
++              kfree(ed->pattern); /* allocated by AA_READ_X */
++              kfree(ed->compiled);
++              ed->pattern = NULL;
++              ed->compiled = NULL;
++      }
++
++      return error;
++}
++
++unsigned int aamatch_match(const char *pathname, const char *entry_name,
++                         enum entry_match_type entry_type, void *entry_extradata)
++{
++      int ret;
++
++      if (entry_type == aa_entry_pattern) {
++              int pcreret;
++              struct aamatch_entry *ed =
++                      (struct aamatch_entry *) entry_extradata;
++
++              pcreret = pcre_exec(ed->compiled, NULL,
++                                  pathname, strlen(pathname),
++                                  0, 0, NULL, 0);
++
++              ret = (pcreret >= 0);
++
++              // XXX - this needs access to subdomain_debug,  hmmm
++              //AA_DEBUG("%s(%d): %s %s %d\n", __FUNCTION__,
++              //       ret, pathname, ed->pattern, pcreret);
++      } else {
++              ret = aamatch_match_common(pathname, entry_name, entry_type);
++      }
++
++        return ret;
++}
++
++EXPORT_SYMBOL_GPL(aamatch_alloc);
++EXPORT_SYMBOL_GPL(aamatch_free);
++EXPORT_SYMBOL_GPL(aamatch_features);
++EXPORT_SYMBOL_GPL(aamatch_serialize);
++EXPORT_SYMBOL_GPL(aamatch_match);
++
++MODULE_DESCRIPTION("AppArmor aa_match module [pcre]");
++MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
++MODULE_LICENSE("GPL");
+Index: b/security/apparmor/match/pcre_exec.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/pcre_exec.c
+@@ -0,0 +1,1945 @@
++/*
++ *  This is a modified version of pcre.c containing only the code/data
++ *  required to support pcre_exec()
++ */
++
++
++/*************************************************
++*      Perl-Compatible Regular Expressions       *
++*************************************************/
++
++/*
++This is a library of functions to support regular expressions whose syntax
++and semantics are as close as possible to those of the Perl 5 language. See
++the file Tech.Notes for some information on the internals.
++
++Written by: Philip Hazel <ph10@cam.ac.uk>
++
++           Copyright (c) 1997-2001 University of Cambridge
++
++-----------------------------------------------------------------------------
++Permission is granted to anyone to use this software for any purpose on any
++computer system, and to redistribute it freely, subject to the following
++restrictions:
++
++1. This software is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
++
++2. The origin of this software must not be misrepresented, either by
++   explicit claim or by omission.
++
++3. Altered versions must be plainly marked as such, and must not be
++   misrepresented as being the original software.
++
++4. If PCRE is embedded in any software that is released under the GNU
++   General Purpose Licence (GPL), then the terms of that licence shall
++   supersede any condition above with which it is incompatible.
++-----------------------------------------------------------------------------
++*/
++
++
++/* Define DEBUG to get debugging output on stdout. */
++
++/* #define DEBUG */
++
++/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef
++inline, and there are *still* stupid compilers about that don't like indented
++pre-processor statements. I suppose it's only been 10 years... */
++
++#ifdef DEBUG
++#define DPRINTF(p) PCRE_PRINTF p
++#else
++#define DPRINTF(p) /*nothing*/
++#endif
++
++/* Include the internals header, which itself includes Standard C headers plus
++the external pcre header. */
++
++#include "pcre_exec.h"
++
++
++/* ----  CODE DELETED ---- */
++
++
++/* Min and max values for the common repeats; for the maxima, 0 => infinity */
++
++static const char rep_min[] = { 0, 0, 1, 1, 0, 0 };
++static const char rep_max[] = { 0, 0, 0, 0, 1, 1 };
++
++
++/* ----  CODE DELETED ---- */
++
++
++/* Structure for building a chain of data that actually lives on the
++ * stack, for holding the values of the subject pointer at the start of each
++ * subpattern, so as to detect when an empty string has been matched by a
++ * subpattern - to break infinite loops. */
++
++typedef struct eptrblock {
++        struct eptrblock *prev;
++          const uschar *saved_eptr;
++} eptrblock;
++
++/* Flag bits for the match() function */
++
++#define match_condassert   0x01    /* Called to check a condition assertion */
++#define match_isgroup      0x02    /* Set if start of bracketed group */
++
++
++/* ----  CODE DELETED ---- */
++
++
++/*************************************************
++ * *               Global variables                 *
++ * *************************************************/
++
++/* PCRE is thread-clean and doesn't use any global variables in the normal
++ * sense. However, it calls memory allocation and free functions via the two
++ * indirections below, which are can be changed by the caller, but are shared
++ * between all threads. */
++
++#ifdef __KERNEL__
++static void *kern_malloc(size_t sz)
++{
++              return kmalloc(sz, GFP_KERNEL);
++}
++void  *(*pcre_malloc)(size_t) = kern_malloc;
++void  (*pcre_free)(const void *) = kfree;
++#else
++void  *(*pcre_malloc)(size_t) = malloc;
++void  (*pcre_free)(const void *) = free;
++#endif
++
++
++/*************************************************
++ * *    Macros and tables for character handling    *
++ * *************************************************/
++
++/* When UTF-8 encoding is being used, a character is no longer just a single
++ * byte. The macros for character handling generate simple sequences when used in
++ * byte-mode, and more complicated ones for UTF-8 characters. */
++
++#ifndef SUPPORT_UTF8
++#define GETCHARINC(c, eptr) c = *eptr++;
++#define GETCHARLEN(c, eptr, len) c = *eptr;
++#define BACKCHAR(eptr)
++#endif
++
++/* ----  CODE DELETED ---- */
++
++#ifdef DEBUG
++/*************************************************
++*        Debugging function to print chars       *
++*************************************************/
++
++/* Print a sequence of chars in printable format, stopping at the end of the
++subject if the requested.
++
++Arguments:
++  p           points to characters
++  length      number to print
++  is_subject  TRUE if printing from within md->start_subject
++  md          pointer to matching data block, if is_subject is TRUE
++
++Returns:     nothing
++*/
++
++static void
++pchars(const uschar *p, int length, BOOL is_subject, match_data *md)
++{
++int c;
++if (is_subject && length > md->end_subject - p) length = md->end_subject - p;
++while (length-- > 0)
++  if (isprint(c = *(p++))) PCRE_PRINTF("%c", c); else PCRE_PRINTF("\\x%02x", c);
++}
++#endif /* DEBUG */
++
++/* ----  CODE DELETED ---- */
++
++
++/*************************************************
++*          Match a back-reference                *
++*************************************************/
++
++/* If a back reference hasn't been set, the length that is passed is greater
++than the number of characters left in the string, so the match fails.
++
++Arguments:
++  offset      index into the offset vector
++  eptr        points into the subject
++  length      length to be matched
++  md          points to match data block
++  ims         the ims flags
++
++Returns:      TRUE if matched
++*/
++
++static BOOL
++match_ref(int offset, register const uschar *eptr, int length, match_data *md,
++  unsigned long int ims)
++{
++const uschar *p = md->start_subject + md->offset_vector[offset];
++
++#ifdef DEBUG
++if (eptr >= md->end_subject)
++  PCRE_PRINTF("matching subject <null>");
++else
++  {
++  PCRE_PRINTF("matching subject ");
++  pchars(eptr, length, TRUE, md);
++  }
++PCRE_PRINTF(" against backref ");
++pchars(p, length, FALSE, md);
++PCRE_PRINTF("\n");
++#endif
++
++/* Always fail if not enough characters left */
++
++if (length > md->end_subject - eptr) return FALSE;
++
++/* Separate the caselesss case for speed */
++
++if ((ims & PCRE_CASELESS) != 0)
++  {
++  while (length-- > 0)
++    if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE;
++  }
++else
++  { while (length-- > 0) if (*p++ != *eptr++) return FALSE; }
++
++return TRUE;
++}
++
++
++/*************************************************
++*         Match from current position            *
++*************************************************/
++
++/* On entry ecode points to the first opcode, and eptr to the first character
++in the subject string, while eptrb holds the value of eptr at the start of the
++last bracketed group - used for breaking infinite loops matching zero-length
++strings.
++
++Arguments:
++   eptr        pointer in subject
++   ecode       position in code
++   offset_top  current top pointer
++   md          pointer to "static" info for the match
++   ims         current /i, /m, and /s options
++   eptrb       pointer to chain of blocks containing eptr at start of
++                 brackets - for testing for empty matches
++   flags       can contain
++                 match_condassert - this is an assertion condition
++                 match_isgroup - this is the start of a bracketed group
++
++Returns:       TRUE if matched
++*/
++
++static BOOL
++match(register const uschar *eptr, register const uschar *ecode,
++  int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb,
++  int flags)
++{
++unsigned long int original_ims = ims;   /* Save for resetting on ')' */
++eptrblock newptrb;
++
++/* At the start of a bracketed group, add the current subject pointer to the
++stack of such pointers, to be re-instated at the end of the group when we hit
++the closing ket. When match() is called in other circumstances, we don't add to
++the stack. */
++
++if ((flags & match_isgroup) != 0)
++  {
++  newptrb.prev = eptrb;
++  newptrb.saved_eptr = eptr;
++  eptrb = &newptrb;
++  }
++
++/* Now start processing the operations. */
++
++for (;;)
++  {
++  int op = (int)*ecode;
++  int min, max, ctype;
++  register int i;
++  register int c;
++  BOOL minimize = FALSE;
++
++  /* Opening capturing bracket. If there is space in the offset vector, save
++  the current subject position in the working slot at the top of the vector. We
++  mustn't change the current values of the data slot, because they may be set
++  from a previous iteration of this group, and be referred to by a reference
++  inside the group.
++
++  If the bracket fails to match, we need to restore this value and also the
++  values of the final offsets, in case they were set by a previous iteration of
++  the same bracket.
++
++  If there isn't enough space in the offset vector, treat this as if it were a
++  non-capturing bracket. Don't worry about setting the flag for the error case
++  here; that is handled in the code for KET. */
++
++  if (op > OP_BRA)
++    {
++    int offset;
++    int number = op - OP_BRA;
++
++    /* For extended extraction brackets (large number), we have to fish out the
++    number from a dummy opcode at the start. */
++
++    if (number > EXTRACT_BASIC_MAX) number = (ecode[4] << 8) | ecode[5];
++    offset = number << 1;
++
++#ifdef DEBUG
++    PCRE_PRINTF("start bracket %d subject=", number);
++    pchars(eptr, 16, TRUE, md);
++    PCRE_PRINTF("\n");
++#endif
++
++    if (offset < md->offset_max)
++      {
++      int save_offset1 = md->offset_vector[offset];
++      int save_offset2 = md->offset_vector[offset+1];
++      int save_offset3 = md->offset_vector[md->offset_end - number];
++
++      DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3));
++      md->offset_vector[md->offset_end - number] = eptr - md->start_subject;
++
++      do
++        {
++        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
++          return TRUE;
++        ecode += (ecode[1] << 8) + ecode[2];
++        }
++      while (*ecode == OP_ALT);
++
++      DPRINTF(("bracket %d failed\n", number));
++
++      md->offset_vector[offset] = save_offset1;
++      md->offset_vector[offset+1] = save_offset2;
++      md->offset_vector[md->offset_end - number] = save_offset3;
++
++      return FALSE;
++      }
++
++    /* Insufficient room for saving captured contents */
++
++    else op = OP_BRA;
++    }
++
++  /* Other types of node can be handled by a switch */
++
++  switch(op)
++    {
++    case OP_BRA:     /* Non-capturing bracket: optimized */
++    DPRINTF(("start bracket 0\n"));
++    do
++      {
++      if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
++        return TRUE;
++      ecode += (ecode[1] << 8) + ecode[2];
++      }
++    while (*ecode == OP_ALT);
++    DPRINTF(("bracket 0 failed\n"));
++    return FALSE;
++
++    /* Conditional group: compilation checked that there are no more than
++    two branches. If the condition is false, skipping the first branch takes us
++    past the end if there is only one branch, but that's OK because that is
++    exactly what going to the ket would do. */
++
++    case OP_COND:
++    if (ecode[3] == OP_CREF)         /* Condition is extraction test */
++      {
++      int offset = (ecode[4] << 9) | (ecode[5] << 1); /* Doubled ref number */
++      return match(eptr,
++        ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)?
++          6 : 3 + (ecode[1] << 8) + ecode[2]),
++        offset_top, md, ims, eptrb, match_isgroup);
++      }
++
++    /* The condition is an assertion. Call match() to evaluate it - setting
++    the final argument TRUE causes it to stop at the end of an assertion. */
++
++    else
++      {
++      if (match(eptr, ecode+3, offset_top, md, ims, NULL,
++          match_condassert | match_isgroup))
++        {
++        ecode += 3 + (ecode[4] << 8) + ecode[5];
++        while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2];
++        }
++      else ecode += (ecode[1] << 8) + ecode[2];
++      return match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup);
++      }
++    /* Control never reaches here */
++
++    /* Skip over conditional reference or large extraction number data if
++    encountered. */
++
++    case OP_CREF:
++    case OP_BRANUMBER:
++    ecode += 3;
++    break;
++
++    /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched
++    an empty string - recursion will then try other alternatives, if any. */
++
++    case OP_END:
++    if (md->notempty && eptr == md->start_match) return FALSE;
++    md->end_match_ptr = eptr;          /* Record where we ended */
++    md->end_offset_top = offset_top;   /* and how many extracts were taken */
++    return TRUE;
++
++    /* Change option settings */
++
++    case OP_OPT:
++    ims = ecode[1];
++    ecode += 2;
++    DPRINTF(("ims set to %02lx\n", ims));
++    break;
++
++    /* Assertion brackets. Check the alternative branches in turn - the
++    matching won't pass the KET for an assertion. If any one branch matches,
++    the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
++    start of each branch to move the current point backwards, so the code at
++    this level is identical to the lookahead case. */
++
++    case OP_ASSERT:
++    case OP_ASSERTBACK:
++    do
++      {
++      if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) break;
++      ecode += (ecode[1] << 8) + ecode[2];
++      }
++    while (*ecode == OP_ALT);
++    if (*ecode == OP_KET) return FALSE;
++
++    /* If checking an assertion for a condition, return TRUE. */
++
++    if ((flags & match_condassert) != 0) return TRUE;
++
++    /* Continue from after the assertion, updating the offsets high water
++    mark, since extracts may have been taken during the assertion. */
++
++    do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
++    ecode += 3;
++    offset_top = md->end_offset_top;
++    continue;
++
++    /* Negative assertion: all branches must fail to match */
++
++    case OP_ASSERT_NOT:
++    case OP_ASSERTBACK_NOT:
++    do
++      {
++      if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup))
++        return FALSE;
++      ecode += (ecode[1] << 8) + ecode[2];
++      }
++    while (*ecode == OP_ALT);
++
++    if ((flags & match_condassert) != 0) return TRUE;
++
++    ecode += 3;
++    continue;
++
++    /* Move the subject pointer back. This occurs only at the start of
++    each branch of a lookbehind assertion. If we are too close to the start to
++    move back, this match function fails. When working with UTF-8 we move
++    back a number of characters, not bytes. */
++
++    case OP_REVERSE:
++#ifdef SUPPORT_UTF8
++    c = (ecode[1] << 8) + ecode[2];
++    for (i = 0; i < c; i++)
++      {
++      eptr--;
++      BACKCHAR(eptr)
++      }
++#else
++    eptr -= (ecode[1] << 8) + ecode[2];
++#endif
++
++    if (eptr < md->start_subject) return FALSE;
++    ecode += 3;
++    break;
++
++    /* Recursion matches the current regex, nested. If there are any capturing
++    brackets started but not finished, we have to save their starting points
++    and reinstate them after the recursion. However, we don't know how many
++    such there are (offset_top records the completed total) so we just have
++    to save all the potential data. There may be up to 99 such values, which
++    is a bit large to put on the stack, but using malloc for small numbers
++    seems expensive. As a compromise, the stack is used when there are fewer
++    than 16 values to store; otherwise malloc is used. A problem is what to do
++    if the malloc fails ... there is no way of returning to the top level with
++    an error. Save the top 15 values on the stack, and accept that the rest
++    may be wrong. */
++
++    case OP_RECURSE:
++      {
++      BOOL rc;
++      int *save;
++      int stacksave[15];
++
++      c = md->offset_max;
++
++      if (c < 16) save = stacksave; else
++        {
++        save = (int *)(pcre_malloc)((c+1) * sizeof(int));
++        if (save == NULL)
++          {
++          save = stacksave;
++          c = 15;
++          }
++        }
++
++      for (i = 1; i <= c; i++)
++        save[i] = md->offset_vector[md->offset_end - i];
++      rc = match(eptr, md->start_pattern, offset_top, md, ims, eptrb,
++        match_isgroup);
++      for (i = 1; i <= c; i++)
++        md->offset_vector[md->offset_end - i] = save[i];
++      if (save != stacksave) (pcre_free)(save);
++      if (!rc) return FALSE;
++
++      /* In case the recursion has set more capturing values, save the final
++      number, then move along the subject till after the recursive match,
++      and advance one byte in the pattern code. */
++
++      offset_top = md->end_offset_top;
++      eptr = md->end_match_ptr;
++      ecode++;
++      }
++    break;
++
++    /* "Once" brackets are like assertion brackets except that after a match,
++    the point in the subject string is not moved back. Thus there can never be
++    a move back into the brackets. Check the alternative branches in turn - the
++    matching won't pass the KET for this kind of subpattern. If any one branch
++    matches, we carry on as at the end of a normal bracket, leaving the subject
++    pointer. */
++
++    case OP_ONCE:
++      {
++      const uschar *prev = ecode;
++      const uschar *saved_eptr = eptr;
++
++      do
++        {
++        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup))
++          break;
++        ecode += (ecode[1] << 8) + ecode[2];
++        }
++      while (*ecode == OP_ALT);
++
++      /* If hit the end of the group (which could be repeated), fail */
++
++      if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE;
++
++      /* Continue as from after the assertion, updating the offsets high water
++      mark, since extracts may have been taken. */
++
++      do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
++
++      offset_top = md->end_offset_top;
++      eptr = md->end_match_ptr;
++
++      /* For a non-repeating ket, just continue at this level. This also
++      happens for a repeating ket if no characters were matched in the group.
++      This is the forcible breaking of infinite loops as implemented in Perl
++      5.005. If there is an options reset, it will get obeyed in the normal
++      course of events. */
++
++      if (*ecode == OP_KET || eptr == saved_eptr)
++        {
++        ecode += 3;
++        break;
++        }
++
++      /* The repeating kets try the rest of the pattern or restart from the
++      preceding bracket, in the appropriate order. We need to reset any options
++      that changed within the bracket before re-running it, so check the next
++      opcode. */
++
++      if (ecode[3] == OP_OPT)
++        {
++        ims = (ims & ~PCRE_IMS) | ecode[4];
++        DPRINTF(("ims set to %02lx at group repeat\n", ims));
++        }
++
++      if (*ecode == OP_KETRMIN)
++        {
++        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) ||
++            match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup))
++              return TRUE;
++        }
++      else  /* OP_KETRMAX */
++        {
++        if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) ||
++            match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE;
++        }
++      }
++    return FALSE;
++
++    /* An alternation is the end of a branch; scan along to find the end of the
++    bracketed group and go to there. */
++
++    case OP_ALT:
++    do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT);
++    break;
++
++    /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating
++    that it may occur zero times. It may repeat infinitely, or not at all -
++    i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper
++    repeat limits are compiled as a number of copies, with the optional ones
++    preceded by BRAZERO or BRAMINZERO. */
++
++    case OP_BRAZERO:
++      {
++      const uschar *next = ecode+1;
++      if (match(eptr, next, offset_top, md, ims, eptrb, match_isgroup))
++        return TRUE;
++      do next += (next[1] << 8) + next[2]; while (*next == OP_ALT);
++      ecode = next + 3;
++      }
++    break;
++
++    case OP_BRAMINZERO:
++      {
++      const uschar *next = ecode+1;
++      do next += (next[1] << 8) + next[2]; while (*next == OP_ALT);
++      if (match(eptr, next+3, offset_top, md, ims, eptrb, match_isgroup))
++        return TRUE;
++      ecode++;
++      }
++    break;
++
++    /* End of a group, repeated or non-repeating. If we are at the end of
++    an assertion "group", stop matching and return TRUE, but record the
++    current high water mark for use by positive assertions. Do this also
++    for the "once" (not-backup up) groups. */
++
++    case OP_KET:
++    case OP_KETRMIN:
++    case OP_KETRMAX:
++      {
++      const uschar *prev = ecode - (ecode[1] << 8) - ecode[2];
++      const uschar *saved_eptr = eptrb->saved_eptr;
++
++      eptrb = eptrb->prev;    /* Back up the stack of bracket start pointers */
++
++      if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT ||
++          *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT ||
++          *prev == OP_ONCE)
++        {
++        md->end_match_ptr = eptr;      /* For ONCE */
++        md->end_offset_top = offset_top;
++        return TRUE;
++        }
++
++      /* In all other cases except a conditional group we have to check the
++      group number back at the start and if necessary complete handling an
++      extraction by setting the offsets and bumping the high water mark. */
++
++      if (*prev != OP_COND)
++        {
++        int offset;
++        int number = *prev - OP_BRA;
++
++        /* For extended extraction brackets (large number), we have to fish out
++        the number from a dummy opcode at the start. */
++
++        if (number > EXTRACT_BASIC_MAX) number = (prev[4] << 8) | prev[5];
++        offset = number << 1;
++
++#ifdef DEBUG
++        PCRE_PRINTF("end bracket %d", number);
++        PCRE_PRINTF("\n");
++#endif
++
++        if (number > 0)
++          {
++          if (offset >= md->offset_max) md->offset_overflow = TRUE; else
++            {
++            md->offset_vector[offset] =
++              md->offset_vector[md->offset_end - number];
++            md->offset_vector[offset+1] = eptr - md->start_subject;
++            if (offset_top <= offset) offset_top = offset + 2;
++            }
++          }
++        }
++
++      /* Reset the value of the ims flags, in case they got changed during
++      the group. */
++
++      ims = original_ims;
++      DPRINTF(("ims reset to %02lx\n", ims));
++
++      /* For a non-repeating ket, just continue at this level. This also
++      happens for a repeating ket if no characters were matched in the group.
++      This is the forcible breaking of infinite loops as implemented in Perl
++      5.005. If there is an options reset, it will get obeyed in the normal
++      course of events. */
++
++      if (*ecode == OP_KET || eptr == saved_eptr)
++        {
++        ecode += 3;
++        break;
++        }
++
++      /* The repeating kets try the rest of the pattern or restart from the
++      preceding bracket, in the appropriate order. */
++
++      if (*ecode == OP_KETRMIN)
++        {
++        if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) ||
++            match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup))
++              return TRUE;
++        }
++      else  /* OP_KETRMAX */
++        {
++        if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) ||
++            match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE;
++        }
++      }
++    return FALSE;
++
++    /* Start of subject unless notbol, or after internal newline if multiline */
++
++    case OP_CIRC:
++    if (md->notbol && eptr == md->start_subject) return FALSE;
++    if ((ims & PCRE_MULTILINE) != 0)
++      {
++      if (eptr != md->start_subject && eptr[-1] != NEWLINE) return FALSE;
++      ecode++;
++      break;
++      }
++    /* ... else fall through */
++
++    /* Start of subject assertion */
++
++    case OP_SOD:
++    if (eptr != md->start_subject) return FALSE;
++    ecode++;
++    break;
++
++    /* Assert before internal newline if multiline, or before a terminating
++    newline unless endonly is set, else end of subject unless noteol is set. */
++
++    case OP_DOLL:
++    if ((ims & PCRE_MULTILINE) != 0)
++      {
++      if (eptr < md->end_subject) { if (*eptr != NEWLINE) return FALSE; }
++        else { if (md->noteol) return FALSE; }
++      ecode++;
++      break;
++      }
++    else
++      {
++      if (md->noteol) return FALSE;
++      if (!md->endonly)
++        {
++        if (eptr < md->end_subject - 1 ||
++           (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE;
++
++        ecode++;
++        break;
++        }
++      }
++    /* ... else fall through */
++
++    /* End of subject assertion (\z) */
++
++    case OP_EOD:
++    if (eptr < md->end_subject) return FALSE;
++    ecode++;
++    break;
++
++    /* End of subject or ending \n assertion (\Z) */
++
++    case OP_EODN:
++    if (eptr < md->end_subject - 1 ||
++       (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE;
++    ecode++;
++    break;
++
++    /* Word boundary assertions */
++
++    case OP_NOT_WORD_BOUNDARY:
++    case OP_WORD_BOUNDARY:
++      {
++      BOOL prev_is_word = (eptr != md->start_subject) &&
++        ((md->ctypes[eptr[-1]] & ctype_word) != 0);
++      BOOL cur_is_word = (eptr < md->end_subject) &&
++        ((md->ctypes[*eptr] & ctype_word) != 0);
++      if ((*ecode++ == OP_WORD_BOUNDARY)?
++           cur_is_word == prev_is_word : cur_is_word != prev_is_word)
++        return FALSE;
++      }
++    break;
++
++    /* Match a single character type; inline for speed */
++
++    case OP_ANY:
++    if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE)
++      return FALSE;
++    if (eptr++ >= md->end_subject) return FALSE;
++#ifdef SUPPORT_UTF8
++    if (md->utf8)
++      while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
++#endif
++    ecode++;
++    break;
++
++    case OP_NOT_DIGIT:
++    if (eptr >= md->end_subject ||
++       (md->ctypes[*eptr++] & ctype_digit) != 0)
++      return FALSE;
++    ecode++;
++    break;
++
++    case OP_DIGIT:
++    if (eptr >= md->end_subject ||
++       (md->ctypes[*eptr++] & ctype_digit) == 0)
++      return FALSE;
++    ecode++;
++    break;
++
++    case OP_NOT_WHITESPACE:
++    if (eptr >= md->end_subject ||
++       (md->ctypes[*eptr++] & ctype_space) != 0)
++      return FALSE;
++    ecode++;
++    break;
++
++    case OP_WHITESPACE:
++    if (eptr >= md->end_subject ||
++       (md->ctypes[*eptr++] & ctype_space) == 0)
++      return FALSE;
++    ecode++;
++    break;
++
++    case OP_NOT_WORDCHAR:
++    if (eptr >= md->end_subject ||
++       (md->ctypes[*eptr++] & ctype_word) != 0)
++      return FALSE;
++    ecode++;
++    break;
++
++    case OP_WORDCHAR:
++    if (eptr >= md->end_subject ||
++       (md->ctypes[*eptr++] & ctype_word) == 0)
++      return FALSE;
++    ecode++;
++    break;
++
++    /* Match a back reference, possibly repeatedly. Look past the end of the
++    item to see if there is repeat information following. The code is similar
++    to that for character classes, but repeated for efficiency. Then obey
++    similar code to character type repeats - written out again for speed.
++    However, if the referenced string is the empty string, always treat
++    it as matched, any number of times (otherwise there could be infinite
++    loops). */
++
++    case OP_REF:
++      {
++      int length;
++      int offset = (ecode[1] << 9) | (ecode[2] << 1); /* Doubled ref number */
++      ecode += 3;                                     /* Advance past item */
++
++      /* If the reference is unset, set the length to be longer than the amount
++      of subject left; this ensures that every attempt at a match fails. We
++      can't just fail here, because of the possibility of quantifiers with zero
++      minima. */
++
++      length = (offset >= offset_top || md->offset_vector[offset] < 0)?
++        md->end_subject - eptr + 1 :
++        md->offset_vector[offset+1] - md->offset_vector[offset];
++
++      /* Set up for repetition, or handle the non-repeated case */
++
++      switch (*ecode)
++        {
++        case OP_CRSTAR:
++        case OP_CRMINSTAR:
++        case OP_CRPLUS:
++        case OP_CRMINPLUS:
++        case OP_CRQUERY:
++        case OP_CRMINQUERY:
++        c = *ecode++ - OP_CRSTAR;
++        minimize = (c & 1) != 0;
++        min = rep_min[c];                 /* Pick up values from tables; */
++        max = rep_max[c];                 /* zero for max => infinity */
++        if (max == 0) max = INT_MAX;
++        break;
++
++        case OP_CRRANGE:
++        case OP_CRMINRANGE:
++        minimize = (*ecode == OP_CRMINRANGE);
++        min = (ecode[1] << 8) + ecode[2];
++        max = (ecode[3] << 8) + ecode[4];
++        if (max == 0) max = INT_MAX;
++        ecode += 5;
++        break;
++
++        default:               /* No repeat follows */
++        if (!match_ref(offset, eptr, length, md, ims)) return FALSE;
++        eptr += length;
++        continue;              /* With the main loop */
++        }
++
++      /* If the length of the reference is zero, just continue with the
++      main loop. */
++
++      if (length == 0) continue;
++
++      /* First, ensure the minimum number of matches are present. We get back
++      the length of the reference string explicitly rather than passing the
++      address of eptr, so that eptr can be a register variable. */
++
++      for (i = 1; i <= min; i++)
++        {
++        if (!match_ref(offset, eptr, length, md, ims)) return FALSE;
++        eptr += length;
++        }
++
++      /* If min = max, continue at the same level without recursion.
++      They are not both allowed to be zero. */
++
++      if (min == max) continue;
++
++      /* If minimizing, keep trying and advancing the pointer */
++
++      if (minimize)
++        {
++        for (i = min;; i++)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          if (i >= max || !match_ref(offset, eptr, length, md, ims))
++            return FALSE;
++          eptr += length;
++          }
++        /* Control never gets here */
++        }
++
++      /* If maximizing, find the longest string and work backwards */
++
++      else
++        {
++        const uschar *pp = eptr;
++        for (i = min; i < max; i++)
++          {
++          if (!match_ref(offset, eptr, length, md, ims)) break;
++          eptr += length;
++          }
++        while (eptr >= pp)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          eptr -= length;
++          }
++        return FALSE;
++        }
++      }
++    /* Control never gets here */
++
++
++
++    /* Match a character class, possibly repeatedly. Look past the end of the
++    item to see if there is repeat information following. Then obey similar
++    code to character type repeats - written out again for speed. */
++
++    case OP_CLASS:
++      {
++      const uschar *data = ecode + 1;  /* Save for matching */
++      ecode += 33;                     /* Advance past the item */
++
++      switch (*ecode)
++        {
++        case OP_CRSTAR:
++        case OP_CRMINSTAR:
++        case OP_CRPLUS:
++        case OP_CRMINPLUS:
++        case OP_CRQUERY:
++        case OP_CRMINQUERY:
++        c = *ecode++ - OP_CRSTAR;
++        minimize = (c & 1) != 0;
++        min = rep_min[c];                 /* Pick up values from tables; */
++        max = rep_max[c];                 /* zero for max => infinity */
++        if (max == 0) max = INT_MAX;
++        break;
++
++        case OP_CRRANGE:
++        case OP_CRMINRANGE:
++        minimize = (*ecode == OP_CRMINRANGE);
++        min = (ecode[1] << 8) + ecode[2];
++        max = (ecode[3] << 8) + ecode[4];
++        if (max == 0) max = INT_MAX;
++        ecode += 5;
++        break;
++
++        default:               /* No repeat follows */
++        min = max = 1;
++        break;
++        }
++
++      /* First, ensure the minimum number of matches are present. */
++
++      for (i = 1; i <= min; i++)
++        {
++        if (eptr >= md->end_subject) return FALSE;
++        GETCHARINC(c, eptr)         /* Get character; increment eptr */
++
++#ifdef SUPPORT_UTF8
++        /* We do not yet support class members > 255 */
++        if (c > 255) return FALSE;
++#endif
++
++        if ((data[c/8] & (1 << (c&7))) != 0) continue;
++        return FALSE;
++        }
++
++      /* If max == min we can continue with the main loop without the
++      need to recurse. */
++
++      if (min == max) continue;
++
++      /* If minimizing, keep testing the rest of the expression and advancing
++      the pointer while it matches the class. */
++
++      if (minimize)
++        {
++        for (i = min;; i++)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          if (i >= max || eptr >= md->end_subject) return FALSE;
++          GETCHARINC(c, eptr)       /* Get character; increment eptr */
++
++#ifdef SUPPORT_UTF8
++          /* We do not yet support class members > 255 */
++          if (c > 255) return FALSE;
++#endif
++          if ((data[c/8] & (1 << (c&7))) != 0) continue;
++          return FALSE;
++          }
++        /* Control never gets here */
++        }
++
++      /* If maximizing, find the longest possible run, then work backwards. */
++
++      else
++        {
++        const uschar *pp = eptr;
++        int len = 1;
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject) break;
++          GETCHARLEN(c, eptr, len)  /* Get character, set length if UTF-8 */
++
++#ifdef SUPPORT_UTF8
++          /* We do not yet support class members > 255 */
++          if (c > 255) break;
++#endif
++          if ((data[c/8] & (1 << (c&7))) == 0) break;
++          eptr += len;
++          }
++
++        while (eptr >= pp)
++          {
++          if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++
++#ifdef SUPPORT_UTF8
++          BACKCHAR(eptr)
++#endif
++          }
++        return FALSE;
++        }
++      }
++    /* Control never gets here */
++
++    /* Match a run of characters */
++
++    case OP_CHARS:
++      {
++      register int length = ecode[1];
++      ecode += 2;
++
++#ifdef DEBUG    /* Sigh. Some compilers never learn. */
++      if (eptr >= md->end_subject)
++        PCRE_PRINTF("matching subject <null> against pattern ");
++      else
++        {
++        PCRE_PRINTF("matching subject ");
++        pchars(eptr, length, TRUE, md);
++        PCRE_PRINTF(" against pattern ");
++        }
++      pchars(ecode, length, FALSE, md);
++      PCRE_PRINTF("\n");
++#endif
++
++      if (length > md->end_subject - eptr) return FALSE;
++      if ((ims & PCRE_CASELESS) != 0)
++        {
++        while (length-- > 0)
++          if (md->lcc[*ecode++] != md->lcc[*eptr++])
++            return FALSE;
++        }
++      else
++        {
++        while (length-- > 0) if (*ecode++ != *eptr++) return FALSE;
++        }
++      }
++    break;
++
++    /* Match a single character repeatedly; different opcodes share code. */
++
++    case OP_EXACT:
++    min = max = (ecode[1] << 8) + ecode[2];
++    ecode += 3;
++    goto REPEATCHAR;
++
++    case OP_UPTO:
++    case OP_MINUPTO:
++    min = 0;
++    max = (ecode[1] << 8) + ecode[2];
++    minimize = *ecode == OP_MINUPTO;
++    ecode += 3;
++    goto REPEATCHAR;
++
++    case OP_STAR:
++    case OP_MINSTAR:
++    case OP_PLUS:
++    case OP_MINPLUS:
++    case OP_QUERY:
++    case OP_MINQUERY:
++    c = *ecode++ - OP_STAR;
++    minimize = (c & 1) != 0;
++    min = rep_min[c];                 /* Pick up values from tables; */
++    max = rep_max[c];                 /* zero for max => infinity */
++    if (max == 0) max = INT_MAX;
++
++    /* Common code for all repeated single-character matches. We can give
++    up quickly if there are fewer than the minimum number of characters left in
++    the subject. */
++
++    REPEATCHAR:
++    if (min > md->end_subject - eptr) return FALSE;
++    c = *ecode++;
++
++    /* The code is duplicated for the caseless and caseful cases, for speed,
++    since matching characters is likely to be quite common. First, ensure the
++    minimum number of matches are present. If min = max, continue at the same
++    level without recursing. Otherwise, if minimizing, keep trying the rest of
++    the expression and advancing one matching character if failing, up to the
++    maximum. Alternatively, if maximizing, find the maximum number of
++    characters and work backwards. */
++
++    DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max,
++      max, eptr));
++
++    if ((ims & PCRE_CASELESS) != 0)
++      {
++      c = md->lcc[c];
++      for (i = 1; i <= min; i++)
++        if (c != md->lcc[*eptr++]) return FALSE;
++      if (min == max) continue;
++      if (minimize)
++        {
++        for (i = min;; i++)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          if (i >= max || eptr >= md->end_subject ||
++              c != md->lcc[*eptr++])
++            return FALSE;
++          }
++        /* Control never gets here */
++        }
++      else
++        {
++        const uschar *pp = eptr;
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || c != md->lcc[*eptr]) break;
++          eptr++;
++          }
++        while (eptr >= pp)
++          if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++        return FALSE;
++        }
++      /* Control never gets here */
++      }
++
++    /* Caseful comparisons */
++
++    else
++      {
++      for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE;
++      if (min == max) continue;
++      if (minimize)
++        {
++        for (i = min;; i++)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE;
++          }
++        /* Control never gets here */
++        }
++      else
++        {
++        const uschar *pp = eptr;
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || c != *eptr) break;
++          eptr++;
++          }
++        while (eptr >= pp)
++         if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
++           return TRUE;
++        return FALSE;
++        }
++      }
++    /* Control never gets here */
++
++    /* Match a negated single character */
++
++    case OP_NOT:
++    if (eptr >= md->end_subject) return FALSE;
++    ecode++;
++    if ((ims & PCRE_CASELESS) != 0)
++      {
++      if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE;
++      }
++    else
++      {
++      if (*ecode++ == *eptr++) return FALSE;
++      }
++    break;
++
++    /* Match a negated single character repeatedly. This is almost a repeat of
++    the code for a repeated single character, but I haven't found a nice way of
++    commoning these up that doesn't require a test of the positive/negative
++    option for each character match. Maybe that wouldn't add very much to the
++    time taken, but character matching *is* what this is all about... */
++
++    case OP_NOTEXACT:
++    min = max = (ecode[1] << 8) + ecode[2];
++    ecode += 3;
++    goto REPEATNOTCHAR;
++
++    case OP_NOTUPTO:
++    case OP_NOTMINUPTO:
++    min = 0;
++    max = (ecode[1] << 8) + ecode[2];
++    minimize = *ecode == OP_NOTMINUPTO;
++    ecode += 3;
++    goto REPEATNOTCHAR;
++
++    case OP_NOTSTAR:
++    case OP_NOTMINSTAR:
++    case OP_NOTPLUS:
++    case OP_NOTMINPLUS:
++    case OP_NOTQUERY:
++    case OP_NOTMINQUERY:
++    c = *ecode++ - OP_NOTSTAR;
++    minimize = (c & 1) != 0;
++    min = rep_min[c];                 /* Pick up values from tables; */
++    max = rep_max[c];                 /* zero for max => infinity */
++    if (max == 0) max = INT_MAX;
++
++    /* Common code for all repeated single-character matches. We can give
++    up quickly if there are fewer than the minimum number of characters left in
++    the subject. */
++
++    REPEATNOTCHAR:
++    if (min > md->end_subject - eptr) return FALSE;
++    c = *ecode++;
++
++    /* The code is duplicated for the caseless and caseful cases, for speed,
++    since matching characters is likely to be quite common. First, ensure the
++    minimum number of matches are present. If min = max, continue at the same
++    level without recursing. Otherwise, if minimizing, keep trying the rest of
++    the expression and advancing one matching character if failing, up to the
++    maximum. Alternatively, if maximizing, find the maximum number of
++    characters and work backwards. */
++
++    DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max,
++      max, eptr));
++
++    if ((ims & PCRE_CASELESS) != 0)
++      {
++      c = md->lcc[c];
++      for (i = 1; i <= min; i++)
++        if (c == md->lcc[*eptr++]) return FALSE;
++      if (min == max) continue;
++      if (minimize)
++        {
++        for (i = min;; i++)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          if (i >= max || eptr >= md->end_subject ||
++              c == md->lcc[*eptr++])
++            return FALSE;
++          }
++        /* Control never gets here */
++        }
++      else
++        {
++        const uschar *pp = eptr;
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || c == md->lcc[*eptr]) break;
++          eptr++;
++          }
++        while (eptr >= pp)
++          if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++        return FALSE;
++        }
++      /* Control never gets here */
++      }
++
++    /* Caseful comparisons */
++
++    else
++      {
++      for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE;
++      if (min == max) continue;
++      if (minimize)
++        {
++        for (i = min;; i++)
++          {
++          if (match(eptr, ecode, offset_top, md, ims, eptrb, 0))
++            return TRUE;
++          if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE;
++          }
++        /* Control never gets here */
++        }
++      else
++        {
++        const uschar *pp = eptr;
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || c == *eptr) break;
++          eptr++;
++          }
++        while (eptr >= pp)
++         if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
++           return TRUE;
++        return FALSE;
++        }
++      }
++    /* Control never gets here */
++
++    /* Match a single character type repeatedly; several different opcodes
++    share code. This is very similar to the code for single characters, but we
++    repeat it in the interests of efficiency. */
++
++    case OP_TYPEEXACT:
++    min = max = (ecode[1] << 8) + ecode[2];
++    minimize = TRUE;
++    ecode += 3;
++    goto REPEATTYPE;
++
++    case OP_TYPEUPTO:
++    case OP_TYPEMINUPTO:
++    min = 0;
++    max = (ecode[1] << 8) + ecode[2];
++    minimize = *ecode == OP_TYPEMINUPTO;
++    ecode += 3;
++    goto REPEATTYPE;
++
++    case OP_TYPESTAR:
++    case OP_TYPEMINSTAR:
++    case OP_TYPEPLUS:
++    case OP_TYPEMINPLUS:
++    case OP_TYPEQUERY:
++    case OP_TYPEMINQUERY:
++    c = *ecode++ - OP_TYPESTAR;
++    minimize = (c & 1) != 0;
++    min = rep_min[c];                 /* Pick up values from tables; */
++    max = rep_max[c];                 /* zero for max => infinity */
++    if (max == 0) max = INT_MAX;
++
++    /* Common code for all repeated single character type matches */
++
++    REPEATTYPE:
++    ctype = *ecode++;      /* Code for the character type */
++
++    /* First, ensure the minimum number of matches are present. Use inline
++    code for maximizing the speed, and do the type test once at the start
++    (i.e. keep it out of the loop). Also we can test that there are at least
++    the minimum number of bytes before we start, except when doing '.' in
++    UTF8 mode. Leave the test in in all cases; in the special case we have
++    to test after each character. */
++
++    if (min > md->end_subject - eptr) return FALSE;
++    if (min > 0) switch(ctype)
++      {
++      case OP_ANY:
++#ifdef SUPPORT_UTF8
++      if (md->utf8)
++        {
++        for (i = 1; i <= min; i++)
++          {
++          if (eptr >= md->end_subject ||
++             (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0))
++            return FALSE;
++          while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
++          }
++        break;
++        }
++#endif
++      /* Non-UTF8 can be faster */
++      if ((ims & PCRE_DOTALL) == 0)
++        { for (i = 1; i <= min; i++) if (*eptr++ == NEWLINE) return FALSE; }
++      else eptr += min;
++      break;
++
++      case OP_NOT_DIGIT:
++      for (i = 1; i <= min; i++)
++        if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE;
++      break;
++
++      case OP_DIGIT:
++      for (i = 1; i <= min; i++)
++        if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE;
++      break;
++
++      case OP_NOT_WHITESPACE:
++      for (i = 1; i <= min; i++)
++        if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE;
++      break;
++
++      case OP_WHITESPACE:
++      for (i = 1; i <= min; i++)
++        if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE;
++      break;
++
++      case OP_NOT_WORDCHAR:
++      for (i = 1; i <= min; i++)
++        if ((md->ctypes[*eptr++] & ctype_word) != 0)
++          return FALSE;
++      break;
++
++      case OP_WORDCHAR:
++      for (i = 1; i <= min; i++)
++        if ((md->ctypes[*eptr++] & ctype_word) == 0)
++          return FALSE;
++      break;
++      }
++
++    /* If min = max, continue at the same level without recursing */
++
++    if (min == max) continue;
++
++    /* If minimizing, we have to test the rest of the pattern before each
++    subsequent match. */
++
++    if (minimize)
++      {
++      for (i = min;; i++)
++        {
++        if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) return TRUE;
++        if (i >= max || eptr >= md->end_subject) return FALSE;
++
++        c = *eptr++;
++        switch(ctype)
++          {
++          case OP_ANY:
++          if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return FALSE;
++#ifdef SUPPORT_UTF8
++          if (md->utf8)
++            while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
++#endif
++          break;
++
++          case OP_NOT_DIGIT:
++          if ((md->ctypes[c] & ctype_digit) != 0) return FALSE;
++          break;
++
++          case OP_DIGIT:
++          if ((md->ctypes[c] & ctype_digit) == 0) return FALSE;
++          break;
++
++          case OP_NOT_WHITESPACE:
++          if ((md->ctypes[c] & ctype_space) != 0) return FALSE;
++          break;
++
++          case OP_WHITESPACE:
++          if  ((md->ctypes[c] & ctype_space) == 0) return FALSE;
++          break;
++
++          case OP_NOT_WORDCHAR:
++          if ((md->ctypes[c] & ctype_word) != 0) return FALSE;
++          break;
++
++          case OP_WORDCHAR:
++          if ((md->ctypes[c] & ctype_word) == 0) return FALSE;
++          break;
++          }
++        }
++      /* Control never gets here */
++      }
++
++    /* If maximizing it is worth using inline code for speed, doing the type
++    test once at the start (i.e. keep it out of the loop). */
++
++    else
++      {
++      const uschar *pp = eptr;
++      switch(ctype)
++        {
++        case OP_ANY:
++
++        /* Special code is required for UTF8, but when the maximum is unlimited
++        we don't need it. */
++
++#ifdef SUPPORT_UTF8
++        if (md->utf8 && max < INT_MAX)
++          {
++          if ((ims & PCRE_DOTALL) == 0)
++            {
++            for (i = min; i < max; i++)
++              {
++              if (eptr >= md->end_subject || *eptr++ == NEWLINE) break;
++              while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
++              }
++            }
++          else
++            {
++            for (i = min; i < max; i++)
++              {
++              eptr++;
++              while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
++              }
++            }
++          break;
++          }
++#endif
++        /* Non-UTF8 can be faster */
++        if ((ims & PCRE_DOTALL) == 0)
++          {
++          for (i = min; i < max; i++)
++            {
++            if (eptr >= md->end_subject || *eptr == NEWLINE) break;
++            eptr++;
++            }
++          }
++        else
++          {
++          c = max - min;
++          if (c > md->end_subject - eptr) c = md->end_subject - eptr;
++          eptr += c;
++          }
++        break;
++
++        case OP_NOT_DIGIT:
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0)
++            break;
++          eptr++;
++          }
++        break;
++
++        case OP_DIGIT:
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0)
++            break;
++          eptr++;
++          }
++        break;
++
++        case OP_NOT_WHITESPACE:
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0)
++            break;
++          eptr++;
++          }
++        break;
++
++        case OP_WHITESPACE:
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0)
++            break;
++          eptr++;
++          }
++        break;
++
++        case OP_NOT_WORDCHAR:
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0)
++            break;
++          eptr++;
++          }
++        break;
++
++        case OP_WORDCHAR:
++        for (i = min; i < max; i++)
++          {
++          if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0)
++            break;
++          eptr++;
++          }
++        break;
++        }
++
++      while (eptr >= pp)
++        {
++        if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0))
++          return TRUE;
++#ifdef SUPPORT_UTF8
++        if (md->utf8)
++          while (eptr > pp && (*eptr & 0xc0) == 0x80) eptr--;
++#endif
++        }
++      return FALSE;
++      }
++    /* Control never gets here */
++
++    /* There's been some horrible disaster. */
++
++    default:
++    DPRINTF(("Unknown opcode %d\n", *ecode));
++    md->errorcode = PCRE_ERROR_UNKNOWN_NODE;
++    return FALSE;
++    }
++
++  /* Do not stick any code in here without much thought; it is assumed
++  that "continue" in the code above comes out to here to repeat the main
++  loop. */
++
++  }             /* End of main loop */
++/* Control never reaches here */
++}
++
++
++/*************************************************
++*         Execute a Regular Expression           *
++*************************************************/
++
++/* This function applies a compiled re to a subject string and picks out
++portions of the string if it matches. Two elements in the vector are set for
++each substring: the offsets to the start and end of the substring.
++
++Arguments:
++  external_re     points to the compiled expression
++  external_extra  points to "hints" from pcre_study() or is NULL
++  subject         points to the subject string
++  length          length of subject string (may contain binary zeros)
++  start_offset    where to start in the subject string
++  options         option bits
++  offsets         points to a vector of ints to be filled in with offsets
++  offsetcount     the number of elements in the vector
++
++Returns:          > 0 => success; value is the number of elements filled in
++                  = 0 => success, but offsets is not big enough
++                   -1 => failed to match
++                 < -1 => some kind of unexpected problem
++*/
++
++int
++pcre_exec(const pcre *external_re, const pcre_extra *external_extra,
++  const char *subject, int length, int start_offset, int options, int *offsets,
++  int offsetcount)
++{
++int resetcount, ocount;
++int first_char = -1;
++int req_char = -1;
++int req_char2 = -1;
++unsigned long int ims = 0;
++match_data match_block;
++const uschar *start_bits = NULL;
++const uschar *start_match = (const uschar *)subject + start_offset;
++const uschar *end_subject;
++const uschar *req_char_ptr = start_match - 1;
++const real_pcre *re = (const real_pcre *)external_re;
++const real_pcre_extra *extra = (const real_pcre_extra *)external_extra;
++BOOL using_temporary_offsets = FALSE;
++BOOL anchored;
++BOOL startline;
++
++if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
++
++if (re == NULL || subject == NULL ||
++   (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
++if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
++
++anchored = ((re->options | options) & PCRE_ANCHORED) != 0;
++startline = (re->options & PCRE_STARTLINE) != 0;
++
++match_block.start_pattern = re->code;
++match_block.start_subject = (const uschar *)subject;
++match_block.end_subject = match_block.start_subject + length;
++end_subject = match_block.end_subject;
++
++match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0;
++match_block.utf8 = (re->options & PCRE_UTF8) != 0;
++
++match_block.notbol = (options & PCRE_NOTBOL) != 0;
++match_block.noteol = (options & PCRE_NOTEOL) != 0;
++match_block.notempty = (options & PCRE_NOTEMPTY) != 0;
++
++match_block.errorcode = PCRE_ERROR_NOMATCH;     /* Default error */
++
++match_block.lcc = re->tables + lcc_offset;
++match_block.ctypes = re->tables + ctypes_offset;
++
++/* The ims options can vary during the matching as a result of the presence
++of (?ims) items in the pattern. They are kept in a local variable so that
++restoring at the exit of a group is easy. */
++
++ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL);
++
++/* If the expression has got more back references than the offsets supplied can
++hold, we get a temporary bit of working store to use during the matching.
++Otherwise, we can use the vector supplied, rounding down its size to a multiple
++of 3. */
++
++ocount = offsetcount - (offsetcount % 3);
++
++if (re->top_backref > 0 && re->top_backref >= ocount/3)
++  {
++  ocount = re->top_backref * 3 + 3;
++  match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int));
++  if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY;
++  using_temporary_offsets = TRUE;
++  DPRINTF(("Got memory to hold back references\n"));
++  }
++else match_block.offset_vector = offsets;
++
++match_block.offset_end = ocount;
++match_block.offset_max = (2*ocount)/3;
++match_block.offset_overflow = FALSE;
++
++/* Compute the minimum number of offsets that we need to reset each time. Doing
++this makes a huge difference to execution time when there aren't many brackets
++in the pattern. */
++
++resetcount = 2 + re->top_bracket * 2;
++if (resetcount > offsetcount) resetcount = ocount;
++
++/* Reset the working variable associated with each extraction. These should
++never be used unless previously set, but they get saved and restored, and so we
++initialize them to avoid reading uninitialized locations. */
++
++if (match_block.offset_vector != NULL)
++  {
++  register int *iptr = match_block.offset_vector + ocount;
++  register int *iend = iptr - resetcount/2 + 1;
++  while (--iptr >= iend) *iptr = -1;
++  }
++
++/* Set up the first character to match, if available. The first_char value is
++never set for an anchored regular expression, but the anchoring may be forced
++at run time, so we have to test for anchoring. The first char may be unset for
++an unanchored pattern, of course. If there's no first char and the pattern was
++studied, there may be a bitmap of possible first characters. */
++
++if (!anchored)
++  {
++  if ((re->options & PCRE_FIRSTSET) != 0)
++    {
++    first_char = re->first_char;
++    if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char];
++    }
++  else
++    if (!startline && extra != NULL &&
++      (extra->options & PCRE_STUDY_MAPPED) != 0)
++        start_bits = extra->start_bits;
++  }
++
++/* For anchored or unanchored matches, there may be a "last known required
++character" set. If the PCRE_CASELESS is set, implying that the match starts
++caselessly, or if there are any changes of this flag within the regex, set up
++both cases of the character. Otherwise set the two values the same, which will
++avoid duplicate testing (which takes significant time). This covers the vast
++majority of cases. It will be suboptimal when the case flag changes in a regex
++and the required character in fact is caseful. */
++
++if ((re->options & PCRE_REQCHSET) != 0)
++  {
++  req_char = re->req_char;
++  req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0)?
++    (re->tables + fcc_offset)[req_char] : req_char;
++  }
++
++/* Loop for handling unanchored repeated matching attempts; for anchored regexs
++the loop runs just once. */
++
++do
++  {
++  int rc;
++  register int *iptr = match_block.offset_vector;
++  register int *iend = iptr + resetcount;
++
++  /* Reset the maximum number of extractions we might see. */
++
++  while (iptr < iend) *iptr++ = -1;
++
++  /* Advance to a unique first char if possible */
++
++  if (first_char >= 0)
++    {
++    if ((ims & PCRE_CASELESS) != 0)
++      while (start_match < end_subject &&
++             match_block.lcc[*start_match] != first_char)
++        start_match++;
++    else
++      while (start_match < end_subject && *start_match != first_char)
++        start_match++;
++    }
++
++  /* Or to just after \n for a multiline match if possible */
++
++  else if (startline)
++    {
++    if (start_match > match_block.start_subject + start_offset)
++      {
++      while (start_match < end_subject && start_match[-1] != NEWLINE)
++        start_match++;
++      }
++    }
++
++  /* Or to a non-unique first char after study */
++
++  else if (start_bits != NULL)
++    {
++    while (start_match < end_subject)
++      {
++      register int c = *start_match;
++      if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break;
++      }
++    }
++
++#ifdef DEBUG  /* Sigh. Some compilers never learn. */
++  PCRE_PRINTF(">>>> Match against: ");
++  pchars(start_match, end_subject - start_match, TRUE, &match_block);
++  PCRE_PRINTF("\n");
++#endif
++
++  /* If req_char is set, we know that that character must appear in the subject
++  for the match to succeed. If the first character is set, req_char must be
++  later in the subject; otherwise the test starts at the match point. This
++  optimization can save a huge amount of backtracking in patterns with nested
++  unlimited repeats that aren't going to match. We don't know what the state of
++  case matching may be when this character is hit, so test for it in both its
++  cases if necessary. However, the different cased versions will not be set up
++  unless PCRE_CASELESS was given or the casing state changes within the regex.
++  Writing separate code makes it go faster, as does using an autoincrement and
++  backing off on a match. */
++
++  if (req_char >= 0)
++    {
++    register const uschar *p = start_match + ((first_char >= 0)? 1 : 0);
++
++    /* We don't need to repeat the search if we haven't yet reached the
++    place we found it at last time. */
++
++    if (p > req_char_ptr)
++      {
++      /* Do a single test if no case difference is set up */
++
++      if (req_char == req_char2)
++        {
++        while (p < end_subject)
++          {
++          if (*p++ == req_char) { p--; break; }
++          }
++        }
++
++      /* Otherwise test for either case */
++
++      else
++        {
++        while (p < end_subject)
++          {
++          register int pp = *p++;
++          if (pp == req_char || pp == req_char2) { p--; break; }
++          }
++        }
++
++      /* If we can't find the required character, break the matching loop */
++
++      if (p >= end_subject) break;
++
++      /* If we have found the required character, save the point where we
++      found it, so that we don't search again next time round the loop if
++      the start hasn't passed this character yet. */
++
++      req_char_ptr = p;
++      }
++    }
++
++  /* When a match occurs, substrings will be set for all internal extractions;
++  we just need to set up the whole thing as substring 0 before returning. If
++  there were too many extractions, set the return code to zero. In the case
++  where we had to get some local store to hold offsets for backreferences, copy
++  those back references that we can. In this case there need not be overflow
++  if certain parts of the pattern were not used. */
++
++  match_block.start_match = start_match;
++  if (!match(start_match, re->code, 2, &match_block, ims, NULL, match_isgroup))
++    continue;
++
++  /* Copy the offset information from temporary store if necessary */
++
++  if (using_temporary_offsets)
++    {
++    if (offsetcount >= 4)
++      {
++      memcpy(offsets + 2, match_block.offset_vector + 2,
++        (offsetcount - 2) * sizeof(int));
++      DPRINTF(("Copied offsets from temporary memory\n"));
++      }
++    if (match_block.end_offset_top > offsetcount)
++      match_block.offset_overflow = TRUE;
++
++    DPRINTF(("Freeing temporary memory\n"));
++    (pcre_free)(match_block.offset_vector);
++    }
++
++  rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2;
++
++  if (offsetcount < 2) rc = 0; else
++    {
++    offsets[0] = start_match - match_block.start_subject;
++    offsets[1] = match_block.end_match_ptr - match_block.start_subject;
++    }
++
++  DPRINTF((">>>> returning %d\n", rc));
++  return rc;
++  }
++
++/* This "while" is the end of the "do" above */
++
++while (!anchored &&
++       match_block.errorcode == PCRE_ERROR_NOMATCH &&
++       start_match++ < end_subject);
++
++if (using_temporary_offsets)
++  {
++  DPRINTF(("Freeing temporary memory\n"));
++  (pcre_free)(match_block.offset_vector);
++  }
++
++DPRINTF((">>>> returning %d\n", match_block.errorcode));
++
++return match_block.errorcode;
++}
++
++/* End of pcre.c */
+Index: b/security/apparmor/match/pcre_exec.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/pcre_exec.h
+@@ -0,0 +1,308 @@
++/*
++ *  This is a modified header file containing the definitions from
++ *  pcre.h and internal.h required to support pcre_exec()
++ */
++
++
++/*************************************************
++*       Perl-Compatible Regular Expressions      *
++*************************************************/
++
++/* Copyright (c) 1997-2001 University of Cambridge */
++
++#ifndef _PCRE_H
++#define _PCRE_H
++
++/* ----- CODE ADDED ---- */
++
++#ifdef __KERNEL__
++#include <linux/slab.h>       // for kmalloc/kfree
++#endif
++
++#ifdef __KERNEL__
++#define PCRE_PRINTF printk
++#define isprint(x) ((unsigned char)(x) >= 128 && (unsigned char)(x) <= 255)
++#else
++#define PCRE_PRINTF printf
++#endif
++
++/* The value of NEWLINE determines the newline character. The default is to
++ * leave it up to the compiler, but some sites want to force a particular value.
++ * On Unix systems, "configure" can be used to override this default. */
++
++#ifndef NEWLINE
++#define NEWLINE '\n'
++#endif
++
++/* ----  CODE DELETED ---- */
++
++/* Options */
++
++#define PCRE_CASELESS        0x0001
++#define PCRE_MULTILINE       0x0002
++#define PCRE_DOTALL          0x0004
++#define PCRE_EXTENDED        0x0008
++#define PCRE_ANCHORED        0x0010
++#define PCRE_DOLLAR_ENDONLY  0x0020
++#define PCRE_EXTRA           0x0040
++#define PCRE_NOTBOL          0x0080
++#define PCRE_NOTEOL          0x0100
++#define PCRE_UNGREEDY        0x0200
++#define PCRE_NOTEMPTY        0x0400
++#define PCRE_UTF8            0x0800
++
++/* Exec-time and get-time error codes */
++
++#define PCRE_ERROR_NOMATCH        (-1)
++#define PCRE_ERROR_NULL           (-2)
++#define PCRE_ERROR_BADOPTION      (-3)
++#define PCRE_ERROR_BADMAGIC       (-4)
++#define PCRE_ERROR_UNKNOWN_NODE   (-5)
++#define PCRE_ERROR_NOMEMORY       (-6)
++#define PCRE_ERROR_NOSUBSTRING    (-7)
++
++/* ----  CODE DELETED ---- */
++
++/* Types */
++
++struct real_pcre;        /* declaration; the definition is private  */
++struct real_pcre_extra;  /* declaration; the definition is private */
++
++typedef struct real_pcre pcre;
++typedef struct real_pcre_extra pcre_extra;
++
++/* ----  CODE DELETED ---- */
++
++extern int pcre_exec(const pcre *, const pcre_extra *,
++                   const char *, int, int, int, int *,
++                   int);
++
++/* ----  CODE ADDED (from internal.h) ---- */
++
++/* These are the public options that can change during matching. */
++
++#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL)
++
++/* Private options flags start at the most significant end of the four bytes,
++but skip the top bit so we can use ints for convenience without getting tangled
++with negative values. The public options defined in pcre.h start at the least
++significant end. Make sure they don't overlap, though now that we have expanded
++to four bytes there is plenty of space. */
++
++#define PCRE_FIRSTSET      0x40000000  /* first_char is set */
++#define PCRE_REQCHSET      0x20000000  /* req_char is set */
++#define PCRE_STARTLINE     0x10000000  /* start after \n for multiline */
++#define PCRE_ICHANGED      0x04000000  /* i option changes within regex */
++
++/* Options for the "extra" block produced by pcre_study(). */
++
++#define PCRE_STUDY_MAPPED   0x01     /* a map of starting chars exists */
++
++/* Masks for identifying the public options which are permitted at compile
++time, run time or study time, respectively. */
++
++#define PUBLIC_EXEC_OPTIONS \
++    (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY)
++
++/* Magic number to provide a small check against being handed junk. */
++
++#define MAGIC_NUMBER  0x50435245UL   /* 'PCRE' */
++
++typedef int BOOL;
++
++#define FALSE   0
++#define TRUE    1
++
++/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets
++that extract substrings. Starting from 1 (i.e. after OP_END), the values up to
++OP_EOD must correspond in order to the list of escapes immediately above. */
++
++enum {
++  OP_END,            /* End of pattern */
++
++  /* Values corresponding to backslashed metacharacters */
++
++  OP_SOD,            /* Start of data: \A */
++  OP_NOT_WORD_BOUNDARY,  /* \B */
++  OP_WORD_BOUNDARY,      /* \b */
++  OP_NOT_DIGIT,          /* \D */
++  OP_DIGIT,              /* \d */
++  OP_NOT_WHITESPACE,     /* \S */
++  OP_WHITESPACE,         /* \s */
++  OP_NOT_WORDCHAR,       /* \W */
++  OP_WORDCHAR,           /* \w */
++  OP_EODN,           /* End of data or \n at end of data: \Z. */
++  OP_EOD,            /* End of data: \z */
++
++  OP_OPT,            /* Set runtime options */
++  OP_CIRC,           /* Start of line - varies with multiline switch */
++  OP_DOLL,           /* End of line - varies with multiline switch */
++  OP_ANY,            /* Match any character */
++  OP_CHARS,          /* Match string of characters */
++  OP_NOT,            /* Match anything but the following char */
++
++  OP_STAR,           /* The maximizing and minimizing versions of */
++  OP_MINSTAR,        /* all these opcodes must come in pairs, with */
++  OP_PLUS,           /* the minimizing one second. */
++  OP_MINPLUS,        /* This first set applies to single characters */
++  OP_QUERY,
++  OP_MINQUERY,
++  OP_UPTO,           /* From 0 to n matches */
++  OP_MINUPTO,
++  OP_EXACT,          /* Exactly n matches */
++
++  OP_NOTSTAR,        /* The maximizing and minimizing versions of */
++  OP_NOTMINSTAR,     /* all these opcodes must come in pairs, with */
++  OP_NOTPLUS,        /* the minimizing one second. */
++  OP_NOTMINPLUS,     /* This first set applies to "not" single characters */
++  OP_NOTQUERY,
++  OP_NOTMINQUERY,
++  OP_NOTUPTO,        /* From 0 to n matches */
++  OP_NOTMINUPTO,
++  OP_NOTEXACT,       /* Exactly n matches */
++
++  OP_TYPESTAR,       /* The maximizing and minimizing versions of */
++  OP_TYPEMINSTAR,    /* all these opcodes must come in pairs, with */
++  OP_TYPEPLUS,       /* the minimizing one second. These codes must */
++  OP_TYPEMINPLUS,    /* be in exactly the same order as those above. */
++  OP_TYPEQUERY,      /* This set applies to character types such as \d */
++  OP_TYPEMINQUERY,
++  OP_TYPEUPTO,       /* From 0 to n matches */
++  OP_TYPEMINUPTO,
++  OP_TYPEEXACT,      /* Exactly n matches */
++
++  OP_CRSTAR,         /* The maximizing and minimizing versions of */
++  OP_CRMINSTAR,      /* all these opcodes must come in pairs, with */
++  OP_CRPLUS,         /* the minimizing one second. These codes must */
++  OP_CRMINPLUS,      /* be in exactly the same order as those above. */
++  OP_CRQUERY,        /* These are for character classes and back refs */
++  OP_CRMINQUERY,
++  OP_CRRANGE,        /* These are different to the three seta above. */
++  OP_CRMINRANGE,
++
++  OP_CLASS,          /* Match a character class */
++  OP_REF,            /* Match a back reference */
++  OP_RECURSE,        /* Match this pattern recursively */
++
++  OP_ALT,            /* Start of alternation */
++  OP_KET,            /* End of group that doesn't have an unbounded repeat */
++  OP_KETRMAX,        /* These two must remain together and in this */
++  OP_KETRMIN,        /* order. They are for groups the repeat for ever. */
++
++  /* The assertions must come before ONCE and COND */
++
++  OP_ASSERT,         /* Positive lookahead */
++  OP_ASSERT_NOT,     /* Negative lookahead */
++  OP_ASSERTBACK,     /* Positive lookbehind */
++  OP_ASSERTBACK_NOT, /* Negative lookbehind */
++  OP_REVERSE,        /* Move pointer back - used in lookbehind assertions */
++
++  /* ONCE and COND must come after the assertions, with ONCE first, as there's
++  a test for >= ONCE for a subpattern that isn't an assertion. */
++
++  OP_ONCE,           /* Once matched, don't back up into the subpattern */
++  OP_COND,           /* Conditional group */
++  OP_CREF,           /* Used to hold an extraction string number (cond ref) */
++
++  OP_BRAZERO,        /* These two must remain together and in this */
++  OP_BRAMINZERO,     /* order. */
++
++  OP_BRANUMBER,      /* Used for extracting brackets whose number is greater
++                        than can fit into an opcode. */
++
++  OP_BRA             /* This and greater values are used for brackets that
++                        extract substrings up to a basic limit. After that,
++                        use is made of OP_BRANUMBER. */
++};
++
++/* The highest extraction number before we have to start using additional
++bytes. (Originally PCRE didn't have support for extraction counts highter than
++this number.) The value is limited by the number of opcodes left after OP_BRA,
++i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional
++opcodes. */
++
++#define EXTRACT_BASIC_MAX  150
++
++/* All character handling must be done as unsigned characters. Otherwise there
++are problems with top-bit-set characters and functions such as isspace().
++However, we leave the interface to the outside world as char *, because that
++should make things easier for callers. We define a short type for unsigned char
++to save lots of typing. I tried "uchar", but it causes problems on Digital
++Unix, where it is defined in sys/types, so use "uschar" instead. */
++
++typedef unsigned char uschar;
++
++/* The real format of the start of the pcre block; the actual code vector
++runs on as long as necessary after the end. */
++
++typedef struct real_pcre {
++  unsigned long int magic_number;
++  size_t size;
++  const unsigned char *tables;
++  unsigned long int options;
++  unsigned short int top_bracket;
++  unsigned short int top_backref;
++  uschar first_char;
++  uschar req_char;
++  uschar code[1];
++} real_pcre;
++
++/* The real format of the extra block returned by pcre_study(). */
++
++typedef struct real_pcre_extra {
++  uschar options;
++  uschar start_bits[32];
++} real_pcre_extra;
++
++/* Structure for passing "static" information around between the functions
++doing the matching, so that they are thread-safe. */
++
++typedef struct match_data {
++  int    errorcode;             /* As it says */
++  int   *offset_vector;         /* Offset vector */
++  int    offset_end;            /* One past the end */
++  int    offset_max;            /* The maximum usable for return data */
++  const uschar *lcc;            /* Points to lower casing table */
++  const uschar *ctypes;         /* Points to table of type maps */
++  BOOL   offset_overflow;       /* Set if too many extractions */
++  BOOL   notbol;                /* NOTBOL flag */
++  BOOL   noteol;                /* NOTEOL flag */
++  BOOL   utf8;                  /* UTF8 flag */
++  BOOL   endonly;               /* Dollar not before final \n */
++  BOOL   notempty;              /* Empty string match not wanted */
++  const uschar *start_pattern;  /* For use when recursing */
++  const uschar *start_subject;  /* Start of the subject string */
++  const uschar *end_subject;    /* End of the subject string */
++  const uschar *start_match;    /* Start of this match attempt */
++  const uschar *end_match_ptr;  /* Subject position at end match */
++  int    end_offset_top;        /* Highwater mark at end of match */
++} match_data;
++
++/* Bit definitions for entries in the pcre_ctypes table. */
++
++#define ctype_space   0x01
++#define ctype_letter  0x02
++#define ctype_digit   0x04
++#define ctype_xdigit  0x08
++#define ctype_word    0x10   /* alphameric or '_' */
++#define ctype_meta    0x80   /* regexp meta char or zero (end pattern) */
++
++/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set
++of bits for a class map. Some classes are built by combining these tables. */
++
++#define cbit_length  320      /* Length of the cbits table */
++
++/* Offsets of the various tables from the base tables pointer, and
++total length. */
++
++#define lcc_offset      0
++#define fcc_offset    256
++
++#define fcc_offset    256
++#define cbits_offset  512
++#define ctypes_offset (cbits_offset + cbit_length)
++
++/* ----- CODE ADDED ---- */
++
++#endif // _PCRE_H
++ /* End of pcre.h */
+Index: b/security/apparmor/match/pcre_tables.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/match/pcre_tables.h
+@@ -0,0 +1,184 @@
++
++/*************************************************
++*      Perl-Compatible Regular Expressions       *
++*************************************************/
++
++/* This file is automatically written by the dftables auxiliary
++program. If you edit it by hand, you might like to edit the Makefile to
++prevent its ever being regenerated.
++
++This file is #included in the compilation of pcre.c to build the default
++character tables which are used when no tables are passed to the compile
++function. */
++
++static unsigned char pcre_default_tables[] = {
++
++/* This table is a lower casing table. */
++
++    0,  1,  2,  3,  4,  5,  6,  7,
++    8,  9, 10, 11, 12, 13, 14, 15,
++   16, 17, 18, 19, 20, 21, 22, 23,
++   24, 25, 26, 27, 28, 29, 30, 31,
++   32, 33, 34, 35, 36, 37, 38, 39,
++   40, 41, 42, 43, 44, 45, 46, 47,
++   48, 49, 50, 51, 52, 53, 54, 55,
++   56, 57, 58, 59, 60, 61, 62, 63,
++   64, 97, 98, 99,100,101,102,103,
++  104,105,106,107,108,109,110,111,
++  112,113,114,115,116,117,118,119,
++  120,121,122, 91, 92, 93, 94, 95,
++   96, 97, 98, 99,100,101,102,103,
++  104,105,106,107,108,109,110,111,
++  112,113,114,115,116,117,118,119,
++  120,121,122,123,124,125,126,127,
++  128,129,130,131,132,133,134,135,
++  136,137,138,139,140,141,142,143,
++  144,145,146,147,148,149,150,151,
++  152,153,154,155,156,157,158,159,
++  160,161,162,163,164,165,166,167,
++  168,169,170,171,172,173,174,175,
++  176,177,178,179,180,181,182,183,
++  184,185,186,187,188,189,190,191,
++  192,193,194,195,196,197,198,199,
++  200,201,202,203,204,205,206,207,
++  208,209,210,211,212,213,214,215,
++  216,217,218,219,220,221,222,223,
++  224,225,226,227,228,229,230,231,
++  232,233,234,235,236,237,238,239,
++  240,241,242,243,244,245,246,247,
++  248,249,250,251,252,253,254,255,
++
++/* This table is a case flipping table. */
++
++    0,  1,  2,  3,  4,  5,  6,  7,
++    8,  9, 10, 11, 12, 13, 14, 15,
++   16, 17, 18, 19, 20, 21, 22, 23,
++   24, 25, 26, 27, 28, 29, 30, 31,
++   32, 33, 34, 35, 36, 37, 38, 39,
++   40, 41, 42, 43, 44, 45, 46, 47,
++   48, 49, 50, 51, 52, 53, 54, 55,
++   56, 57, 58, 59, 60, 61, 62, 63,
++   64, 97, 98, 99,100,101,102,103,
++  104,105,106,107,108,109,110,111,
++  112,113,114,115,116,117,118,119,
++  120,121,122, 91, 92, 93, 94, 95,
++   96, 65, 66, 67, 68, 69, 70, 71,
++   72, 73, 74, 75, 76, 77, 78, 79,
++   80, 81, 82, 83, 84, 85, 86, 87,
++   88, 89, 90,123,124,125,126,127,
++  128,129,130,131,132,133,134,135,
++  136,137,138,139,140,141,142,143,
++  144,145,146,147,148,149,150,151,
++  152,153,154,155,156,157,158,159,
++  160,161,162,163,164,165,166,167,
++  168,169,170,171,172,173,174,175,
++  176,177,178,179,180,181,182,183,
++  184,185,186,187,188,189,190,191,
++  192,193,194,195,196,197,198,199,
++  200,201,202,203,204,205,206,207,
++  208,209,210,211,212,213,214,215,
++  216,217,218,219,220,221,222,223,
++  224,225,226,227,228,229,230,231,
++  232,233,234,235,236,237,238,239,
++  240,241,242,243,244,245,246,247,
++  248,249,250,251,252,253,254,255,
++
++/* This table contains bit maps for various character classes.
++Each map is 32 bytes long and the bits run from the least
++significant end of each byte. The classes that have their own
++maps are: space, xdigit, digit, upper, lower, word, graph
++print, punct, and cntrl. Other classes are built from combinations. */
++
++  0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
++  0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
++  0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,
++  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
++  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,
++  0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++  0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++
++/* This table identifies various classes of character by individual bits:
++  0x01   white space character
++  0x02   letter
++  0x04   decimal digit
++  0x08   hexadecimal digit
++  0x10   alphanumeric or '_'
++  0x80   regular expression metacharacter or binary zero
++*/
++
++  0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7 */
++  0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /*   8- 15 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */
++  0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /*    - '  */
++  0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /*  ( - /  */
++  0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /*  0 - 7  */
++  0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /*  8 - ?  */
++  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  @ - G  */
++  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  H - O  */
++  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  P - W  */
++  0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /*  X - _  */
++  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  ` - g  */
++  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  h - o  */
++  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  p - w  */
++  0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /*  x -127 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
++  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
++
++/* End of chartables.c */
+Index: b/security/apparmor/module_interface.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/module_interface.c
+@@ -0,0 +1,845 @@
++/*
++ *    Copyright (C) 1998-2005 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 userspace policy interface
++ */
++
++#include <asm/unaligned.h>
++
++#include "apparmor.h"
++#include "inline.h"
++#include "module_interface.h"
++#include "match/match.h"
++
++/* aa_code defined in module_interface.h */
++
++const int aacode_datasize[] = { 1, 2, 4, 8, 2, 2, 4, 0, 0, 0, 0, 0, 0 };
++
++struct aa_taskreplace_data {
++      struct aaprofile *old_profile;
++      struct aaprofile *new_profile;
++};
++
++/* inlines must be forward of there use in newer version of gcc,
++   just forward declaring with a prototype won't work anymore */
++
++static inline void free_aa_entry(struct aa_entry *entry)
++{
++      if (entry) {
++              kfree(entry->filename);
++              aamatch_free(entry->extradata);
++              kfree(entry);
++      }
++}
++
++/**
++ * alloc_aa_entry - create new empty aa_entry
++ * This routine allocates, initializes, and returns a new aa_entry
++ * file entry structure.  Structure is zeroed.  Returns new structure on
++ * success, %NULL on failure.
++ */
++static inline struct aa_entry *alloc_aa_entry(void)
++{
++      struct aa_entry *entry;
++
++      AA_DEBUG("%s\n", __FUNCTION__);
++      entry = kzalloc(sizeof(struct aa_entry), GFP_KERNEL);
++      if (entry) {
++              int i;
++              INIT_LIST_HEAD(&entry->list);
++              for (i = 0; i <= POS_AA_FILE_MAX; i++) {
++                      INIT_LIST_HEAD(&entry->listp[i]);
++              }
++      }
++      return entry;
++}
++
++/**
++ * free_aaprofile_rcu - rcu callback for free profiles
++ * @head: rcu_head struct of the profile whose reference is being put.
++ *
++ * the rcu callback routine, which delays the freeing of a profile when
++ * its last reference is put.
++ */
++static void free_aaprofile_rcu(struct rcu_head *head)
++{
++      struct aaprofile *p = container_of(head, struct aaprofile, rcu);
++      free_aaprofile(p);
++}
++
++/**
++ * task_remove - remove profile from a task's subdomain
++ * @sd: task's subdomain
++ *
++ * remove the active profile from a task's subdomain, switching the task
++ * to an unconfined state.
++ */
++static inline void task_remove(struct subdomain *sd)
++{
++      /* spin_lock(&sd_lock) held here */
++      AA_DEBUG("%s: removing profile from task %s(%d) profile %s active %s\n",
++               __FUNCTION__,
++               sd->task->comm,
++               sd->task->pid,
++               BASE_PROFILE(sd->active)->name,
++               sd->active->name);
++
++      aa_switch_unconfined(sd);
++}
++
++/** taskremove_iter - Iterator to unconfine subdomains which match cookie
++ * @sd: subdomain to consider for profile removal
++ * @cookie: pointer to the oldprofile which is being removed
++ *
++ * If the subdomain's active profile matches old_profile,  then call
++ * task_remove() to remove the profile leaving the task (subdomain) unconfined.
++ */
++static int taskremove_iter(struct subdomain *sd, void *cookie)
++{
++      struct aaprofile *old_profile = (struct aaprofile *)cookie;
++      unsigned long flags;
++
++      spin_lock_irqsave(&sd_lock, flags);
++
++      if (__aa_is_confined(sd) && BASE_PROFILE(sd->active) == old_profile) {
++              task_remove(sd);
++      }
++
++      spin_unlock_irqrestore(&sd_lock, flags);
++
++      return 0;
++}
++
++/** task_replace - replace subdomain's current profile with a new profile
++ * @sd: subdomain to replace the profile on
++ * @new: new profile
++ *
++ * Replace a task's (subdomain's) active profile with a new profile.  If
++ * task was in a hat then the new profile will also be in the equivalent
++ * hat in the new profile if it exists.  If it doesn't exist the
++ * task will be placed in the special null_profile state.
++ */
++static inline void task_replace(struct subdomain *sd, struct aaprofile *new)
++{
++      AA_DEBUG("%s: replacing profile for task %s(%d) "
++               "profile=%s (%p) active=%s (%p)\n",
++               __FUNCTION__,
++               sd->task->comm, sd->task->pid,
++               BASE_PROFILE(sd->active)->name, BASE_PROFILE(sd->active),
++               sd->active->name, sd->active);
++
++      if (!sd->active)
++              goto out;
++
++      if (IN_SUBPROFILE(sd->active)) {
++              struct aaprofile *nactive;
++
++              /* The old profile was in a hat, check to see if the new
++               * profile has an equivalent hat */
++              nactive = __aa_find_profile(sd->active->name, &new->sub);
++
++              if (!nactive)
++                      nactive = get_aaprofile(new->null_profile);
++
++              aa_switch(sd, nactive);
++              put_aaprofile(nactive);
++      } else {
++              aa_switch(sd, new);
++      }
++
++ out:
++      return;
++}
++
++/** taskreplace_iter - Iterator to replace a subdomain's profile
++ * @sd: subdomain to consider for profile replacement
++ * @cookie: pointer to the old profile which is being replaced.
++ *
++ * If the subdomain's active profile matches old_profile call
++ * task_replace() to replace with the subdomain's active profile with
++ * the new profile.
++ */
++static int taskreplace_iter(struct subdomain *sd, void *cookie)
++{
++      struct aa_taskreplace_data *data = (struct aa_taskreplace_data *)cookie;
++      unsigned long flags;
++
++      spin_lock_irqsave(&sd_lock, flags);
++
++      if (__aa_is_confined(sd) &&
++          BASE_PROFILE(sd->active) == data->old_profile)
++              task_replace(sd, data->new_profile);
++
++      spin_unlock_irqrestore(&sd_lock, flags);
++
++      return 0;
++}
++
++static inline int aa_inbounds(struct aa_ext *e, size_t size)
++{
++      return (e->pos + size <= e->end);
++}
++
++/**
++ * aaconvert - convert trailing values of serialized type codes
++ * @code: type code
++ * @dest: pointer to object to receive the converted value
++ * @src:  pointer to value to convert
++ *
++ * for serialized type codes which have a trailing value, convert it
++ * and place it in @dest.  If a code does not have a trailing value nop.
++ */
++static void aaconvert(enum aa_code code, void *dest, void *src)
++{
++      switch (code) {
++      case AA_U8:
++              *(u8 *)dest = *(u8 *) src;
++              break;
++      case AA_U16:
++      case AA_NAME:
++      case AA_DYN_STRING:
++              *(u16 *)dest = le16_to_cpu(get_unaligned((u16 *)src));
++              break;
++      case AA_U32:
++      case AA_STATIC_BLOB:
++              *(u32 *)dest = le32_to_cpu(get_unaligned((u32 *)src));
++              break;
++      case AA_U64:
++              *(u64 *)dest = le64_to_cpu(get_unaligned((u64 *)src));
++              break;
++      default:
++              /* nop - all other type codes do not have a trailing value */
++              ;
++      }
++}
++
++/**
++ * aa_is_X - check if the next element is of type X
++ * @e: serialized data extent information
++ * @code: type code
++ * @data: object located at @e->pos (of type @code) is written into @data
++ *        if @data is non-null.  if data is null it means skip this
++ *        entry
++ * check to see if the next element in the serialized data stream is of type
++ * X and check that it is with in bounds, if so put the associated value in
++ * @data.
++ * return the size of bytes associated with the returned data
++ *        for complex object like blob and string a pointer to the allocated
++ *        data is returned in data, but the size of the blob or string is
++ *        returned.
++ */
++static u32 aa_is_X(struct aa_ext *e, enum aa_code code, void *data)
++{
++      void *pos = e->pos;
++      int ret = 0;
++      if (!aa_inbounds(e, AA_CODE_BYTE + aacode_datasize[code]))
++              goto fail;
++      if (code != *(u8 *)e->pos)
++              goto out;
++      e->pos += AA_CODE_BYTE;
++      if (code == AA_NAME) {
++              u16 size;
++              /* name codes are followed by X bytes */
++              size = le16_to_cpu(get_unaligned((u16 *)e->pos));
++              if (!aa_inbounds(e, (size_t) size))
++                      goto fail;
++              if (data)
++                      *(u16 *)data = size;
++              e->pos += aacode_datasize[code];
++              ret = 1 + aacode_datasize[code];
++      } else if (code == AA_DYN_STRING) {
++              u16 size;
++              char *str;
++              /* strings codes are followed by X bytes */
++              size = le16_to_cpu(get_unaligned((u16 *)e->pos));
++              e->pos += aacode_datasize[code];
++              if (!aa_inbounds(e, (size_t) size))
++                      goto fail;
++              if (data) {
++                      * (char **)data = NULL;
++                      str = kmalloc(size, GFP_KERNEL);
++                      if (!str)
++                              goto fail;
++                      memcpy(str, e->pos, (size_t) size);
++                      str[size-1] = '\0';
++                      * (char **)data = str;
++              }
++              e->pos += size;
++              ret = size;
++      } else if (code == AA_STATIC_BLOB) {
++              u32 size;
++              /* blobs are followed by X bytes, that can be 2^32 */
++              size = le32_to_cpu(get_unaligned((u32 *)e->pos));
++              e->pos += aacode_datasize[code];
++              if (!aa_inbounds(e, (size_t) size))
++                      goto fail;
++              if (data)
++                      memcpy(data, e->pos, (size_t) size);
++              e->pos += size;
++              ret = size;
++      } else {
++              if (data)
++                      aaconvert(code, data, e->pos);
++              e->pos += aacode_datasize[code];
++              ret = 1 + aacode_datasize[code];
++      }
++out:
++      return ret;
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++/**
++ * aa_is_nameX - check is the next element is of type X with a name of @name
++ * @e: serialized data extent information
++ * @code: type code
++ * @data: location to store deserialized data if match isX criteria
++ * @name: name to match to the serialized element.
++ *
++ * check that the next serialized data element is of type X and has a tag
++ * name @name.  If the code matches and name (if specified) matches then
++ * the packed data is unpacked into *data.  (Note for strings this is the
++ * size, and the next data in the stream is the string data)
++ * returns %0 if either match failes
++ */
++static int aa_is_nameX(struct aa_ext *e, enum aa_code code, void *data,
++                     const char *name)
++{
++      void *pos = e->pos;
++      u16 size;
++      u32 ret;
++      /* check for presence of a tagname, and if present name size
++       * AA_NAME tag value is a u16 */
++      if (aa_is_X(e, AA_NAME, &size)) {
++              /* if a name is specified it must match. otherwise skip tag */
++              if (name && ((strlen(name) != size-1) ||
++                           strncmp(name, (char *)e->pos, (size_t)size-1)))
++                      goto fail;
++              e->pos += size;
++      } else if (name) {
++              goto fail;
++      }
++
++      /* now check if data actually matches */
++      ret = aa_is_X(e, code, data);
++      if (!ret)
++              goto fail;
++      return ret;
++
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++/* macro to wrap error case to make a block of reads look nicer */
++#define AA_READ_X(E, C, D, N) \
++      do { \
++              u32 __ret; \
++              __ret = aa_is_nameX((E), (C), (D), (N)); \
++              if (!__ret) \
++                      goto fail; \
++      } while (0)
++
++/**
++ * aa_activate_net_entry - unpacked serialized net entries
++ * @e: serialized data extent information
++ *
++ * Ignore/skips net entries if they are present in the serialized data
++ * stream.  Network confinement rules are currently unsupported but some
++ * user side tools can generate them so they are currently ignored.
++ */
++static inline int aa_activate_net_entry(struct aa_ext *e)
++{
++      AA_READ_X(e, AA_STRUCT, NULL, "ne");
++      AA_READ_X(e, AA_U32, NULL, NULL);
++      AA_READ_X(e, AA_U32, NULL, NULL);
++      AA_READ_X(e, AA_U32, NULL, NULL);
++      AA_READ_X(e, AA_U16, NULL, NULL);
++      AA_READ_X(e, AA_U16, NULL, NULL);
++      AA_READ_X(e, AA_U32, NULL, NULL);
++      AA_READ_X(e, AA_U32, NULL, NULL);
++      AA_READ_X(e, AA_U16, NULL, NULL);
++      AA_READ_X(e, AA_U16, NULL, NULL);
++      /* interface name is optional so just ignore return code */
++      aa_is_nameX(e, AA_DYN_STRING, NULL, NULL);
++      AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
++
++      return 1;
++fail:
++      return 0;
++}
++
++/**
++ * aa_activate_file_entry - unpack serialized file entry
++ * @e: serialized data extent information
++ *
++ * unpack the information used for a file ACL entry.
++ */
++static inline struct aa_entry *aa_activate_file_entry(struct aa_ext *e)
++{
++      struct aa_entry *entry = NULL;
++
++      if (!(entry = alloc_aa_entry()))
++              goto fail;
++
++      AA_READ_X(e, AA_STRUCT, NULL, "fe");
++      AA_READ_X(e, AA_DYN_STRING, &entry->filename, NULL);
++      AA_READ_X(e, AA_U32, &entry->mode, NULL);
++      AA_READ_X(e, AA_U32, &entry->type, NULL);
++
++      entry->extradata = aamatch_alloc(entry->type);
++      if (IS_ERR(entry->extradata)) {
++              entry->extradata = NULL;
++              goto fail;
++      }
++
++      if (entry->extradata &&
++          aamatch_serialize(entry->extradata, e, aa_is_nameX) != 0) {
++              goto fail;
++      }
++      AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
++
++      switch (entry->type) {
++      case aa_entry_literal:
++              AA_DEBUG("%s: %s [no pattern] mode=0x%x\n",
++                       __FUNCTION__,
++                       entry->filename,
++                       entry->mode);
++              break;
++      case aa_entry_tailglob:
++              AA_DEBUG("%s: %s [tailglob] mode=0x%x\n",
++                       __FUNCTION__,
++                       entry->filename,
++                       entry->mode);
++              break;
++      case aa_entry_pattern:
++              AA_DEBUG("%s: %s mode=0x%x\n",
++                       __FUNCTION__,
++                       entry->filename,
++                       entry->mode);
++              break;
++      default:
++              AA_WARN("%s: INVALID entry_match_type %d\n",
++                      __FUNCTION__,
++                      (int)entry->type);
++              goto fail;
++      }
++
++      return entry;
++
++fail:
++      free_aa_entry(entry);
++      return NULL;
++}
++
++/**
++ * check_rule_and_add - check a file rule is valid and add to a profile
++ * @file_entry: file rule to add
++ * @profile: profile to add the rule to
++ * @message: error message returned if the addition failes.
++ *
++ * perform consistency check to ensure that a file rule entry is valid.
++ * If the rule is valid it is added to the profile.
++ */
++static inline int check_rule_and_add(struct aa_entry *file_entry,
++                                   struct aaprofile *profile,
++                                   const char **message)
++{
++      /* verify consistency of x, px, ix, ux for entry against
++         possible duplicates for this entry */
++      int mode = AA_EXEC_MODIFIER_MASK(file_entry->mode);
++      int i;
++
++      if (mode && !(AA_MAY_EXEC & file_entry->mode)) {
++              *message = "inconsistent rule, x modifiers without x";
++              goto out;
++      }
++
++      /* check that only 1 of the modifiers is set */
++      if (mode && (mode & (mode - 1))) {
++              *message = "inconsistent rule, multiple x modifiers";
++              goto out;
++      }
++
++      /* ix -> m (required so that target exec binary may map itself) */
++               if (mode & AA_EXEC_INHERIT)
++                       file_entry->mode |= AA_EXEC_MMAP;
++
++      list_add(&file_entry->list, &profile->file_entry);
++      profile->num_file_entries++;
++
++      mode = file_entry->mode;
++
++      /* Handle partitioned lists
++       * Chain entries onto sublists based on individual
++       * permission bits. This allows more rapid searching.
++       */
++      for (i = 0; i <= POS_AA_FILE_MAX; i++) {
++              if (mode & (1 << i))
++                      /* profile->file_entryp[i] initially set to
++                       * NULL in alloc_aaprofile() */
++                      list_add(&file_entry->listp[i],
++                               &profile->file_entryp[i]);
++      }
++
++      return 1;
++
++out:
++      free_aa_entry(file_entry);
++      return 0;
++}
++
++#define AA_ENTRY_LIST(NAME) \
++      do { \
++      if (aa_is_nameX(e, AA_LIST, NULL, (NAME))) { \
++              rulename = ""; \
++              error_string = "Invalid file entry"; \
++              while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) { \
++                      struct aa_entry *file_entry; \
++                      file_entry = aa_activate_file_entry(e); \
++                      if (!file_entry) \
++                              goto fail; \
++                      if (!check_rule_and_add(file_entry, profile, \
++                                              &error_string)) { \
++                              rulename = file_entry->filename; \
++                              goto fail; \
++                      } \
++              } \
++      } \
++      } while (0)
++
++/**
++ * aa_activate_profile - unpack a serialized profile
++ * @e: serialized data extent information
++ * @error: error code returned if unpacking fails
++ */
++static struct aaprofile *aa_activate_profile(struct aa_ext *e, ssize_t *error)
++{
++      struct aaprofile *profile = NULL;
++      const char *rulename = "";
++      const char *error_string = "Invalid Profile";
++
++      *error = -EPROTO;
++
++      profile = alloc_aaprofile();
++      if (!profile) {
++              error_string = "Could not allocate profile";
++              *error = -ENOMEM;
++              goto fail;
++      }
++
++      /* check that we have the right struct being passed */
++      AA_READ_X(e, AA_STRUCT, NULL, "profile");
++      AA_READ_X(e, AA_DYN_STRING, &profile->name, NULL);
++
++      error_string = "Invalid flags";
++      /* per profile debug flags (debug, complain, audit) */
++      AA_READ_X(e, AA_STRUCT, NULL, "flags");
++      AA_READ_X(e, AA_U32, &(profile->flags.debug), NULL);
++      AA_READ_X(e, AA_U32, &(profile->flags.complain), NULL);
++      AA_READ_X(e, AA_U32, &(profile->flags.audit), NULL);
++      AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
++
++      error_string = "Invalid capabilities";
++      AA_READ_X(e, AA_U32, &(profile->capabilities), NULL);
++
++      /* get the file entries. */
++      AA_ENTRY_LIST("pgent");         /* pcre rules */
++      AA_ENTRY_LIST("sgent");         /* simple globs */
++      AA_ENTRY_LIST("fent");          /* regular file entries */
++
++      /* get the net entries */
++      if (aa_is_nameX(e, AA_LIST, NULL, "net")) {
++              error_string = "Invalid net entry";
++              while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) {
++                      if (!aa_activate_net_entry(e))
++                              goto fail;
++              }
++      }
++      rulename = "";
++
++      /* get subprofiles */
++      if (aa_is_nameX(e, AA_LIST, NULL, "hats")) {
++              error_string = "Invalid profile hat";
++              while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) {
++                      struct aaprofile *subprofile;
++                      subprofile = aa_activate_profile(e, error);
++                      if (!subprofile)
++                              goto fail;
++                      subprofile->parent = profile;
++                      list_add(&subprofile->list, &profile->sub);
++              }
++      }
++
++      error_string = "Invalid end of profile";
++      AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
++
++      return profile;
++
++fail:
++      AA_WARN("%s: %s %s in profile %s\n", INTERFACE_ID, rulename,
++              error_string, profile && profile->name ? profile->name
++              : "unknown");
++
++      if (profile) {
++              free_aaprofile(profile);
++              profile = NULL;
++      }
++
++      return NULL;
++}
++
++/**
++ * aa_activate_top_profile - unpack a serialized base profile
++ * @e: serialized data extent information
++ * @error: error code returned if unpacking fails
++ *
++ * check interface version unpack a profile and all its hats and patch
++ * in any extra information that the profile needs.
++ */
++static void *aa_activate_top_profile(struct aa_ext *e, ssize_t *error)
++{
++      struct aaprofile *profile = NULL;
++
++      /* get the interface version */
++      if (!aa_is_nameX(e, AA_U32, &e->version, "version")) {
++              AA_WARN("%s: version missing\n", INTERFACE_ID);
++              *error = -EPROTONOSUPPORT;
++              goto fail;
++      }
++
++      /* check that the interface version is currently supported */
++      if (e->version != 2) {
++              AA_WARN("%s: unsupported interface version (%d)\n",
++                      INTERFACE_ID, e->version);
++              *error = -EPROTONOSUPPORT;
++              goto fail;
++      }
++
++      profile = aa_activate_profile(e, error);
++      if (!profile)
++              goto fail;
++
++      if (!list_empty(&profile->sub) || profile->flags.complain) {
++              if (attach_nullprofile(profile))
++                      goto fail;
++      }
++      return profile;
++
++fail:
++      free_aaprofile(profile);
++      return NULL;
++}
++
++/**
++ * aa_file_prof_add - add a new profile to the profile list
++ * @data: serialized data stream
++ * @size: size of the serialized data stream
++ *
++ * unpack and add a profile to the profile list.  Return %0 or error
++ */
++ssize_t aa_file_prof_add(void *data, size_t size)
++{
++      struct aaprofile *profile = NULL;
++
++      struct aa_ext e = {
++              .start = data,
++              .end = data + size,
++              .pos = data
++      };
++      ssize_t error;
++
++      profile = aa_activate_top_profile(&e, &error);
++      if (!profile) {
++              AA_DEBUG("couldn't activate profile\n");
++              goto out;
++      }
++
++      /* aa_activate_top_profile allocates profile with initial 1 count
++       * aa_profilelist_add transfers that ref to profile list without
++       * further incrementing
++       */
++      if (aa_profilelist_add(profile)) {
++              error = size;
++      } else {
++              AA_WARN("trying to add profile (%s) that already exists.\n",
++                      profile->name);
++              put_aaprofile(profile);
++              error = -EEXIST;
++      }
++
++out:
++      return error;
++}
++
++/**
++ * aa_file_prof_repl - replace a profile on the profile list
++ * @udata: serialized data stream
++ * @size: size of the serialized data stream
++ *
++ * unpack and replace a profile on the profile list and uses of that profile
++ * by any subdomain.  If the profile does not exist on the profile list
++ * it is added.  Return %0 or error.
++ */
++ssize_t aa_file_prof_repl(void *udata, size_t size)
++{
++      struct aa_taskreplace_data data;
++      struct aa_ext e = {
++              .start = udata,
++              .end = udata + size,
++              .pos = udata
++      };
++
++      ssize_t error;
++
++      data.new_profile = aa_activate_top_profile(&e, &error);
++      if (!data.new_profile) {
++              AA_DEBUG("couldn't activate profile\n");
++              goto out;
++      }
++
++      /* Refcount on data.new_profile is 1 (aa_activate_top_profile).
++       *
++       * This reference will be inherited by aa_profilelist_replace for it's
++       * profile list reference but this isn't sufficient.
++       *
++       * Another replace (*for-same-profile*) may race us here.
++       * Task A calls aa_profilelist_replace(new_profile) and is interrupted.
++       * Task B old_profile = aa_profilelist_replace() will return task A's
++       * new_profile with the count of 1.  If task B proceeeds to put this
++       * profile it will dissapear from under task A.
++       *
++       * Grab extra reference on new_profile to prevent this
++       */
++
++      get_aaprofile(data.new_profile);
++
++      data.old_profile = aa_profilelist_replace(data.new_profile);
++
++      /* If there was an old profile,  find all currently executing tasks
++       * using this profile and replace the old profile with the new.
++       */
++      if (data.old_profile) {
++              AA_DEBUG("%s: try to replace profile (%p)%s\n",
++                       __FUNCTION__,
++                       data.old_profile,
++                       data.old_profile->name);
++
++              aa_subdomainlist_iterate(taskreplace_iter, (void *)&data);
++
++              /* it's off global list, and we are done replacing */
++              put_aaprofile(data.old_profile);
++      }
++
++      /* release extra reference obtained above (race) */
++      put_aaprofile(data.new_profile);
++
++      error = size;
++
++out:
++      return error;
++}
++
++/**
++ * aa_file_prof_remove - remove a profile from the system
++ * @name: name of the profile to remove
++ * @size: size of the name
++ *
++ * remove a profile from the profile list and all subdomain references
++ * to said profile.  Return %0 on success, else error.
++ */
++ssize_t aa_file_prof_remove(const char *name, size_t size)
++{
++      struct aaprofile *old_profile;
++
++      /* if the old profile exists it will be removed from the list and
++       * a reference is returned.
++       */
++      old_profile = aa_profilelist_remove(name);
++
++      if (old_profile) {
++              /* remove profile from any tasks using it */
++              aa_subdomainlist_iterate(taskremove_iter, (void *)old_profile);
++
++              /* drop reference obtained by aa_profilelist_remove */
++              put_aaprofile(old_profile);
++      } else {
++              AA_WARN("%s: trying to remove profile (%s) that "
++                      "doesn't exist - skipping.\n", __FUNCTION__, name);
++              return -ENOENT;
++      }
++
++      return size;
++}
++
++/**
++ * free_aaprofile_kref - free aaprofile by kref (called by put_aaprofile)
++ * @kr: kref callback for freeing of a profile
++ */
++void free_aaprofile_kref(struct kref *kr)
++{
++      struct aaprofile *p=container_of(kr, struct aaprofile, count);
++
++      call_rcu(&p->rcu, free_aaprofile_rcu);
++}
++
++/**
++ * free_aaprofile - free aaprofile structure
++ * @profile: the profile to free
++ *
++ * free a profile, its file entries hats and null_profile.  All references
++ * to the profile, its hats and null_profile must have been put.
++ * If the profile was referenced by a subdomain free_aaprofile should be
++ * called from an rcu callback routine.
++ */
++void free_aaprofile(struct aaprofile *profile)
++{
++      struct aa_entry *ent, *tmp;
++      struct aaprofile *p, *ptmp;
++
++      AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
++
++      if (!profile)
++              return;
++
++      /* profile is still on global profile list -- invalid */
++      if (!list_empty(&profile->list)) {
++              AA_ERROR("%s: internal error, "
++                       "profile '%s' still on global list\n",
++                       __FUNCTION__,
++                       profile->name);
++              BUG();
++      }
++
++      list_for_each_entry_safe(ent, tmp, &profile->file_entry, list) {
++              if (ent->filename)
++                      AA_DEBUG("freeing aa_entry: %p %s\n",
++                               ent->filename, ent->filename);
++              list_del_init(&ent->list);
++              free_aa_entry(ent);
++      }
++
++      /* use free_aaprofile instead of put_aaprofile to destroy the
++       * null_profile, because the null_profile use the same reference
++       * counting as hats, ie. the count goes to the base profile.
++       */
++      free_aaprofile(profile->null_profile);
++      list_for_each_entry_safe(p, ptmp, &profile->sub, list) {
++              list_del_init(&p->list);
++              p->parent = NULL;
++              put_aaprofile(p);
++      }
++
++      if (profile->name) {
++              AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name);
++              kfree(profile->name);
++      }
++
++      kfree(profile);
++}
+Index: b/security/apparmor/module_interface.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/module_interface.h
+@@ -0,0 +1,37 @@
++#ifndef __MODULEINTERFACE_H
++#define __MODULEINTERFACE_H
++
++/* Codes of the types of basic structures that are understood */
++#define AA_CODE_BYTE (sizeof(u8))
++#define INTERFACE_ID "INTERFACE"
++
++#define APPARMOR_INTERFACE_VERSION 2
++
++enum aa_code {
++      AA_U8,
++      AA_U16,
++      AA_U32,
++      AA_U64,
++      AA_NAME,        /* same as string except it is items name */
++      AA_DYN_STRING,
++      AA_STATIC_BLOB,
++      AA_STRUCT,
++      AA_STRUCTEND,
++      AA_LIST,
++      AA_LISTEND,
++      AA_OFFSET,
++      AA_BAD
++};
++
++/* aa_ext tracks the kernel buffer and read position in it.  The interface
++ * data is copied into a kernel buffer in apparmorfs and then handed off to
++ * the activate routines.
++ */
++struct aa_ext {
++      void *start;
++      void *end;
++      void *pos;      /* pointer to current position in the buffer */
++      u32 version;
++};
++
++#endif /* __MODULEINTERFACE_H */
+Index: b/security/apparmor/procattr.c
+===================================================================
+--- /dev/null
++++ b/security/apparmor/procattr.c
+@@ -0,0 +1,332 @@
++/*
++ *    Copyright (C) 2005 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
++ */
++
++/* for isspace */
++#include <linux/ctype.h>
++
++#include "apparmor.h"
++#include "inline.h"
++
++size_t aa_getprocattr(struct aaprofile *active, char *str, size_t size)
++{
++      int error = -EACCES;    /* default to a perm denied */
++      size_t len;
++
++      if (active) {
++              size_t lena, lenm, lenp = 0;
++              const char *enforce_str = " (enforce)";
++              const char *complain_str = " (complain)";
++              const char *mode_str =
++                      PROFILE_COMPLAIN(active) ? complain_str : enforce_str;
++
++              lenm = strlen(mode_str);
++
++              lena = strlen(active->name);
++
++              len = lena;
++              if (IN_SUBPROFILE(active)) {
++                      lenp = strlen(BASE_PROFILE(active)->name);
++                      len += (lenp + 1);      /* +1 for ^ */
++              }
++              /* DONT null terminate strings we output via proc */
++              len += (lenm + 1);      /* for \n */
++
++              if (len <= size) {
++                      if (lenp) {
++                              memcpy(str, BASE_PROFILE(active)->name,
++                                     lenp);
++                              str += lenp;
++                              *str++ = '^';
++                      }
++
++                      memcpy(str, active->name, lena);
++                      str += lena;
++                      memcpy(str, mode_str, lenm);
++                      str += lenm;
++                      *str++ = '\n';
++                      error = len;
++              } else if (size == 0) {
++                      error = len;
++              } else {
++                      error = -ERANGE;
++              }
++      } else {
++              const char *unconstrained_str = "unconstrained\n";
++              len = strlen(unconstrained_str);
++
++              /* DONT null terminate strings we output via proc */
++              if (len <= size) {
++                      memcpy(str, unconstrained_str, len);
++                      error = len;
++              } else if (size == 0) {
++                      error = len;
++              } else {
++                      error = -ERANGE;
++              }
++      }
++
++      return error;
++
++}
++
++int aa_setprocattr_changehat(char *hatinfo, size_t infosize)
++{
++      int error = -EINVAL;
++      char *token = NULL, *hat, *smagic, *tmp;
++      u32 magic;
++      int rc, len, consumed;
++      unsigned long flags;
++
++      AA_DEBUG("%s: %p %zd\n", __FUNCTION__, hatinfo, infosize);
++
++      /* strip leading white space */
++      while (infosize && isspace(*hatinfo)) {
++              hatinfo++;
++              infosize--;
++      }
++
++      if (infosize == 0)
++              goto out;
++
++      /*
++       * Copy string to a new buffer so we can play with it
++       * It may be zero terminated but we add a trailing 0
++       * for 100% safety
++       */
++      token = kmalloc(infosize + 1, GFP_KERNEL);
++
++      if (!token) {
++              error = -ENOMEM;
++              goto out;
++      }
++
++      memcpy(token, hatinfo, infosize);
++      token[infosize] = 0;
++
++      /* error is INVAL until we have at least parsed something */
++      error = -EINVAL;
++
++      tmp = token;
++      while (*tmp && *tmp != '^') {
++              tmp++;
++      }
++
++      if (!*tmp || tmp == token) {
++              AA_WARN("%s: Invalid input '%s'\n", __FUNCTION__, token);
++              goto out;
++      }
++
++      /* split magic and hat into two strings */
++      *tmp = 0;
++      smagic = token;
++
++      /*
++       * Initially set consumed=strlen(magic), as if sscanf
++       * consumes all input via the %x it will not process the %n
++       * directive. Otherwise, if sscanf does not consume all the
++       * input it will process the %n and update consumed.
++       */
++      consumed = len = strlen(smagic);
++
++      rc = sscanf(smagic, "%x%n", &magic, &consumed);
++
++      if (rc != 1 || consumed != len) {
++              AA_WARN("%s: Invalid hex magic %s\n",
++                      __FUNCTION__,
++                      smagic);
++              goto out;
++      }
++
++      hat = tmp + 1;
++
++      if (!*hat)
++              hat = NULL;
++
++      if (!hat && !magic) {
++              AA_WARN("%s: Invalid input, NULL hat and NULL magic\n",
++                      __FUNCTION__);
++              goto out;
++      }
++
++      AA_DEBUG("%s: Magic 0x%x Hat '%s'\n",
++               __FUNCTION__, magic, hat ? hat : NULL);
++
++      spin_lock_irqsave(&sd_lock, flags);
++      error = aa_change_hat(hat, magic);
++      spin_unlock_irqrestore(&sd_lock, flags);
++
++out:
++      if (token) {
++              memset(token, 0, infosize);
++              kfree(token);
++      }
++
++      return error;
++}
++
++int aa_setprocattr_setprofile(struct task_struct *p, char *profilename,
++                            size_t profilesize)
++{
++      int error = -EINVAL;
++      struct aaprofile *profile = NULL;
++      struct subdomain *sd;
++      char *name = NULL;
++      unsigned long flags;
++
++      AA_DEBUG("%s: current %s(%d)\n",
++               __FUNCTION__, current->comm, current->pid);
++
++      /* strip leading white space */
++      while (profilesize && isspace(*profilename)) {
++              profilename++;
++              profilesize--;
++      }
++
++      if (profilesize == 0)
++              goto out;
++
++      /*
++       * Copy string to a new buffer so we guarantee it is zero
++       * terminated
++       */
++      name = kmalloc(profilesize + 1, GFP_KERNEL);
++
++      if (!name) {
++              error = -ENOMEM;
++              goto out;
++      }
++
++      strncpy(name, profilename, profilesize);
++      name[profilesize] = 0;
++
++ repeat:
++      if (strcmp(name, "unconstrained") != 0) {
++              profile = aa_profilelist_find(name);
++              if (!profile) {
++                      AA_WARN("%s: Unable to switch task %s(%d) to profile"
++                              "'%s'. No such profile.\n",
++                              __FUNCTION__,
++                              p->comm, p->pid,
++                              name);
++
++                      error = -EINVAL;
++                      goto out;
++              }
++      }
++
++      spin_lock_irqsave(&sd_lock, flags);
++
++      sd = AA_SUBDOMAIN(p->security);
++
++      /* switch to unconstrained */
++      if (!profile) {
++              if (__aa_is_confined(sd)) {
++                      AA_WARN("%s: Unconstraining task %s(%d) "
++                              "profile %s active %s\n",
++                              __FUNCTION__,
++                              p->comm, p->pid,
++                              BASE_PROFILE(sd->active)->name,
++                              sd->active->name);
++
++                      aa_switch_unconfined(sd);
++              } else {
++                      AA_WARN("%s: task %s(%d) "
++                              "is already unconstrained\n",
++                              __FUNCTION__, p->comm, p->pid);
++              }
++      } else {
++              if (!sd) {
++                      /* this task was created before module was
++                       * loaded, allocate a subdomain
++                       */
++                      AA_WARN("%s: task %s(%d) has no subdomain\n",
++                              __FUNCTION__, p->comm, p->pid);
++
++                      /* unlock so we can safely GFP_KERNEL */
++                      spin_unlock_irqrestore(&sd_lock, flags);
++
++                      sd = alloc_subdomain(p);
++                      if (!sd) {
++                              AA_WARN("%s: Unable to allocate subdomain for "
++                                      "task %s(%d). Cannot confine task to "
++                                      "profile %s\n",
++                                      __FUNCTION__,
++                                      p->comm, p->pid,
++                                      name);
++
++                              error = -ENOMEM;
++                              put_aaprofile(profile);
++
++                              goto out;
++                      }
++
++                      spin_lock_irqsave(&sd_lock, flags);
++                      if (!AA_SUBDOMAIN(p->security)) {
++                              p->security = sd;
++                      } else { /* race */
++                              free_subdomain(sd);
++                              sd = AA_SUBDOMAIN(p->security);
++                      }
++              }
++
++              /* ensure the profile hasn't been replaced */
++
++              if (unlikely(profile->isstale)) {
++                      WARN_ON(profile == null_complain_profile);
++
++                      /* drop refcnt obtained from earlier get_aaprofile */
++                      put_aaprofile(profile);
++                      profile = aa_profilelist_find(name);
++
++                      if (!profile) {
++                              /* Race, profile was removed. */
++                              spin_unlock_irqrestore(&sd_lock, flags);
++                              goto repeat;
++                      }
++              }
++
++              /* we do not do a normal task replace since we are not
++               * replacing with the same profile.
++               * If existing process is in a hat, it will be moved
++               * into the new parent profile, even if this new
++               * profile has a identical named hat.
++               */
++
++              AA_WARN("%s: Switching task %s(%d) "
++                      "profile %s active %s to new profile %s\n",
++                      __FUNCTION__,
++                      p->comm, p->pid,
++                      sd->active ? BASE_PROFILE(sd->active)->name :
++                              "unconstrained",
++                      sd->active ? sd->active->name : "unconstrained",
++                      name);
++
++              aa_switch(sd, profile);
++
++              put_aaprofile(profile); /* drop ref we obtained above
++                                       * from aa_profilelist_find
++                                       */
++
++              /* Reset magic in case we were in a subhat before
++               * This is the only case where we zero the magic after
++               * calling aa_switch
++               */
++              sd->hat_magic = 0;
++      }
++
++      spin_unlock_irqrestore(&sd_lock, flags);
++
++      error = 0;
++out:
++      kfree(name);
++
++      return error;
++}
+Index: b/security/apparmor/shared.h
+===================================================================
+--- /dev/null
++++ b/security/apparmor/shared.h
+@@ -0,0 +1,46 @@
++/*
++ *    Copyright (C) 2000, 2001, 2004, 2005 Novell/SUSE
++ *
++ *    Immunix AppArmor LSM
++ *
++ *    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 _SHARED_H
++#define _SHARED_H
++
++/* start of system offsets */
++#define POS_AA_FILE_MIN                       0
++#define POS_AA_MAY_EXEC                       POS_AA_FILE_MIN
++#define POS_AA_MAY_WRITE              (POS_AA_MAY_EXEC + 1)
++#define POS_AA_MAY_READ                       (POS_AA_MAY_WRITE + 1)
++#define POS_AA_MAY_APPEND             (POS_AA_MAY_READ + 1)
++/* end of system offsets */
++
++#define POS_AA_MAY_LINK                       (POS_AA_MAY_APPEND + 1)
++#define POS_AA_EXEC_INHERIT           (POS_AA_MAY_LINK + 1)
++#define POS_AA_EXEC_UNCONSTRAINED     (POS_AA_EXEC_INHERIT + 1)
++#define POS_AA_EXEC_PROFILE           (POS_AA_EXEC_UNCONSTRAINED + 1)
++#define POS_AA_EXEC_MMAP              (POS_AA_EXEC_PROFILE + 1)
++#define POS_AA_EXEC_UNSAFE            (POS_AA_EXEC_MMAP + 1)
++#define POS_AA_FILE_MAX                       POS_AA_EXEC_UNSAFE
++
++/* Modeled after MAY_READ, MAY_WRITE, MAY_EXEC def'ns */
++#define AA_MAY_EXEC                   (0x01 << POS_AA_MAY_EXEC)
++#define AA_MAY_WRITE                  (0x01 << POS_AA_MAY_WRITE)
++#define AA_MAY_READ                   (0x01 << POS_AA_MAY_READ)
++#define AA_MAY_LINK                   (0x01 << POS_AA_MAY_LINK)
++#define AA_EXEC_INHERIT                       (0x01 << POS_AA_EXEC_INHERIT)
++#define AA_EXEC_UNCONSTRAINED         (0x01 << POS_AA_EXEC_UNCONSTRAINED)
++#define AA_EXEC_PROFILE                       (0x01 << POS_AA_EXEC_PROFILE)
++#define AA_EXEC_MMAP                  (0x01 << POS_AA_EXEC_MMAP)
++#define AA_EXEC_UNSAFE                        (0x01 << POS_AA_EXEC_UNSAFE)
++
++#define AA_EXEC_MODIFIERS             (AA_EXEC_INHERIT | \
++                                       AA_EXEC_UNCONSTRAINED | \
++                                       AA_EXEC_PROFILE)
++
++#endif /* _SHARED_H */
This page took 0.428022 seconds and 4 git commands to generate.