File apparmor-272.patch of Package kernel

diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/aamatch/Kbuild linux-2.6.x/security/apparmor/aamatch/Kbuild
--- linux-2.6.x.orig/security/apparmor/aamatch/Kbuild	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/aamatch/Kbuild	2006-08-04 13:07:32.000000000 -0700
@@ -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
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/aamatch/match.h linux-2.6.x/security/apparmor/aamatch/match.h
--- linux-2.6.x.orig/security/apparmor/aamatch/match.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/aamatch/match.h	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,137 @@
+/*
+ *	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").
+
+ * sdmatch_alloc
+ * sdmatch_free
+ * sdmatch_features
+ * sdmatch_serialize
+ * sdmatch_match
+ *
+ * The intent is for the primary module to export (via virtual fs entries)
+ * the features provided by the submodule (sdmatch_features) so that the
+ * parser may only load policy that can be supported.
+ *
+ * The primary module will call sdmatch_serialize to allow the submodule
+ * to consume submodule specific data from parser data stream and will call
+ * sdmatch_match to determine if a pathname matches an sd_entry.
+ */
+
+typedef int (*sdmatch_serializecb)
+	(struct sd_ext *, enum sd_code, void *, const char *);
+
+/**
+ * sdmatch_alloc: allocate extradata (if necessary)
+ * @entry_type: type of entry being allocated
+ * Return value: NULL indicates no data was allocated (ERR_PTR(x) on error)
+ */
+extern void* sdmatch_alloc(enum entry_t entry_type);
+
+/**
+ * sdmatch_free: release data allocated by sdmatch_alloc
+ * @entry_extradata: data previously allocated by sdmatch_alloc
+ */
+extern void sdmatch_free(void *entry_extradata);
+
+/**
+ * sdmatch_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* sdmatch_features(void);
+
+/**
+ * sdmatch_serialize: serialize extradata
+ * @entry_extradata: data previously allocated by sdmatch_alloc
+ * @e: input stream
+ * @cb: callback fn (consume incoming data stream)
+ * Return value: 0 success, -ve error
+ */
+extern int sdmatch_serialize(void *entry_extradata, struct sd_ext *e,
+			     sdmatch_serializecb cb);
+
+/**
+ * sdmatch_match: determine if pathname matches entry
+ * @pathname: pathname to verify
+ * @entry_name: entry name
+ * @entry_type: type of entry
+ * @entry_extradata: data previously allocated by sdmatch_alloc
+ * Return value: 1 match, 0 othersise
+ */
+extern unsigned int sdmatch_match(const char *pathname, const char *entry_name,
+				  enum entry_t entry_type,
+				  void *entry_extradata);
+
+
+/**
+ * sd_getentry_type - return string representation of entry_t
+ * @etype: entry type
+ */
+static inline const char *sd_getentry_type(enum entry_t etype)
+{
+	const char *etype_names[] = {
+		"sd_entry_literal",
+		"sd_entry_tailglob",
+		"sd_entry_pattern",
+		"sd_entry_invalid"
+	};
+
+	if (etype >= sd_entry_invalid) {
+		etype = sd_entry_invalid;
+	}
+
+	return etype_names[etype];
+}
+
+/**
+ * sdmatch_match_common - helper function to check if a pathname matches
+ * a literal/tailglob
+ * @path: path requested to search for
+ * @entry_name: name from sd_entry
+ * @etype: type of entry
+ */
+static inline int sdmatch_match_common(const char *path,
+					   const char *entry_name,
+			   		   enum entry_t etype)
+{
+	int retval;
+
+	/* literal, no pattern matching characters */
+	if (etype == sd_entry_literal) {
+		retval = (strcmp(entry_name, path) == 0);
+	/* trailing ** glob pattern */
+	} else if (etype == sd_entry_tailglob) {
+		retval = (strncmp(entry_name, path,
+				  strlen(entry_name) - 2) == 0);
+	} else {
+		SD_WARN("%s: Invalid entry_t %d\n", __FUNCTION__, etype);
+		retval = 0;
+	}
+
+#if 0
+	SD_DEBUG("%s(%d): %s %s [%s]\n",
+		__FUNCTION__, retval, path, entry_name,
+		sd_getentry_type(etype));
+#endif
+
+	return retval;
+}
+
+#endif /* __MATCH_H */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/aamatch/match_pcre.c linux-2.6.x/security/apparmor/aamatch/match_pcre.c
--- linux-2.6.x.orig/security/apparmor/aamatch/match_pcre.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/aamatch/match_pcre.c	2006-08-04 13:07:32.000000000 -0700
@@ -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 sdmatch_entry
+{
+	char *pattern;
+	pcre *compiled;
+};
+
+void* sdmatch_alloc(enum entry_t entry_type)
+{
+void *ptr=NULL;
+
+	if (entry_type == sd_entry_pattern) {
+		ptr = kmalloc(sizeof(struct sdmatch_entry), GFP_KERNEL);
+		if (ptr)
+			memset(ptr, 0, sizeof(struct sdmatch_entry));
+		else
+			ptr=ERR_PTR(-ENOMEM);
+	} else if (entry_type != sd_entry_literal &&
+		   entry_type != sd_entry_tailglob) {
+		ptr = ERR_PTR(-EINVAL);
+	}
+
+	return ptr;
+}
+
+void sdmatch_free(void *ptr)
+{
+	if (ptr) {
+		struct sdmatch_entry *ed = (struct sdmatch_entry *) ptr;
+		kfree(ed->pattern);
+		kfree(ed->compiled);	/* allocated by SD_READ_X */
+	}
+	kfree(ptr);
+}
+
+const char *sdmatch_features(void)
+{
+	return features;
+}
+
+int sdmatch_serialize(void *entry_extradata, struct sd_ext *e,
+		      sdmatch_serializecb cb)
+{
+#define SD_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 sdmatch_entry *ed = (struct sdmatch_entry *) entry_extradata;
+
+	if (ed == NULL)
+		goto done;
+
+	SD_READ_X(e, SD_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 */
+	SD_READ_X(e, SD_STRUCT, NULL, "pcre");
+	SD_READ_X(e, SD_U32, &size, "pattern.size");
+	SD_READ_X(e, SD_U32, &magic, "pattern.magic");
+
+	/* 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);
+
+	SD_READ_X(e, SD_U32, &opts, "pattern.options");
+	ed->compiled->options = opts;
+	SD_READ_X(e, SD_U16, &ed->compiled->top_bracket, "pattern.top_bracket");
+	SD_READ_X(e, SD_U16, &ed->compiled->top_backref, "pattern.top_backref");
+	SD_READ_X(e, SD_U8, &t_char, "pattern.first_char");
+	ed->compiled->first_char = t_char;
+	SD_READ_X(e, SD_U8, &t_char, "pattern.req_char");
+	ed->compiled->req_char = t_char;
+	SD_READ_X(e, SD_U8, &t_char, "pattern.code[0]");
+	ed->compiled->code[0] = t_char;
+
+	SD_READ_X(e, SD_STATIC_BLOB, &ed->compiled->code[1], NULL);
+
+	SD_READ_X(e, SD_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 SD_READ_X */
+		kfree(ed->compiled);
+		ed->pattern = NULL;
+		ed->compiled = NULL;
+	}
+
+	return error;
+}
+
+unsigned int sdmatch_match(const char *pathname, const char *entry_name,
+			   enum entry_t entry_type, void *entry_extradata)
+{
+	int ret;
+
+	if (entry_type == sd_entry_pattern) {
+		int pcreret;
+		struct sdmatch_entry *ed =
+			(struct sdmatch_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
+        	//SD_DEBUG("%s(%d): %s %s %d\n", __FUNCTION__,
+		//	 ret, pathname, ed->pattern, pcreret);
+	} else {
+		ret = sdmatch_match_common(pathname, entry_name, entry_type);
+	}
+
+        return ret;
+}
+
+EXPORT_SYMBOL_GPL(sdmatch_alloc);
+EXPORT_SYMBOL_GPL(sdmatch_free);
+EXPORT_SYMBOL_GPL(sdmatch_features);
+EXPORT_SYMBOL_GPL(sdmatch_serialize);
+EXPORT_SYMBOL_GPL(sdmatch_match);
+
+MODULE_DESCRIPTION("AppArmor aa_match module [pcre]");
+MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
+MODULE_LICENSE("GPL");
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/aamatch/pcre_exec.c linux-2.6.x/security/apparmor/aamatch/pcre_exec.c
--- linux-2.6.x.orig/security/apparmor/aamatch/pcre_exec.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/aamatch/pcre_exec.c	2006-08-04 13:07:32.000000000 -0700
@@ -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 */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/aamatch/pcre_exec.h linux-2.6.x/security/apparmor/aamatch/pcre_exec.h
--- linux-2.6.x.orig/security/apparmor/aamatch/pcre_exec.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/aamatch/pcre_exec.h	2006-08-04 13:07:32.000000000 -0700
@@ -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 */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/aamatch/pcre_tables.h linux-2.6.x/security/apparmor/aamatch/pcre_tables.h
--- linux-2.6.x.orig/security/apparmor/aamatch/pcre_tables.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/aamatch/pcre_tables.h	2006-08-04 13:07:32.000000000 -0700
@@ -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 */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/apparmorfs.c linux-2.6.x/security/apparmor/apparmorfs.c
--- linux-2.6.x.orig/security/apparmor/apparmorfs.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/apparmorfs.c	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,440 @@
+/*
+ *	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 "aamatch/match.h"
+
+#define SECFS_SD "apparmor"
+static struct dentry *sdfs_dentry = NULL;
+
+/* profile */
+extern struct seq_operations subdomainfs_profiles_op;
+static int sd_prof_open(struct inode *inode, struct file *file);
+static int sd_prof_release(struct inode *inode, struct file *file);
+
+static struct file_operations subdomainfs_profiles_fops = {
+	.open =		sd_prof_open,
+	.read =		seq_read,
+	.llseek =	seq_lseek,
+	.release =	sd_prof_release,
+};
+
+/* version */
+static ssize_t sd_version_read(struct file *file, char __user *buf,
+			       size_t size, loff_t *ppos);
+
+static struct file_operations subdomainfs_version_fops = {
+	.read = 	sd_version_read,
+};
+
+/* matching */
+static ssize_t sd_matching_read(struct file *file, char __user *buf,
+			       size_t size, loff_t *ppos);
+
+static struct file_operations subdomainfs_matching_fops = {
+	.read = 	sd_matching_read,
+};
+
+
+/* interface */
+extern ssize_t sd_file_prof_add(void *, size_t);
+extern ssize_t sd_file_prof_repl(void *, size_t);
+extern ssize_t sd_file_prof_remove(const char *, int);
+
+static ssize_t sd_profile_load(struct file *f, const char __user *buf,
+			       size_t size, loff_t *pos);
+static ssize_t sd_profile_replace(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos);
+static ssize_t sd_profile_remove(struct file *f, const char __user *buf,
+				 size_t size, loff_t *pos);
+
+static struct file_operations subdomainfs_profile_load = {
+	.write = sd_profile_load
+};
+
+static struct file_operations subdomainfs_profile_replace = {
+	.write = sd_profile_replace
+};
+
+static struct file_operations subdomainfs_profile_remove = {
+	.write = sd_profile_remove
+};
+
+
+/* control */
+static u64 sd_control_get(void *data);
+static void sd_control_set(void *data, u64 val);
+
+DEFINE_SIMPLE_ATTRIBUTE(subdomainfs_control_fops, sd_control_get,
+			sd_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/subdomain */
+	{SECFS_SD, 	S_IFDIR, 0550},	/* DO NOT EDIT/MOVE */
+
+	/* interface for obtaining list of profiles currently loaded */
+	{"profiles", 	S_IFREG, 0440, &subdomainfs_profiles_fops,
+				       NULL},
+
+	/* interface for obtaining version# of subdomain */
+	{"version",  	S_IFREG, 0440, &subdomainfs_version_fops,
+				       NULL},
+
+	/* interface for obtaining matching features supported */
+	{"matching",  	S_IFREG, 0440, &subdomainfs_matching_fops,
+				       NULL},
+
+	/* interface for loading/removing/replacing profiles */
+	{".load",    	S_IFREG, 0640, &subdomainfs_profile_load,
+				       NULL},
+	{".replace", 	S_IFREG, 0640, &subdomainfs_profile_replace,
+				       NULL},
+	{".remove",  	S_IFREG, 0640, &subdomainfs_profile_remove,
+				       NULL},
+
+	/* interface for setting binary config values */
+	{"control",  	S_IFDIR, 0550},
+	{"complain", 	S_IFREG, 0640, &subdomainfs_control_fops,
+				       &subdomain_complain},
+	{"audit",    	S_IFREG, 0640, &subdomainfs_control_fops,
+				       &subdomain_audit},
+	{"debug",    	S_IFREG, 0640, &subdomainfs_control_fops,
+				       &subdomain_debug},
+	{"logsyscall", 	S_IFREG, 0640, &subdomainfs_control_fops,
+				       &subdomain_logsyscall},
+	{NULL,       	S_IFDIR, 0},
+
+	/* root end */
+	{NULL,       	S_IFDIR, 0}
+};
+
+#define SDFS_DENTRY root_entries[0].dentry
+
+static const unsigned int num_entries =
+	sizeof(root_entries) / sizeof(struct root_entry);
+
+
+
+static int sd_prof_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &subdomainfs_profiles_op);
+}
+
+
+static int sd_prof_release(struct inode *inode, struct file *file)
+{
+	return seq_release(inode, file);
+}
+
+static ssize_t sd_version_read(struct file *file, char __user *buf,
+			       size_t size, loff_t *ppos)
+{
+	const char *version = apparmor_version_nl();
+
+	return simple_read_from_buffer(buf, size, ppos, version,
+				       strlen(version));
+}
+
+static ssize_t sd_matching_read(struct file *file, char __user *buf,
+			       size_t size, loff_t *ppos)
+{
+	const char *matching = sdmatch_features();
+
+	return simple_read_from_buffer(buf, size, ppos, matching,
+				       strlen(matching));
+}
+
+static char *sd_simple_write_to_buffer(const char __user *userbuf,
+				       size_t alloc_size, size_t copy_size,
+				       loff_t *pos, const char *msg)
+{
+	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.
+	 */
+	if (sd_is_confined()) {
+		struct subdomain *sd = SD_SUBDOMAIN(current->security);
+
+		SD_WARN("REJECTING access to profile %s (%s(%d) "
+			"profile %s active %s)\n",
+			msg, current->comm, current->pid,
+			sd->profile->name, sd->active->name);
+
+		data = ERR_PTR(-EPERM);
+		goto out;
+	}
+
+	data = vmalloc(alloc_size);
+	if (data == NULL) {
+		data = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+
+	if (copy_from_user(data, userbuf, copy_size)) {
+		vfree(data);
+		data = ERR_PTR(-EFAULT);
+		goto out;
+	}
+
+out:
+	return data;
+}
+
+static ssize_t sd_profile_load(struct file *f, const char __user *buf,
+			       size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	data = sd_simple_write_to_buffer(buf, size, size, pos, "load");
+
+	if (!IS_ERR(data)) {
+		error = sd_file_prof_add(data, size);
+		vfree(data);
+	} else {
+		error = PTR_ERR(data);
+	}
+
+	return error;
+}
+
+static ssize_t sd_profile_replace(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	data = sd_simple_write_to_buffer(buf, size, size, pos, "replacement");
+
+	if (!IS_ERR(data)) {
+		error = sd_file_prof_repl(data, size);
+		vfree(data);
+	} else {
+		error = PTR_ERR(data);
+	}
+
+	return error;
+}
+
+static ssize_t sd_profile_remove(struct file *f, const char __user *buf,
+				  size_t size, loff_t *pos)
+{
+	char *data;
+	ssize_t error;
+
+	/* sd_file_prof_remove needs a null terminated string so 1 extra
+	 * byte is allocated and null the copied data is then null terminated
+	 */
+	data = sd_simple_write_to_buffer(buf, size+1, size, pos, "removal");
+
+	if (!IS_ERR(data)) {
+		data[size] = 0;
+		error = sd_file_prof_remove(data, size);
+		vfree(data);
+	} else {
+		error = PTR_ERR(data);
+	}
+
+	return error;
+}
+
+static u64 sd_control_get(void *data)
+{
+	return *(int *)data;
+}
+
+static void sd_control_set(void *data, u64 val)
+{
+	if (val > 1)
+		val = 1;
+
+	*(int*)data = (int)val;
+}
+
+static void clear_subdomainfs(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);
+
+			SD_DEBUG("%s: deleted subdomainfs 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_subdomainfs(struct dentry *root)
+{
+	unsigned int i, parent_index, depth;
+
+#define ENT root_entries[i]
+
+	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_SD) != 0 ||
+	    root_entries[0].mode != S_IFDIR) {
+		SD_ERROR("%s: root entry 0 is not SECFS_SD/dir\n",
+			__FUNCTION__);
+		goto error;
+	}
+
+	/* 2. Verify table structure */
+	parent_index = 0;
+	depth = 1;
+
+	for (i = 1; i < num_entries; i++) {
+		ENT.parent_index = parent_index;
+
+		if (ENT.name && ENT.mode == S_IFDIR) {
+			depth++;
+			parent_index = i;
+		} else if (!ENT.name) {
+			if (ENT.mode != S_IFDIR || depth == 0) {
+				SD_ERROR("%s: root_entry %d invalid (%u %d)",
+					 __FUNCTION__, i,
+					 ENT.mode, ENT.parent_index);
+				goto error;
+			}
+
+			depth--;
+			parent_index = root_entries[parent_index].parent_index;
+		}
+	}
+
+	if (depth != 0) {
+		SD_ERROR("%s: root_entry table not correctly terminated\n",
+			__FUNCTION__);
+		goto error;
+	}
+
+	/* 3. Create root (parent=NULL) */
+	i=0;
+
+	ENT.dentry = securityfs_create_file(ENT.name,
+					ENT.mode | ENT.access,
+					NULL, NULL, NULL);
+
+	if (ENT.dentry)
+		SD_DEBUG("%s: created securityfs/subdomain [dentry=%p]\n",
+			__FUNCTION__, ENT.dentry);
+	else
+		goto error;
+
+
+	/* 4. create remaining nodes */
+	for (i = 1; i < num_entries; i++) {
+		struct dentry *parent;
+
+		/* end of directory ? */
+		if (!ENT.name)
+			continue;
+
+		parent = root_entries[ENT.parent_index].dentry;
+
+		ENT.dentry = securityfs_create_file(ENT.name,
+					ENT.mode | ENT.access,
+					parent,
+					ENT.mode != S_IFDIR ? ENT.data : NULL,
+					ENT.mode != S_IFDIR ? ENT.fops : NULL);
+
+		if (!ENT.dentry)
+			goto cleanup_error;
+
+		SD_DEBUG("%s: added subdomainfs entry "
+			 "name=%s mode=%x dentry=%p [parent %p]\n",
+			__FUNCTION__, ENT.name, ENT.mode|ENT.access,
+			ENT.dentry, parent);
+	}
+
+	return 1;
+
+cleanup_error:
+	clear_subdomainfs();
+
+error:
+	return 0;
+}
+
+int create_subdomainfs(void)
+{
+	if (SDFS_DENTRY)
+		SD_ERROR("%s: Subdomain securityfs already exists\n",
+			__FUNCTION__);
+	else if (!populate_subdomainfs(sdfs_dentry))
+		SD_ERROR("%s: Error populating Subdomain securityfs\n",
+			__FUNCTION__);
+
+	return (SDFS_DENTRY != NULL);
+}
+
+int destroy_subdomainfs(void)
+{
+	if (SDFS_DENTRY)
+		clear_subdomainfs();
+
+	return 1;
+}
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/apparmor.h linux-2.6.x/security/apparmor/apparmor.h
--- linux-2.6.x.orig/security/apparmor/apparmor.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/apparmor.h	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,302 @@
+/*
+ *	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 __SUBDOMAIN_H
+#define __SUBDOMAIN_H
+
+/* defn of iattr */
+#include <linux/fs.h>
+
+/* defn of linux_binprm */
+#include <linux/binfmts.h>
+
+#include "shared.h"
+
+/* Control parameters (0 or 1), settable thru module/boot flags or
+ * via /sys/kernel/security/subdomain/control */
+extern int subdomain_complain;
+extern int subdomain_debug;
+extern int subdomain_audit;
+extern int subdomain_logsyscall;
+
+#define SD_UNCONSTRAINED "unconstrained"
+
+/* $ echo -n subdomain.o | md5sum | cut -c -8 */
+#define  SD_ID_MAGIC		0x8c235e38
+
+#define PROFILE_COMPLAIN(_profile) \
+	(subdomain_complain == 1 || ((_profile) && (_profile)->flags.complain))
+
+#define SUBDOMAIN_COMPLAIN(_sd) \
+	(subdomain_complain == 1 || \
+	 ((_sd) && (_sd)->active && (_sd)->active->flags.complain))
+
+#define SUBDOMAIN_AUDIT(_sd) \
+	(subdomain_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 SD_DEBUG(fmt, args...)						\
+	do {								\
+		if (subdomain_debug)					\
+			printk(KERN_DEBUG "AppArmor: " fmt, ##args);	\
+	} while (0)
+#define SD_INFO(fmt, args...)	printk(KERN_INFO "AppArmor: " fmt, ##args)
+#define SD_WARN(fmt, args...)	printk(KERN_WARNING "AppArmor: " fmt, ##args)
+#define SD_ERROR(fmt, args...)	printk(KERN_ERR "AppArmor: " fmt, ##args)
+
+/* basic AppArmor data structures */
+
+struct flagval {
+	int debug;
+	int complain;
+	int audit;
+};
+
+enum entry_t {
+	sd_entry_literal,
+	sd_entry_tailglob,
+	sd_entry_pattern,
+	sd_entry_invalid
+};
+
+/**
+ * sd_entry - file ACL *
+ * Each entry describes a file and an allowed access mode.
+ */
+struct sd_entry {
+	char *filename;
+	int mode;		/* mode is 'or' of READ, WRITE, EXECUTE,
+				 * INHERIT, UNCONSTRAINED, and LIBRARY
+				 * (meaning don't prefetch). */
+
+	enum entry_t entry_type;
+	void *extradata;
+
+	struct list_head list;
+	struct list_head listp[POS_SD_FILE_MAX + 1];
+};
+
+#define SD_SECURE_EXEC_NEEDED 0x00000001
+
+#define SD_EXEC_MODIFIER_MASK(mask) ((mask) & SD_EXEC_MODIFIERS)
+
+#define SD_EXEC_MASK(mask) ((mask) & (SD_MAY_EXEC | SD_EXEC_MODIFIERS))
+
+#define SD_EXEC_UNSAFE_MASK(mask) ((mask) & (SD_MAY_EXEC |\
+					     SD_EXEC_MODIFIERS |\
+					     SD_EXEC_UNSAFE))
+
+/**
+ * sdprofile - basic confinement data
+ *
+ * The AppArmor profile contains the basic confinement data.  Each profile
+ * has a name and potentially a list of subdomain entries. The profiles are
+ * connected in a list
+ */
+struct sdprofile {
+	char *name;			/* profile name */
+
+	struct list_head file_entry;	/* file ACL */
+	struct list_head file_entryp[POS_SD_FILE_MAX + 1];
+	struct list_head list;		/* list of profiles */
+	struct list_head sub;		/* sub profiles, for change_hat */
+	struct flagval flags;		/* per profile debug flags */
+
+	int isstale;			/* is profile stale */
+
+	int num_file_entries;
+	int num_file_pentries[POS_SD_FILE_MAX + 1];
+
+	kernel_cap_t capabilities;
+
+	atomic_t count;			/* reference count */
+};
+
+enum sdfile_type {
+	sd_file_default,
+	sd_file_shmem
+};
+
+/**
+ * sdfile - file pointer confinement data
+ *
+ * Data structure assigned to each open file (by subdomain_file_alloc_security)
+ */
+struct sdfile {
+	enum sdfile_type type;
+	struct sdprofile *profile;
+};
+
+/**
+ * subdomain - a task's subdomain
+ *
+ * Contains the original profile obtained from execve() as well as the
+ * current active profile (which could change due to change_hat).  Plus
+ * the hat_magic needed during change_hat.
+ */
+struct subdomain {
+	__u32 sd_magic;			/* magic value to distinguish blobs */
+	struct sdprofile *profile;	/* The profile obtained from execve() */
+	struct sdprofile *active;	/* The current active profile */
+	__u32 sd_hat_magic;		/* used with change_hat */
+	struct list_head list;		/* list of subdomains */
+	struct task_struct *task;
+};
+
+typedef int (*sd_iter) (struct subdomain *, void *);
+
+/* sd_path_data
+ * temp (cookie) data used by sd_path_* functions, see inline.h
+ */
+struct sd_path_data {
+	struct dentry *root, *dentry;
+	struct namespace *namespace;
+	struct list_head *head, *pos;
+	int errno;
+};
+
+#define SD_SUBDOMAIN(sec)	((struct subdomain*)(sec))
+#define SD_PROFILE(sec)		((struct sdprofile*)(sec))
+
+/* Lock protecting access to 'struct subdomain' accesses */
+extern rwlock_t sd_lock;
+
+extern struct sdprofile *null_profile;
+extern struct sdprofile *null_complain_profile;
+
+/** sd_audit
+ *
+ * Auditing structure
+ */
+
+struct sd_audit {
+	unsigned short type, flags;
+	unsigned int result;
+	unsigned int gfp_mask;
+	int errorcode;
+
+	const char *name;
+	unsigned int ival;
+	union{
+		const void *pval;
+		va_list vaval;
+	};
+};
+
+/* audit types */
+#define SD_AUDITTYPE_FILE	1
+#define SD_AUDITTYPE_DIR	2
+#define SD_AUDITTYPE_ATTR	3
+#define SD_AUDITTYPE_XATTR	4
+#define SD_AUDITTYPE_LINK	5
+#define SD_AUDITTYPE_CAP	6
+#define SD_AUDITTYPE_MSG	7
+#define SD_AUDITTYPE_SYSCALL	8
+#define SD_AUDITTYPE__END	9
+
+/* audit flags */
+#define SD_AUDITFLAG_AUDITSS_SYSCALL 1 /* log syscall context */
+#define SD_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(sd, gfp, hint, fmt, args...) \
+	do {\
+		sd_audit_message(sd, gfp, 0, \
+			"LOGPROF-HINT " hint " " fmt, ##args);\
+	} while(0)
+
+/* diroptype */
+#define SD_DIR_MKDIR 0
+#define SD_DIR_RMDIR 1
+
+/* xattroptype */
+#define SD_XATTR_GET    0
+#define SD_XATTR_SET    1
+#define SD_XATTR_LIST   2
+#define SD_XATTR_REMOVE 3
+
+/* main.c */
+extern int alloc_nullprofiles(void);
+extern void free_nullprofiles(void);
+extern int sd_audit_message(struct subdomain *, unsigned int gfp, int,
+			    const char *, ...);
+extern int sd_audit_syscallreject(struct subdomain *, unsigned int gfp,
+				  const char *);
+extern int sd_audit(struct subdomain *, const struct sd_audit *);
+extern char *sd_get_name(struct dentry *dentry, struct vfsmount *mnt);
+
+extern int sd_attr(struct subdomain *sd, struct dentry *dentry,
+		   struct iattr *iattr);
+extern int sd_xattr(struct subdomain *sd, struct dentry *dentry,
+		    const char *xattr, int xattroptype);
+extern int sd_capability(struct subdomain *sd, int cap);
+extern int sd_perm(struct subdomain *sd, struct dentry *dentry,
+		   struct vfsmount *mnt, int mask);
+extern int sd_perm_nameidata(struct subdomain *sd, struct nameidata *nd,
+			     int mask);
+extern int sd_perm_dentry(struct subdomain *sd, struct dentry *dentry,
+			  int mask);
+extern int sd_perm_dir(struct subdomain *sd, struct dentry *dentry,
+		       int diroptype);
+extern int sd_link(struct subdomain *sd,
+		   struct dentry *link, struct dentry *target);
+extern int sd_fork(struct task_struct *p);
+extern int sd_register(struct linux_binprm *bprm);
+extern void sd_release(struct task_struct *p);
+extern int sd_change_hat(const char *id, __u32 hat_magic);
+extern int sd_associate_filp(struct file *filp);
+
+/* list.c */
+extern struct sdprofile *sd_profilelist_find(const char *name);
+extern int sd_profilelist_add(struct sdprofile *profile);
+extern struct sdprofile *sd_profilelist_remove(const char *name);
+extern void sd_profilelist_release(void);
+extern struct sdprofile *sd_profilelist_replace(struct sdprofile *profile);
+extern void sd_profile_dump(struct sdprofile *);
+extern void sd_profilelist_dump(void);
+extern void sd_subdomainlist_add(struct subdomain *);
+extern void sd_subdomainlist_remove(struct subdomain *);
+extern void sd_subdomainlist_iterate(sd_iter, void *);
+extern void sd_subdomainlist_iterateremove(sd_iter, void *);
+extern void sd_subdomainlist_release(void);
+
+/* subdomain_interface.c */
+extern void free_sdprofile(struct sdprofile *profile);
+extern int sd_sys_security(unsigned int id, unsigned call, unsigned long *args);
+
+/* procattr.c */
+extern size_t sd_getprocattr(struct subdomain *sd, char *str, size_t size);
+extern int sd_setprocattr_changehat(char *hatinfo, size_t infosize);
+extern int sd_setprocattr_setprofile(struct task_struct *p, char *profilename,
+				     size_t profilesize);
+
+/* apparmorfs.c */
+extern int create_subdomainfs(void);
+extern int destroy_subdomainfs(void);
+
+/* capabilities.c */
+extern const char *capability_to_name(unsigned int cap);
+
+/* apparmor_version.c */
+extern const char *apparmor_version(void);
+extern const char *apparmor_version_nl(void);
+
+#endif				/* __SUBDOMAIN_H */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/apparmor_version.c linux-2.6.x/security/apparmor/apparmor_version.c
--- linux-2.6.x.orig/security/apparmor/apparmor_version.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/apparmor_version.c	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,42 @@
+/*
+ *	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 version definition
+ */
+
+#ifndef APPARMOR_VERSION
+#error "-DAPPARMOR_VERSION must be specified when compiling this file"
+#endif
+
+#define APPARMOR_VERSION_STR_PFX "APPARMOR_VERSION="
+
+#include <linux/module.h>
+MODULE_VERSION(APPARMOR_VERSION);
+
+/* apparmor_version_str exists to allow a strings on module to
+ * see APPARMOR_VERSION= prefix
+ */
+static const char *apparmor_version_str =
+		APPARMOR_VERSION_STR_PFX APPARMOR_VERSION;
+
+/* apparmor_version_str_nl exists to allow an easy way to get a newline
+ * terminated string without having to do dynamic memory allocation
+ */
+static const char *apparmor_version_str_nl = APPARMOR_VERSION "\n";
+
+const char *apparmor_version(void)
+{
+	const int len = sizeof(APPARMOR_VERSION_STR_PFX) - 1;
+
+	return apparmor_version_str + len;
+}
+
+const char *apparmor_version_nl(void)
+{
+	return apparmor_version_str_nl;
+}
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/capabilities.c linux-2.6.x/security/apparmor/capabilities.c
--- linux-2.6.x.orig/security/apparmor/capabilities.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/capabilities.c	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,54 @@
+/*
+ *	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 *capnames[] = {
+	"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"
+};
+
+const char *capability_to_name(unsigned int cap)
+{
+	const char *capname;
+
+	capname = (cap < (sizeof(capnames) / sizeof(char *))
+		   ? capnames[cap] : "invalid-capability");
+
+	return capname;
+}
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/inline.h linux-2.6.x/security/apparmor/inline.h
--- linux-2.6.x.orig/security/apparmor/inline.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/inline.h	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,364 @@
+/*
+ *	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/namespace.h>
+
+static inline int __sd_is_confined(struct subdomain *sd)
+{
+	int rc = 0;
+
+	if (sd && sd->sd_magic == SD_ID_MAGIC && sd->profile) {
+		BUG_ON(!sd->active);
+		rc = 1;
+	}
+
+	return rc;
+}
+
+/**
+ *  sd_is_confined
+ *  @sd: subdomain
+ *
+ *  Check if @sd is confined (contains a valid profile)
+ *  Return 1 if confined, 0 otherwise.
+ */
+static inline int sd_is_confined(void)
+{
+	struct subdomain *sd = SD_SUBDOMAIN(current->security);
+	return __sd_is_confined(sd);
+}
+
+static inline int __sd_sub_defined(struct subdomain *sd)
+{
+	return __sd_is_confined(sd) && !list_empty(&sd->profile->sub);
+}
+
+/**
+ * sd_sub_defined
+ * @sd: subdomain
+ *
+ * Check if @sd has at least one subprofile
+ * Return 1 if true, 0 otherwise
+ */
+static inline int sd_sub_defined(void)
+{
+	struct subdomain *sd = SD_SUBDOMAIN(current->security);
+	return __sd_sub_defined(sd);
+}
+
+/**
+ * get_sdprofile
+ * @p: profile
+ *
+ * Increment refcount on profile
+ */
+static inline struct sdprofile *get_sdprofile(struct sdprofile *p)
+{
+	if (p)
+		atomic_inc(&p->count);
+	return p;
+}
+
+/**
+ * put_sdprofile
+ * @p: profile
+ *
+ * Decrement refcount on profile
+ */
+static inline void put_sdprofile(struct sdprofile *p)
+{
+	if (p)
+		if (atomic_dec_and_test(&p->count))
+			free_sdprofile(p);
+}
+
+/**
+ * sd_switch
+ * @sd: subdomain to switch
+ * @profile: new profile
+ * @active:  new active
+ *
+ * Change subdomain to use new profiles.
+ */
+static inline void sd_switch(struct subdomain *sd,
+		      		 struct sdprofile *profile,
+				 struct sdprofile *active)
+{
+	/* noop if NULL */
+	put_sdprofile(sd->profile);
+	put_sdprofile(sd->active);
+
+	sd->profile = get_sdprofile(profile);
+	sd->active = get_sdprofile(active);
+}
+
+/**
+ * sd_switch_unconfined
+ * @sd: subdomain to switch
+ *
+ * Change subdomain to unconfined
+ */
+static inline void sd_switch_unconfined(struct subdomain *sd)
+{
+	sd_switch(sd, NULL, NULL);
+
+	/* reset magic in case we were in a subhat before */
+	sd->sd_hat_magic = 0;
+}
+
+/**
+ * alloc_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 = kmalloc(sizeof(struct subdomain), GFP_KERNEL);
+	if (!sd)
+		goto out;
+
+	/* zero it first */
+	memset(sd, 0, sizeof(struct subdomain));
+	sd->sd_magic = SD_ID_MAGIC;
+
+	/* back pointer to task */
+	sd->task = tsk;
+
+	/* any readers of the list must make sure that they can handle
+	 * case where sd->profile and sd->active are not yet set (null)
+	 */
+	sd_subdomainlist_add(sd);
+
+out:
+	return sd;
+}
+
+/**
+ * free_subdomain
+ * @sd: subdomain
+ *
+ * Free a subdomain previously allocated by alloc_subdomain
+ */
+static inline void free_subdomain(struct subdomain *sd)
+{
+	sd_subdomainlist_remove(sd);
+	kfree(sd);
+}
+
+/**
+ * alloc_sdprofile
+ *
+ * Allocate, initialize and return a new zeroed profile.
+ * Returns NULL on failure.
+ */
+static inline struct sdprofile *alloc_sdprofile(void)
+{
+	struct sdprofile *profile;
+
+	profile = (struct sdprofile *)kmalloc(sizeof(struct sdprofile),
+					      GFP_KERNEL);
+	SD_DEBUG("%s(%p)\n", __FUNCTION__, profile);
+	if (profile) {
+		int i;
+		memset(profile, 0, sizeof(struct sdprofile));
+		INIT_LIST_HEAD(&profile->list);
+		INIT_LIST_HEAD(&profile->sub);
+		INIT_LIST_HEAD(&profile->file_entry);
+		for (i = 0; i <= POS_SD_FILE_MAX; i++) {
+			INIT_LIST_HEAD(&profile->file_entryp[i]);
+		}
+	}
+	return profile;
+}
+
+/**
+ * sd_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 sd_put_name(const char *name)
+{
+	free_page((unsigned long)name);
+}
+
+/** __sd_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 sdprofile *__sd_find_profile(const char *name,
+						      struct list_head *head)
+{
+	struct sdprofile *p;
+
+	if (!name || !head)
+		return NULL;
+
+	SD_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_sdprofile(p);
+			return p;
+		} else {
+			SD_DEBUG("%s: skipping %s\n", __FUNCTION__, p->name);
+		}
+	}
+	return NULL;
+}
+
+static inline struct subdomain *__get_sdcopy(struct subdomain *new,
+						 struct task_struct *tsk)
+{
+	struct subdomain *old, *temp = NULL;
+
+	old = SD_SUBDOMAIN(tsk->security);
+
+	if (old) {
+		new->sd_magic = old->sd_magic;
+		new->sd_hat_magic = old->sd_hat_magic;
+
+		new->active = get_sdprofile(old->active);
+
+		if (old->profile == old->active)
+			new->profile = new->active;
+		else
+			new->profile = get_sdprofile(old->profile);
+
+		temp = new;
+	}
+
+	return temp;
+}
+
+/** get_sdcopy
+ * @new: subdomain to hold copy
+ *
+ * Make copy of current subdomain containing refcounted profile and active
+ * Used to protect readers against racing writers (changehat and profile
+ * replacement).
+ */
+static inline struct subdomain *get_sdcopy(struct subdomain *new)
+{
+	struct subdomain *temp;
+	unsigned long flags;
+
+	read_lock_irqsave(&sd_lock, flags);
+
+	temp = __get_sdcopy(new, current);
+
+	read_unlock_irqrestore(&sd_lock, flags);
+
+	return temp;
+}
+
+/** get_sdcopy
+ * @temp: subdomain to drop refcounts on
+ *
+ * Drop refcounted profile/active in copy of subdomain made by get_sdcopy
+ */
+static inline void put_sdcopy(struct subdomain *temp)
+{
+	if (temp) {
+		put_sdprofile(temp->active);
+		if (temp->active != temp->profile)
+			(void)put_sdprofile(temp->profile);
+	}
+}
+
+/** sd_path_begin2
+ * @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 sd_path_begin2(struct dentry *rdentry,
+				      struct dentry *dentry,
+				      struct sd_path_data *data)
+{
+	data->dentry = dentry;
+	data->root = dget(rdentry->d_sb->s_root);
+	data->namespace = current->namespace;
+	data->head = &data->namespace->list;
+	data->pos = data->head->next;
+	prefetch(data->pos->next);
+	data->errno = 0;
+
+	down_read(&namespace_sem);
+}
+
+/** sd_path_begin
+ * @dentry filesystem root dentry and object to obtain pathname from
+ *
+ * Utility function for calling _sd_path_begin for when the dentry we are
+ * looking for and the root are the same (this is the usual case).
+ */
+static inline void sd_path_begin(struct dentry *dentry,
+				     struct sd_path_data *data)
+{
+	sd_path_begin2(dentry, dentry, data);
+}
+
+/** sd_path_end
+ * @data: data object previously initialized by sd_path_begin
+ *
+ * End iterating over vfsmounts.
+ * If an error occured in begin or get, it is returned. Otherwise 0.
+ */
+static inline int sd_path_end(struct sd_path_data *data)
+{
+	up_read(&namespace_sem);
+	dput(data->root);
+
+	return data->errno;
+}
+
+/** sd_path_getname
+ * @data: data object previously initialized by sd_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 sd_path_end() and inspect return code to differentiate)
+ */
+static inline char *sd_path_getname(struct sd_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 = sd_get_name(data->dentry, mnt);
+			if (IS_ERR(name)) {
+				data->errno = PTR_ERR(name);
+				name = NULL;
+			}
+			break;
+		}
+	}
+
+	return name;
+}
+
+#endif /* __INLINE_H__ */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/Kbuild linux-2.6.x/security/apparmor/Kbuild
--- linux-2.6.x.orig/security/apparmor/Kbuild	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/Kbuild	2006-08-04 13:07:32.000000000 -0700
@@ -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 apparmor_version.o
+
+obj-$(CONFIG_SECURITY_APPARMOR) += aamatch/
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/Kconfig linux-2.6.x/security/apparmor/Kconfig
--- linux-2.6.x.orig/security/apparmor/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/Kconfig	2006-10-11 20:38:26.000000000 -0700
@@ -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.
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/list.c linux-2.6.x/security/apparmor/list.c
--- linux-2.6.x.orig/security/apparmor/list.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/list.c	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,271 @@
+/*
+ *	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;
+
+/**
+ * sd_profilelist_find
+ * @name: profile name (program name)
+ *
+ * Search the profile list for profile @name.  Return refcounted profile on
+ * success, NULL on failure.
+ */
+struct sdprofile *sd_profilelist_find(const char *name)
+{
+	struct sdprofile *p = NULL;
+	if (name) {
+		read_lock(&profile_lock);
+		p = __sd_find_profile(name, &profile_list);
+		read_unlock(&profile_lock);
+	}
+	return p;
+}
+
+/**
+ * sd_profilelist_add
+ * @profile: new profile to add to list
+ *
+ * Add new profile to list.  Reference count on profile is incremented.
+ * Return 1 on success, 0 on failure (bad profile or already exists)
+ */
+int sd_profilelist_add(struct sdprofile *profile)
+{
+	struct sdprofile *old_profile;
+	int ret = 0;
+
+	if (!profile)
+		goto out;
+
+	write_lock(&profile_lock);
+	old_profile = __sd_find_profile(profile->name, &profile_list);
+	if (old_profile) {
+		put_sdprofile(old_profile);
+		goto out;
+	}
+	profile = get_sdprofile(profile);
+
+	list_add(&profile->list, &profile_list);
+	ret = 1;
+ out:
+	write_unlock(&profile_lock);
+	return ret;
+}
+
+/**
+ * sd_profilelist_remove
+ * @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 sdprofile *sd_profilelist_remove(const char *name)
+{
+	struct sdprofile *profile = NULL;
+	struct sdprofile *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;
+}
+
+/**
+ * sd_profilelist_replace
+ * @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.  This is an atomic
+ * list operation.  Returns the old profile (which is still refcounted) if
+ * there was one, or NULL.
+ */
+struct sdprofile *sd_profilelist_replace(struct sdprofile *profile)
+{
+	struct sdprofile *oldprofile;
+
+	write_lock(&profile_lock);
+	oldprofile = __sd_find_profile(profile->name, &profile_list);
+	if (oldprofile) {
+		list_del_init(&oldprofile->list);
+		/* mark old profile as stale */
+		oldprofile->isstale = 1;
+
+		/* __sd_find_profile incremented count, so adjust down */
+		put_sdprofile(oldprofile);
+	}
+	profile = get_sdprofile(profile);
+	list_add(&profile->list, &profile_list);
+	write_unlock(&profile_lock);
+
+	return oldprofile;
+}
+
+/**
+ * sd_profilelist_release
+ *
+ * Remove all profiles from profile_list
+ */
+void sd_profilelist_release(void)
+{
+	struct sdprofile *p, *tmp;
+
+	write_lock(&profile_lock);
+	list_for_each_entry_safe(p, tmp, &profile_list, list) {
+		list_del_init(&p->list);
+		put_sdprofile(p);
+	}
+	write_unlock(&profile_lock);
+}
+
+/**
+ * sd_subdomainlist_add
+ * @sd: new subdomain
+ *
+ * Add subdomain to subdomain_list
+ */
+void sd_subdomainlist_add(struct subdomain *sd)
+{
+	unsigned long flags;
+
+	if (!sd) {
+		SD_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);
+}
+
+/**
+ * sd_subdomainlist_remove
+ * @sd: subdomain to be removed
+ *
+ * Remove subdomain from subdomain_list
+ */
+void sd_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);
+	}
+}
+
+/**
+ * sd_subdomainlist_iterate
+ * @func: method to be called for each element
+ * @cookie: user passed data
+ *
+ * Iterate over subdomain list, stop when sd_iter func returns non zero
+ */
+void sd_subdomainlist_iterate(sd_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);
+}
+
+/**
+ * sd_subdomainlist_release
+ *
+ * Remove all subdomains from subdomain_list
+ */
+void sd_subdomainlist_release()
+{
+	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 subdomainfs.c to iterate over profile_list
+ */
+static void *p_start(struct seq_file *f, loff_t *pos)
+{
+	struct sdprofile *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 sdprofile *)p)->list.next;
+	(*pos)++;
+	return lh == &profile_list ?
+			NULL : list_entry(lh, struct sdprofile, 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 sdprofile *profile = (struct sdprofile *)v;
+	seq_printf(f, "%s (%s)\n", profile->name,
+		   PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
+	return 0;
+}
+
+struct seq_operations subdomainfs_profiles_op = {
+	.start =	p_start,
+	.next =		p_next,
+	.stop =		p_stop,
+	.show =		seq_show_profile,
+};
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/lsm.c linux-2.6.x/security/apparmor/lsm.c
--- linux-2.6.x.orig/security/apparmor/lsm.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/lsm.c	2006-10-10 14:30:04.000000000 -0700
@@ -0,0 +1,959 @@
+/*
+ *	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 (previously called "SubDomain")
+ */
+
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+
+/* superblock types */
+
+/* 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)
+
+#include <asm/mman.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+/* main SD lock [see get_sdcopy and put_sdcopy] */
+rwlock_t sd_lock = RW_LOCK_UNLOCKED;
+
+/* Flag values, also controllable via subdomainfs/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 (used to be 'bitch' mode) */
+int subdomain_complain = 0;
+module_param_named(complain, subdomain_complain, int, S_IRUSR);
+MODULE_PARM_DESC(subdomain_complain, "Toggle AppArmor complain mode");
+
+/* Debug mode */
+int subdomain_debug = 0;
+module_param_named(debug, subdomain_debug, int, S_IRUSR);
+MODULE_PARM_DESC(subdomain_debug, "Toggle AppArmor debug mode");
+
+/* Audit mode */
+int subdomain_audit = 0;
+module_param_named(audit, subdomain_audit, int, S_IRUSR);
+MODULE_PARM_DESC(subdomain_audit, "Toggle AppArmor audit mode");
+
+/* Syscall logging mode */
+int subdomain_logsyscall = 0;
+module_param_named(logsyscall, subdomain_logsyscall, int, S_IRUSR);
+MODULE_PARM_DESC(subdomain_logsyscall, "Toggle AppArmor logsyscall mode");
+
+#ifndef MODULE
+static int __init sd_getopt_complain(char *str)
+{
+	get_option(&str, &subdomain_complain);
+	return 1;
+}
+__setup("subdomain_complain=", sd_getopt_complain);
+
+static int __init sd_getopt_debug(char *str)
+{
+	get_option(&str, &subdomain_debug);
+	return 1;
+}
+__setup("subdomain_debug=", sd_getopt_debug);
+
+static int __init sd_getopt_audit(char *str)
+{
+	get_option(&str, &subdomain_audit);
+	return 1;
+}
+__setup("subdomain_audit=", sd_getopt_audit);
+
+static int __init sd_getopt_logsyscall(char *str)
+{
+	get_option(&str, &subdomain_logsyscall);
+	return 1;
+}
+__setup("subdomain_logsyscall=", sd_getopt_logsyscall);
+#endif
+
+static int subdomain_ptrace(struct task_struct *parent,
+			    struct task_struct *child)
+{
+	int error;
+	struct subdomain *sd;
+	unsigned long flags;
+
+	error = cap_ptrace(parent, child);
+
+	if (error == 0 && parent->security) {
+		read_lock_irqsave(&sd_lock, flags);
+
+		sd = SD_SUBDOMAIN(parent->security);
+
+		if (__sd_is_confined(sd)) {
+			error = sd_audit_syscallreject(sd, GFP_ATOMIC,
+						       "ptrace");
+			WARN_ON(error != -EPERM);
+		}
+
+		read_unlock_irqrestore(&sd_lock, flags);
+	}
+
+	return error;
+}
+
+static int subdomain_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 subdomain_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 subdomain_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 subdomain_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 && current->security) {
+		struct subdomain *sd, sdcopy;
+		unsigned long flags;
+
+		read_lock_irqsave(&sd_lock, flags);
+		sd = __get_sdcopy(&sdcopy, tsk);
+		read_unlock_irqrestore(&sd_lock, flags);
+
+		error = sd_capability(sd, cap);
+
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+
+static int subdomain_sysctl(struct ctl_table *table, int op)
+{
+	int error = 0;
+	struct subdomain *sd;
+	unsigned long flags;
+
+	if (!current->security)
+		return 0;
+
+	read_lock_irqsave(&sd_lock, flags);
+
+	sd = SD_SUBDOMAIN(current->security);
+
+	if ((op & 002) && __sd_is_confined(sd) && !capable(CAP_SYS_ADMIN)) {
+		error = sd_audit_syscallreject(sd, GFP_ATOMIC,
+					       "sysctl (write)");
+		WARN_ON(error != -EPERM);
+	}
+
+	read_unlock_irqrestore(&sd_lock, flags);
+
+	return error;
+}
+
+static int subdomain_syslog(int type)
+{
+	return cap_syslog(type);
+}
+
+static int subdomain_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+	return cap_netlink_send(sk, skb);
+}
+
+static int subdomain_netlink_recv(struct sk_buff *skb, int cap)
+{
+	return cap_netlink_recv(skb, cap);
+}
+
+static void subdomain_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+{
+	cap_bprm_apply_creds(bprm, unsafe);
+	return;
+}
+
+static int subdomain_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 sd_register(bprm);
+}
+
+static int subdomain_bprm_secureexec(struct linux_binprm *bprm)
+{
+	int ret = cap_bprm_secureexec(bprm);
+
+	if (ret == 0 && (unsigned long)bprm->security & SD_SECURE_EXEC_NEEDED) {
+		SD_DEBUG("%s: secureexec required for %s\n",
+			__FUNCTION__, bprm->filename);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int subdomain_sb_mount(char *dev_name, struct nameidata *nd, char *type,
+			      unsigned long flags, void *data)
+{
+	int error = 0;
+	struct subdomain *sd;
+	unsigned long lockflags;
+
+	if (!current->security)
+		return 0;
+
+	read_lock_irqsave(&sd_lock, lockflags);
+
+	sd = SD_SUBDOMAIN(current->security);
+
+	if (__sd_is_confined(sd)) {
+		error = sd_audit_syscallreject(sd, GFP_ATOMIC, "mount");
+		WARN_ON(error != -EPERM);
+	}
+
+	read_unlock_irqrestore(&sd_lock, lockflags);
+
+	return error;
+}
+
+static int subdomain_umount(struct vfsmount *mnt, int flags)
+{
+	int error = 0;
+	struct subdomain *sd;
+	unsigned long lockflags;
+
+	if (!current->security)
+		return 0;
+
+	read_lock_irqsave(&sd_lock, lockflags);
+
+	sd = SD_SUBDOMAIN(current->security);
+
+	if (__sd_is_confined(sd)) {
+		error = sd_audit_syscallreject(sd, GFP_ATOMIC, "umount");
+		WARN_ON(error != -EPERM);
+	}
+
+	read_unlock_irqrestore(&sd_lock, lockflags);
+
+	return error;
+}
+
+static int subdomain_inode_mkdir(struct inode *inode, struct dentry *dentry,
+				 int mask)
+{
+	struct subdomain sdcopy, *sd;
+	int error;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	error = sd_perm_dir(sd, dentry, SD_DIR_MKDIR);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_rmdir(struct inode *inode, struct dentry *dentry)
+{
+	struct subdomain sdcopy, *sd;
+	int error;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	error = sd_perm_dir(sd, dentry, SD_DIR_RMDIR);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_create(struct inode *inode, struct dentry *dentry,
+				  int mask)
+{
+	struct subdomain sdcopy, *sd;
+	int error;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	/* At a minimum, need write perm to create */
+	error = sd_perm_dentry(sd, dentry, MAY_WRITE);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_link(struct dentry *old_dentry, struct inode *inode,
+				struct dentry *new_dentry)
+{
+	int error = 0;
+	struct subdomain sdcopy, *sd;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+	error = sd_link(sd, new_dentry, old_dentry);
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_unlink(struct inode *inode, struct dentry *dentry)
+{
+	struct subdomain sdcopy, *sd;
+	int error;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	error = sd_perm_dentry(sd, dentry, MAY_WRITE);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_mknod(struct inode *inode, struct dentry *dentry,
+				 int mode, dev_t dev)
+{
+	struct subdomain sdcopy, *sd;
+	int error = 0;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	error = sd_perm_dentry(sd, dentry, MAY_WRITE);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_rename(struct inode *old_inode,
+				  struct dentry *old_dentry,
+				  struct inode *new_inode,
+				  struct dentry *new_dentry)
+{
+	struct subdomain sdcopy, *sd;
+	int error = 0;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	error = sd_perm_dentry(sd, old_dentry,
+			       MAY_READ | MAY_WRITE);
+
+	if (!error)
+		error = sd_perm_dentry(sd, new_dentry, MAY_WRITE);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_inode_permission(struct inode *inode, int mask,
+				      struct nameidata *nd)
+{
+	int error = 0;
+
+	/* Do not perform check on pipes or sockets
+	 * Same as subdomain_file_permission
+	 */
+	if (current->security && VALID_FSTYPE(inode)) {
+		struct subdomain sdcopy, *sd;
+
+		sd = get_sdcopy(&sdcopy);
+		error = sd_perm_nameidata(sd, nd, mask);
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+
+static int subdomain_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+	struct subdomain sdcopy, *sd;
+	int error = 0;
+
+	if (current->security && VALID_FSTYPE(dentry->d_inode)) {
+
+		sd = get_sdcopy(&sdcopy);
+
+		/*
+		 * Mediate any attempt to change attributes of a file
+		 * (chmod, chown, chgrp, etc)
+		 */
+		error = sd_attr(sd, dentry, iattr);
+
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+
+static int subdomain_inode_setxattr(struct dentry *dentry, char *name,
+				    void *value, size_t size, int flags)
+{
+	int error = 0;
+
+	if (current->security && VALID_FSTYPE(dentry->d_inode)) {
+		struct subdomain sdcopy, *sd;
+
+		sd = get_sdcopy(&sdcopy);
+		error = sd_xattr(sd, dentry, name, SD_XATTR_SET);
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+
+static int subdomain_inode_getxattr(struct dentry *dentry, char *name)
+{
+	int error = 0;
+
+	if (current->security && VALID_FSTYPE(dentry->d_inode)) {
+		struct subdomain sdcopy, *sd;
+
+		sd = get_sdcopy(&sdcopy);
+		error = sd_xattr(sd, dentry, name, SD_XATTR_GET);
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+static int subdomain_inode_listxattr(struct dentry *dentry)
+{
+	int error = 0;
+
+	if (current->security && VALID_FSTYPE(dentry->d_inode)) {
+		struct subdomain sdcopy, *sd;
+
+		sd = get_sdcopy(&sdcopy);
+		error = sd_xattr(sd, dentry, NULL, SD_XATTR_LIST);
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+
+static int subdomain_inode_removexattr(struct dentry *dentry, char *name)
+{
+	int error = 0;
+
+	if (current->security && VALID_FSTYPE(dentry->d_inode)) {
+		struct subdomain sdcopy, *sd;
+
+		sd = get_sdcopy(&sdcopy);
+		error = sd_xattr(sd, dentry, name, SD_XATTR_REMOVE);
+		put_sdcopy(sd);
+	}
+
+	return error;
+}
+
+static int subdomain_file_permission(struct file *file, int mask)
+{
+	struct subdomain sdcopy, *sd;
+	struct sdfile *sdf;
+	int error = 0;
+
+	if (!current->security ||
+	    !(sdf = (struct sdfile *)file->f_security) ||
+	    !VALID_FSTYPE(file->f_dentry->d_inode))
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	if (__sd_is_confined(sd) && sdf->profile != sd->active)
+		error = sd_perm(sd, file->f_dentry, file->f_vfsmnt,
+				mask & (MAY_EXEC | MAY_WRITE | MAY_READ));
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_file_alloc_security(struct file *file)
+{
+	struct subdomain sdcopy, *sd;
+	int error = 0;
+
+	if (!current->security)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	if (__sd_is_confined(sd)) {
+		struct sdfile *sdf;
+
+		sdf = kmalloc(sizeof(struct sdfile), GFP_KERNEL);
+
+		if (sdf) {
+			sdf->type = sd_file_default;
+			sdf->profile = get_sdprofile(sd->active);
+		} else {
+			error = -ENOMEM;
+		}
+
+		file->f_security = sdf;
+	}
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static void subdomain_file_free_security(struct file *file)
+{
+	struct sdfile *sdf = (struct sdfile *)file->f_security;
+
+	if (sdf) {
+		put_sdprofile(sdf->profile);
+		kfree(sdf);
+	}
+}
+
+static inline int sd_mmap(struct file *file, unsigned long prot,
+				 unsigned long flags)
+{
+	int error = 0, mask = 0;
+	struct subdomain sdcopy, *sd;
+	struct sdfile *sdf;
+
+	if (!current->security || !file ||
+	    !(sdf = (struct sdfile *)file->f_security) ||
+	    sdf->type == sd_file_shmem)
+		return 0;
+
+	sd = get_sdcopy(&sdcopy);
+
+	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 |= SD_EXEC_MMAP;
+
+	SD_DEBUG("%s: 0x%x\n", __FUNCTION__, mask);
+
+	if (mask)
+		error = sd_perm(sd, file->f_dentry, file->f_vfsmnt, mask);
+
+	put_sdcopy(sd);
+
+	return error;
+}
+
+static int subdomain_file_mmap(struct file *file, unsigned long reqprot,
+			       unsigned long prot, unsigned long flags)
+{
+	return sd_mmap(file, prot, flags);
+}
+
+static int subdomain_file_mprotect(struct vm_area_struct* vma,
+				   unsigned long reqprot, unsigned long prot)
+{
+	return sd_mmap(vma->vm_file, prot,
+		!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
+}
+
+static int subdomain_task_alloc_security(struct task_struct *p)
+{
+	return sd_fork(p);
+}
+
+static void subdomain_task_free_security(struct task_struct *p)
+{
+	if (p->security)
+		sd_release(p);
+}
+
+static int subdomain_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 subdomain_task_reparent_to_init(struct task_struct *p)
+{
+	cap_task_reparent_to_init(p);
+	return;
+}
+
+static int subdomain_shm_shmat(struct shmid_kernel* shp, char __user *shmaddr,
+			       int shmflg)
+{
+	struct sdfile *sdf = (struct sdfile *)shp->shm_file->f_security;
+
+	if (sdf)
+		sdf->type = sd_file_shmem;
+
+	return 0;
+}
+
+static int subdomain_getprocattr(struct task_struct *p, char *name, void *value,
+				 size_t size)
+{
+	int error;
+	struct subdomain sdcopy, *sd;
+	char *str = value;
+	unsigned long flags;
+
+	/* Subdomain only supports the "current" process attribute */
+	if (strcmp(name, "current") != 0) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	if (!size) {
+		error = -ERANGE;
+		goto out;
+	}
+
+	/* must be task querying itself or admin */
+	if (current != p && !capable(CAP_SYS_ADMIN)) {
+		error = -EPERM;
+		goto out;
+	}
+
+	read_lock_irqsave(&sd_lock, flags);
+
+	sd = __get_sdcopy(&sdcopy, p);
+
+	read_unlock_irqrestore(&sd_lock, flags);
+
+	error = sd_getprocattr(sd, str, size);
+	put_sdcopy(sd);
+
+out:
+	return error;
+}
+
+static int subdomain_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 */
+	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) {
+			SD_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 = sd_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) {
+		int confined;
+		unsigned long flags;
+
+		/* only an unconfined process with admin capabilities
+		 * may change the profile of another task
+		 */
+
+		if (!capable(CAP_SYS_ADMIN)) {
+			SD_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;
+		}
+
+		read_lock_irqsave(&sd_lock, flags);
+		confined = sd_is_confined();
+		read_unlock_irqrestore(&sd_lock, flags);
+
+		if (!confined) {
+			char *profile = cmd + strlen(cmd_setprofile);
+			size_t profilesize = size - strlen(cmd_setprofile);
+
+			error = sd_setprocattr_setprofile(p, profile, profilesize);
+			if (error == 0)
+				/* success,
+				 * set return to #bytes in orig request
+				 */
+				error = size;
+		} else {
+			SD_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;
+		}
+	} else {
+		/* unknown operation */
+		SD_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 subdomain_ops = {
+	.ptrace =			subdomain_ptrace,
+	.capget =			subdomain_capget,
+	.capset_check =			subdomain_capset_check,
+	.capset_set =			subdomain_capset_set,
+	.sysctl =			subdomain_sysctl,
+	.capable =			subdomain_capable,
+	.syslog =			subdomain_syslog,
+
+	.netlink_send =			subdomain_netlink_send,
+	.netlink_recv =			subdomain_netlink_recv,
+
+	.bprm_apply_creds =		subdomain_bprm_apply_creds,
+	.bprm_set_security =		subdomain_bprm_set_security,
+	.bprm_secureexec =		subdomain_bprm_secureexec,
+
+	.sb_mount =			subdomain_sb_mount,
+	.sb_umount =			subdomain_umount,
+
+	.inode_mkdir =			subdomain_inode_mkdir,
+	.inode_rmdir =			subdomain_inode_rmdir,
+	.inode_create =			subdomain_inode_create,
+	.inode_link =			subdomain_inode_link,
+	.inode_unlink =			subdomain_inode_unlink,
+	.inode_mknod =			subdomain_inode_mknod,
+	.inode_rename =			subdomain_inode_rename,
+	.inode_permission =		subdomain_inode_permission,
+	.inode_setattr =		subdomain_inode_setattr,
+	.inode_setxattr =		subdomain_inode_setxattr,
+	.inode_getxattr =		subdomain_inode_getxattr,
+	.inode_listxattr =		subdomain_inode_listxattr,
+	.inode_removexattr =		subdomain_inode_removexattr,
+	.file_permission =		subdomain_file_permission,
+	.file_alloc_security =		subdomain_file_alloc_security,
+	.file_free_security =		subdomain_file_free_security,
+	.file_mmap =			subdomain_file_mmap,
+	.file_mprotect =		subdomain_file_mprotect,
+
+	.task_alloc_security =		subdomain_task_alloc_security,
+	.task_free_security =		subdomain_task_free_security,
+	.task_post_setuid =		subdomain_task_post_setuid,
+	.task_reparent_to_init =	subdomain_task_reparent_to_init,
+
+	.shm_shmat =			subdomain_shm_shmat,
+
+	.getprocattr =			subdomain_getprocattr,
+	.setprocattr =			subdomain_setprocattr,
+};
+
+static int __init subdomain_init(void)
+{
+	int error = 0;
+	const char *complainmsg = ": complainmode enabled";
+
+	if (!create_subdomainfs()) {
+		SD_ERROR("Unable to activate AppArmor filesystem\n");
+		error = -ENOENT;
+		goto createfs_out;
+	}
+
+	if (!alloc_nullprofiles()){
+		SD_ERROR("Unable to allocate null profiles\n");
+		error = -ENOMEM;
+		goto alloc_out;
+	}
+
+	if ((error = register_security(&subdomain_ops))) {
+		SD_WARN("Unable to load AppArmor\n");
+		goto register_security_out;
+	}
+
+	SD_INFO("AppArmor (version %s) initialized%s\n",
+		apparmor_version(),
+		subdomain_complain ? complainmsg : "");
+	sd_audit_message(NULL, GFP_KERNEL, 0,
+		"AppArmor (version %s) initialized%s\n",
+		apparmor_version(),
+		subdomain_complain ? complainmsg : "");
+
+	return error;
+
+register_security_out:
+	free_nullprofiles();
+
+alloc_out:
+	(void)destroy_subdomainfs();
+
+createfs_out:
+	return error;
+
+}
+
+static int subdomain_exit_removeall_iter(struct subdomain *sd, void *cookie)
+{
+	/* write_lock(&sd_lock) held here */
+
+	if (__sd_is_confined(sd)) {
+		SD_DEBUG("%s: Dropping profiles %s(%d) "
+			 "profile %s(%p) active %s(%p)\n",
+			 __FUNCTION__,
+			 sd->task->comm, sd->task->pid,
+			 sd->profile->name, sd->profile,
+			 sd->active->name, sd->active);
+		sd_switch_unconfined(sd);
+	}
+
+	return 0;
+}
+
+static void __exit subdomain_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)
+	 */
+	sd_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
+	 */
+
+	write_lock_irqsave(&sd_lock, flags);
+	sd_subdomainlist_iterate(subdomain_exit_removeall_iter, NULL);
+	write_unlock_irqrestore(&sd_lock, flags);
+
+	/* Free up list of active subdomain */
+	sd_subdomainlist_release();
+
+	free_nullprofiles();
+
+	if (!destroy_subdomainfs())
+		SD_WARN("Unable to properly deactivate AppArmor fs\n");
+
+	if (unregister_security(&subdomain_ops))
+		SD_WARN("Unable to properly unregister AppArmor\n");
+
+	SD_INFO("AppArmor protection removed\n");
+	sd_audit_message(NULL, GFP_KERNEL, 0,
+		"AppArmor protection removed\n");
+}
+
+module_init(subdomain_init);
+module_exit(subdomain_exit);
+
+MODULE_DESCRIPTION("AppArmor process confinement");
+MODULE_AUTHOR("Tony Jones <tonyj@suse.de>");
+MODULE_LICENSE("GPL");
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/main.c linux-2.6.x/security/apparmor/main.c
--- linux-2.6.x.orig/security/apparmor/main.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/main.c	2006-10-24 16:23:21.000000000 -0700
@@ -0,0 +1,1691 @@
+/*
+ *	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 "aamatch/match.h"
+
+#include "inline.h"
+
+/* NULL profile
+ *
+ * Used when an attempt is made to changehat into a non-existant
+ * subhat.   In the NULL profile,  no file access is allowed
+ * (currently full network access is allowed).  Using a NULL
+ * profile ensures that active is always non zero.
+ *
+ * Leaving the NULL profile is by either successfully changehatting
+ * into a sibling hat, or changehatting back to the parent (NULL hat).
+ */
+struct sdprofile *null_profile;
+
+/* 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 subdomain is
+ * unloaded
+ */
+struct sdprofile *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;
+	}
+
+	SD_ERROR("An error occured while translating %s %p "
+		 "inode# %s to a pathname. Error %d\n",
+		 dtype,
+		 dentry,
+		 buf,
+		 error);
+}
+
+/**
+ * sd_taskattr_access:
+ * @name: name of file to check permission
+ * @mask: permission mask requested for file
+ *
+ * Determine if request is for write access to /proc/self/attr/current
+ */
+static inline int sd_taskattr_access(const char *procrelname)
+{
+/*
+ * assumes a 32bit pid, which requires max 10 decimal digits to represent
+ * sizeof includes trailing \0
+ */
+	char buf[sizeof("/attr/current") + 10];
+	const int maxbuflen = sizeof(buf);
+
+	snprintf(buf, maxbuflen, "%d/attr/current", current->pid);
+	buf[maxbuflen - 1] = 0;
+
+	return strcmp(buf, procrelname) == 0;
+}
+
+/**
+ * sd_file_mode - get full mode for file entry from profile
+ * @profile: profile
+ * @name: filename
+ */
+static inline int sd_file_mode(struct sdprofile *profile, const char *name)
+{
+	struct sd_entry *entry;
+	int mode = 0;
+
+	SD_DEBUG("%s: %s\n", __FUNCTION__, name);
+	if (!name) {
+		SD_DEBUG("%s: no name\n", __FUNCTION__);
+		goto out;
+	}
+
+	if (!profile) {
+		SD_DEBUG("%s: no profile\n", __FUNCTION__);
+		goto out;
+	}
+	list_for_each_entry(entry, &profile->file_entry, list) {
+		if (sdmatch_match(name, entry->filename,
+				  entry->entry_type, entry->extradata))
+			mode |= entry->mode;
+	}
+out:
+	return mode;
+}
+
+/**
+ * sd_get_execmode - calculate what qualifier to apply to an exec
+ * @sd: subdomain 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
+ *         SD_MAY_EXEC is returned indicating a naked x
+ *         if the has an exec qualifier then only the qualifier bit {pui}
+ *         is returned (SD_MAY_EXEC) is not set.
+ * @unsafe: true if secure_exec should be overridden
+ *
+ * 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 not confined
+ *       *xmod = SD_MAY_EXEC
+ *       *unsafe = 0
+ *    if exec rule matched
+ *       if the rule has an execution mode qualifier {pui} then
+ *          *xmod = the execution qualifier of the rule {pui}
+ *       else
+ *          *xmod = SD_MAY_EXEC
+ *       unsafe = presence of unsafe flag
+ */
+static inline int sd_get_execmode(struct subdomain *sd, const char *name,
+				  int *xmod, int *unsafe)
+{
+	struct sdprofile *profile;
+	struct sd_entry *entry;
+	struct sd_entry *match = NULL;
+
+	int pattern_match_invalid = 0, rc = 0;
+
+	/* not confined */
+	if (!__sd_is_confined(sd)) {
+		SD_DEBUG("%s: not confined\n", __FUNCTION__);
+		goto not_confined;
+	}
+
+	profile = sd->active;
+
+	/* 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, &profile->file_entryp[POS_SD_MAY_EXEC],
+			    listp[POS_SD_MAY_EXEC]) {
+		if (!pattern_match_invalid &&
+		    entry->entry_type == sd_entry_pattern &&
+		    sdmatch_match(name, entry->filename,
+				  entry->entry_type, entry->extradata)) {
+			if (match &&
+			    SD_EXEC_UNSAFE_MASK(entry->mode) !=
+			    SD_EXEC_UNSAFE_MASK(match->mode))
+				pattern_match_invalid = 1;
+			else
+				/* keep searching for an exact match */
+				match = entry;
+		} else if ((entry->entry_type == sd_entry_literal ||
+			    (!pattern_match_invalid &&
+			     entry->entry_type == sd_entry_tailglob)) &&
+			    sdmatch_match(name, entry->filename,
+					  entry->entry_type,
+					  entry->extradata)) {
+			if (entry->entry_type == sd_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 &&
+				    SD_EXEC_UNSAFE_MASK(entry->mode) !=
+				    SD_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 = SD_EXEC_MASK(match->mode);
+
+		/* check for qualifiers, if present
+		 * we just return the qualifier
+		 */
+		if (mode & ~SD_MAY_EXEC)
+			mode = mode & ~SD_MAY_EXEC;
+
+		*xmod = mode;
+		*unsafe = (match->mode & SD_EXEC_UNSAFE);
+	} else if (!match) {
+		SD_DEBUG("%s: Unable to find execute entry in profile "
+			 "for image '%s'\n",
+			 __FUNCTION__,
+			 name);
+	} else if (pattern_match_invalid) {
+		SD_WARN("%s: Inconsistency in profile %s. "
+			"Two (or more) patterns specify conflicting exec "
+			"qualifiers ('u', 'i' or 'p') for image %s\n",
+			__FUNCTION__,
+			sd->active->name,
+			name);
+	}
+
+	return rc;
+
+not_confined:
+	*xmod = SD_MAY_EXEC;
+	*unsafe = 0;
+	return 1;
+}
+
+/**
+ * sd_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 sd_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 sd_permerror2result(int perm_result, struct sd_audit *sa)
+{
+	if (perm_result == 0) {	/* success */
+		sa->result = 1;
+		sa->errorcode = 0;
+	} else { /* -ve internal error code or +ve mask of denied perms */
+		sa->result = 0;
+		sa->errorcode = perm_result;
+	}
+}
+
+/*************************
+ * MAIN INTERNAL FUNCTIONS
+ ************************/
+
+/**
+ * sd_file_perm - calculate access mode for file
+ * @subdomain: current subdomain
+ * @name: name of file to calculate mode for
+ * @mask: permission mask requested for file
+ *
+ * Search the sd_entry list in @profile.
+ * 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 sd_file_perm(struct subdomain *sd, const char *name,
+				 int mask)
+{
+	struct sdprofile *profile;
+	int i, error = 0, mode;
+
+#define PROCPFX "/proc/"
+#define PROCLEN sizeof(PROCPFX) - 1
+
+	SD_DEBUG("%s: %s 0x%x\n", __FUNCTION__, name, mask);
+
+	/* should not enter with other than R/W/M/X/L */
+	BUG_ON(mask &
+	       ~(SD_MAY_READ | SD_MAY_WRITE | SD_MAY_EXEC |
+		 SD_EXEC_MMAP | SD_MAY_LINK));
+
+	/* not confined */
+	if (!__sd_is_confined(sd)) {
+		/* exit with access allowed */
+		SD_DEBUG("%s: not confined\n", __FUNCTION__);
+		goto done;
+	}
+
+	/* 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 &&
+	    (!list_empty(&sd->profile->sub) || SUBDOMAIN_COMPLAIN(sd)) &&
+	    sd_taskattr_access(name + PROCLEN))
+		goto done;
+
+	profile = sd->active;
+
+	mode = 0;
+
+	/* iterate over partition, one permission bit at a time */
+	for (i = 0; i <= POS_SD_FILE_MAX; i++) {
+		struct sd_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, &profile->file_entryp[i],
+				    listp[i]) {
+			if (sdmatch_match(name, entry->filename,
+				entry->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) {
+					SD_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;
+}
+
+/**
+ * sd_link_perm - test permission to link to a file
+ * @sd: current subdomain
+ * @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 sd_link_perm(struct subdomain *sd,
+			const char *link, const char *target)
+{
+	int l_mode, t_mode, ret;
+	struct sdprofile *profile = sd->active;
+
+	l_mode = sd_file_mode(profile, link);
+	if (l_mode & SD_MAY_LINK) {
+		/* mask off link bit */
+		l_mode &= ~SD_MAY_LINK;
+
+		t_mode = sd_file_mode(profile, target);
+		t_mode &= ~SD_MAY_LINK;
+
+		ret = (l_mode == t_mode);
+	} else {
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/**
+ * _sd_perm_dentry
+ * @sd: current subdomain
+ * @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 _sd_perm_dentry(struct subdomain *sd, struct dentry *dentry,
+			   int mask, const char **pname)
+{
+	char *name = NULL, *failed_name = NULL;
+	struct sd_path_data data;
+	int error = 0, failed_error = 0, sdpath_error,
+	    sdcomplain = SUBDOMAIN_COMPLAIN(sd);
+
+	/* search all paths to dentry */
+
+	sd_path_begin(dentry, &data);
+	do {
+		name = sd_path_getname(&data);
+		if (name) {
+			/* error here is 0 (success) or +ve (mask of perms) */
+			error = sd_file_perm(sd, name, mask);
+
+			/* access via any path is enough */
+			if (sdcomplain || error == 0)
+				break; /* Caller must free name */
+
+			/* Already have an path that failed? */
+			if (failed_name) {
+				sd_put_name(name);
+			} else {
+				failed_name = name;
+				failed_error = error;
+			}
+		}
+	} while (name);
+
+	if ((sdpath_error = sd_path_end(&data)) != 0) {
+		dentry_xlate_error(dentry, sdpath_error, "dentry");
+
+		WARN_ON(name);	/* name should not be set if error */
+		error = sdpath_error;
+		name = NULL;
+	} else if (name) {
+		if (failed_name)
+			sd_put_name(failed_name);
+	} else {
+		name = failed_name;
+		error = failed_error;
+	}
+
+	*pname = name;
+
+	return error;
+}
+
+/**************************
+ * GLOBAL UTILITY FUNCTIONS
+ *************************/
+
+/**
+ * alloc_nullprofiles - Allocate null profiles
+ */
+int alloc_nullprofiles(void)
+{
+	null_profile = alloc_sdprofile();
+	null_complain_profile = alloc_sdprofile();
+
+	if (!null_profile || !null_complain_profile)
+		goto fail;
+
+	null_profile->name = kstrdup("null-profile", GFP_KERNEL);
+	null_complain_profile->name =
+		kstrdup("null-complain-profile", GFP_KERNEL);
+
+	if (!null_profile->name ||
+	    !null_complain_profile->name)
+		goto fail;
+
+	get_sdprofile(null_profile);
+	get_sdprofile(null_complain_profile);
+	null_complain_profile->flags.complain = 1;
+
+	return 1;
+
+fail:
+	/* free_sdprofile is safe for freeing partially constructed objects */
+	free_sdprofile(null_profile);
+	free_sdprofile(null_complain_profile);
+	null_profile = null_complain_profile = NULL;
+	return 0;
+}
+
+/**
+ * free_nullprofiles - Free null profiles
+ */
+void free_nullprofiles(void)
+{
+	put_sdprofile(null_complain_profile);
+	put_sdprofile(null_profile);
+	null_profile = null_complain_profile = NULL;
+}
+
+/**
+ * sd_audit_message - Log a message to the audit subsystem
+ * @sd: current subdomain
+ * @gfp: allocation flags
+ * @flags: audit flags
+ * @fmt: varargs fmt
+ */
+int sd_audit_message(struct subdomain *sd, unsigned int gfp, int flags,
+		     const char *fmt, ...)
+{
+	int ret;
+	struct sd_audit sa;
+
+	sa.type = SD_AUDITTYPE_MSG;
+	sa.name = fmt;
+	va_start(sa.vaval, fmt);
+	sa.flags = flags;
+	sa.gfp_mask = gfp;
+	sa.errorcode = 0;
+	sa.result = 0;	/* fake failure: force message to be logged */
+
+	ret = sd_audit(sd, &sa);
+
+	va_end(sa.vaval);
+
+	return ret;
+}
+
+/**
+ * sd_audit_syscallreject - Log a syscall rejection to the audit subsystem
+ * @sd: current subdomain
+ * @msg: string describing syscall being rejected
+ * @gfp: memory allocation flags
+ */
+int sd_audit_syscallreject(struct subdomain *sd, unsigned int gfp,
+			   const char *msg)
+{
+	struct sd_audit sa;
+
+	sa.type = SD_AUDITTYPE_SYSCALL;
+	sa.name = msg;
+	sa.flags = 0;
+	sa.gfp_mask = gfp;
+	sa.errorcode = 0;
+	sa.result = 0; /* failure */
+
+	return sd_audit(sd, &sa);
+}
+
+/**
+ * sd_audit - Log an audit event to the audit subsystem
+ * @sd: current subdomain
+ * @sa: audit event
+ */
+int sd_audit(struct subdomain *sd, const struct sd_audit *sa)
+{
+	struct audit_buffer *ab = NULL;
+	struct audit_context *ctx;
+
+	const char *logcls;
+	unsigned int flags;
+	int sdaudit = 0,
+	    sdcomplain = 0,
+	    error = -EINVAL,
+	    opspec_error = -EACCES;
+
+	const unsigned int gfp_mask = sa->gfp_mask;
+
+	WARN_ON(sa->type >= SD_AUDITTYPE__END);
+
+	/*
+	 * sa->result:	  1 success, 0 failure
+	 * sa->errorcode: success: 0
+	 *		  failure: +ve mask of failed permissions or -ve
+	 *		  system error
+	 */
+
+	if (likely(sa->result)) {
+		if (likely(!SUBDOMAIN_AUDIT(sd))) {
+			/* nothing to log */
+			error = 0;
+			goto out;
+		} else {
+			sdaudit = 1;
+			logcls = "AUDITING";
+		}
+	} else if (sa->errorcode < 0) {
+		audit_log(current->audit_context, gfp_mask, AUDIT_SD,
+			"Internal error auditing event type %d (error %d)",
+			sa->type, sa->errorcode);
+		SD_ERROR("Internal error auditing event type %d (error %d)\n",
+			sa->type, sa->errorcode);
+		error = sa->errorcode;
+		goto out;
+	} else if (sa->type == SD_AUDITTYPE_SYSCALL) {
+		/* Currently SD_AUDITTYPE_SYSCALL is for rejects only.
+		 * Values set by sd_audit_syscallreject will get us here.
+		 */
+		logcls = "REJECTING";
+	} else {
+		sdcomplain = SUBDOMAIN_COMPLAIN(sd);
+		logcls = sdcomplain ? "PERMITTING" : "REJECTING";
+	}
+
+	/* In future extend w/ per-profile flags
+	 * (flags |= sa->active->flags)
+	 */
+	flags = sa->flags;
+	if (subdomain_logsyscall)
+		flags |= SD_AUDITFLAG_AUDITSS_SYSCALL;
+
+
+	/* Force full audit syscall logging regardless of global setting if
+	 * we are rejecting a syscall
+	 */
+	if (sa->type == SD_AUDITTYPE_SYSCALL) {
+		ctx = current->audit_context;
+	} else {
+		ctx = (flags & SD_AUDITFLAG_AUDITSS_SYSCALL) ?
+			current->audit_context : NULL;
+	}
+
+	ab = audit_log_start(ctx, gfp_mask, AUDIT_SD);
+
+	if (!ab) {
+		SD_ERROR("Unable to log event (%d) to audit subsys\n",
+			sa->type);
+		if (sdcomplain)
+			error = 0;
+		goto out;
+	}
+
+	/* messages get special handling */
+	if (sa->type == SD_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 == SD_AUDITTYPE_FILE) {
+		int perm = sdaudit ? sa->ival : sa->errorcode;
+
+		audit_log_format(ab, "%s%s%s%s%s access to %s ",
+			perm & SD_EXEC_MMAP ? "m" : "",
+			perm & SD_MAY_READ  ? "r" : "",
+			perm & SD_MAY_WRITE ? "w" : "",
+			perm & SD_MAY_EXEC  ? "x" : "",
+			perm & SD_MAY_LINK  ? "l" : "",
+			sa->name);
+
+		opspec_error = -EPERM;
+
+	} else if (sa->type == SD_AUDITTYPE_DIR) {
+		audit_log_format(ab, "%s on %s ",
+			sa->ival == SD_DIR_MKDIR ? "mkdir" : "rmdir",
+			sa->name);
+
+	} else if (sa->type == SD_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 == SD_AUDITTYPE_XATTR) {
+		const char *fmt;
+		switch (sa->ival) {
+			case SD_XATTR_GET:
+				fmt = "xattr get";
+				break;
+			case SD_XATTR_SET:
+				fmt = "xattr set";
+				break;
+			case SD_XATTR_LIST:
+				fmt = "xattr list";
+				break;
+			case SD_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 == SD_AUDITTYPE_LINK) {
+		audit_log_format(ab,
+			"link access from %s to %s ",
+			sa->name,
+			(char*)sa->pval);
+
+	} else if (sa->type == SD_AUDITTYPE_CAP) {
+		audit_log_format(ab,
+			"access to capability '%s' ",
+			capability_to_name(sa->ival));
+
+		opspec_error = -EPERM;
+	} else if (sa->type == SD_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,
+		sd->profile->name, sd->active->name);
+
+	audit_log_end(ab);
+
+	if (sdcomplain)
+		error = 0;
+	else
+		error = sa->result ? 0 : opspec_error;
+
+out:
+	return error;
+}
+
+/**
+ * sd_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.
+ * sd_put_name must be used to free allocated buffer.
+ */
+char *sd_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';
+
+		SD_DEBUG("%s: full_path=%s\n", __FUNCTION__, name);
+	}
+
+out:
+	return name;
+}
+
+/***********************************
+ * GLOBAL PERMISSION CHECK FUNCTIONS
+ ***********************************/
+
+/**
+ * sd_attr - check whether attribute change allowed
+ * @sd: subdomain to check against to check against
+ * @dentry: file to check
+ * @iattr: attribute changes requested
+ */
+int sd_attr(struct subdomain *sd, struct dentry *dentry, struct iattr *iattr)
+{
+	int error = 0, permerror;
+	struct sd_audit sa;
+
+	if (!__sd_is_confined(sd))
+		goto out;
+
+	sa.type = SD_AUDITTYPE_ATTR;
+	sa.pval = iattr;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	permerror = _sd_perm_dentry(sd, dentry, MAY_WRITE, &sa.name);
+	sd_permerror2result(permerror, &sa);
+
+	error = sd_audit(sd, &sa);
+
+	sd_put_name(sa.name);
+
+out:
+	return error;
+}
+
+int sd_xattr(struct subdomain *sd, struct dentry *dentry, const char *xattr,
+	     int xattroptype)
+{
+	int error = 0, permerror, mask = 0;
+	struct sd_audit sa;
+
+	/* if not confined or empty mask permission granted */
+	if (!__sd_is_confined(sd))
+		goto out;
+
+	if (xattroptype == SD_XATTR_GET || xattroptype == SD_XATTR_LIST)
+		mask = MAY_READ;
+	else if (xattroptype == SD_XATTR_SET || xattroptype == SD_XATTR_REMOVE)
+		mask = MAY_WRITE;
+
+	sa.type = SD_AUDITTYPE_XATTR;
+	sa.ival = xattroptype;
+	sa.pval = xattr;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	permerror = _sd_perm_dentry(sd, dentry, mask, &sa.name);
+	sd_permerror2result(permerror, &sa);
+
+	error = sd_audit(sd, &sa);
+
+	sd_put_name(sa.name);
+
+out:
+	return error;
+}
+
+/**
+ * sd_perm - basic subdomain permissions check
+ * @sd: subdomain to check against
+ * @dentry: dentry
+ * @mnt: mountpoint
+ * @mask: access mode requested
+ *
+ * Determine if access (mask) for dentry is authorized by subdomain sd.
+ * Result, 0 (success), -ve (error)
+ */
+int sd_perm(struct subdomain *sd, struct dentry *dentry, struct vfsmount *mnt,
+	    int mask)
+{
+	int error = 0, permerror;
+	struct sd_audit sa;
+
+	if (!__sd_is_confined(sd))
+		goto out;
+
+	if ((mask = sd_filter_mask(mask, dentry->d_inode)) == 0)
+		goto out;
+
+	sa.type = SD_AUDITTYPE_FILE;
+	sa.name = sd_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 = sd_file_perm(sd, sa.name, mask);
+	}
+
+	sd_permerror2result(permerror, &sa);
+
+	error = sd_audit(sd, &sa);
+
+	sd_put_name(sa.name);
+
+out:
+	return error;
+}
+
+/**
+ * sd_perm_nameidata: interface to sd_perm accepting nameidata
+ * @sd: subdomain to check against
+ * @nd: namespace data (for vfsmnt and dentry)
+ * @mask: access mode requested
+ */
+int sd_perm_nameidata(struct subdomain *sd, struct nameidata *nd, int mask)
+{
+	int error = 0;
+
+	if (nd)
+		error = sd_perm(sd, nd->dentry, nd->mnt, mask);
+
+	return error;
+}
+
+/**
+ * sd_perm_dentry - file permissions interface when no vfsmnt available
+ * @sd: current subdomain
+ * @dentry: requested dentry
+ * @mask: access mode requested
+ *
+ * Determine if access (mask) for dentry is authorized by subdomain sd.
+ * Result, 0 (success), -ve (error)
+ */
+int sd_perm_dentry(struct subdomain *sd, struct dentry *dentry, int mask)
+{
+	int error = 0, permerror;
+	struct sd_audit sa;
+
+	if (!__sd_is_confined(sd))
+		goto out;
+
+	if ((mask = sd_filter_mask(mask, dentry->d_inode)) == 0)
+		goto out;
+
+	sa.type = SD_AUDITTYPE_FILE;
+	sa.ival = mask;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	permerror = _sd_perm_dentry(sd, dentry, mask, &sa.name);
+	sd_permerror2result(permerror, &sa);
+
+	error = sd_audit(sd, &sa);
+
+	sd_put_name(sa.name);
+
+out:
+	return error;
+}
+
+/**
+ * sd_perm_dir
+ * @sd: current subdomain
+ * @dentry: requested dentry
+ * @mode: SD_DIR_MKDIR or SD_DIR_RMDIR
+ *
+ * Determine if directory operation (make/remove) for dentry is authorized
+ * by subdomain sd.
+ * Result, 0 (success), -ve (error)
+ */
+int sd_perm_dir(struct subdomain *sd, struct dentry *dentry, int diroptype)
+{
+	int error = 0, permerror, mask;
+	struct sd_audit sa;
+
+	BUG_ON(diroptype != SD_DIR_MKDIR && diroptype != SD_DIR_RMDIR);
+
+	if (!__sd_is_confined(sd))
+		goto out;
+
+	mask = MAY_WRITE;
+
+	sa.type = SD_AUDITTYPE_DIR;
+	sa.ival = diroptype;
+	sa.flags = 0;
+	sa.gfp_mask = GFP_KERNEL;
+
+	permerror = _sd_perm_dentry(sd, dentry, mask, &sa.name);
+	sd_permerror2result(permerror, &sa);
+
+	error = sd_audit(sd, &sa);
+
+	sd_put_name(sa.name);
+
+out:
+	return error;
+}
+
+/**
+ * sd_capability - test permission to use capability
+ * @sd: subdomain to check against
+ * @cap: capability to be tested
+ *
+ * Look up capability in active profile capability set.
+ * Return 0 (success), -EPERM (error)
+ */
+int sd_capability(struct subdomain *sd, int cap)
+{
+	int error = 0;
+
+	if (__sd_is_confined(sd)) {
+		struct sd_audit sa;
+
+		sa.type = SD_AUDITTYPE_CAP;
+		sa.name = NULL;
+		sa.ival = cap;
+		sa.flags = 0;
+		sa.errorcode = 0;
+		sa.result = cap_raised(sd->active->capabilities, cap);
+		sa.gfp_mask = GFP_ATOMIC;
+
+		error = sd_audit(sd, &sa);
+	}
+
+	return error;
+}
+
+/**
+ * sd_link - hard link check
+ * @link: dentry for link being created
+ * @target: dentry for link target
+ * @sd: subdomain to check against
+ *
+ * Checks link permissions for all possible name combinations.  This is
+ * particularly ugly.  Returns 0 on sucess, error otherwise.
+ */
+int sd_link(struct subdomain *sd, struct dentry *link, struct dentry *target)
+{
+	char *iname = NULL, *oname = NULL,
+	     *failed_iname = NULL, *failed_oname = NULL;
+	unsigned int result = 0;
+	int error, sdpath_error, errorcode = 0, match = 0,
+	    sdcomplain = SUBDOMAIN_COMPLAIN(sd);
+	struct sd_path_data idata, odata;
+	struct sd_audit sa;
+
+	if (!__sd_is_confined(sd))
+		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.
+	 */
+
+	sd_path_begin2(target, link, &odata);
+	do {
+		oname = sd_path_getname(&odata);
+		if (oname) {
+			sd_path_begin(target, &idata);
+			do {
+				iname = sd_path_getname(&idata);
+				if (iname) {
+					result = sd_link_perm(sd, oname, iname);
+
+					/* access via any path is enough */
+					if (result || sdcomplain) {
+						match = 1;
+						break;
+					}
+
+					/* Already have an path that failed? */
+					if (failed_iname) {
+						sd_put_name(iname);
+					} else {
+						failed_iname = iname;
+						failed_oname = oname;
+					}
+				}
+			} while (iname && !match);
+
+			/* should not be possible if we matched */
+			if ((sdpath_error = sd_path_end(&idata)) != 0) {
+				dentry_xlate_error(target, sdpath_error,
+						   "inner dentry [link]");
+
+				/* name should not be set if error */
+				WARN_ON(iname);
+
+				errorcode = sdpath_error;
+			}
+
+			/* don't release if we're saving it */
+			if (!match && failed_oname != oname)
+				sd_put_name(oname);
+		}
+	} while (oname && !match);
+
+	if (errorcode != 0) {
+		/* inner error */
+		(void)sd_path_end(&odata);
+	} else if ((sdpath_error = sd_path_end(&odata)) != 0) {
+		dentry_xlate_error(link, sdpath_error, "outer dentry [link]");
+
+		errorcode = sdpath_error;
+	}
+
+	if (errorcode != 0) {
+		/* inner or outer error */
+		result = 0;
+	} else if (match) {
+		result = 1;
+	} else {
+		/* failed to match */
+		WARN_ON(iname);
+		WARN_ON(oname);
+
+		result = 0;
+		iname = failed_iname;
+		oname = failed_oname;
+	}
+
+	sa.type = SD_AUDITTYPE_LINK;
+	sa.name = oname;	/* link */
+	sa.pval = iname;	/* target */
+	sa.flags = 0;
+	sa.errorcode = errorcode;
+	sa.result = result;
+	sa.gfp_mask = GFP_KERNEL;
+
+	error = sd_audit(sd, &sa);
+
+	if (failed_oname != oname)
+		sd_put_name(failed_oname);
+	if (failed_iname != iname)
+		sd_put_name(failed_iname);
+
+	sd_put_name(oname);
+	sd_put_name(iname);
+
+	return error;
+}
+
+/**********************************
+ * GLOBAL PROCESS RELATED FUNCTIONS
+ *********************************/
+
+/**
+ * sd_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
+ * for the child if it subsequently execs (in sd_register).
+ * Return 0 on sucess.
+ */
+
+int sd_fork(struct task_struct *p)
+{
+	struct subdomain *sd = SD_SUBDOMAIN(current->security);
+	struct subdomain *newsd = NULL;
+
+	SD_DEBUG("%s\n", __FUNCTION__);
+
+	if (__sd_is_confined(sd)) {
+		unsigned long flags;
+
+		newsd = alloc_subdomain(p);
+
+		if (!newsd)
+			return -ENOMEM;
+
+		/* Can get away with a read rather than write lock here
+		 * as we just allocated newsd above, so we can guarantee
+		 * that it's active/profile are null and therefore a replace
+		 * cannot happen.
+		 */
+		read_lock_irqsave(&sd_lock, flags);
+		sd_switch(newsd, sd->profile, sd->active);
+		newsd->sd_hat_magic = sd->sd_hat_magic;
+		read_unlock_irqrestore(&sd_lock, flags);
+
+		if (SUBDOMAIN_COMPLAIN(sd) &&
+		    sd->active == null_complain_profile)
+			LOG_HINT(sd, GFP_KERNEL, HINT_FORK,
+				"pid=%d child=%d\n",
+				current->pid, p->pid);
+	}
+	p->security = newsd;
+	return 0;
+}
+
+/**
+ * sd_register - register a new program
+ * @filp: file of program being registered
+ *
+ * Try to register a new program during execve().  This should give the
+ * new program a valid subdomain.
+ *
+ * This _used_ to be a really simple piece of code :-(
+ *
+ */
+int sd_register(struct linux_binprm *bprm)
+{
+	char *filename;
+	struct file *filp = bprm->file;
+	struct subdomain *sd, sdcopy;
+	struct sdprofile *newprofile = NULL, unconstrained_flag;
+	int 	error = -ENOMEM,
+		exec_mode = 0,
+		findprofile = 0,
+		findprofile_mandatory = 0,
+		unsafe_exec = 0,
+		complain = 0;
+
+	SD_DEBUG("%s\n", __FUNCTION__);
+
+	sd = get_sdcopy(&sdcopy);
+
+	filename = sd_get_name(filp->f_dentry, filp->f_vfsmnt);
+	if (IS_ERR(filename)) {
+		SD_WARN("%s: Failed to get filename\n", __FUNCTION__);
+		goto out;
+	}
+
+	error = 0;
+
+	if (!__sd_is_confined(sd)) {
+		/* Unconfined task, load profile if it exists */
+		findprofile = 1;
+		goto find_profile;
+	}
+
+	complain = SUBDOMAIN_COMPLAIN(sd);
+
+	/* Confined task, determine what mode inherit, unconstrained or
+	 * mandatory to load new profile
+	 */
+	if (sd_get_execmode(sd, filename, &exec_mode, &unsafe_exec)) {
+		switch (exec_mode) {
+		case SD_EXEC_INHERIT:
+			/* do nothing - setting of profile
+			 * already handed in sd_fork
+			 */
+			SD_DEBUG("%s: INHERIT %s\n",
+				 __FUNCTION__,
+				 filename);
+			break;
+
+		case SD_EXEC_UNCONSTRAINED:
+			SD_DEBUG("%s: UNCONSTRAINED %s\n",
+				 __FUNCTION__,
+				 filename);
+
+			/* unload profile */
+			newprofile = &unconstrained_flag;
+			break;
+
+		case SD_EXEC_PROFILE:
+			SD_DEBUG("%s: PROFILE %s\n",
+				 __FUNCTION__,
+				 filename);
+
+			findprofile = 1;
+			findprofile_mandatory = 1;
+			break;
+
+		case SD_MAY_EXEC:
+			/* this should not happen, entries
+			 * with just EXEC only should be
+			 * rejected at profile load time
+			 */
+			SD_ERROR("%s: Rejecting exec(2) of image '%s'. "
+				"SD_MAY_EXEC without exec qualifier invalid "
+				"(%s(%d) profile %s active %s\n",
+				 __FUNCTION__,
+				 filename,
+				 current->comm, current->pid,
+				 sd->profile->name, sd->active->name);
+			error = -EPERM;
+			break;
+
+		default:
+			SD_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,
+				 sd->profile->name, sd->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_sdprofile(null_complain_profile);
+		unsafe_exec = 1;
+	} else {
+		SD_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,
+			sd->profile->name, sd->active->name);
+		error = -EPERM;
+	}
+
+
+find_profile:
+	if (!findprofile)
+		goto apply_profile;
+
+	/* Locate new profile */
+	newprofile = sd_profilelist_find(filename);
+	if (newprofile) {
+		SD_DEBUG("%s: setting profile %s\n",
+			 __FUNCTION__, newprofile->name);
+	} else if (findprofile_mandatory) {
+		/* Profile (mandatory) could not be found */
+
+		if (complain) {
+			LOG_HINT(sd, GFP_KERNEL, HINT_MANDPROF,
+				"image=%s pid=%d profile=%s active=%s\n",
+				filename,
+				current->pid,
+				sd->profile->name,
+				sd->active->name);
+
+			newprofile = get_sdprofile(null_complain_profile);
+		} else {
+			SD_WARN("REJECTING exec(2) of image '%s'. "
+				"Profile mandatory and not found "
+				"(%s(%d) profile %s active %s)\n",
+				filename,
+				current->comm, current->pid,
+				sd->profile->name, sd->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.
+		 */
+
+		BUG_ON(__sd_is_confined(sd));
+
+		SD_DEBUG("%s: No profile found for exec image %s\n",
+			 __FUNCTION__,
+			 filename);
+	} /* newprofile */
+
+
+apply_profile:
+	/* Apply profile if necessary */
+	if (newprofile) {
+		struct subdomain *latest_sd, *lazy_sd = NULL;
+		unsigned long flags;
+
+		if (newprofile == &unconstrained_flag)
+			newprofile = NULL;
+
+		/* grab a write lock
+		 *
+		 * - 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.
+		 *
+		 * - sd is a refcounted copy of the subdomain (get_sdcopy) and
+		 *   not the actual subdomain. This allows us to not have to
+		 *   hold a read lock around all this code. However, we need to
+		 *   change the actual subdomain, not the copy.
+		 *
+		 * - If newprofile points to an actual profile (result of
+		 *   sd_profilelist_find above), this profile may have been
+		 *   replaced.  We need to fix it up.  Doing this to avoid
+		 *   having to hold a write lock around all this code.
+		 */
+
+		if (!sd) {
+			lazy_sd = alloc_subdomain(current);
+		}
+
+		write_lock_irqsave(&sd_lock, flags);
+
+		latest_sd = SD_SUBDOMAIN(current->security);
+
+		if (latest_sd) {
+			if (lazy_sd) {
+				/* raced by setprofile (created latest_sd) */
+				free_subdomain(lazy_sd);
+				lazy_sd = NULL;
+			}
+		} else {
+			if (lazy_sd) {
+				latest_sd = lazy_sd;
+				current->security = lazy_sd;
+			} else {
+				SD_ERROR("%s: Failed to allocate subdomain\n",
+					__FUNCTION__);
+
+				error = -ENOMEM;
+				write_unlock_irqrestore(&sd_lock, flags);
+				goto done;
+			}
+		}
+
+		/* 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)) {
+			BUG_ON(newprofile == null_complain_profile);
+
+			/* drop refcnt obtained from earlier get_sdprofile */
+			put_sdprofile(newprofile);
+
+			newprofile = sd_profilelist_find(filename);
+
+			if (!newprofile) {
+				/* Race, profile was removed, not replaced.
+				 * Redo with error checking
+				 */
+				write_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 (__sd_is_confined(latest_sd) && !unsafe_exec) {
+			unsigned long bprm_flags;
+
+			bprm_flags = SD_SECURE_EXEC_NEEDED;
+			bprm->security = (void*)
+				((unsigned long)bprm->security | bprm_flags);
+		}
+
+		sd_switch(latest_sd, newprofile, newprofile);
+		put_sdprofile(newprofile);
+
+		if (complain && newprofile == null_complain_profile)
+			LOG_HINT(latest_sd, GFP_ATOMIC, HINT_CHGPROF,
+				"pid=%d\n",
+				current->pid);
+
+		write_unlock_irqrestore(&sd_lock, flags);
+	}
+
+done:
+	sd_put_name(filename);
+
+	if (sd)
+		put_sdcopy(sd);
+
+out:
+	return error;
+}
+
+/**
+ * sd_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 must be !NULL. The @p->security blob is freed.
+ */
+void sd_release(struct task_struct *p)
+{
+	struct subdomain *sd = SD_SUBDOMAIN(p->security);
+	p->security = NULL;
+
+	sd_subdomainlist_remove(sd);
+
+	/* release profiles */
+	put_sdprofile(sd->profile);
+	put_sdprofile(sd->active);
+
+	kfree(sd);
+}
+
+/*****************************
+ * GLOBAL SUBPROFILE FUNCTIONS
+ ****************************/
+
+/**
+ * do_change_hat - actually switch hats
+ * @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 sdprofile *sub;
+	struct sdprofile *p = sd->active;
+	int error = 0;
+
+	sub = __sd_find_profile(hat_name, &sd->profile->sub);
+
+	if (sub) {
+		/* change hat */
+		sd->active = 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, GFP_ATOMIC, HINT_UNKNOWN_HAT,
+ 				"%s pid=%d "
+				"profile=%s active=%s\n",
+				hat_name,
+				current->pid,
+				sd->profile->name,
+				sd->active->name);
+			sd->active = get_sdprofile(null_complain_profile);
+		} else {
+			SD_DEBUG("%s: Unknown hatname '%s'. "
+				"Changing to NULL profile "
+				"(%s(%d) profile %s active %s)\n",
+				 __FUNCTION__,
+				 hat_name,
+				 current->comm, current->pid,
+				 sd->profile->name, sd->active->name);
+
+			sd->active = get_sdprofile(null_profile);
+			error = -EACCES;
+		}
+	}
+	put_sdprofile(p);
+
+	return error;
+}
+
+/**
+ * sd_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.
+ */
+#define IN_SUBPROFILE(sd)	((sd)->profile != (sd)->active)
+int sd_change_hat(const char *hat_name, __u32 hat_magic)
+{
+	struct subdomain *sd = SD_SUBDOMAIN(current->security);
+	int error = 0;
+
+	SD_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)) {
+		SD_WARN("%s: %s, 0x%x (pid %d)\n",
+			__FUNCTION__, hat_name ? hat_name : "NULL",
+			hat_magic, current->pid);
+	}
+
+	/* no subdomain: changehat into the null_profile, since the process
+	   has no subdomain do_change_hat won't find a match which will cause
+	   a changehat to null_profile.  We could short circuit this but since
+	   the subdprofile (hat) list is empty we would save very little. */
+
+	/* check to see if an unconfined process is doing a changehat. */
+	if (!__sd_is_confined(sd)) {
+		error = -EPERM;
+		goto out;
+	}
+
+	/* Check whether current domain is parent
+	 * or one of the sibling children
+	 */
+	if (sd->profile == sd->active) {
+		/*
+		 * parent
+		 */
+		if (hat_name) {
+			SD_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->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->sd_hat_magic && sd->sd_hat_magic) {
+			if (!hat_name) {
+				/*
+				 * Got here via changehat(NULL, magic)
+				 * Return from subprofile, back to parent
+				 */
+				put_sdprofile(sd->active);
+				sd->active = get_sdprofile(sd->profile);
+
+				/* Reset hat_magic to zero.
+				 * New value will be passed on next changehat
+				 */
+				sd->sd_hat_magic = 0;
+			} else {
+				/* change to another (sibling) profile */
+				error = do_change_hat(hat_name, sd);
+			}
+		} else if (sd->sd_hat_magic) {
+			SD_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",
+				 sd->profile->name, sd->active->name);
+
+			/* terminate current process */
+			(void)send_sig_info(SIGKILL, NULL, current);
+		} else {	/* sd->sd_hat_magic == NULL */
+			SD_ERROR("KILLING process %s(%d) "
+				 "Task was confined to current subprofile "
+				 "(profile %s active %s)\n",
+				 current->comm, current->pid,
+				 sd->profile->name, sd->active->name);
+
+			/* terminate current process */
+			(void)send_sig_info(SIGKILL, NULL, current);
+		}
+
+	}
+
+out:
+	return error;
+}
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/Makefile linux-2.6.x/security/apparmor/Makefile
--- linux-2.6.x.orig/security/apparmor/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/Makefile	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,25 @@
+# Makefile for AppArmor Linux Security Module (previously called "SubDomain")
+#
+# 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` APPARMOR_VER=${REPO_VERSION} $@
+	mv apparmor.ko apparmor-${KERNELVER}.ko
+	mv aamatch/aamatch_pcre.ko aamatch_pcre-${KERNELVER}.ko
+
+clean:
+	rm -f *.o *.ko *.mod.c .*.cmd Modules.symvers \
+		aamatch/*.o aamatch/*.ko aamatch/.*.cmd aamatch/*.mod.c
+	rm -rf .tmp_versions
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/module_interface.c linux-2.6.x/security/apparmor/module_interface.c
--- linux-2.6.x.orig/security/apparmor/module_interface.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/module_interface.c	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,712 @@
+/*
+ *	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 "aamatch/match.h"
+
+/* sd_code defined in module_interface.h */
+
+const int sdcode_datasize[] = { 1, 2, 4, 8, 2, 2, 4, 0, 0, 0, 0, 0, 0 };
+
+struct sd_taskreplace_data {
+	struct sdprofile *old_profile;
+	struct sdprofile *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_sd_entry(struct sd_entry *entry)
+{
+	if (entry) {
+		kfree(entry->filename);
+		sdmatch_free(entry->extradata);
+		kfree(entry);
+	}
+}
+
+/**
+ * alloc_sd_entry - create new empty sd_entry
+ *
+ * This routine allocates, initializes, and returns a new subdomain
+ * file entry structure.  Structure is zeroed.  Returns new structure on
+ * success, NULL on failure.
+ */
+static inline struct sd_entry *alloc_sd_entry(void)
+{
+	struct sd_entry *entry;
+
+	SD_DEBUG("%s\n", __FUNCTION__);
+	entry = kmalloc(sizeof(struct sd_entry), GFP_KERNEL);
+	if (entry) {
+		int i;
+		memset(entry, 0, sizeof(struct sd_entry));
+		INIT_LIST_HEAD(&entry->list);
+		for (i = 0; i <= POS_SD_FILE_MAX; i++) {
+			INIT_LIST_HEAD(&entry->listp[i]);
+		}
+	}
+	return entry;
+}
+
+/**
+ * free_sdprofile - free sdprofile structure
+ */
+void free_sdprofile(struct sdprofile *profile)
+{
+	struct sd_entry *sdent, *tmp;
+	struct sdprofile *p, *ptmp;
+
+	SD_DEBUG("%s(%p)\n", __FUNCTION__, profile);
+
+	if (!profile)
+		return;
+
+	/* profile is still on global profile list -- invalid */
+	if (!list_empty(&profile->list)) {
+		SD_ERROR("%s: internal error, "
+			 "profile '%s' still on global list\n",
+			 __FUNCTION__,
+			 profile->name);
+		BUG();
+	}
+
+	list_for_each_entry_safe(sdent, tmp, &profile->file_entry, list) {
+		if (sdent->filename)
+			SD_DEBUG("freeing sd_entry: %p %s\n",
+				 sdent->filename, sdent->filename);
+		list_del_init(&sdent->list);
+		free_sd_entry(sdent);
+	}
+
+	list_for_each_entry_safe(p, ptmp, &profile->sub, list) {
+		list_del_init(&p->list);
+		put_sdprofile(p);
+	}
+
+	if (profile->name) {
+		SD_DEBUG("%s: %s\n", __FUNCTION__, profile->name);
+		kfree(profile->name);
+	}
+
+	kfree(profile);
+}
+
+/** task_remove
+ *
+ * remove profile in a task's subdomain leaving the task unconfined
+ *
+ * @sd: task's subdomain
+ */
+static inline void task_remove(struct subdomain *sd)
+{
+	/* write_lock(&sd_lock) held here */
+	SD_DEBUG("%s: removing profile from task %s(%d) profile %s active %s\n",
+		 __FUNCTION__,
+		 sd->task->comm,
+		 sd->task->pid,
+		 sd->profile->name,
+		 sd->active->name);
+
+	sd_switch_unconfined(sd);
+}
+
+/** taskremove_iter
+ *
+ * Iterate over all subdomains.
+ *
+ * If any matches old_profile,  then call task_remove to remove it.
+ * This leaves the task (subdomain) unconfined.
+ */
+static int taskremove_iter(struct subdomain *sd, void *cookie)
+{
+	struct sdprofile *old_profile = (struct sdprofile *)cookie;
+	unsigned long flags;
+
+	write_lock_irqsave(&sd_lock, flags);
+
+	if (__sd_is_confined(sd) && sd->profile == old_profile)
+		task_remove(sd);
+
+	write_unlock_irqrestore(&sd_lock, flags);
+
+	return 0;
+}
+
+/** task_replace
+ *
+ * replace profile in a task's subdomain with newly loaded profile
+ *
+ * @sd: task's subdomain
+ * @new: old profile
+ */
+static inline void task_replace(struct subdomain *sd, struct sdprofile *new)
+{
+	struct sdprofile *nactive = NULL;
+
+	SD_DEBUG("%s: replacing profile for task %s(%d) "
+		 "profile=%s (%p) active=%s (%p)\n",
+		 __FUNCTION__,
+		 sd->task->comm, sd->task->pid,
+		 sd->profile->name, sd->profile,
+		 sd->active->name, sd->active);
+
+	if (sd->profile == sd->active)
+		nactive = get_sdprofile(new);
+	else if (sd->active) {
+		/* old in hat, new profile has hats */
+		nactive = __sd_find_profile(sd->active->name, &new->sub);
+
+		if (!nactive) {
+			if (new->flags.complain)
+				nactive = get_sdprofile(null_complain_profile);
+			else
+				nactive = get_sdprofile(null_profile);
+		}
+	}
+	sd_switch(sd, new, nactive);
+
+	put_sdprofile(nactive);
+}
+
+/** taskreplace_iter
+ *
+ * Iterate over all subdomains.
+ *
+ * If any matches old_profile,  then call task_replace to replace with
+ * new_profile
+ */
+static int taskreplace_iter(struct subdomain *sd, void *cookie)
+{
+	struct sd_taskreplace_data *data = (struct sd_taskreplace_data *)cookie;
+	unsigned long flags;
+
+	write_lock_irqsave(&sd_lock, flags);
+
+	if (__sd_is_confined(sd) && sd->profile == data->old_profile)
+		task_replace(sd, data->new_profile);
+
+	write_unlock_irqrestore(&sd_lock, flags);
+
+	return 0;
+}
+
+static inline int sd_inbounds(struct sd_ext *e, size_t size)
+{
+	return (e->pos + size <= e->end);
+}
+
+/**
+ * sdconvert - for codes that have a trailing value, convert that value
+ *             and put it in dest.
+ *             if a code does not have a trailing value nop
+ * @code: type code
+ * @dest: pointer to object to receive the converted value
+ * @src:  pointer to value to convert
+ */
+static void sdconvert(enum sd_code code, void *dest, void *src)
+{
+	switch (code) {
+	case SD_U8:
+		*(u8 *)dest = *(u8 *) src;
+		break;
+	case SD_U16:
+	case SD_NAME:
+	case SD_DYN_STRING:
+		*(u16 *)dest = le16_to_cpu(get_unaligned((u16 *)src));
+		break;
+	case SD_U32:
+	case SD_STATIC_BLOB:
+		*(u32 *)dest = le32_to_cpu(get_unaligned((u32 *)src));
+		break;
+	case SD_U64:
+		*(u64 *)dest = le64_to_cpu(get_unaligned((u64 *)src));
+		break;
+	default:
+		/* nop - all other type codes do not have a trailing value */
+		;
+	}
+}
+
+/**
+ * sd_is_X - check if the next element is of type X and if it is within
+ *           bounds.  If it is put the associated value in data.
+ * @e: 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
+ * 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 sd_is_X(struct sd_ext *e, enum sd_code code, void *data)
+{
+	void *pos = e->pos;
+	int ret = 0;
+	if (!sd_inbounds(e, SD_CODE_BYTE + sdcode_datasize[code]))
+		goto fail;
+	if (code != *(u8 *)e->pos)
+		goto out;
+	e->pos += SD_CODE_BYTE;
+	if (code == SD_NAME) {
+		u16 size;
+		/* name codes are followed by X bytes */
+		size = le16_to_cpu(get_unaligned((u16 *)e->pos));
+		if (!sd_inbounds(e, (size_t) size))
+			goto fail;
+		if (data)
+			*(u16 *)data = size;
+		e->pos += sdcode_datasize[code];
+		ret = 1 + sdcode_datasize[code];
+	} else if (code == SD_DYN_STRING) {
+		u16 size;
+		char *str;
+		/* strings codes are followed by X bytes */
+		size = le16_to_cpu(get_unaligned((u16 *)e->pos));
+		e->pos += sdcode_datasize[code];
+		if (!sd_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 == SD_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 += sdcode_datasize[code];
+		if (!sd_inbounds(e, (size_t) size))
+			goto fail;
+		if (data)
+			memcpy(data, e->pos, (size_t) size);
+		e->pos += size;
+		ret = size;
+	} else {
+		if (data)
+			sdconvert(code, data, e->pos);
+		e->pos += sdcode_datasize[code];
+		ret = 1 + sdcode_datasize[code];
+	}
+out:
+	return ret;
+fail:
+	e->pos = pos;
+	return 0;
+}
+
+/* sd_is_nameX - check is the next element is X, and its tag is 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 sd_is_nameX(struct sd_ext *e, enum sd_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
+	 * SD_NAME tag value is a u16 */
+	if (sd_is_X(e, SD_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;
+	}
+	/* now check if data actually matches */
+	ret = sd_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 SD_READ_X(E, C, D, N) \
+	do { \
+		u32 __ret; \
+		__ret = sd_is_nameX((E), (C), (D), (N)); \
+		if (!__ret) \
+			goto fail; \
+	} while (0)
+
+/**
+ * sd_activate_net_entry - ignores/skips net entries if the they are present
+ * in the data stream.
+ * @e: extent information
+ */
+static inline int sd_activate_net_entry(struct sd_ext *e)
+{
+	SD_READ_X(e, SD_STRUCT, NULL, "ne");
+	SD_READ_X(e, SD_U32, NULL, NULL);
+	SD_READ_X(e, SD_U32, NULL, NULL);
+	SD_READ_X(e, SD_U32, NULL, NULL);
+	SD_READ_X(e, SD_U16, NULL, NULL);
+	SD_READ_X(e, SD_U16, NULL, NULL);
+	SD_READ_X(e, SD_U32, NULL, NULL);
+	SD_READ_X(e, SD_U32, NULL, NULL);
+	SD_READ_X(e, SD_U16, NULL, NULL);
+	SD_READ_X(e, SD_U16, NULL, NULL);
+	/* interface name is optional so just ignore return code */
+	sd_is_nameX(e, SD_DYN_STRING, NULL, NULL);
+	SD_READ_X(e, SD_STRUCTEND, NULL, NULL);
+
+	return 1;
+fail:
+	return 0;
+}
+
+static inline struct sd_entry *sd_activate_file_entry(struct sd_ext *e)
+{
+	struct sd_entry *entry = NULL;
+
+	if (!(entry = alloc_sd_entry()))
+		goto fail;
+
+	SD_READ_X(e, SD_STRUCT, NULL, "fe");
+	SD_READ_X(e, SD_DYN_STRING, &entry->filename, NULL);
+	SD_READ_X(e, SD_U32, &entry->mode, "file.mode");
+	SD_READ_X(e, SD_U32, &entry->entry_type, "file.pattern_type");
+
+	entry->extradata = sdmatch_alloc(entry->entry_type);
+	if (IS_ERR(entry->extradata)) {
+		entry->extradata = NULL;
+		goto fail;
+	}
+
+	if (entry->extradata &&
+	    sdmatch_serialize(entry->extradata, e, sd_is_nameX) != 0) {
+		goto fail;
+	}
+	SD_READ_X(e, SD_STRUCTEND, NULL, NULL);
+
+	switch (entry->entry_type) {
+	case sd_entry_literal:
+		SD_DEBUG("%s: %s [no pattern] mode=0x%x\n",
+			 __FUNCTION__,
+			 entry->filename,
+			 entry->mode);
+		break;
+	case sd_entry_tailglob:
+		SD_DEBUG("%s: %s [tailglob] mode=0x%x\n",
+			 __FUNCTION__,
+			 entry->filename,
+			 entry->mode);
+		break;
+	case sd_entry_pattern:
+		SD_DEBUG("%s: %s mode=0x%x\n",
+			 __FUNCTION__,
+			 entry->filename,
+			 entry->mode);
+		break;
+	default:
+		SD_WARN("%s: INVALID entry_type %d\n",
+			__FUNCTION__,
+			(int)entry->entry_type);
+		goto fail;
+	}
+
+	return entry;
+
+fail:
+	sdmatch_free(entry->extradata);
+	free_sd_entry(entry);
+	return NULL;
+}
+
+static inline int check_rule_and_add(struct sd_entry *file_entry,
+				     struct sdprofile *profile,
+				     const char **message)
+{
+	/* verify consistency of x, px, ix, ux for entry against
+	   possible duplicates for this entry */
+	int mode = SD_EXEC_MODIFIER_MASK(file_entry->mode);
+	int i;
+
+	if (mode && !(SD_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 & SD_EXEC_INHERIT)
+		file_entry->mode |= SD_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_SD_FILE_MAX; i++) {
+		if (mode & (1 << i))
+			/* profile->file_entryp[i] initially set to
+			 * NULL in alloc_sdprofile() */
+			list_add(&file_entry->listp[i],
+				 &profile->file_entryp[i]);
+	}
+
+	return 1;
+
+out:
+	free_sd_entry(file_entry);
+	return 0;
+}
+
+#define SD_ENTRY_LIST(NAME) \
+	do { \
+	if (sd_is_nameX(e, SD_LIST, NULL, (NAME))) { \
+		rulename = ""; \
+		error_string = "Invalid file entry"; \
+		while (!sd_is_nameX(e, SD_LISTEND, NULL, NULL)) { \
+			struct sd_entry *file_entry; \
+			file_entry = sd_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)
+
+struct sdprofile *sd_activate_profile(struct sd_ext *e, ssize_t *error)
+{
+	struct sdprofile *profile = NULL;
+	const char *rulename = "";
+	const char *error_string = "Invalid Profile";
+
+	*error = -EPROTO;
+
+	profile = alloc_sdprofile();
+	if (!profile) {
+		error_string = "Could not allocate profile";
+		*error = -ENOMEM;
+		goto fail;
+	}
+
+	/* check that we have the right struct being passed */
+	SD_READ_X(e, SD_STRUCT, NULL, "profile");
+	SD_READ_X(e, SD_DYN_STRING, &profile->name, NULL);
+
+	error_string = "Invalid flags";
+	/* per profile debug flags (debug, complain, audit) */
+	SD_READ_X(e, SD_STRUCT, NULL, "flags");
+	SD_READ_X(e, SD_U32, &(profile->flags.debug), "profile.flags.debug");
+	SD_READ_X(e, SD_U32, &(profile->flags.complain),
+		  "profile.flags.complain");
+	SD_READ_X(e, SD_U32, &(profile->flags.audit), "profile.flags.audit");
+	SD_READ_X(e, SD_STRUCTEND, NULL, NULL);
+
+	error_string = "Invalid capabilities";
+	SD_READ_X(e, SD_U32, &(profile->capabilities), "profile.capabilities");
+
+	/* get the file entries. */
+	SD_ENTRY_LIST("pgent");		/* pcre rules */
+	SD_ENTRY_LIST("sgent");		/* simple globs */
+	SD_ENTRY_LIST("fent");		/* regular file entries */
+
+	/* get the net entries */
+	if (sd_is_nameX(e, SD_LIST, NULL, "net")) {
+		error_string = "Invalid net entry";
+		while (!sd_is_nameX(e, SD_LISTEND, NULL, NULL)) {
+			if (!sd_activate_net_entry(e))
+				goto fail;
+		}
+	}
+	rulename = "";
+
+	/* get subprofiles */
+	if (sd_is_nameX(e, SD_LIST, NULL, "hats")) {
+		error_string = "Invalid profile hat";
+		while (!sd_is_nameX(e, SD_LISTEND, NULL, NULL)) {
+			struct sdprofile *subprofile;
+			subprofile = sd_activate_profile(e, error);
+			if (!subprofile)
+				goto fail;
+			get_sdprofile(subprofile);
+			list_add(&subprofile->list, &profile->sub);
+		}
+	}
+
+	error_string = "Invalid end of profile";
+	SD_READ_X(e, SD_STRUCTEND, NULL, NULL);
+
+	return profile;
+
+fail:
+	SD_WARN("%s: %s %s in profile %s\n", INTERFACE_ID, rulename,
+		error_string, profile && profile->name ? profile->name
+		: "unknown");
+
+	if (profile) {
+		free_sdprofile(profile);
+		profile = NULL;
+	}
+
+	return NULL;
+}
+
+void *sd_activate_top_profile(struct sd_ext *e, ssize_t *error)
+{
+	/* get the interface version */
+	if (!sd_is_nameX(e, SD_U32, &e->version, "version")) {
+		SD_WARN("%s: version missing\n", INTERFACE_ID);
+		*error = -EPROTONOSUPPORT;
+		goto out;
+	}
+
+	/* check that the interface version is currently supported */
+	if (e->version != 2) {
+		SD_WARN("%s: unsupported interface version (%d)\n",
+			INTERFACE_ID, e->version);
+		*error = -EPROTONOSUPPORT;
+		goto out;
+	}
+
+	return sd_activate_profile(e, error);
+out:
+	return NULL;
+}
+
+ssize_t sd_file_prof_add(void *data, size_t size)
+{
+	struct sdprofile *profile = NULL;
+
+	struct sd_ext e = { data, data + size, data };
+	ssize_t error;
+
+	profile = sd_activate_top_profile(&e, &error);
+	if (!profile) {
+		SD_DEBUG("couldn't activate profile\n");
+		return error;
+	}
+
+	if (!sd_profilelist_add(profile)) {
+		SD_WARN("trying to add profile (%s) that already exists.\n",
+			profile->name);
+		free_sdprofile(profile);
+		return -EEXIST;
+	}
+
+	return size;
+}
+
+ssize_t sd_file_prof_repl(void *udata, size_t size)
+{
+	struct sd_taskreplace_data data;
+	struct sd_ext e = { udata, udata + size, udata };
+	ssize_t error;
+
+	data.new_profile = sd_activate_top_profile(&e, &error);
+	if (!data.new_profile) {
+		SD_DEBUG("couldn't activate profile\n");
+		return error;
+	}
+	/* Grab reference to close race window (see comment below) */
+	get_sdprofile(data.new_profile);
+
+	/* Replace the profile on the global profile list.
+	 * This list is used by all new exec's to find the correct profile.
+	 * If there was a previous profile, it is returned, else NULL.
+	 *
+	 * N.B sd_profilelist_replace does not drop the refcnt on
+	 * old_profile when removing it from the global list, otherwise it
+	 * could reach zero and be automatically free'd. We nust manually
+	 * drop it at the end of this function when we are finished with it.
+	 */
+	data.old_profile = sd_profilelist_replace(data.new_profile);
+
+	/* RACE window here.
+	 * At this point another task could preempt us trying to replace
+	 * the SAME profile. If it makes it to this point,  it has removed
+	 * the original tasks new_profile from the global list and holds a
+	 * reference of 1 to it in it's old_profile.  If the new task
+	 * reaches the end of the function it will put old_profile causing
+	 * the profile to be deleted.
+	 * When the original task is rescheduled it will continue calling
+	 * sd_subdomainlist_iterate relabelling tasks with a profile
+	 * which points to free'd memory.
+	 */
+
+	/* 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) {
+		SD_DEBUG("%s: try to replace profile (%p)%s\n",
+			 __FUNCTION__,
+			 data.old_profile,
+			 data.old_profile->name);
+
+		sd_subdomainlist_iterate(taskreplace_iter, (void *)&data);
+
+		/* it's off global list, and we are done replacing */
+		put_sdprofile(data.old_profile);
+	}
+
+	/* Free reference obtained above */
+	put_sdprofile(data.new_profile);
+
+	return size;
+}
+
+ssize_t sd_file_prof_remove(const char *name, size_t size)
+{
+	struct sdprofile *old_profile;
+
+	/* if the old profile exists it will be removed from the list and
+	 * a reference is returned.
+	 */
+	old_profile = sd_profilelist_remove(name);
+
+	if (old_profile) {
+		/* remove profile from any tasks using it */
+		sd_subdomainlist_iterate(taskremove_iter, (void *)old_profile);
+
+		/* drop reference obtained by sd_profilelist_remove */
+		put_sdprofile(old_profile);
+	} else {
+		SD_WARN("%s: trying to remove profile (%s) that "
+			"doesn't exist - skipping.\n", __FUNCTION__, name);
+		return -ENOENT;
+	}
+
+	return size;
+}
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/module_interface.h linux-2.6.x/security/apparmor/module_interface.h
--- linux-2.6.x.orig/security/apparmor/module_interface.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/module_interface.h	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,37 @@
+#ifndef __MODULEINTERFACE_H
+#define __MODULEINTERFACE_H
+
+/* Codes of the types of basic structures that are understood */
+#define SD_CODE_BYTE (sizeof(u8))
+#define INTERFACE_ID "INTERFACE"
+
+#define SUBDOMAIN_INTERFACE_VERSION 2
+
+enum sd_code {
+	SD_U8,
+	SD_U16,
+	SD_U32,
+	SD_U64,
+	SD_NAME,	/* same as string except it is items name */
+	SD_DYN_STRING,
+	SD_STATIC_BLOB,
+	SD_STRUCT,
+	SD_STRUCTEND,
+	SD_LIST,
+	SD_LISTEND,
+	SD_OFFSET,
+	SD_BAD
+};
+
+/* sd_ext tracks the kernel buffer and read position in it.  The interface
+ * data is copied into a kernel buffer in subdomainfs and then handed off to
+ * the activate routines.
+ */
+struct sd_ext {
+	void *start;
+	void *end;
+	void *pos;	/* pointer to current position in the buffer */
+	u32 version;
+};
+
+#endif /* __MODULEINTERFACE_H */
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/procattr.c linux-2.6.x/security/apparmor/procattr.c
--- linux-2.6.x.orig/security/apparmor/procattr.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/procattr.c	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,329 @@
+/*
+ *	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 sd_getprocattr(struct subdomain *sd, char *str, size_t size)
+{
+	int error = -EACCES;	/* default to a perm denied */
+	size_t len;
+
+	if (__sd_is_confined(sd)) {
+		size_t lena, lenm, lenp = 0;
+		const char *enforce_str = " (enforce)";
+		const char *complain_str = " (complain)";
+		const char *mode_str =
+			SUBDOMAIN_COMPLAIN(sd) ? complain_str : enforce_str;
+
+		lenm = strlen(mode_str);
+
+		lena = strlen(sd->active->name);
+
+		len = lena;
+		if (sd->active != sd->profile) {
+			lenp = strlen(sd->profile->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, sd->profile->name, lenp);
+				str += lenp;
+				*str++ = '^';
+			}
+
+			memcpy(str, sd->active->name, lena);
+			str += lena;
+			memcpy(str, mode_str, lenm);
+			str += lenm;
+			*str++ = '\n';
+			error = len;
+		} else {
+			error = -ERANGE;
+		}
+	} else {
+		const char *unconstrained_str = SD_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 {
+			error = -ERANGE;
+		}
+	}
+
+	return error;
+
+}
+int sd_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;
+
+	SD_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) {
+		SD_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) {
+		SD_WARN("%s: Invalid hex magic %s\n",
+			__FUNCTION__,
+			smagic);
+		goto out;
+	}
+
+	hat = tmp + 1;
+
+	if (!*hat)
+		hat = NULL;
+
+	if (!hat && !magic) {
+		SD_WARN("%s: Invalid input, NULL hat and NULL magic\n",
+			__FUNCTION__);
+		goto out;
+	}
+
+	SD_DEBUG("%s: Magic 0x%x Hat '%s'\n",
+		 __FUNCTION__, magic, hat ? hat : NULL);
+
+	write_lock_irqsave(&sd_lock, flags);
+	error = sd_change_hat(hat, magic);
+	write_unlock_irqrestore(&sd_lock, flags);
+
+out:
+	if (token) {
+		memset(token, 0, infosize);
+		kfree(token);
+	}
+
+	return error;
+}
+
+int sd_setprocattr_setprofile(struct task_struct *p, char *profilename,
+			      size_t profilesize)
+{
+	int error = -EINVAL;
+	struct sdprofile *profile;
+	struct subdomain *sd;
+	char *name = NULL;
+	unsigned long flags;
+
+	SD_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, SD_UNCONSTRAINED) == 0)
+		profile = null_profile;
+	else
+		profile = sd_profilelist_find(name);
+
+	if (!profile) {
+		SD_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;
+	}
+
+
+	write_lock_irqsave(&sd_lock, flags);
+
+	sd = SD_SUBDOMAIN(p->security);
+
+	/* switch to unconstrained */
+	if (profile == null_profile) {
+		if (__sd_is_confined(sd)) {
+			SD_WARN("%s: Unconstraining task %s(%d) "
+				"profile %s active %s\n",
+				__FUNCTION__,
+				p->comm, p->pid,
+				sd->profile->name,
+				sd->active->name);
+
+			sd_switch_unconfined(sd);
+		} else {
+			SD_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
+			 */
+			SD_WARN("%s: task %s(%d) has no subdomain\n",
+				__FUNCTION__, p->comm, p->pid);
+
+			/* unlock so we can safely GFP_KERNEL */
+			write_unlock_irqrestore(&sd_lock, flags);
+
+			sd = alloc_subdomain(p);
+			if (!sd) {
+				SD_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_sdprofile(profile);
+
+				goto out;
+			}
+
+			write_lock_irqsave(&sd_lock, flags);
+			if (!p->security) {
+				p->security = sd;
+			} else { /* race */
+				free_subdomain(sd);
+				sd = SD_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_sdprofile */
+			put_sdprofile(profile);
+			profile = sd_profilelist_find(name);
+
+			if (!profile) {
+				/* Race, profile was removed. */
+				write_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.
+		 */
+
+		SD_WARN("%s: Switching task %s(%d) "
+			"profile %s active %s to new profile %s\n",
+			__FUNCTION__,
+			p->comm, p->pid,
+			sd->profile ? sd->profile->name : SD_UNCONSTRAINED,
+			sd->active ? sd->profile->name : SD_UNCONSTRAINED,
+			name);
+
+		sd_switch(sd, profile, profile);
+
+		put_sdprofile(profile); /* drop ref we obtained above
+					 * from sd_profilelist_find
+					 */
+
+		/* Reset magic in case we were in a subhat before
+		 * This is the only case where we zero the magic after
+		 * calling sd_switch
+		 */
+		sd->sd_hat_magic = 0;
+	}
+
+	write_unlock_irqrestore(&sd_lock, flags);
+
+	error = 0;
+
+out:
+	kfree(name);
+
+	return error;
+}
diff -Naur --show-c-function linux-2.6.x.orig/security/apparmor/shared.h linux-2.6.x/security/apparmor/shared.h
--- linux-2.6.x.orig/security/apparmor/shared.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.x/security/apparmor/shared.h	2006-08-04 13:07:32.000000000 -0700
@@ -0,0 +1,47 @@
+/*
+ *	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_SD_FILE_MIN			0
+#define POS_SD_MAY_EXEC			POS_SD_FILE_MIN
+#define POS_SD_MAY_WRITE		(POS_SD_MAY_EXEC + 1)
+#define POS_SD_MAY_READ			(POS_SD_MAY_WRITE + 1)
+/* not used by Subdomain */
+#define POS_SD_MAY_APPEND		(POS_SD_MAY_READ + 1)
+/* end of system offsets */
+
+#define POS_SD_MAY_LINK			(POS_SD_MAY_APPEND + 1)
+#define POS_SD_EXEC_INHERIT		(POS_SD_MAY_LINK + 1)
+#define POS_SD_EXEC_UNCONSTRAINED	(POS_SD_EXEC_INHERIT + 1)
+#define POS_SD_EXEC_PROFILE		(POS_SD_EXEC_UNCONSTRAINED + 1)
+#define POS_SD_EXEC_MMAP		(POS_SD_EXEC_PROFILE + 1)
+#define POS_SD_EXEC_UNSAFE		(POS_SD_EXEC_MMAP + 1)
+#define POS_SD_FILE_MAX			POS_SD_EXEC_UNSAFE
+
+/* Modeled after MAY_READ, MAY_WRITE, MAY_EXEC def'ns */
+#define SD_MAY_EXEC			(0x01 << POS_SD_MAY_EXEC)
+#define SD_MAY_WRITE			(0x01 << POS_SD_MAY_WRITE)
+#define SD_MAY_READ			(0x01 << POS_SD_MAY_READ)
+#define SD_MAY_LINK			(0x01 << POS_SD_MAY_LINK)
+#define SD_EXEC_INHERIT			(0x01 << POS_SD_EXEC_INHERIT)
+#define SD_EXEC_UNCONSTRAINED		(0x01 << POS_SD_EXEC_UNCONSTRAINED)
+#define SD_EXEC_PROFILE			(0x01 << POS_SD_EXEC_PROFILE)
+#define SD_EXEC_MMAP			(0x01 << POS_SD_EXEC_MMAP)
+#define SD_EXEC_UNSAFE			(0x01 << POS_SD_EXEC_UNSAFE)
+
+#define SD_EXEC_MODIFIERS		(SD_EXEC_INHERIT | \
+					 SD_EXEC_UNCONSTRAINED | \
+					 SD_EXEC_PROFILE)
+
+#endif /* _SHARED_H */
openSUSE Build Service is sponsored by