[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] Resubmission #2: [PATCH 2/3] sVirt AppArmor security driver



Attached is an updated patch_2_apparmor_driver_updated.patch and
patch_3_docs_updated.patch. Thanks for the review! These two patches
along with the previous patch_1_reenable-nonfile-labels.patch pass
syntax-check, make check (for the tests I added) and introduce no
regressions over the previous patch. See below for inline comments. In
addition to implenting your suggestions, the patch also now supports
<readonly/> for disks and I defensively quote the pid, monitor and
logfile. I also added ReserveSecurityLabel (a noop) along with stubs
for SetSecurityHostdevLabel() and RestoreSecurityHostdevLabel().

Jamie

On Wed, 30 Sep 2009, Daniel P. Berrange wrote:

> > +static int
> > +profile_status(const char *str, const int check_enforcing)
> > +{
> 
> Since you've already got yourself a 'rc' variable declared & initialized,
> it'd benice to replace the duplicated error paths:
> 
Done (along with others you mentioned).

> > +static int
> > +profile_status_file(const char *str)
> 
> This one should just be   virReportOOMError(NULL);
> 
Done (along with others you mentioned).

> > +static int
> > +load_profile(virConnectPtr conn, const char *profile, virDomainObjPtr vm,
> 
> No need to report an error when the virDomainDef* functions
> fail, since they'll have already done that for you.
> 
Done

> Casting away const multiple times here. THis can be avoided by
> changing 
> 
This is a favorite thing for me to do. :P Done (along with others you
mentioned).

> I find the out-of-order initialization of argv indicies here
> a little confusing. Even though it'd be duplicating a little,
> I think it'd be clearer to have
> 
Done

> I think I'd prefer to see this returning a string allocated on the heap,
> rather than the caller's stack. The whole method could thus be simplified
> down to just
> 
Done

> > +/* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
> > + * mode and 1 if enforcing
> > + */
> > +static int
> > +use_apparmor(void)
> 
> Is this the best way to check if app armour is enabled on a  host ?
> It ought to be possible to use the app armour security driver for
> protecting guests regardless of whether the libvirtd daemon itself
> has a profile surely.
> 
Actually, this is required. Currently, an unconfined process may not
aa_change_profile(), which is why I am very careful here.

> > +if WITH_SECDRIVER_APPARMOR
> > +bin_PROGRAMS += virt-aa-helper
> 
> Since this program is only really intended for libvirt to call
> rather than general admin usage I think it'd be beter to list
> it as    libexec_PROGRAMS, so it ends up in /usr/libexec
> instead of /usr/bin
> 
> I think I'd actually have it in the src/security/ directory,
> since we usually keep libexec programs alongside the driver
> code that's using them, just have tools/ for end user programs
> 
Done and done.

> > +    if (STRNEQLEN(AA_PREFIX, uuid, strlen(AA_PREFIX)))
> > +        return -1;
> 
> This one can just be
> 
>       if (!STRPREFIX(uuid, AA_PREFIX))
>             return -1;
> 
Done

> How about just using  virUUIDParse to check UUID validity, such as
> 
>       char rawuuid[VIR_UUID_BUFLEN];
>       if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
>           return -1;
>       return 0;
> 
Done

> FYI, there'a convenient libc function for checking a string for bad
> characters - strspn, you can use it in this way:
> 
>      if (strspn(name, bad) != strlen(name))
>          return -1;
> 
Ah, missed that one. I actually used strcspn() instead. Done


> > +static int
> > +valid_path(const char *path, const bool readonly)
> 
> More const issues. Rather than manually declaring the array
> size, and indexing manually its safer to initialize the
> array at time of declaration with all the strings.  I'd
> probably go for separate arrays for the read-write vs
> read-only difference
> 
Done

> > +static int
> > +vah_add_file(virBufferPtr buf, const char *path, const char *perms)
> > +
> > +    if (virBufferError(buf)) {
> > +         vah_error(NULL, 0, "failed to allocate file buffer");
> > +         rc = -1;
> > +    }
> 
> It is not really neeccesssar to check virBufferError here. The
> idea is that you can call virBufferVSprintf() as many times
> as you like in a row without any checking, and then only check
> virBufferError at the end when you've finishing writing to the
> buffer.
> 
Done

> > +static int
> > +get_files(vahControl * ctl)
> > +{
> > +    virBuffer uuid_buf = VIR_BUFFER_INITIALIZER;
> 
> Using virBuffer for a single printf here is a little overkill - the
> virAsprintf() function would be more than sufficient
> 
Yeah, at one point I was thinking of doing more here and forgot to go
back to virAsprintf(). Done.

> > +    /* needs patch to hostusb.h to work
> > +    for (i = 0; i < ctl->def->nhostdevs; i++)
> > +        if (ctl->def->hostdevs[i]) {
> > +            virDomainHostdevDefPtr hostdev = ctl->def->hostdevs[i];
> > +            if (hostdev->source.subsys.type ==
> > +                VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
> > +                usbDevice *usb = usbGetDevice(NULL,
> > +                           hostdev->source.subsys.u.usb.bus,
> > +                           hostdev->source.subsys.u.usb.device);
> > +                if (usb == NULL)
> > +                    continue;
> > +                rc = vah_add_file(&buf, usb->path, "rw");
> > +                usbFreeDevice(NULL, usb);
> 
> This is the bit of code that should be changed to use the
> usbDeviceFileIterate() method, since you can't assume there
> is only 1 path which is why we didn't make 'path' public.
> 
Done. virt-aa-helper now does this right, but overall attach-device with
a USB or PCI host device still does not work (need to figure out the best
way to get the hostdev XML to virt-aa-helper, as previously discussed).
Users can adjust the profile/abstraction in the meantime as desired.

> If you remove the virBufferError check from the vah_add_file
> calls, it could just go in here
> 
Done

-- 
Jamie Strandboge             | http://www.canonical.com
diff -Naurp libvirt.orig/configure.in libvirt/configure.in
--- libvirt.orig/configure.in	2009-09-22 12:51:57.000000000 -0500
+++ libvirt/configure.in	2009-10-05 15:22:59.000000000 -0500
@@ -799,6 +799,83 @@ fi
 AM_CONDITIONAL([WITH_SECDRIVER_SELINUX], [test "$with_secdriver_selinux" != "no"])
 
 
+dnl AppArmor
+AC_ARG_WITH([apparmor],
+  [  --with-apparmor        use AppArmor to manage security],
+  [],
+  [with_apparmor=check])
+
+APPARMOR_CFLAGS=
+APPARMOR_LIBS=
+if test "$with_apparmor" != "no"; then
+  old_cflags="$CFLAGS"
+  old_libs="$LIBS"
+  if test "$with_apparmor" = "check"; then
+    AC_CHECK_HEADER([sys/apparmor.h],[],[with_apparmor=no])
+    AC_CHECK_LIB([apparmor], [aa_change_profile],[],[with_apparmor=no])
+    AC_CHECK_LIB([apparmor], [aa_change_hat],[],[with_apparmor=no])
+    if test "$with_apparmor" != "no"; then
+      with_apparmor="yes"
+    fi
+  else
+    fail=0
+    AC_CHECK_HEADER([sys/apparmor.h],[],[fail=1])
+    AC_CHECK_LIB([apparmor], [aa_change_profile],[],[fail=1])
+    AC_CHECK_LIB([apparmor], [aa_change_hat],[],[fail=1])
+    test $fail = 1 &&
+      AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])
+  fi
+  CFLAGS="$old_cflags"
+  LIBS="$old_libs"
+fi
+if test "$with_apparmor" = "yes"; then
+  APPARMOR_LIBS="-lapparmor"
+  AC_DEFINE_UNQUOTED([HAVE_APPARMOR], 1, [whether AppArmor is available for security])
+  AC_DEFINE_UNQUOTED([APPARMOR_DIR], "/etc/apparmor.d", [path to apparmor directory])
+  AC_DEFINE_UNQUOTED([APPARMOR_PROFILES_PATH], "/sys/kernel/security/apparmor/profiles", [path to kernel profiles])
+fi
+AM_CONDITIONAL([HAVE_APPARMOR], [test "$with_apparmor" != "no"])
+AC_SUBST([APPARMOR_CFLAGS])
+AC_SUBST([APPARMOR_LIBS])
+
+
+AC_ARG_WITH([secdriver-apparmor],
+  [  --with-secdriver-apparmor         use AppArmor security driver],
+  [],
+  [with_secdriver_apparmor=check])
+
+if test "$with_apparmor" != "yes" ; then
+  if test "$with_secdriver_apparmor" = "check" ; then
+    with_secdriver_apparmor=no
+  else
+    AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])
+  fi
+else
+  old_cflags="$CFLAGS"
+  old_libs="$LIBS"
+  CFLAGS="$CFLAGS $APPARMOR_CFLAGS"
+  LIBS="$CFLAGS $APPARMOR_LIBS"
+
+  fail=0
+  AC_CHECK_FUNC([change_hat], [], [fail=1])
+  AC_CHECK_FUNC([aa_change_profile], [], [fail=1])
+  CFLAGS="$old_cflags"
+  LIBS="$old_libs"
+
+  if test "$fail" = "1" ; then
+    if test "$with_secdriver_apparmor" = "check" ; then
+      with_secdriver_apparmor=no
+    else
+      AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])
+    fi
+  else
+    with_secdriver_apparmor=yes
+    AC_DEFINE_UNQUOTED([WITH_SECDRIVER_APPARMOR], 1, [whether AppArmor security driver is available])
+  fi
+fi
+AM_CONDITIONAL([WITH_SECDRIVER_APPARMOR], [test "$with_secdriver_apparmor" != "no"])
+
+
 
 dnl NUMA lib
 AC_ARG_WITH([numactl],
@@ -1743,6 +1820,7 @@ AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Security Drivers])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux])
+AC_MSG_NOTICE([ AppArmor: $with_secdriver_apparmor])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Driver Loadable Modules])
 AC_MSG_NOTICE([])
@@ -1790,6 +1868,11 @@ AC_MSG_NOTICE([ selinux: $SELINUX_CFLAGS
 else
 AC_MSG_NOTICE([ selinux: no])
 fi
+if test "$with_apparmor" = "yes" ; then
+AC_MSG_NOTICE([ apparmor: $APPARMOR_CFLAGS $APPARMOR_LIBS])
+else
+AC_MSG_NOTICE([ apparmor: no])
+fi
 if test "$with_numactl" = "yes" ; then
 AC_MSG_NOTICE([ numactl: $NUMACTL_CFLAGS $NUMACTL_LIBS])
 else
diff -Naurp libvirt.orig/po/POTFILES.in libvirt/po/POTFILES.in
--- libvirt.orig/po/POTFILES.in	2009-10-02 11:09:13.000000000 -0500
+++ libvirt/po/POTFILES.in	2009-10-05 15:22:59.000000000 -0500
@@ -30,7 +30,9 @@ src/qemu/qemu_monitor_text.c
 src/remote/remote_driver.c
 src/secret/secret_driver.c
 src/security/security_driver.c
+src/security/security_apparmor.c
 src/security/security_selinux.c
+src/security/virt-aa-helper.c
 src/storage/storage_backend.c
 src/storage/storage_backend_disk.c
 src/storage/storage_backend_fs.c
diff -Naurp libvirt.orig/src/Makefile.am libvirt/src/Makefile.am
--- libvirt.orig/src/Makefile.am	2009-10-02 11:09:13.000000000 -0500
+++ libvirt/src/Makefile.am	2009-10-05 15:22:59.000000000 -0500
@@ -153,6 +153,9 @@ LXC_CONTROLLER_SOURCES =					\
 		lxc/lxc_controller.c				\
 		lxc/veth.c lxc/veth.h
 
+SECURITY_DRIVER_APPARMOR_HELPER_SOURCES =			\
+		security/virt-aa-helper.c
+
 PHYP_DRIVER_SOURCES =						\
 		phyp/phyp_driver.c phyp/phyp_driver.h
 
@@ -238,6 +241,9 @@ SECURITY_DRIVER_SOURCES =					\
 SECURITY_DRIVER_SELINUX_SOURCES =				\
 		security/security_selinux.h security/security_selinux.c
 
+SECURITY_DRIVER_APPARMOR_SOURCES =				\
+		security/security_apparmor.h security/security_apparmor.c
+
 
 NODE_DEVICE_DRIVER_SOURCES =					\
 		node_device/node_device_driver.c node_device/node_device_driver.h
@@ -641,9 +647,15 @@ noinst_LTLIBRARIES += libvirt_driver_sec
 libvirt_la_LIBADD += libvirt_driver_security.la
 libvirt_driver_security_la_CFLAGS = \
 		-I top_srcdir@/src/conf
+libvirt_driver_security_la_LDFLAGS =
 if WITH_SECDRIVER_SELINUX
 libvirt_driver_security_la_SOURCES += $(SECURITY_DRIVER_SELINUX_SOURCES)
 endif
+if WITH_SECDRIVER_APPARMOR
+libvirt_driver_security_la_SOURCES += $(SECURITY_DRIVER_APPARMOR_SOURCES)
+libvirt_driver_security_la_CFLAGS += $(APPARMOR_CFLAGS)
+libvirt_driver_security_la_LDFLAGS += $(APPARMOR_LIBS)
+endif
 
 # Add all conditional sources just in case...
 EXTRA_DIST +=							\
@@ -671,6 +683,7 @@ EXTRA_DIST +=							\
 		$(NODE_DEVICE_DRIVER_HAL_SOURCES)		\
 		$(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)		\
 		$(SECURITY_DRIVER_SELINUX_SOURCES)		\
+		$(SECURITY_DRIVER_APPARMOR_SOURCES)		\
 		$(SECRET_DRIVER_SOURCES)			\
 		$(VBOX_DRIVER_EXTRA_DIST)
 
@@ -795,6 +808,26 @@ endif
 endif
 EXTRA_DIST += $(LXC_CONTROLLER_SOURCES)
 
+if WITH_SECDRIVER_APPARMOR
+if WITH_LIBVIRTD
+libexec_PROGRAMS += virt-aa-helper
+
+virt_aa_helper_SOURCES = $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
+
+virt_aa_helper_LDFLAGS = $(WARN_CFLAGS)
+virt_aa_helper_LDADD =						\
+		$(WARN_CFLAGS)					\
+		$(LIBXML_LIBS)					\
+		@top_srcdir@/gnulib/lib/libgnu.la		\
+		@top_srcdir@/src/libvirt_conf.la		\
+		@top_srcdir@/src/libvirt_util.la
+virt_aa_helper_CFLAGS =						\
+		-I top_srcdir@/src/conf				\
+		-I top_srcdir@/src/security
+endif
+endif
+EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
+
 install-data-local:
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt"
 	$(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/images"
diff -Naurp libvirt.orig/src/security/security_apparmor.c libvirt/src/security/security_apparmor.c
--- libvirt.orig/src/security/security_apparmor.c	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/src/security/security_apparmor.c	2009-10-05 15:23:14.000000000 -0500
@@ -0,0 +1,607 @@
+
+/*
+ * AppArmor security driver for libvirt
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Author:
+ *   Jamie Strandboge <jamie canonical com>
+ *   Based on security_selinux.c by James Morris <jmorris namei org>
+ *
+ * AppArmor security driver.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/apparmor.h>
+#include <errno.h>
+#include <unistd.h>
+#include <wait.h>
+#include <stdbool.h>
+
+#include "internal.h"
+
+#include "security_driver.h"
+#include "security_apparmor.h"
+#include "util.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "uuid.h"
+
+#define VIR_FROM_THIS VIR_FROM_SECURITY
+#define SECURITY_APPARMOR_VOID_DOI      "0"
+#define SECURITY_APPARMOR_NAME          "apparmor"
+#define VIRT_AA_HELPER BINDIR "/virt-aa-helper"
+
+/*
+ * profile_status returns '-1' on error, '0' if loaded
+ *
+ * If check_enforcing is set to '1', then returns '-1' on error, '0' if
+ * loaded in complain mode, and '1' if loaded in enforcing mode.
+ */
+static int
+profile_status(const char *str, const int check_enforcing)
+{
+    char *content = NULL;
+    char *tmp = NULL;
+    char *etmp = NULL;
+    int rc = -1;
+
+    /* create string that is '<str> \0' for accurate matching */
+    if (virAsprintf(&tmp, "%s ", str) == -1)
+        return rc;
+
+    if (check_enforcing != 0) {
+        /* create string that is '<str> (enforce)\0' for accurate matching */
+        if (virAsprintf(&etmp, "%s (enforce)", str) == -1) {
+            VIR_FREE(tmp);
+            return rc;
+        }
+    }
+
+    if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
+        virReportSystemError(NULL, errno,
+                             _("Failed to read AppArmor profiles list "
+                             "\'%s\'"), APPARMOR_PROFILES_PATH);
+        if (check_enforcing != 0)
+            VIR_FREE(etmp);
+        goto clean;
+    }
+
+    if (strstr(content, tmp) != NULL)
+        rc = 0;
+    if (check_enforcing != 0) {
+        if (rc == 0 && strstr(content, etmp) != NULL)
+            rc = 1;                 /* return '1' if loaded and enforcing */
+        VIR_FREE(etmp);
+    }
+
+    VIR_FREE(content);
+  clean:
+    VIR_FREE(tmp);
+
+    return rc;
+}
+
+static int
+profile_loaded(const char *str)
+{
+    return profile_status(str, 0);
+}
+
+/*
+ * profile_status_file returns '-1' on error, '0' if file on disk is in
+ * complain mode and '1' if file on disk is in enforcing mode
+ */
+static int
+profile_status_file(const char *str)
+{
+    char profile[PATH_MAX];
+    char *content = NULL;
+    char *tmp = NULL;
+    int rc = -1;
+    int len;
+
+    if (snprintf(profile, PATH_MAX, "%s/%s", APPARMOR_DIR "/libvirt", str)
+       > PATH_MAX - 1) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               "%s", _("profile name exceeds maximum length"));
+    }
+
+    if (!virFileExists(profile)) {
+        return rc;
+    }
+
+    if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
+        virReportSystemError(NULL, errno,
+                             _("Failed to read \'%s\'"), profile);
+        return rc;
+    }
+
+    /* create string that is ' <str> flags=(complain)\0' */
+    if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
+        virReportOOMError(NULL);
+        goto clean;
+    }
+
+    if (strstr(content, tmp) != NULL)
+        rc = 0;
+    else
+        rc = 1;
+
+    VIR_FREE(tmp);
+  clean:
+    VIR_FREE(content);
+
+    return rc;
+}
+
+/*
+ * load (add) a profile. Will create one if necessary
+ */
+static int
+load_profile(virConnectPtr conn, const char *profile, virDomainObjPtr vm,
+             virDomainDiskDefPtr disk)
+{
+    int rc = -1, status, ret;
+    bool create = true;
+    char *xml = NULL;
+    int pipefd[2];
+    pid_t child;
+
+    if (pipe(pipefd) < -1) {
+        virReportSystemError(conn, errno, "%s", _("unable to create pipe"));
+        return rc;
+    }
+
+    xml = virDomainDefFormat(conn, vm->def, VIR_DOMAIN_XML_SECURE);
+    if (!xml)
+        goto failed;
+
+    if (profile_status_file(profile) >= 0)
+        create = false;
+
+    if (create) {
+        const char *const argv[] = {
+            VIRT_AA_HELPER, "-c", "-u", profile, NULL
+        };
+        ret = virExec(conn, argv, NULL, NULL, &child,
+                      pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
+    } else if (disk && disk->src) {
+        const char *const argv[] = {
+            VIRT_AA_HELPER, "-r", "-u", profile, "-f", disk->src, NULL
+        };
+        ret = virExec(conn, argv, NULL, NULL, &child,
+                      pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
+    } else {
+        const char *const argv[] = {
+            VIRT_AA_HELPER, "-r", "-u", profile, NULL
+        };
+        ret = virExec(conn, argv, NULL, NULL, &child,
+                      pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
+    }
+    if (ret < 0)
+        goto clean;
+
+    /* parent continues here */
+    if (safewrite(pipefd[1], xml, strlen(xml)) < 0) {
+        virReportSystemError(conn, errno, "%s", _("unable to write to pipe"));
+        goto clean;
+    }
+    close(pipefd[1]);
+    rc = 0;
+
+  rewait:
+    if (waitpid(child, &status, 0) != child) {
+        if (errno == EINTR)
+            goto rewait;
+
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("Unexpected exit status from virt-aa-helper "
+                               "%d pid %lu"),
+                               WEXITSTATUS(status), (unsigned long)child);
+        rc = -1;
+    }
+
+  clean:
+    VIR_FREE(xml);
+
+  failed:
+    if (pipefd[0] > 0)
+        close(pipefd[0]);
+    if (pipefd[1] > 0)
+        close(pipefd[1]);
+
+    return rc;
+}
+
+static int
+remove_profile(const char *profile)
+{
+    int rc = -1;
+    const char * const argv[] = {
+        VIRT_AA_HELPER, "-R", "-u", profile, NULL
+    };
+
+    if (virRun(NULL, argv, NULL) == 0)
+        rc = 0;
+
+    return rc;
+}
+
+static char *
+get_profile_name(virConnectPtr conn, virDomainObjPtr vm)
+{
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char *name = NULL;
+
+    virUUIDFormat(vm->def->uuid, uuidstr);
+    if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
+        virReportOOMError(conn);
+        return NULL;
+    }
+
+    return name;
+}
+
+/* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
+ * mode and 1 if enforcing. This is required because at present you cannot
+ * aa_change_profile() from a process that is unconfined.
+ */
+static int
+use_apparmor(void)
+{
+    char libvirt_daemon[PATH_MAX];
+    int rc = -1;
+    ssize_t len = 0;
+
+    if ((len = readlink("/proc/self/exe", libvirt_daemon,
+                        PATH_MAX - 1)) < 0) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               "%s", _("could not find libvirtd"));
+        return rc;
+    }
+    libvirt_daemon[len] = '\0';
+
+    if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
+        return rc;
+
+    return profile_status(libvirt_daemon, 1);
+}
+
+/* Called on libvirtd startup to see if AppArmor is available */
+static int
+AppArmorSecurityDriverProbe(void)
+{
+    char template[PATH_MAX];
+
+    if (use_apparmor() < 0)
+        return SECURITY_DRIVER_DISABLE;
+
+    /* see if template file exists */
+    if (snprintf(template, PATH_MAX, "%s/TEMPLATE",
+                 APPARMOR_DIR "/libvirt") > PATH_MAX - 1) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               "%s", _("template too large"));
+        return SECURITY_DRIVER_DISABLE;
+    }
+
+    if (!virFileExists(template)) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _("template \'%s\' does not exist"), template);
+        return SECURITY_DRIVER_DISABLE;
+    }
+
+    return SECURITY_DRIVER_ENABLE;
+}
+
+/* Security driver initialization. DOI is for 'Domain of Interpretation' and is
+ * currently not used.
+ */
+static int
+AppArmorSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv)
+{
+    virSecurityDriverSetDOI(conn, drv, SECURITY_APPARMOR_VOID_DOI);
+    return 0;
+}
+
+/* Currently called in qemudStartVMDaemon to setup a 'label'. We look for and
+ * use a profile based on the UUID, otherwise create one based on a template.
+ * Keep in mind that this is called on 'start' with RestoreSecurityLabel being
+ * called on shutdown.
+*/
+static int
+AppArmorGenSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
+{
+    int rc = -1;
+    char *profile_name = NULL;
+
+    if ((vm->def->seclabel.label) ||
+        (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               "%s",
+                               _("security label already defined for VM"));
+        return rc;
+    }
+
+    if ((profile_name = get_profile_name(conn, vm)) == NULL)
+        return rc;
+
+    /* if the profile is not already loaded, then load one */
+    if (profile_loaded(profile_name) < 0) {
+        if (load_profile(conn, profile_name, vm, NULL) < 0) {
+            virSecurityReportError(conn, VIR_ERR_ERROR,
+                                   _("cannot generate AppArmor profile "
+                                   "\'%s\'"), profile_name);
+            goto clean;
+        }
+    }
+
+    vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
+    if (!vm->def->seclabel.label) {
+        virReportOOMError(NULL);
+        goto clean;
+    }
+
+    /* set imagelabel the same as label (but we won't use it) */
+    vm->def->seclabel.imagelabel = strndup(profile_name,
+                                           strlen(profile_name));
+    if (!vm->def->seclabel.imagelabel) {
+        virReportOOMError(NULL);
+        goto err;
+    }
+
+    vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
+    if (!vm->def->seclabel.model) {
+        virReportOOMError(conn);
+        goto err;
+    }
+
+    rc = 0;
+    goto clean;
+
+  err:
+    remove_profile(profile_name);
+    VIR_FREE(vm->def->seclabel.label);
+    VIR_FREE(vm->def->seclabel.imagelabel);
+    VIR_FREE(vm->def->seclabel.model);
+
+  clean:
+    VIR_FREE(profile_name);
+
+    return rc;
+}
+
+/* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
+ * running.
+ */
+static int
+AppArmorGetSecurityLabel(virConnectPtr conn,
+                         virDomainObjPtr vm, virSecurityLabelPtr sec)
+{
+    int rc = -1;
+    char *profile_name = NULL;
+
+    if ((profile_name = get_profile_name(conn, vm)) == NULL)
+        return rc;
+
+    if (virStrcpy(sec->label, profile_name,
+        VIR_SECURITY_LABEL_BUFLEN) == NULL) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               "%s", _("error copying profile name"));
+        goto clean;
+    }
+
+    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               "%s", _("error calling profile_status()"));
+        goto clean;
+    }
+    rc = 0;
+
+  clean:
+    VIR_FREE(profile_name);
+
+    return rc;
+}
+
+/* Called on VM shutdown and destroy. See AppArmorGenSecurityLabel (above) for
+ * more details. Currently called via qemudShutdownVMDaemon.
+ */
+static int
+AppArmorRestoreSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int rc = 0;
+
+    if (secdef->imagelabel) {
+        if ((rc = remove_profile(secdef->label)) != 0) {
+            virSecurityReportError(conn, VIR_ERR_ERROR,
+                                   _("could not remove profile for \'%s\'"),
+                                   secdef->label);
+        }
+        VIR_FREE(secdef->model);
+        VIR_FREE(secdef->label);
+        VIR_FREE(secdef->imagelabel);
+    }
+    return rc;
+}
+
+/* Called via virExecWithHook. Output goes to
+ * LOCAL_STATE_DIR/log/libvirt/qemu/<vm name>.log
+ */
+static int
+AppArmorSetSecurityLabel(virConnectPtr conn,
+                         virSecurityDriverPtr drv, virDomainObjPtr vm)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int rc = -1;
+    char *profile_name = NULL;
+
+    if ((profile_name = get_profile_name(conn, vm)) == NULL)
+        return rc;
+
+    if (STRNEQ(drv->name, secdef->model)) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("security label driver mismatch: "
+                               "\'%s\' model configured for domain, but "
+                               "hypervisor driver is \'%s\'."),
+                               secdef->model, drv->name);
+        if (use_apparmor() > 0)
+            goto clean;
+    }
+
+    if (aa_change_profile(profile_name) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("error calling aa_change_profile()"));
+        goto clean;
+    }
+    rc = 0;
+
+  clean:
+    VIR_FREE(profile_name);
+
+    return rc;
+}
+
+
+/* Called when hotplugging */
+static int
+AppArmorRestoreSecurityImageLabel(virConnectPtr conn,
+                                  virDomainObjPtr vm,
+                                  virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int rc = -1;
+    char *profile_name = NULL;
+
+    if (secdef->imagelabel) {
+        if ((profile_name = get_profile_name(conn, vm)) == NULL)
+            return rc;
+
+        /* Update the profile only if it is loaded */
+        if (profile_loaded(secdef->imagelabel) >= 0) {
+            if (load_profile(conn, secdef->imagelabel, vm, NULL) < 0) {
+                virSecurityReportError(conn, VIR_ERR_ERROR,
+                                       _("cannot update AppArmor profile "
+                                       "\'%s\'"),
+                                       secdef->imagelabel);
+                goto clean;
+            }
+        }
+    }
+    rc = 0;
+  clean:
+    VIR_FREE(profile_name);
+
+    return rc;
+}
+
+/* Called when hotplugging */
+static int
+AppArmorSetSecurityImageLabel(virConnectPtr conn,
+                              virDomainObjPtr vm, virDomainDiskDefPtr disk)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int rc = -1;
+    char *profile_name;
+
+    if (!disk->src)
+        return 0;
+
+    if (secdef->imagelabel) {
+        /* if the device doesn't exist, error out */
+        if (!virFileExists(disk->src)) {
+            virSecurityReportError(conn, VIR_ERR_ERROR,
+                                   _("\'%s\' does not exist"), disk->src);
+            return rc;
+        }
+
+        if ((profile_name = get_profile_name(conn, vm)) == NULL)
+            return rc;
+
+        /* update the profile only if it is loaded */
+        if (profile_loaded(secdef->imagelabel) >= 0) {
+            if (load_profile(conn, secdef->imagelabel, vm, disk) < 0) {
+                virSecurityReportError(conn, VIR_ERR_ERROR,
+                                     _("cannot update AppArmor profile "
+                                     "\'%s\'"),
+                                     secdef->imagelabel);
+                goto clean;
+            }
+        }
+    }
+    rc = 0;
+
+  clean:
+    VIR_FREE(profile_name);
+
+    return rc;
+}
+
+static int
+AppArmorSecurityVerify(virConnectPtr conn, virDomainDefPtr def)
+{
+    const virSecurityLabelDefPtr secdef = &def->seclabel;
+
+    if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
+        if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
+            virSecurityReportError(conn, VIR_ERR_XML_ERROR,
+                                   _("Invalid security label \'%s\'"),
+                                   secdef->label);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+AppArmorReserveSecurityLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
+                            virDomainObjPtr vm ATTRIBUTE_UNUSED)
+{
+    /* NOOP. Nothing to reserve with AppArmor */
+    return 0;
+}
+
+static int
+AppArmorSetSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                virDomainObjPtr vm ATTRIBUTE_UNUSED,
+                                virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
+
+{
+    /* TODO: call load_profile with an update vm->def */
+    return 0;
+}
+
+static int
+AppArmorRestoreSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                    virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
+
+{
+    /* TODO: call load_profile (needs virDomainObjPtr vm) */
+    return 0;
+}
+
+virSecurityDriver virAppArmorSecurityDriver = {
+    .name = SECURITY_APPARMOR_NAME,
+    .probe = AppArmorSecurityDriverProbe,
+    .open = AppArmorSecurityDriverOpen,
+    .domainSecurityVerify = AppArmorSecurityVerify,
+    .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
+    .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
+    .domainGenSecurityLabel = AppArmorGenSecurityLabel,
+    .domainReserveSecurityLabel = AppArmorReserveSecurityLabel,
+    .domainGetSecurityLabel = AppArmorGetSecurityLabel,
+    .domainRestoreSecurityLabel = AppArmorRestoreSecurityLabel,
+    .domainSetSecurityLabel = AppArmorSetSecurityLabel,
+    .domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel,
+    .domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel,
+};
diff -Naurp libvirt.orig/src/security/security_apparmor.h libvirt/src/security/security_apparmor.h
--- libvirt.orig/src/security/security_apparmor.h	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/src/security/security_apparmor.h	2009-10-05 15:22:59.000000000 -0500
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Author:
+ *   Jamie Strandboge <jamie canonical com>
+ *
+ */
+#ifndef __VIR_SECURITY_APPARMOR_H__
+#define __VIR_SECURITY_APPARMOR_H__
+
+extern virSecurityDriver virAppArmorSecurityDriver;
+
+#define AA_PREFIX  "libvirt-"
+#define PROFILE_NAME_SIZE  8 + VIR_UUID_STRING_BUFLEN /* AA_PREFIX + uuid */
+#define MAX_FILE_LEN       (1024*1024*10)  /* 10MB limit for sanity check */
+
+#endif /* __VIR_SECURITY_APPARMOR_H__ */
diff -Naurp libvirt.orig/src/security/security_driver.c libvirt/src/security/security_driver.c
--- libvirt.orig/src/security/security_driver.c	2009-09-22 12:51:57.000000000 -0500
+++ libvirt/src/security/security_driver.c	2009-10-05 15:22:59.000000000 -0500
@@ -20,10 +20,17 @@
 #include "security_selinux.h"
 #endif
 
+#ifdef WITH_SECDRIVER_APPARMOR
+#include "security_apparmor.h"
+#endif
+
 static virSecurityDriverPtr security_drivers[] = {
 #ifdef WITH_SECDRIVER_SELINUX
     &virSELinuxSecurityDriver,
 #endif
+#ifdef WITH_SECDRIVER_APPARMOR
+    &virAppArmorSecurityDriver,
+#endif
     NULL
 };
 
diff -Naurp libvirt.orig/src/security/virt-aa-helper.c libvirt/src/security/virt-aa-helper.c
--- libvirt.orig/src/security/virt-aa-helper.c	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/src/security/virt-aa-helper.c	2009-10-05 15:22:59.000000000 -0500
@@ -0,0 +1,1005 @@
+
+/*
+ * virt-aa-helper: wrapper program used by AppArmor security driver.
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Author:
+ *   Jamie Strandboge <jamie canonical com>
+ *
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <sys/utsname.h>
+
+#include "internal.h"
+#include "buf.h"
+#include "util.h"
+#include "memory.h"
+
+#include "security_driver.h"
+#include "security_apparmor.h"
+#include "domain_conf.h"
+#include "xml.h"
+#include "uuid.h"
+#include "hostusb.h"
+#include "pci.h"
+
+static char *progname;
+
+typedef struct {
+    char uuid[PROFILE_NAME_SIZE];       /* UUID of vm */
+    bool dryrun;                /* dry run */
+    char cmd;                   /* 'c'   create
+                                 * 'a'   add (load)
+                                 * 'r'   replace
+                                 * 'R'   remove */
+    char *files;                /* list of files */
+    virDomainDefPtr def;        /* VM definition */
+    virCapsPtr caps;            /* VM capabilities */
+    char *hvm;                  /* type of hypervisor (eg hvm, xen) */
+    int bits;                   /* bits in the guest */
+    char *newdisk;              /* newly added disk */
+} vahControl;
+
+static int
+vahDeinit(vahControl * ctl)
+{
+    if (ctl == NULL)
+        return -1;
+
+    VIR_FREE(ctl->def);
+    if (ctl->caps)
+        virCapabilitiesFree(ctl->caps);
+    free(ctl->files);
+    free(ctl->hvm);
+    free(ctl->newdisk);
+
+    return 0;
+}
+
+/*
+ * Print usage
+ */
+static void
+vah_usage(void)
+{
+    fprintf(stdout, "\n%s [options] [< def.xml]\n\n"
+            "  Options:\n"
+            "    -a | --add                     load profile\n"
+            "    -c | --create                  create profile from template\n"
+            "    -D | --delete                  unload and delete profile\n"
+            "    -r | --replace                 reload profile\n"
+            "    -R | --remove                  unload profile\n"
+            "    -h | --help                    this help\n"
+            "    -u | --uuid <uuid>             uuid (profile name)\n"
+            "    -H | --hvm <hvm>               hypervisor type\n"
+            "    -b | --bits <bits>             architecture bits\n"
+            "\n", progname);
+
+    fprintf(stdout, "This command is intended to be used by libvirtd "
+            "and not used directly.\n");
+    return;
+}
+
+static void
+vah_error(vahControl * ctl, int doexit, const char *str)
+{
+    fprintf(stderr, _("%s: error: %s\n"), progname, str);
+
+    if (doexit) {
+        if (ctl != NULL)
+            vahDeinit(ctl);
+        exit(EXIT_FAILURE);
+    }
+}
+
+static void
+vah_warning(const char *str)
+{
+    fprintf(stderr, _("%s: warning: %s\n"), progname, str);
+}
+
+static void
+vah_info(const char *str)
+{
+    fprintf(stderr, _("%s:\n%s\n"), progname, str);
+}
+
+/*
+ * Replace @oldstr in @orig with @repstr
+ * @len is number of bytes allocated for @orig. Assumes @orig, @oldstr and
+ * @repstr are null terminated
+ */
+static int
+replace_string(char *orig, const size_t len, const char *oldstr,
+               const char *repstr)
+{
+    int idx;
+    char *pos = NULL;
+    char *tmp = NULL;
+
+    if ((pos = strstr(orig, oldstr)) == NULL) {
+        vah_error(NULL, 0, "could not find replacement string");
+        return -1;
+    }
+
+    if (VIR_ALLOC_N(tmp, len) < 0) {
+        vah_error(NULL, 0, "could not allocate memory for string");
+        return -1;
+    }
+    tmp[0] = '\0';
+
+    idx = abs(pos - orig);
+
+    /* copy everything up to oldstr */
+    strncat(tmp, orig, idx);
+
+    /* add the replacement string */
+    if (strlen(tmp) + strlen(repstr) > len - 1) {
+        vah_error(NULL, 0, "not enough space in target buffer");
+        VIR_FREE(tmp);
+        return -1;
+    }
+    strcat(tmp, repstr);
+
+    /* add everything after oldstr */
+    if (strlen(tmp) + strlen(orig) - (idx + strlen(oldstr)) > len - 1) {
+        vah_error(NULL, 0, "not enough space in target buffer");
+        VIR_FREE(tmp);
+        return -1;
+    }
+    strncat(tmp, orig + idx + strlen(oldstr),
+            strlen(orig) - (idx + strlen(oldstr)));
+
+    if (virStrcpy(orig, tmp, len) == NULL) {
+        vah_error(NULL, 0, "error replacing string");
+        VIR_FREE(tmp);
+        return -1;
+    }
+    VIR_FREE(tmp);
+
+    return 0;
+}
+
+/*
+ * run an apparmor_parser command
+ */
+static int
+parserCommand(const char *profile_name, const char cmd)
+{
+    char flag[3];
+    char profile[PATH_MAX];
+
+    if (strchr("arR", cmd) == NULL) {
+        vah_error(NULL, 0, "invalid flag");
+        return -1;
+    }
+
+    snprintf(flag, 3, "-%c", cmd);
+
+    if (snprintf(profile, PATH_MAX, "%s/%s",
+                 APPARMOR_DIR "/libvirt", profile_name) > PATH_MAX - 1) {
+        vah_error(NULL, 0, "profile name exceeds maximum length");
+        return -1;
+    }
+
+    if (!virFileExists(profile)) {
+        vah_error(NULL, 0, "profile does not exist");
+        return -1;
+    } else {
+        const char * const argv[] = {
+            "/sbin/apparmor_parser", flag, profile, NULL
+        };
+        if (virRun(NULL, argv, NULL) != 0) {
+            vah_error(NULL, 0, "failed to run apparmor_parser");
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Update the dynamic files
+ */
+static int
+update_include_file(const char *include_file, const char *included_files)
+{
+    int rc = -1;
+    int plen;
+    int fd;
+    char *pcontent = NULL;
+    const char *warning =
+         "# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
+
+    if (virAsprintf(&pcontent, "%s%s", warning, included_files) == -1) {
+        vah_error(NULL, 0, "could not allocate memory for profile");
+        return rc;
+    }
+
+    plen = strlen(pcontent);
+    if (plen > MAX_FILE_LEN) {
+        vah_error(NULL, 0, "invalid length for new profile");
+        goto clean;
+    }
+
+    /* only update the disk profile if it is different */
+    if (virFileExists(include_file)) {
+        char *existing = NULL;
+        int flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
+        if (flen < 0)
+            goto clean;
+
+        if (flen == plen) {
+            if (STREQLEN(existing, pcontent, plen)) {
+                rc = 0;
+                VIR_FREE(existing);
+                goto clean;
+            }
+        }
+        VIR_FREE(existing);
+    }
+
+    /* write the file */
+    if ((fd = open(include_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
+        vah_error(NULL, 0, "failed to create include file");
+        goto clean;
+    }
+
+    if (safewrite(fd, pcontent, plen) < 0) { /* don't write the '\0' */
+        close(fd);
+        vah_error(NULL, 0, "failed to write to profile");
+        goto clean;
+    }
+
+    if (close(fd) != 0) {
+        vah_error(NULL, 0, "failed to close or write to profile");
+        goto clean;
+    }
+    rc = 0;
+
+  clean:
+    VIR_FREE(pcontent);
+
+    return rc;
+}
+
+/*
+ * Create a profile based on a template
+ */
+static int
+create_profile(const char *profile, const char *profile_name,
+               const char *profile_files)
+{
+    char template[PATH_MAX];
+    char *tcontent = NULL;
+    char *pcontent = NULL;
+    char *replace_name = NULL;
+    char *replace_files = NULL;
+    const char *template_name = "\nprofile LIBVIRT_TEMPLATE";
+    const char *template_end = "\n}";
+    int tlen, plen;
+    int fd;
+    int rc = -1;
+
+    if (virFileExists(profile)) {
+        vah_error(NULL, 0, "profile exists");
+        goto end;
+    }
+
+    if (snprintf(template, PATH_MAX, "%s/TEMPLATE",
+                 APPARMOR_DIR "/libvirt") > PATH_MAX - 1) {
+        vah_error(NULL, 0, "template name exceeds maximum length");
+        goto end;
+    }
+
+    if (!virFileExists(template)) {
+        vah_error(NULL, 0, "template does not exist");
+        goto end;
+    }
+
+    if ((tlen = virFileReadAll(template, MAX_FILE_LEN, &tcontent)) < 0) {
+        vah_error(NULL, 0, "failed to read AppArmor template");
+        goto end;
+    }
+
+    if (strstr(tcontent, template_name) == NULL) {
+        vah_error(NULL, 0, "no replacement string in template");
+        goto clean_tcontent;
+    }
+
+    if (strstr(tcontent, template_end) == NULL) {
+        vah_error(NULL, 0, "no replacement string in template");
+        goto clean_tcontent;
+    }
+
+    /* '\nprofile <profile_name>\0' */
+    if (virAsprintf(&replace_name, "\nprofile %s", profile_name) == -1) {
+        vah_error(NULL, 0, "could not allocate memory for profile name");
+        goto clean_tcontent;
+    }
+
+    /* '\n<profile_files>\n}\0' */
+    if (virAsprintf(&replace_files, "\n%s\n}", profile_files) == -1) {
+        vah_error(NULL, 0, "could not allocate memory for profile files");
+        VIR_FREE(replace_name);
+        goto clean_tcontent;
+    }
+
+    plen = tlen + strlen(replace_name) - strlen(template_name) +
+           strlen(replace_files) - strlen(template_end) + 1;
+    if (plen > MAX_FILE_LEN || plen < tlen) {
+        vah_error(NULL, 0, "invalid length for new profile");
+        goto clean_replace;
+    }
+
+    if (VIR_ALLOC_N(pcontent, plen) < 0) {
+        vah_error(NULL, 0, "could not allocate memory for profile");
+        goto clean_replace;
+    }
+    pcontent[0] = '\0';
+    strcpy(pcontent, tcontent);
+
+    if (replace_string(pcontent, plen, template_name, replace_name) < 0)
+        goto clean_all;
+
+    if (replace_string(pcontent, plen, template_end, replace_files) < 0)
+        goto clean_all;
+
+    /* write the file */
+    if ((fd = open(profile, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1) {
+        vah_error(NULL, 0, "failed to create profile");
+        goto clean_all;
+    }
+
+    if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
+        close(fd);
+        vah_error(NULL, 0, "failed to write to profile");
+        goto clean_all;
+    }
+
+    if (close(fd) != 0) {
+        vah_error(NULL, 0, "failed to close or write to profile");
+        goto clean_all;
+    }
+    rc = 0;
+
+  clean_all:
+    VIR_FREE(pcontent);
+  clean_replace:
+    VIR_FREE(replace_name);
+    VIR_FREE(replace_files);
+  clean_tcontent:
+    VIR_FREE(tcontent);
+  end:
+    return rc;
+}
+
+/*
+ * Load an existing profile
+ */
+static int
+parserLoad(const char *profile_name)
+{
+    return parserCommand(profile_name, 'a');
+}
+
+/*
+ * Remove an existing profile
+ */
+static int
+parserRemove(const char *profile_name)
+{
+    return parserCommand(profile_name, 'R');
+}
+
+/*
+ * Replace an existing profile
+ */
+static int
+parserReplace(const char *profile_name)
+{
+    return parserCommand(profile_name, 'r');
+}
+
+static int
+valid_uuid(const char *uuid)
+{
+    unsigned char rawuuid[VIR_UUID_BUFLEN];
+
+    if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
+        return -1;
+
+    if (!STRPREFIX(uuid, AA_PREFIX))
+        return -1;
+
+    if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
+        return -1;
+
+    return 0;
+}
+
+static int
+valid_name(const char *name)
+{
+    /* just try to filter out any dangerous characters in the name that can be
+     * used to subvert the profile */
+    const char *bad = " /[]*";
+
+    if (strlen(name) == 0 || strlen(name) > PATH_MAX - 1)
+        return -1;
+
+    if (strcspn(name, bad) != strlen(name))
+        return -1;
+
+    return 0;
+}
+
+/* see if one of the strings in arr starts with str */
+static int
+array_starts_with(const char *str, const char * const *arr, const long size)
+{
+    int i;
+    for (i = 0; i < size; i++) {
+        if (strlen(str) < strlen(arr[i]))
+            continue;
+
+        if (STRPREFIX(str, arr[i]))
+            return 0;
+    }
+    return 1;
+}
+
+/*
+ * Don't allow access to special files or restricted paths such as /bin, /sbin,
+ * /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
+ * access to system files which could be used to elevate privileges. This is a
+ * safety measure in case libvirtd is under a restrictive profile and is
+ * subverted and trying to escape confinement.
+ *
+ * Note that we cannot exclude block devices because they are valid devices.
+ * The TEMPLATE file can be adjusted to explicitly disallow these if needed.
+ *
+ * RETURN: -1 on error, 0 if ok, 1 if blocked
+ */
+static int
+valid_path(const char *path, const bool readonly)
+{
+    struct stat sb;
+    int npaths;
+    const char * const restricted[] = {
+        "/bin/",
+        "/etc/",
+        "/lib",
+        "/lost+found/",
+        "/proc/",
+        "/sbin/",
+        "/selinux/",
+        "/sys/",
+        "/usr/bin/",
+        "/usr/lib",
+        "/usr/sbin/",
+        "/usr/share/",
+        "/usr/local/bin/",
+        "/usr/local/etc/",
+        "/usr/local/lib",
+        "/usr/local/sbin/"
+    };
+    /* these paths are ok for readonly, but not read/write */
+    const char * const restricted_rw[] = {
+        "/boot/",
+        "/vmlinuz",
+        "/initrd",
+        "/initrd.img"
+    };
+
+    if (path == NULL || strlen(path) > PATH_MAX - 1) {
+        vah_error(NULL, 0, "bad pathname");
+        return -1;
+    }
+
+    /* Don't allow double quotes, since we use them to quote the filename
+     * and this will confuse the apparmor parser.
+     */
+    if (strchr(path, '"') != NULL)
+        return 1;
+
+    if (!virFileExists(path))
+        vah_warning("path does not exist, skipping file type checks");
+    else {
+        if (stat(path, &sb) == -1)
+            return -1;
+
+        switch (sb.st_mode & S_IFMT) {
+            case S_IFDIR:
+                return 1;
+                break;
+            case S_IFIFO:
+                return 1;
+                break;
+            case S_IFSOCK:
+                return 1;
+                break;
+            default:
+                break;
+        }
+    }
+
+    npaths = sizeof(restricted)/sizeof *(restricted);
+    if (array_starts_with(path, restricted, npaths) == 0)
+        return 1;
+
+    npaths = sizeof(restricted_rw)/sizeof *(restricted_rw);
+    if (!readonly) {
+        if (array_starts_with(path, restricted_rw, npaths) == 0)
+            return 1;
+    }
+
+    return 0;
+}
+
+static int
+get_definition(vahControl * ctl, const char *xmlStr)
+{
+    int rc = -1;
+    struct utsname utsname;
+    virCapsGuestPtr guest;  /* this is freed when caps is freed */
+
+    /*
+     * mock up some capabilities. We don't currently use these explicitly,
+     * but need them for virDomainDefParseString().
+     */
+
+    /* Really, this never fails - look at the man-page. */
+    uname (&utsname);
+
+    /* set some defaults if not specified */
+    if (!ctl->bits)
+        ctl->bits = 32;
+    if (!ctl->hvm)
+        ctl->hvm = strdup("hvm");
+
+    if ((ctl->caps = virCapabilitiesNew(utsname.machine, 1, 1)) == NULL) {
+        vah_error(ctl, 0, "could not allocate memory");
+        goto exit;
+    }
+
+    if ((guest = virCapabilitiesAddGuest(ctl->caps,
+                                         ctl->hvm,
+                                         utsname.machine,
+                                         ctl->bits,
+                                         NULL,
+                                         NULL,
+                                         0,
+                                         NULL)) == NULL) {
+        vah_error(ctl, 0, "could not allocate memory");
+        goto exit;
+    }
+
+    ctl->def = virDomainDefParseString(NULL, ctl->caps, xmlStr, 0);
+    if (ctl->def == NULL) {
+        vah_error(ctl, 0, "could not parse XML");
+        goto exit;
+    }
+
+    if (!ctl->def->name) {
+        vah_error(ctl, 0, "could not find name in XML");
+        goto exit;
+    }
+
+    if (valid_name(ctl->def->name) != 0) {
+        vah_error(ctl, 0, "bad name");
+        goto exit;
+    }
+
+    rc = 0;
+
+  exit:
+    return rc;
+}
+
+static int
+vah_add_file(virBufferPtr buf, const char *path, const char *perms)
+{
+    char *tmp = NULL;
+    int rc = -1;
+    bool readonly = true;
+
+    if (path == NULL)
+        return rc;
+
+    if (virFileExists(path)) {
+        if ((tmp = realpath(path, NULL)) == NULL) {
+            vah_error(NULL, 0, path);
+            vah_error(NULL, 0, "  could not find realpath for disk");
+            return rc;
+        }
+    } else
+        if ((tmp = strdup(path)) == NULL)
+            return rc;
+
+    if (strchr(perms, 'w') != NULL)
+        readonly = false;
+
+    rc = valid_path(tmp, readonly);
+    if (rc != 0) {
+        if (rc > 0) {
+            vah_error(NULL, 0, path);
+            vah_error(NULL, 0, "  skipped restricted file");
+        }
+        goto clean;
+    }
+
+    virBufferVSprintf(buf, "  \"%s\" %s,\n", tmp, perms);
+
+  clean:
+    free(tmp);
+
+    return rc;
+}
+
+static int
+file_iterate_cb(virConnectPtr conn ATTRIBUTE_UNUSED,
+                usbDevice *dev ATTRIBUTE_UNUSED,
+                const char *file, void *opaque)
+{
+    virBufferPtr buf = opaque;
+    return vah_add_file(buf, file, "rw");
+}
+
+static int
+get_files(vahControl * ctl)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int rc = -1;
+    int i;
+    char *uuid;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+    /* verify uuid is same as what we were given on the command line */
+    virUUIDFormat(ctl->def->uuid, uuidstr);
+    if (virAsprintf(&uuid, "%s%s", AA_PREFIX, uuidstr) == -1) {
+        vah_error(ctl, 0, "could not allocate memory");
+        return rc;
+    }
+
+    if (STRNEQ(uuid, ctl->uuid)) {
+        vah_error(ctl, 0, "given uuid does not match XML uuid");
+        goto clean;
+    }
+
+    for (i = 0; i < ctl->def->ndisks; i++)
+        if (ctl->def->disks[i] && ctl->def->disks[i]->src) {
+            int ret;
+
+            if (ctl->def->disks[i]->readonly)
+                ret = vah_add_file(&buf, ctl->def->disks[i]->src, "r");
+            else
+                ret = vah_add_file(&buf, ctl->def->disks[i]->src, "rw");
+
+            if (ret != 0)
+                goto clean;
+        }
+
+    for (i = 0; i < ctl->def->nserials; i++)
+        if (ctl->def->serials[i] && ctl->def->serials[i]->data.file.path)
+            if (vah_add_file(&buf,
+                             ctl->def->serials[i]->data.file.path, "w") != 0)
+                goto clean;
+
+    if (ctl->def->console && ctl->def->console->data.file.path)
+        if (vah_add_file(&buf, ctl->def->console->data.file.path, "w") != 0)
+            goto clean;
+
+    if (ctl->def->os.kernel && ctl->def->os.kernel)
+        if (vah_add_file(&buf, ctl->def->os.kernel, "r") != 0)
+            goto clean;
+
+    if (ctl->def->os.initrd && ctl->def->os.initrd)
+        if (vah_add_file(&buf, ctl->def->os.initrd, "r") != 0)
+            goto clean;
+
+    if (ctl->def->os.loader && ctl->def->os.loader)
+        if (vah_add_file(&buf, ctl->def->os.loader, "r") != 0)
+            goto clean;
+
+    for (i = 0; i < ctl->def->nhostdevs; i++)
+        if (ctl->def->hostdevs[i]) {
+            virDomainHostdevDefPtr dev = ctl->def->hostdevs[i];
+            switch (dev->source.subsys.type) {
+            case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
+                if (dev->source.subsys.u.usb.bus &&
+                    dev->source.subsys.u.usb.device) {
+                    usbDevice *usb = usbGetDevice(NULL,
+                               dev->source.subsys.u.usb.bus,
+                               dev->source.subsys.u.usb.device);
+                    if (usb == NULL)
+                        continue;
+                    rc = usbDeviceFileIterate(NULL, usb,
+                                              file_iterate_cb, &buf);
+                    usbFreeDevice(NULL, usb);
+                    if (rc != 0)
+                        goto clean;
+                    else {
+                        /* TODO: deal with product/vendor better */
+                        rc = 0;
+                    }
+                }
+                break;
+            }
+/* TODO: update so files in /sys are readonly
+            case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
+                pciDevice *pci = pciGetDevice(NULL,
+                           dev->source.subsys.u.pci.domain,
+                           dev->source.subsys.u.pci.bus,
+                           dev->source.subsys.u.pci.slot,
+                           dev->source.subsys.u.pci.function);
+
+                if (pci == NULL)
+                    continue;
+
+                rc = pciDeviceFileIterate(NULL, pci, file_iterate_cb, &buf);
+                pciFreeDevice(NULL, pci);
+
+                break;
+            }
+*/
+            default:
+                rc = 0;
+                break;
+            } /* switch */
+        }
+
+    if (ctl->newdisk)
+        if (vah_add_file(&buf, ctl->newdisk, "rw") != 0)
+            goto clean;
+
+    if (virBufferError(&buf)) {
+         vah_error(NULL, 0, "failed to allocate file buffer");
+         goto clean;
+    }
+
+    rc = 0;
+    ctl->files = virBufferContentAndReset(&buf);
+
+  clean:
+    VIR_FREE(uuid);
+    return rc;
+}
+
+static int
+vahParseArgv(vahControl * ctl, int argc, char **argv)
+{
+    int arg, idx = 0;
+    struct option opt[] = {
+        {"add", 0, 0, 'a'},
+        {"create", 0, 0, 'c'},
+        {"dryrun", 0, 0, 'd'},
+        {"delete", 0, 0, 'D'},
+        {"add-file", 0, 0, 'f'},
+        {"help", 0, 0, 'h'},
+        {"replace", 0, 0, 'r'},
+        {"remove", 0, 0, 'R'},
+        {"uuid", 1, 0, 'u'},
+        {"hvm", 1, 0, 'H'},
+        {"bits", 1, 0, 'b'},
+        {0, 0, 0, 0}
+    };
+    int bits;
+
+    while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:f:", opt,
+            &idx)) != -1) {
+        switch (arg) {
+            case 'a':
+                ctl->cmd = 'a';
+                break;
+            case 'b':
+                bits = atoi(optarg);
+                if (bits == 32 || bits == 64)
+                    ctl->bits = bits;
+                else
+                    vah_error(ctl, 1, "invalid bits (should be 32 or 64)");
+                break;
+            case 'c':
+                ctl->cmd = 'c';
+                break;
+            case 'd':
+                ctl->dryrun = true;
+                break;
+            case 'D':
+                ctl->cmd = 'D';
+                break;
+            case 'f':
+                if ((ctl->newdisk = strdup(optarg)) == NULL)
+                    vah_error(ctl, 1, "could not allocate memory for disk");
+                break;
+            case 'h':
+                vah_usage();
+                exit(EXIT_SUCCESS);
+                break;
+            case 'H':
+                if ((ctl->hvm = strdup(optarg)) == NULL)
+                    vah_error(ctl, 1, "could not allocate memory for hvm");
+                break;
+            case 'r':
+                ctl->cmd = 'r';
+                break;
+            case 'R':
+                ctl->cmd = 'R';
+                break;
+            case 'u':
+                if (strlen(optarg) > PROFILE_NAME_SIZE - 1)
+                    vah_error(ctl, 1, "invalid UUID");
+                if (virStrcpy((char *) ctl->uuid, optarg,
+                    PROFILE_NAME_SIZE) == NULL)
+                    vah_error(ctl, 1, "error copying UUID");
+                break;
+            default:
+                vah_error(ctl, 1, "unsupported option");
+                break;
+        }
+    }
+    if (strchr("acDrR", ctl->cmd) == NULL)
+        vah_error(ctl, 1, "bad command");
+
+    if (valid_uuid(ctl->uuid) != 0)
+        vah_error(ctl, 1, "invalid UUID");
+
+    if (!ctl->cmd) {
+        vah_usage();
+        exit(EXIT_FAILURE);
+    }
+
+    if (ctl->cmd == 'c' || ctl->cmd == 'r') {
+        char *xmlStr = NULL;
+        if (virFileReadLimFD(STDIN_FILENO, MAX_FILE_LEN, &xmlStr) < 0)
+            vah_error(ctl, 1, "could not read xml file");
+
+        if (get_definition(ctl, xmlStr) != 0 || ctl->def == NULL) {
+            VIR_FREE(xmlStr);
+            vah_error(ctl, 1, "could not get VM definition");
+        }
+        VIR_FREE(xmlStr);
+
+        if (get_files(ctl) != 0)
+            vah_error(ctl, 1, "invalid VM definition");
+    }
+    return 0;
+}
+
+
+/*
+ * virt-aa-helper -c -u UUID < file.xml
+ * virt-aa-helper -r -u UUID [-f <file>] < file.xml
+ * virt-aa-helper -a -u UUID
+ * virt-aa-helper -R -u UUID
+ * virt-aa-helper -D -u UUID
+ */
+int
+main(int argc, char **argv)
+{
+    vahControl _ctl, *ctl = &_ctl;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int rc = -1;
+    char profile[PATH_MAX];
+    char include_file[PATH_MAX];
+
+    /* clear the environment */
+    environ = NULL;
+    if (setenv("PATH", "/sbin:/usr/sbin", 1) != 0) {
+        vah_error(ctl, 1, "could not set PATH");
+    }
+    if (setenv("IFS", " \t\n", 1) != 0) {
+        vah_error(ctl, 1, "could not set IFS");
+    }
+
+    if (!(progname = strrchr(argv[0], '/')))
+        progname = argv[0];
+    else
+        progname++;
+
+    memset(ctl, 0, sizeof(vahControl));
+
+    if (vahParseArgv(ctl, argc, argv) != 0)
+        vah_error(ctl, 1, "could not parse arguments");
+
+    if (snprintf(profile, PATH_MAX, "%s/%s",
+                 APPARMOR_DIR "/libvirt", ctl->uuid) > PATH_MAX - 1)
+        vah_error(ctl, 1, "profile name exceeds maximum length");
+
+    if (snprintf(include_file, PATH_MAX, "%s/%s.files",
+                 APPARMOR_DIR "/libvirt", ctl->uuid) > PATH_MAX - 1)
+        vah_error(ctl, 1, "disk profile name exceeds maximum length");
+
+    if (ctl->cmd == 'a')
+        rc = parserLoad(ctl->uuid);
+    else if (ctl->cmd == 'R' || ctl->cmd == 'D') {
+        rc = parserRemove(ctl->uuid);
+        if (ctl->cmd == 'D') {
+            unlink(include_file);
+            unlink(profile);
+        }
+    } else if (ctl->cmd == 'c' || ctl->cmd == 'r') {
+        char *included_files = NULL;
+
+        if (ctl->cmd == 'c' && virFileExists(profile))
+            vah_error(ctl, 1, "profile exists");
+
+        virBufferVSprintf(&buf, "  \"%s/log/libvirt/**/%s.log\" w,\n",
+                          LOCAL_STATE_DIR, ctl->def->name);
+        virBufferVSprintf(&buf, "  \"%s/lib/libvirt/**/%s.monitor\" rw,\n",
+                          LOCAL_STATE_DIR, ctl->def->name);
+        virBufferVSprintf(&buf, "  \"%s/run/libvirt/**/%s.pid\" rwk,\n",
+                          LOCAL_STATE_DIR, ctl->def->name);
+        if (ctl->files)
+            virBufferVSprintf(&buf, "%s", ctl->files);
+
+        if (virBufferError(&buf))
+            vah_error(ctl, 1, "failed to allocate buffer");
+
+        included_files = virBufferContentAndReset(&buf);
+
+        /* (re)create the include file using included_files */
+        if (ctl->dryrun) {
+            vah_info(include_file);
+            vah_info(included_files);
+            rc = 0;
+        } else if ((rc = update_include_file(include_file,
+                                             included_files)) != 0)
+            goto clean;
+
+
+        /* create the profile from TEMPLATE */
+        if (ctl->cmd == 'c') {
+            char *tmp = NULL;
+            if (virAsprintf(&tmp, "  #include <libvirt/%s.files>\n",
+                            ctl->uuid) == -1) {
+                vah_error(ctl, 0, "could not allocate memory");
+                goto clean;
+            }
+
+            if (ctl->dryrun) {
+                vah_info(profile);
+                vah_info(ctl->uuid);
+                vah_info(tmp);
+                rc = 0;
+            } else if ((rc = create_profile(profile, ctl->uuid, tmp)) != 0) {
+                vah_error(ctl, 0, "could not create profile");
+                unlink(include_file);
+            }
+            VIR_FREE(tmp);
+        }
+
+        if (rc == 0 && !ctl->dryrun) {
+            if (ctl->cmd == 'c')
+                rc = parserLoad(ctl->uuid);
+            else
+                rc = parserReplace(ctl->uuid);
+
+            /* cleanup */
+            if (rc != 0) {
+                unlink(include_file);
+                if (ctl->cmd == 'c')
+                    unlink(profile);
+            }
+        }
+      clean:
+        VIR_FREE(included_files);
+    }
+
+    vahDeinit(ctl);
+    exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff -Naurp libvirt.orig/tests/Makefile.am libvirt/tests/Makefile.am
--- libvirt.orig/tests/Makefile.am	2009-09-25 10:50:21.000000000 -0500
+++ libvirt/tests/Makefile.am	2009-10-05 15:22:59.000000000 -0500
@@ -16,6 +16,7 @@ INCLUDES = \
 	$(GNUTLS_CFLAGS) \
 	$(SASL_CFLAGS) \
 	$(SELINUX_CFLAGS) \
+	$(APPARMOR_CFLAGS) \
         -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \
          $(COVERAGE_CFLAGS) \
          $(WARN_CFLAGS)
@@ -31,6 +32,7 @@ LDADDS = \
         $(GNUTLS_LIBS) \
         $(SASL_LIBS) \
         $(SELINUX_LIBS) \
+        $(APPARMOR_LIBS) \
         $(WARN_CFLAGS) \
 	../src/libvirt_test.la \
 	../gnulib/lib/libgnu.la \
@@ -84,6 +86,10 @@ if WITH_SECDRIVER_SELINUX
 noinst_PROGRAMS += seclabeltest
 endif
 
+if WITH_SECDRIVER_APPARMOR
+noinst_PROGRAMS += secaatest
+endif
+
 if WITH_CIL
 noinst_PROGRAMS += object-locking
 endif
@@ -119,6 +125,9 @@ test_scripts +=				\
 	virsh-synopsis
 endif
 
+if WITH_SECDRIVER_APPARMOR
+test_scripts += virt-aa-helper-test
+endif
 EXTRA_DIST += $(test_scripts)
 
 TESTS = virshtest \
@@ -149,6 +158,10 @@ if WITH_SECDRIVER_SELINUX
 TESTS += seclabeltest
 endif
 
+if WITH_SECDRIVER_APPARMOR
+TESTS += secaatest
+endif
+
 if WITH_LIBVIRTD
 noinst_PROGRAMS += eventtest
 TESTS += eventtest
@@ -285,6 +298,14 @@ else
 EXTRA_DIST += seclabeltest.c
 endif
 
+if WITH_SECDRIVER_APPARMOR
+secaatest_SOURCES = \
+	secaatest.c
+secaatest_LDADD = ../src/libvirt_driver_security.la $(LDADDS)
+else
+EXTRA_DIST += secaatest.c
+endif
+
 qparamtest_SOURCES = \
 	qparamtest.c testutils.h testutils.c
 qparamtest_LDADD = $(LDADDS)
diff -Naurp libvirt.orig/tests/secaatest.c libvirt/tests/secaatest.c
--- libvirt.orig/tests/secaatest.c	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/tests/secaatest.c	2009-10-05 15:22:59.000000000 -0500
@@ -0,0 +1,45 @@
+#include <config.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "security/security_driver.h"
+
+int
+main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
+{
+    int ret;
+
+    const char *doi, *model;
+    virSecurityDriverPtr security_drv;
+
+    ret = virSecurityDriverStartup (&security_drv, "apparmor");
+    if (ret == -1)
+    {
+        fprintf (stderr, "Failed to start security driver");
+        exit (-1);
+    }
+    /* No security driver wanted to be enabled: just return */
+    if (ret == -2)
+        return 0;
+
+    model = virSecurityDriverGetModel (security_drv);
+    if (!model)
+    {
+        fprintf (stderr, "Failed to copy secModel model: %s",
+                 strerror (errno));
+        exit (-1);
+    }
+
+    doi = virSecurityDriverGetDOI (security_drv);
+    if (!doi)
+    {
+        fprintf (stderr, "Failed to copy secModel DOI: %s",
+                 strerror (errno));
+        exit (-1);
+    }
+
+    return 0;
+}
diff -Naurp libvirt.orig/tests/virt-aa-helper-test libvirt/tests/virt-aa-helper-test
--- libvirt.orig/tests/virt-aa-helper-test	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/tests/virt-aa-helper-test	2009-10-05 15:22:59.000000000 -0500
@@ -0,0 +1,263 @@
+#!/bin/sh
+set -e
+
+test_hostdev="no"
+if [ "$1" = "test_hostdev" ]; then
+    test_hostdev="yes"
+    shift
+fi
+
+output="/dev/null"
+use_valgrind=""
+ld_library_path="../src/.libs/"
+if [ ! -z "$1" ] && [ "$1" = "-d" ]; then
+    output="/dev/stdout"
+    shift
+fi
+
+exe="../src/virt-aa-helper"
+if [ ! -z "$1" ]; then
+    if [ "$1" = "-v" ]; then
+        use_valgrind="yes"
+        shift
+    fi
+    if [ -n "$1" ]; then
+        exe="$1"
+        shift
+    fi
+fi
+
+if [ ! -x "$exe" ]; then
+    echo "Could not find '$exe'"
+    exit 1
+fi
+
+echo "testing `basename $exe`" >$output
+if [ "$use_valgrind" = "yes" ]; then
+    exe="valgrind --error-exitcode=2 --track-origins=yes $exe"
+fi
+
+extra_args="--dryrun"
+errors=0
+
+tmpdir=`mktemp -d`
+trap "rm -rf $tmpdir" EXIT HUP INT QUIT TERM
+
+template_xml="$tmpdir/template.xml"
+test_xml="$tmpdir/test.xml"
+
+uuid="00000000-0000-0000-0000-0123456789ab"
+disk1="$tmpdir/1.img"
+disk2="$tmpdir/2.img"
+relative_disk1="$tmpdir/./../`basename $tmpdir`//./1.img"
+nonexistent="$tmpdir/nonexistant.img"
+bad_disk="/etc/passwd"
+valid_uuid="libvirt-$uuid"
+nonexistent_uuid="libvirt-00000000-0000-0000-0000-000000000001"
+
+cat > "$template_xml" <<EOM
+<domain type='kvm'>
+  <name>virt-aa-helper-test</name>
+  <uuid>###UUID###</uuid>
+  <memory>524288</memory>
+  <currentMemory>524288</currentMemory>
+  <vcpu>1</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+  </features>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/kvm</emulator>
+    <disk type='file' device='disk'>
+      <source file='###DISK###'/>
+      <target dev='hda' bus='ide'/>
+    </disk>
+    <interface type='network'>
+      <mac address='52:54:00:50:4b:26'/>
+      <source network='default'/>
+      <model type='virtio'/>
+    </interface>
+    <input type='tablet' bus='usb'/>
+    <input type='mouse' bus='ps2'/>
+    <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'/>
+    <video>
+      <model type='cirrus' vram='9216' heads='1'/>
+    </video>
+  </devices>
+</domain>
+EOM
+
+touch "$disk1" "$disk2"
+
+testme() {
+    expected="$1"
+    outstr="$2"
+    args="$3"
+    input=""
+
+    if [ -n "$4" ]; then
+        input="$4"
+        if [ ! -e "$input" ]; then
+            echo "FAIL: could not find $input" >$output
+            echo "FAIL: could not find $input"
+            echo " '$extra_args $args': "
+            errors=$(($errors + 1))
+        fi
+    fi
+
+    echo -n "  $outstr: " >$output
+    echo -n " '$extra_args $args" >$output
+    if [ -n "$input" ]; then
+        echo -n " < $input" >$output
+    fi
+    echo "': " >$output
+    set +e
+    if [ -n "$input" ]; then
+        LD_LIBRARY_PATH="$ld_library_path" $exe $extra_args $args < $input >$output 2>&1
+    else
+        LD_LIBRARY_PATH="$ld_library_path" $exe $extra_args $args >$output 2>&1
+    fi
+    rc="$?"
+    set -e
+    if [ "$rc" = "$expected" ]; then
+        echo "pass" >$output
+    else
+        echo "FAIL: exited with '$rc'" >$output
+        echo "FAIL: exited with '$rc'"
+        echo -n "  $outstr: "
+        echo " '$extra_args $args': "
+        errors=$(($errors + 1))
+        #exit $rc
+    fi
+}
+
+# Expected failures
+echo "Expected failures:" >$output
+testme "1" "invalid arg" "-z"
+testme "1" "invalid case" "-A"
+testme "1" "not enough args" "-c"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
+testme "1" "no -u with -c" "-c" "$test_xml"
+testme "1" "bad uuid (bad digit)" "-c -u libvirt-00000000-0000-0000-0000-00000000000g" "$test_xml"
+testme "1" "bad uuid (too long)" "-c -u ${valid_uuid}abcdef" "$test_xml"
+testme "1" "bad uuid (too short)" "-c -u libvirt-00000000-0000-0000-0000-0123456789a" "$test_xml"
+testme "1" "non-matching uuid" "-c -u libvirt-00000000-0000-0000-0000-00000000000a" "$test_xml"
+testme "1" "missing uuid" "-c -u" "$test_xml"
+testme "1" "no -u with -R" "-R"
+testme "1" "non-existent uuid" "-R -u $nonexistent_uuid"
+testme "1" "no -u with -r" "-r"
+testme "1" "old '-n' option" "-c -n foo -u $valid_uuid" "$test_xml"
+testme "1" "invalid bits" "-c -b 15 -u $valid_uuid" "$test_xml"
+testme "1" "invalid bits2" "-c -b a -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$bad_disk,g" > "$test_xml"
+testme "1" "bad disk" "-c -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$bad_disk,g" | sed "s,</devices>,<disk type='file' device='disk'><source file='$disk2'<target dev='hda' bus='ide'/></disk></devices>,g" > "$test_xml"
+testme "1" "bad disk2" "-c -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<devices>,g" > "$test_xml"
+testme "1" "malformed xml" "-c -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,/boot/initrd,g" > "$test_xml"
+testme "1" "disk in /boot" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,/boot/initrd,g" > "$test_xml"
+testme "1" "-r with invalid -f" "-r -u $valid_uuid -f $bad_disk" "$test_xml"
+
+
+echo "Expected pass:" >$output
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
+testme "0" "create" "-c -u $valid_uuid" "$test_xml"
+testme "0" "create with bits (32)" "-c -b 32 -u $valid_uuid" "$test_xml"
+testme "0" "create with bits (64)" "-c -b 64 -u $valid_uuid" "$test_xml"
+testme "0" "create with hvm" "-c -H hvm -u $valid_uuid" "$test_xml"
+testme "0" "create with hvm and bits" "-c -H hvm --bits 32 -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</disk>,</disk><disk type='file' device='disk'><source file='$disk2'/><target dev='hdb' bus='ide'/></disk>,g" > "$test_xml"
+testme "0" "create multiple disks" "-c -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###',${disk1}'/><readonly,g" > "$test_xml"
+testme "0" "create (readonly)" "-c -u $valid_uuid" "$test_xml"
+
+if [ "$test_hostdev" = "yes" ]; then
+    cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</disk>,</disk><hostdev mode='subsystem' type='usb'><source><address bus='002' device='004'/></source></hostdev>,g" > "$test_xml"
+    testme "0" "create hostdev (USB)" "-c -u $valid_uuid" "$test_xml"
+
+    cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</disk>,</disk><hostdev mode='subsystem' type='pci'><source><address bus='0x00' slot='0x00' function='0x0'/></source></hostdev>,g" > "$test_xml"
+    testme "0" "create hostdev (PCI)" "-c -u $valid_uuid" "$test_xml"
+fi
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$nonexistent,g" > "$test_xml"
+testme "0" "create (non-existent disk)" "-c -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$relative_disk1,g" > "$test_xml"
+testme "0" "create (relative path)" "-c -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk2,g" > "$test_xml"
+testme "0" "replace" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$nonexistent,g" > "$test_xml"
+testme "0" "replace (non-existent disk)" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
+testme "0" "replace (adding disk)" "-r -u $valid_uuid -f $disk2" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" > "$test_xml"
+testme "0" "replace (adding non-existent disk)" "-r -u $valid_uuid -f $nonexistent" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<disk type='block' device='cdrom'><target dev='hdc' bus='ide'/><readonly/></disk></devices>,g" > "$test_xml"
+testme "0" "disk (empty cdrom)" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<serial type='file'><source path='$tmpdir/serial.log'/><target port='0'/></serial></devices>,g" > "$test_xml"
+testme "0" "serial" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<serial type='pty'><target port='0'/></serial></devices>,g" > "$test_xml"
+testme "0" "serial (pty)" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<console type='file'><source path='$tmpdir/console.log'/><target port='0'/></console></devices>,g" > "$test_xml"
+touch "$tmpdir/console.log"
+testme "0" "console" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</devices>,<console type='pty'><target port='0'/></console></devices>,g" > "$test_xml"
+testme "0" "console (pty)" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<kernel>$tmpdir/kernel</kernel></os>,g" > "$test_xml"
+touch "$tmpdir/kernel"
+testme "0" "kernel" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>$tmpdir/initrd</initrd></os>,g" > "$test_xml"
+touch "$tmpdir/initrd"
+testme "0" "initrd" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<kernel>/boot/kernel</kernel></os>,g" > "$test_xml"
+testme "0" "kernel in /boot" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>/boot/initrd</initrd></os>,g" > "$test_xml"
+testme "0" "initrd in /boot" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<kernel>/vmlinuz</kernel></os>,g" > "$test_xml"
+testme "0" "kernel is /vmlinuz" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>/initrd/ramdisk</initrd></os>,g" > "$test_xml"
+testme "0" "initrd is /initrd/ramdisk" "-r -u $valid_uuid" "$test_xml"
+
+cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1,g" | sed "s,</os>,<initrd>/initrd.img</initrd></os>,g" > "$test_xml"
+testme "0" "initrd is /initrd.img" "-r -u $valid_uuid" "$test_xml"
+
+testme "0" "help" "-h"
+
+echo "" >$output
+if [ "$errors" != "0" ]; then
+    echo "FAIL: $errors error(s)" >$output
+    exit 1
+fi
+echo PASS >$output
diff -Naurp libvirt.orig/docs/drvqemu.html.in libvirt/docs/drvqemu.html.in
--- libvirt.orig/docs/drvqemu.html.in	2009-10-05 10:32:51.000000000 -0500
+++ libvirt/docs/drvqemu.html.in	2009-10-05 10:32:07.000000000 -0500
@@ -296,6 +296,73 @@
       file can be used to change the setting to <code>security_driver="none"</code>
     </p>
 
+    <h3><a name="securitysvirtaa">AppArmor sVirt confinement</a></h3>
+
+    <p>
+      When using basic AppArmor protection for the libvirtd daemon and
+      QEMU virtual machines, the intention is to protect the host OS
+      from a compromised virtual machine process. There is no protection
+      between guests.
+    </p>
+
+    <p>
+      The AppArmor sVirt protection for QEMU virtual machines builds on
+      this basic level of protection, to also allow individual guests to
+      be protected from each other.
+    </p>
+
+    <p>
+      In the sVirt model, if a profile is loaded for the libvirtd daemon,
+      then each <code>qemu:///system</code> QEMU virtual machine will have
+      a profile created for it when the virtual machine is started if one
+      does not already exist. This generated profile uses a profile name
+      based on the UUID of the QEMU virtual machine and contains rules
+      allowing access to only the files it needs to run, such as its disks,
+      pid file and log files. Just before the QEMU virtual machine is
+      started, the libvirtd daemon will change into this unique profile,
+      preventing the QEMU process from accessing any file resources that
+      are present in another QEMU process or the host machine.
+    </p>
+
+    <p>
+      The AppArmor sVirt implementation is flexible in that it allows an
+      administrator to customize the template file in
+      <code>/etc/apparmor.d/libvirt/TEMPLATE</code> for site-specific
+      access for all newly created QEMU virtual machines. Also, when a new
+      profile is generated, two files are created:
+      <code>/etc/apparmor.d/libvirt/libvirt-&lt;uuid&gt;</code> and
+      <code>/etc/apparmor.d/libvirt/libvirt-&lt;uuid&gt;.files</code>. The
+      former can be fine-tuned by the administrator to allow custom access
+      for this particular QEMU virtual machine, and the latter will be
+      updated appropriately when required file access changes, such as when
+      a disk is added. This flexibility allows for situations such as
+      having one virtual machine in complain mode with all others in
+      enforce mode.
+    </p>
+
+    <p>
+      While users can define their own AppArmor profile scheme, a typical
+      configuration will include a profile for <code>/usr/sbin/libvirtd</code>,
+      <code>/usr/lib/libvirt/virt-aa-helper</code> (a helper program which the
+      libvirtd daemon uses instead of manipulating AppArmor directly), and
+      an abstraction to be included by <code>/etc/apparmor.d/libvirt/TEMPLATE</code>
+      (typically <code>/etc/apparmor.d/abstractions/libvirt-qemu</code>).
+      An example profile scheme can be found in the examples/apparmor
+      directory of the source distribution.
+    </p>
+
+    <p>
+      If the sVirt security model is active, then the node capabilities
+      XML will include its details. If a virtual machine is currently
+      protected by the security model, then the guest XML will include
+      its assigned profile name. If enabled at compile time, the sVirt
+      security model will be activated if AppArmor is available on the host
+      OS and a profile for the libvirtd daemon is loaded when libvirtd is
+      started. To disable sVirt, and revert to the basic level of AppArmor
+      protection (host protection only), the <code>/etc/libvirt/qemu.conf</code>
+      file can be used to change the setting to <code>security_driver="none"</code>.
+    </p>
+
 
     <h3><a name="securityacl">Cgroups device ACLs</a></h3>
 
diff -Naurp libvirt.orig/examples/apparmor/libvirt-qemu libvirt/examples/apparmor/libvirt-qemu
--- libvirt.orig/examples/apparmor/libvirt-qemu	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/examples/apparmor/libvirt-qemu	2009-10-05 10:32:07.000000000 -0500
@@ -0,0 +1,71 @@
+# Last Modified: Wed Jul  8 09:57:41 2009
+
+  #include <abstractions/base>
+  #include <abstractions/consoles>
+  #include <abstractions/nameservice>
+
+  # required for reading disk images
+  capability dac_override,
+  capability dac_read_search,
+  capability chown,
+
+  network inet stream,
+  network inet6 stream,
+
+  /dev/net/tun rw,
+  /dev/kvm rw,
+  /dev/ptmx rw,
+  /dev/kqemu rw,
+
+  # WARNING: uncommenting these gives the guest direct access to host hardware.
+  # This is required for USB pass through but is a security risk. You have been
+  # warned.
+  #/sys/bus/usb/devices/ r,
+  #/sys/devices/*/*/usb[0-9]*/** r,
+  #/dev/bus/usb/*/[0-9]* rw,
+
+  /usr/share/kvm/** r,
+  /usr/share/qemu/** r,
+  /usr/share/bochs/** r,
+  /usr/share/openbios/** r,
+  /usr/share/openhackware/** r,
+  /usr/share/proll/** r,
+  /usr/share/vgabios/** r,
+
+  # the various binaries
+  /usr/bin/kvm rmix,
+  /usr/bin/qemu rmix,
+  /usr/bin/qemu-system-arm rmix,
+  /usr/bin/qemu-system-cris rmix,
+  /usr/bin/qemu-system-i386 rmix,
+  /usr/bin/qemu-system-m68k rmix,
+  /usr/bin/qemu-system-mips rmix,
+  /usr/bin/qemu-system-mips64 rmix,
+  /usr/bin/qemu-system-mips64el rmix,
+  /usr/bin/qemu-system-mipsel rmix,
+  /usr/bin/qemu-system-ppc rmix,
+  /usr/bin/qemu-system-ppc64 rmix,
+  /usr/bin/qemu-system-ppcemb rmix,
+  /usr/bin/qemu-system-sh4 rmix,
+  /usr/bin/qemu-system-sh4eb rmix,
+  /usr/bin/qemu-system-sparc rmix,
+  /usr/bin/qemu-system-sparc64 rmix,
+  /usr/bin/qemu-system-x86_64 rmix,
+  /usr/bin/qemu-alpha rmix,
+  /usr/bin/qemu-arm rmix,
+  /usr/bin/qemu-armeb rmix,
+  /usr/bin/qemu-cris rmix,
+  /usr/bin/qemu-i386 rmix,
+  /usr/bin/qemu-m68k rmix,
+  /usr/bin/qemu-mips rmix,
+  /usr/bin/qemu-mipsel rmix,
+  /usr/bin/qemu-ppc rmix,
+  /usr/bin/qemu-ppc64 rmix,
+  /usr/bin/qemu-ppc64abi32 rmix,
+  /usr/bin/qemu-sh4 rmix,
+  /usr/bin/qemu-sh4eb rmix,
+  /usr/bin/qemu-sparc rmix,
+  /usr/bin/qemu-sparc64 rmix,
+  /usr/bin/qemu-sparc32plus rmix,
+  /usr/bin/qemu-sparc64 rmix,
+  /usr/bin/qemu-x86_64 rmix,
diff -Naurp libvirt.orig/examples/apparmor/TEMPLATE libvirt/examples/apparmor/TEMPLATE
--- libvirt.orig/examples/apparmor/TEMPLATE	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/examples/apparmor/TEMPLATE	2009-10-05 10:32:07.000000000 -0500
@@ -0,0 +1,9 @@
+#
+# This profile is for the domain whose UUID matches this file.
+#
+
+#include <tunables/global>
+
+profile LIBVIRT_TEMPLATE {
+  #include <abstractions/libvirt-qemu>
+}
diff -Naurp libvirt.orig/examples/apparmor/usr.lib.libvirt.virt-aa-helper libvirt/examples/apparmor/usr.lib.libvirt.virt-aa-helper
--- libvirt.orig/examples/apparmor/usr.lib.libvirt.virt-aa-helper	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/examples/apparmor/usr.lib.libvirt.virt-aa-helper	2009-10-05 10:32:07.000000000 -0500
@@ -0,0 +1,22 @@
+# Last Modified: Mon Jul  06 17:22:37 2009
+#include <tunables/global>
+
+/usr/lib/libvirt/virt-aa-helper {
+  #include <abstractions/base>
+
+  # needed for searching directories
+  capability dac_override,
+  capability dac_read_search,
+
+  # needed for when disk is on a network filesystem
+  network inet,
+
+  deny @{PROC}/[0-9]*/mounts r,
+  @{PROC}/filesystems r,
+
+  /usr/lib/libvirt/virt-aa-helper mr,
+  /sbin/apparmor_parser Ux,
+
+  /etc/apparmor.d/libvirt/* r,
+  /etc/apparmor.d/libvirt/libvirt-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]* rw,
+}
diff -Naurp libvirt.orig/examples/apparmor/usr.sbin.libvirtd libvirt/examples/apparmor/usr.sbin.libvirtd
--- libvirt.orig/examples/apparmor/usr.sbin.libvirtd	1969-12-31 18:00:00.000000000 -0600
+++ libvirt/examples/apparmor/usr.sbin.libvirtd	2009-10-05 10:32:07.000000000 -0500
@@ -0,0 +1,48 @@
+# Last Modified: Wed Sep 23 23:23:58 2009
+#include <tunables/global>
+ {LIBVIRT}="libvirt"
+
+/usr/sbin/libvirtd {
+  #include <abstractions/base>
+
+  capability kill,
+  capability net_admin,
+  capability net_raw,
+  capability setgid,
+  capability sys_admin,
+  capability sys_module,
+  capability sys_ptrace,
+  capability sys_nice,
+  capability sys_chroot,
+  capability setuid,
+  capability dac_override,
+  capability dac_read_search,
+  capability fowner,
+  capability chown,
+  capability setpcap,
+  capability mknod,
+
+  network inet stream,
+
+  # Very lenient profile for libvirtd since we want to first focus on confining
+  # the guests. Guests will have a very restricted profile.
+  /** rwmkl,
+
+  /bin/* Ux,
+  /sbin/* Ux,
+  /usr/bin/* Ux,
+  /usr/sbin/* Ux,
+
+  # force the use of virt-aa-helper
+  audit deny /sbin/apparmor_parser rwxl,
+  audit deny /etc/apparmor.d/libvirt/** wxl,
+  audit deny /sys/kernel/security/apparmor/features rwxl,
+  audit deny /sys/kernel/security/apparmor/matching rwxl,
+  audit deny /sys/kernel/security/apparmor/.* rwxl,
+  /sys/kernel/security/apparmor/profiles r,
+  /usr/lib/libvirt/virt-aa-helper Pxr,
+
+  # allow changing to our UUID-based named profiles
+  change_profile -> @{LIBVIRT}-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*,
+
+}

Attachment: signature.asc
Description: Digital signature


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]