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

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

 



On Tue, 08 Sep 2009, Jamie Strandboge wrote:

> > [PATCH 3]
> > patch_3_security_apparmor.patch:
> > Adds security_apparmor.c, security_apparmor.h, virt-aa-helper.c and
> > updates po/POTFILES.in. virt-aa-helper.c is a new binary which is used
> > exclusively by the AppArmor security driver to manipulate AppArmor.
> > These files compile without warning and pass syntax-check.
> > 

-- 
Jamie Strandboge             | http://www.canonical.com
diff -Nurp ./libvirt.orig/po/POTFILES.in ./libvirt/po/POTFILES.in
--- ./libvirt.orig/po/POTFILES.in	2009-09-08 12:59:59.000000000 -0500
+++ ./libvirt/po/POTFILES.in	2009-09-08 15:32:22.000000000 -0500
@@ -31,6 +31,7 @@ src/qemu_conf.c
 src/qemu_driver.c
 src/remote_internal.c
 src/security.c
+src/security_apparmor.c
 src/security_selinux.c
 src/storage_backend.c
 src/storage_backend_disk.c
@@ -50,6 +51,7 @@ src/uuid.c
 src/vbox/vbox_driver.c
 src/vbox/vbox_tmpl.c
 src/virsh.c
+src/virt-aa-helper.c
 src/virterror.c
 src/xen_inotify.c
 src/xen_internal.c
diff -Nurp ./libvirt.orig/src/security_apparmor.c ./libvirt/src/security_apparmor.c
--- ./libvirt.orig/src/security_apparmor.c	1969-12-31 18:00:00.000000000 -0600
+++ ./libvirt/src/security_apparmor.c	2009-09-08 15:32:42.000000000 -0500
@@ -0,0 +1,625 @@
+
+/*
+ * 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@xxxxxxxxxxxxx>
+ *   Based on security_selinux.c by James Morris <jmorris@xxxxxxxxx>
+ *
+ * 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 "internal.h"
+
+#include "security.h"
+#include "security_apparmor.h"
+#include "util.h"
+#include "memory.h"
+#include "virterror_internal.h"
+#include "datatypes.h"
+
+#define VIR_FROM_THIS VIR_FROM_SECURITY
+#define SECURITY_APPARMOR_VOID_DOI	"0"
+#define SECURITY_APPARMOR_NAME 		"apparmor"
+
+/*
+ * 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 ebuf[1024];
+    char *content = NULL;
+    char *tmp = NULL;
+    char *etmp = NULL;
+    int rc = -1;
+
+    /* create string that is '<str> \0' for accurate matching */
+    if (VIR_ALLOC_N(tmp, strlen(str) + 2) < 0) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _
+                               ("%s: could not allocate memory for string"),
+                               __func__);
+        return rc;
+    }
+    sprintf(tmp, "%s ", str);
+
+    if (check_enforcing != 0) {
+        /* create string that is '<str> (enforce)\0' for accurate matching */
+        if (VIR_ALLOC_N(etmp, strlen(str) + 11) < 0) {
+            virSecurityReportError(NULL, VIR_ERR_ERROR,
+                                   _("%s: could not allocate memory for "
+                                     "string"), __func__);
+            VIR_FREE(tmp);
+            return rc;
+        }
+        sprintf(etmp, "%s (enforce)", str);
+    }
+
+    if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _
+                               ("%s: Failed to read AppArmor profiles list "
+                                "%s: %s"), __func__,
+                               APPARMOR_PROFILES_PATH, virStrerror(errno,
+                                                                   ebuf,
+                                                                   sizeof
+                                                                   ebuf));
+        if (check_enforcing != 0)
+            VIR_FREE(etmp);
+        VIR_FREE(tmp);
+        return rc;
+    }
+
+    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(tmp);
+    VIR_FREE(content);
+
+    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' file on disk is in enforcing mode
+ */
+static int
+profile_status_file(const char *str)
+{
+    char ebuf[1024];
+    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"),
+                               __func__);
+    }
+
+    if (!virFileExists(profile)) {
+        return rc;
+    }
+
+    if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _("%s: Failed to read '%s': %s"),
+                               __func__, profile,
+                               virStrerror(errno, ebuf, sizeof ebuf));
+        return rc;
+    }
+
+    /* create string that is ' <str> flags=(complain)\0' */
+    if (VIR_ALLOC_N(tmp, strlen(str) + 19) < 0) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _
+                               ("%s: could not allocate memory for string"),
+                               __func__);
+        VIR_FREE(content);
+        return rc;
+    }
+    sprintf(tmp, " %s flags=(complain)", str);
+
+    if (strstr(content, tmp) != NULL)
+        rc = 0;
+    else
+        rc = 1;
+
+    VIR_FREE(tmp);
+    VIR_FREE(content);
+
+    return rc;
+}
+
+/*
+ * load (add) a profile. Will create one if necessary. If skip_disk is not
+ * NULL, then don't include the skipped disk.
+ */
+static int
+load_profile(const char *profile, virDomainObjPtr vm,
+             const char *skip_disk)
+{
+    const char **argv;
+    int rc = -1;
+    int offset;
+    size_t i;
+    size_t len;
+    int create;
+    int j;
+
+    create = 1;
+    offset = 6;
+    if (profile_status_file(profile) >= 0)
+        create = 0;
+    len = offset + vm->def->ndisks + 1;
+
+    if (VIR_ALLOC_N(argv, len) < 0) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _("%s: could not allocate memory"),
+                               __func__);
+        return rc;
+    }
+
+    argv[0] = VIRT_AA_HELPER_PATH;
+    argv[2] = (char *) "-u";
+    argv[3] = profile;
+    argv[4] = (char *) "-n";
+    argv[5] = vm->def->name;
+
+    if (create == 0)
+        argv[1] = (char *) "-r";
+    else
+        argv[1] = (char *) "-c";
+
+    /* add disks to argv, skipping skip_disk if it is defined. */
+    j = 0;
+    for (i = 0; i < vm->def->ndisks; i++) {
+        if (vm->def->disks[i]->src == NULL || (skip_disk != NULL &&
+                                               STREQ(skip_disk,
+                                                     vm->def->
+                                                     disks[i]->src))) {
+            continue;
+        }
+        if (offset + j >= len - 1)
+            break;
+        argv[offset + j] = vm->def->disks[i]->src;
+        j++;
+    }
+    argv[offset + j] = NULL;
+
+    if (virRun(NULL, argv, NULL) == 0)
+        rc = 0;
+
+    VIR_FREE(argv);
+    return rc;
+}
+
+static int
+remove_profile(const char *profile)
+{
+    const char *argv[5];
+    int rc = -1;
+
+    argv[0] = VIRT_AA_HELPER_PATH;
+    argv[1] = (char *) "-R";
+    argv[2] = (char *) "-u";
+    argv[3] = profile;
+    argv[4] = NULL;
+
+    if (virRun(NULL, argv, NULL) == 0)
+        rc = 0;
+
+    return rc;
+}
+
+/*
+ * profile_name is buffer to hold name and len is how many bytes in the
+ * buffer
+ */
+static int
+get_profile_name(virConnectPtr conn,
+                 virDomainObjPtr vm, char *profile_name, const size_t len)
+{
+    virDomainPtr dom = NULL;
+    int rc = -1;
+    char *prefix = (char *) "libvirt-";
+
+    if (len < PROFILE_NAME_SIZE) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: profile_name has wrong size"),
+                               __func__);
+        return rc;
+    }
+    profile_name[0] = '\0';
+    strcat(profile_name, prefix);
+
+    /* generate the profile name */
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (!dom) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: could not get domain for VM"),
+                               __func__);
+        return rc;
+    }
+    dom->id = vm->def->id;
+
+    if (virDomainGetUUIDString(dom, &profile_name[strlen(profile_name)]) !=
+        0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: could not find uuid for VM"),
+                               __func__);
+        return rc;
+    }
+
+    return 0;
+}
+
+/* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
+ * mode and 1 if enforcing
+ */
+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: path to libvirt "
+                                 "daemon could not be found"), __func__);
+        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 "
+                                                      "exceeds maximum length"),
+                               __func__);
+        return SECURITY_DRIVER_DISABLE;
+    }
+
+    if (!virFileExists(template)) {
+        virSecurityReportError(NULL, VIR_ERR_ERROR,
+                               _("%s: template '%s' does not exist"),
+                               __func__, 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[PROFILE_NAME_SIZE];
+
+    profile_name[0] = '\0';
+
+    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"),
+                               __func__);
+        return rc;
+    }
+
+    if (get_profile_name(conn, vm, profile_name, sizeof(profile_name)) < 0)
+        return rc;
+
+    /* if the profile is not already loaded, then load one */
+    if (profile_loaded(profile_name) < 0) {
+        if (load_profile(profile_name, vm, NULL) < 0) {
+            virSecurityReportError(conn, VIR_ERR_ERROR,
+                                   _
+                                   ("%s: cannot generate AppArmor profile '%s'"),
+                                   __func__, profile_name);
+            return rc;
+        }
+    }
+
+    vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
+    if (!vm->def->seclabel.label) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _
+                               ("%s: cannot generate AppArmor profile name "
+                                "(label)"), __func__);
+        goto err;
+    }
+
+    /* 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) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _
+                               ("%s: cannot generate AppArmor profile name "
+                                "(imagelabel)"), __func__);
+        goto err;
+    }
+
+    vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
+    if (!vm->def->seclabel.model) {
+        virReportOOMError(conn);
+        goto err;
+    }
+
+    rc = 0;
+    goto done;
+
+  err:
+    remove_profile(profile_name);
+    VIR_FREE(vm->def->seclabel.label);
+    VIR_FREE(vm->def->seclabel.imagelabel);
+    VIR_FREE(vm->def->seclabel.model);
+  done:
+    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[PROFILE_NAME_SIZE];
+
+    profile_name[0] = '\0';
+
+    if (get_profile_name(conn, vm, profile_name, sizeof(profile_name)) < 0)
+        return rc;
+
+    strncpy(sec->label, profile_name, VIR_SECURITY_LABEL_BUFLEN);
+    sec->label[VIR_SECURITY_LABEL_BUFLEN - 1] = '\0';
+
+    if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
+        char ebuf[1024];
+
+        virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                                                      "profile_status(): %s"),
+                               __func__, virStrerror(errno, ebuf,
+                                                     sizeof ebuf));
+        return rc;
+    }
+
+    return 0;
+}
+
+/* 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;
+    char ebuf[1024];
+    int rc = 0;
+
+    if (secdef->imagelabel) {
+        if ((rc = remove_profile(secdef->label)) != 0) {
+            virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: could not "
+                                                          "remove profile for '%s': %s"),
+                                   __func__, secdef->label,
+                                   virStrerror(errno, ebuf, sizeof ebuf));
+        }
+        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;
+    char ebuf[1024];
+    int rc = -1;
+    char profile_name[PROFILE_NAME_SIZE];
+
+    profile_name[0] = '\0';
+
+    if (get_profile_name(conn, vm, profile_name, sizeof(profile_name)) < 0)
+        return rc;
+
+    if (STRNEQ(drv->name, secdef->model)) {
+        virSecurityReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security label driver mismatch: "
+                                 "\'%s\' model configured for domain, but "
+                                 "hypervisor driver is \'%s\'."),
+                               __func__, secdef->model, drv->name);
+        if (use_apparmor() > 0)
+            return rc;
+    }
+
+    if (aa_change_profile(profile_name) < 0) {
+        virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                                                      "aa_change_profile(): %s"),
+                               __func__, virStrerror(errno, ebuf,
+                                                     sizeof ebuf));
+        return rc;
+    }
+
+    return 0;
+}
+
+
+/* Called when hotplugging */
+static int
+AppArmorRestoreSecurityImageLabel(virConnectPtr conn,
+                                  virDomainObjPtr vm,
+                                  virDomainDiskDefPtr disk)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int rc = -1;
+    char profile_name[PROFILE_NAME_SIZE];
+
+    profile_name[0] = '\0';
+
+    if (secdef->imagelabel) {
+        if (get_profile_name(conn, vm, profile_name, sizeof(profile_name))
+            < 0)
+            return rc;
+
+        /* Update the profile only if it is loaded */
+        if (profile_loaded(secdef->imagelabel) >= 0) {
+            if (load_profile(secdef->imagelabel, vm, disk->src) < 0) {
+                virSecurityReportError(conn, VIR_ERR_ERROR,
+                                       _
+                                       ("%s: cannot update AppArmor profile "
+                                        "'%s'"), __func__,
+                                       secdef->imagelabel);
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/* Called when hotplugging */
+static int
+AppArmorSetSecurityImageLabel(virConnectPtr conn,
+                              virDomainObjPtr vm, virDomainDiskDefPtr disk)
+{
+    const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+    int rc = -1;
+    char profile_name[PROFILE_NAME_SIZE];
+
+    profile_name[0] = '\0';
+
+    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: '%s' does not exist"), __func__,
+                                   disk->src);
+            return rc;
+        }
+
+        if (get_profile_name(conn, vm, profile_name, sizeof(profile_name))
+            < 0)
+            return rc;
+
+        /* update the profile only if it is loaded */
+        if (profile_loaded(secdef->imagelabel) >= 0) {
+            if (load_profile(secdef->imagelabel, vm, NULL) < 0) {
+                virSecurityReportError(conn, VIR_ERR_ERROR,
+                                       _
+                                       ("%s: cannot update AppArmor profile "
+                                        "'%s'"), __func__,
+                                       secdef->imagelabel);
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+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;
+}
+
+virSecurityDriver virAppArmorSecurityDriver = {
+    .name = SECURITY_APPARMOR_NAME,
+    .probe = AppArmorSecurityDriverProbe,
+    .open = AppArmorSecurityDriverOpen,
+    .domainSecurityVerify = AppArmorSecurityVerify,
+    .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
+    .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
+    .domainGenSecurityLabel = AppArmorGenSecurityLabel,
+    .domainGetSecurityLabel = AppArmorGetSecurityLabel,
+    .domainRestoreSecurityLabel = AppArmorRestoreSecurityLabel,
+    .domainSetSecurityLabel = AppArmorSetSecurityLabel,
+};
diff -Nurp ./libvirt.orig/src/security_apparmor.h ./libvirt/src/security_apparmor.h
--- ./libvirt.orig/src/security_apparmor.h	1969-12-31 18:00:00.000000000 -0600
+++ ./libvirt/src/security_apparmor.h	2009-09-08 15:32:22.000000000 -0500
@@ -0,0 +1,22 @@
+
+/*
+ * 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@xxxxxxxxxxxxx>
+ *
+ */
+#ifndef __VIR_SECURITY_APPARMOR_H__
+#define __VIR_SECURITY_APPARMOR_H__
+
+extern virSecurityDriver virAppArmorSecurityDriver;
+
+#define PROFILE_NAME_SIZE  8 + VIR_UUID_STRING_BUFLEN   /* libvirt-<uuid> */
+#define MAX_FILE_LEN       (1024*1024*10)  /* 10MB limit for sanity check */
+
+#endif /* __VIR_SECURITY_APPARMOR_H__ */
diff -Nurp ./libvirt.orig/src/virt-aa-helper.c ./libvirt/src/virt-aa-helper.c
--- ./libvirt.orig/src/virt-aa-helper.c	1969-12-31 18:00:00.000000000 -0600
+++ ./libvirt/src/virt-aa-helper.c	2009-09-08 15:32:42.000000000 -0500
@@ -0,0 +1,819 @@
+
+/*
+ * 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@xxxxxxxxxxxxx>
+ *
+ */
+
+#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 "internal.h"
+
+#include "c-ctype.h"
+#include "virterror_internal.h"
+#include "memory.h"
+#include "util.h"
+#include "security.h"
+#include "security_apparmor.h"
+
+static char *progname;
+
+typedef struct {
+    char uuid[PROFILE_NAME_SIZE];       /* UUID of vm */
+    char name[PATH_MAX];        /* name of vm */
+    bool dryrun;                /* dry run */
+    char cmd;                   /* 'c'   create
+                                 * 'a'   add (load)
+                                 * 'r'   replace
+                                 * 'R'   remove */
+    int ndisks;                 /* number of disks */
+    char **disks;               /* list of disks */
+} vahControl;
+
+static int
+vahDeinit(vahControl * ctl)
+{
+    int i;
+
+    if (ctl->disks != NULL) {
+        for (i = 0; i < ctl->ndisks; i++) {
+            if (ctl->disks[i] == NULL)
+                continue;
+            free(ctl->disks[i]);
+        }
+        VIR_FREE(ctl->disks);
+    }
+    return 0;
+}
+
+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)));
+
+    strncpy(orig, tmp, len);
+    orig[len - 1] = '\0';
+    VIR_FREE(tmp);
+
+    return 0;
+}
+
+/*
+ * run an apparmor_parser command
+ */
+static int
+parserCommand(const char *profile_name, const char cmd)
+{
+    const char *argv[4];
+    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;
+    }
+
+    argv[0] = (char *) "/sbin/apparmor_parser";
+    argv[1] = flag;
+    argv[2] = profile;
+    argv[3] = NULL;
+
+    if (virRun(NULL, argv, NULL) != 0) {
+        vah_error(NULL, 0, "failed to run apparmor_parser");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Update the disks file
+ */
+static int
+update_include_file(const char *include_file, const char *included_files)
+{
+    int plen;
+    int fd;
+    char *pcontent = NULL;
+    char *existing = NULL;
+    char *warning = (char *)
+         "# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
+
+    plen = strlen(warning) + strlen(included_files) + 1;
+    if (plen > MAX_FILE_LEN) {
+        vah_error(NULL, 0, "invalid length for new profile");
+        return -1;
+    }
+
+    if (VIR_ALLOC_N(pcontent, plen) < 0) {
+        vah_error(NULL, 0, "could not allocate memory for profile");
+        return -1;
+    }
+    pcontent[0] = '\0';
+
+    if (snprintf(pcontent, plen, "%s%s", warning, included_files) >
+        plen - 1) {
+        vah_error(NULL, 0, "profile content exceeds maximum length");
+        VIR_FREE(pcontent);
+        return -1;
+    }
+
+    /* only update the disk profile if it is different */
+    if (virFileExists(include_file)) {
+        int flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
+        if (flen < 0) {
+            VIR_FREE(pcontent);
+            return -1;
+        }
+        if (flen == plen - 1) {
+            if (STREQLEN(existing, pcontent, plen - 1)) {
+                VIR_FREE(pcontent);
+                VIR_FREE(existing);
+                return 0;
+            }
+        }
+        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 disks profile");
+        VIR_FREE(pcontent);
+        return -1;
+    }
+
+    if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
+        close(fd);
+        vah_error(NULL, 0, "failed to write to profile");
+        VIR_FREE(pcontent);
+        return -1;
+    }
+
+    VIR_FREE(pcontent);
+    if (close(fd) != 0) {
+        vah_error(NULL, 0, "failed to close or write to profile");
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * 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, rnlen, rflen;
+    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,
+                  "failed to find replacement string in template");
+        goto clean_tcontent;
+    }
+
+    if (strstr(tcontent, template_end) == NULL) {
+        vah_error(NULL, 0,
+                  "failed to find replacement string in template");
+        goto clean_tcontent;
+    }
+
+    /* '\nprofile <profile_name>\0' */
+    rnlen = strlen(profile_name) * sizeof(char) + 10;
+    if (VIR_ALLOC_N(replace_name, rnlen) < 0) {
+        vah_error(NULL, 0, "could not allocate memory for profile name");
+        goto clean_tcontent;
+    }
+    if (snprintf(replace_name, rnlen, "\nprofile %s", profile_name) >
+        rnlen - 1) {
+        vah_error(NULL, 0, "profile name exceeds maximum length");
+        VIR_FREE(replace_name);
+        goto clean_tcontent;
+    }
+
+    /* '\n<profile_files>\n}\0' */
+    rflen = strlen(profile_files) * sizeof(char) + 4;
+    if (VIR_ALLOC_N(replace_files, rflen) < 0) {
+        vah_error(NULL, 0, "could not allocate memory for profile disks");
+        VIR_FREE(replace_name);
+        goto clean_tcontent;
+    }
+    if (snprintf(replace_files, rflen, "\n%s\n}", profile_files) >
+        rflen - 1) {
+        vah_error(NULL, 0, "profile disks exceed maximum length");
+        goto clean_replace;
+    }
+
+    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)
+{
+    int i;
+    char *prefix = (char *) "libvirt-";
+
+    if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
+        return -1;
+
+    if (STRNEQLEN(prefix, uuid, strlen(prefix)))
+        return -1;
+
+    for (i = strlen(prefix); i < PROFILE_NAME_SIZE - 1; i++) {
+        if (uuid[i] == '-')
+            continue;
+        if (!c_isxdigit(uuid[i]))
+            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 */
+    char *bad = (char *) " /[]*";
+    int i;
+
+    if (strlen(name) == 0 || strlen(name) > PATH_MAX - 1)
+        return -1;
+
+    for (i = 0; i < strlen(bad); i++)
+        if (strchr(name, bad[i]) != NULL)
+            return -1;
+
+    return 0;
+}
+
+
+/*
+ * Don't allow disks 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.
+ */
+static int
+valid_disk_path(const char *path)
+{
+    struct stat sb;
+    int i;
+    int npaths = 20;
+    char **restricted;
+    int rc = -1;
+
+    if (path == NULL || strlen(path) > PATH_MAX - 1)
+        return rc;
+
+    if (VIR_ALLOC_N(restricted, npaths) < 0) {
+        vah_error(NULL, 0, "could not allocate memory for paths");
+        return rc;
+    }
+
+    restricted[0] = (char *) "/bin/";
+    restricted[1] = (char *) "/boot/";
+    restricted[2] = (char *) "/etc/";
+    restricted[3] = (char *) "/initrd/";
+    restricted[4] = (char *) "/initrd.img";
+    restricted[5] = (char *) "/lib";
+    restricted[6] = (char *) "/lost+found/";
+    restricted[7] = (char *) "/proc/";
+    restricted[8] = (char *) "/sbin/";
+    restricted[9] = (char *) "/selinux/";
+    restricted[10] = (char *) "/sys/";
+    restricted[11] = (char *) "/usr/bin/";
+    restricted[12] = (char *) "/usr/lib";
+    restricted[13] = (char *) "/usr/sbin/";
+    restricted[14] = (char *) "/usr/share/";
+    restricted[15] = (char *) "/usr/local/bin/";
+    restricted[16] = (char *) "/usr/local/etc/";
+    restricted[17] = (char *) "/usr/local/lib";
+    restricted[18] = (char *) "/usr/local/sbin/";
+    restricted[19] = (char *) "/vmlinuz";
+
+    if (stat(path, &sb) == -1)
+        goto end;
+
+    switch (sb.st_mode & S_IFMT) {
+        case S_IFCHR:
+            goto end;
+            break;
+        case S_IFDIR:
+            goto end;
+            break;
+        case S_IFIFO:
+            goto end;
+            break;
+        case S_IFSOCK:
+            goto end;
+            break;
+        default:
+            break;
+    }
+
+    for (i = 0; i < npaths; i++) {
+        if (strlen(path) < strlen(restricted[i]))
+            continue;
+
+        if (STREQLEN(path, restricted[i], strlen(restricted[i]))) {
+            rc = 1;
+            goto end;
+        }
+    }
+    rc = 0;
+
+  end:
+    VIR_FREE(restricted);
+    return rc;
+}
+
+/*
+ * Print usage
+ */
+static void
+vah_usage(void)
+{
+    fprintf(stdout, "\n%s [options] disk1 disk2 ...\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"
+            "    -n | --name <domain name>      domain name\n"
+            "    -u | --uuid <uuid>             uuid (profile name)\n"
+            "\n", progname);
+
+    fprintf(stdout, "This command is intended to be used by libvirtd "
+            "and not used directly.\n");
+    return;
+}
+
+static int
+vahParseArgv(vahControl * ctl, int argc, char **argv)
+{
+    int arg, idx = 0;
+    int i;
+    struct option opt[] = {
+        {"add", 0, 0, 'a'},
+        {"create", 0, 0, 'c'},
+        {"dryrun", 0, 0, 'd'},
+        {"delete", 0, 0, 'D'},
+        {"help", 0, 0, 'h'},
+        {"replace", 0, 0, 'r'},
+        {"remove", 0, 0, 'R'},
+        {"uuid", 1, 0, 'u'},
+        {"name", 1, 0, 'n'},
+        {0, 0, 0, 0}
+    };
+
+    while ((arg = getopt_long(argc, argv, "acdDhrRn:u:", opt, &idx)) != -1) {
+        switch (arg) {
+            case 'a':
+                ctl->cmd = 'a';
+                break;
+            case 'c':
+                ctl->cmd = 'c';
+                break;
+            case 'd':
+                ctl->dryrun = true;
+                break;
+            case 'D':
+                ctl->cmd = 'D';
+                break;
+            case 'h':
+                vah_usage();
+                exit(EXIT_SUCCESS);
+                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");
+                strncpy((char *) ctl->uuid, optarg, PROFILE_NAME_SIZE);
+                ctl->uuid[PROFILE_NAME_SIZE - 1] = '\0';
+                break;
+            case 'n':
+                strncpy((char *) ctl->name, optarg, PATH_MAX);
+                ctl->name[PATH_MAX - 1] = '\0';
+                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 == 'c' || ctl->cmd == 'r') {
+        if (strlen(ctl->name) == 0)
+            vah_error(ctl, 1, "name is required");
+
+        if (valid_name(ctl->name) != 0)
+            vah_error(ctl, 1, "invalid name");
+
+        if (optind >= argc) {
+            ctl->ndisks = 0;
+            vah_warning("profile has 0 disks");
+        } else {
+            ctl->ndisks = 0;
+            for (i = optind; i < argc; i++) {
+                if (virFileExists(argv[i]))
+                    ctl->ndisks++;
+                else
+                    vah_error(NULL, 0, "skipping disk (does not exist)");
+            }
+            if (ctl->ndisks == 0) {
+                vah_warning("profile has 0 disks");
+            } else {
+                if (VIR_ALLOC_N(ctl->disks, ctl->ndisks) < 0)
+                    vah_error(ctl, 1,
+                              "could not allocate memory for disks");
+
+                for (i = 0; i < ctl->ndisks; i++) {
+                    if ((ctl->disks[i] = realpath(argv[optind + i], NULL))
+                        == NULL)
+                        vah_error(ctl, 1, "could not disk, skipping");
+                    if (valid_disk_path(ctl->disks[i]) != 0)
+                        vah_error(ctl, 1, "invalid disk");
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+/*
+ * virt-aa-helper -c -u UUID -n name disk1, disk2, ...
+ * virt-aa-helper -r -u UUID -n name disk1, disk2, ...
+ * 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;
+    int rc = -1;
+    int i, tlen = 0, ilen = 0;
+    char profile[PATH_MAX];
+    char include_file[PATH_MAX];
+    char tmp[3 * PATH_MAX];
+    char *included_files = NULL;
+
+    /* 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') {
+        if (ctl->cmd == 'c' && virFileExists(profile))
+            vah_error(ctl, 1, "profile exists");
+
+        /* build up included_files */
+        for (i = 0; i < ctl->ndisks; i++) {
+            /* '  %s rw,\n' */
+            ilen += strlen(ctl->disks[i]) + strlen("   rw,\n");
+        }
+        ilen += strlen("  /log/libvirt/**/.log w,\n"
+                       "  /lib/libvirt/**/.monitor rw,\n"
+                       "  /run/libvirt/**/.pid rwk,\n");
+        ilen += strlen(LOCAL_STATE_DIR) * 3 + strlen(ctl->name) * 3 + 1;
+
+        if (VIR_ALLOC_N(included_files, ilen) < 0)
+            vah_error(ctl, 1, "could not allocate memory for profile");
+
+        included_files[0] = '\0';
+        tmp[0] = '\0';
+        for (i = 0; i < ctl->ndisks; i++) {
+            if (snprintf(tmp, PATH_MAX, "  %s rw,\n", ctl->disks[i]) >
+                PATH_MAX - 1) {
+                vah_error(ctl, 0, "disk line exceeds maximum length");
+                goto clean;
+            }
+            if (strlen(included_files) + strlen(tmp) > ilen - 1) {
+                vah_error(ctl, 0, "disk lines exceed maximum length");
+                goto clean;
+            }
+            strcat(included_files, tmp);
+        }
+
+        /* While 3*PATH_MAX is not accurate when thinking about PATH_MAX,
+         * it is good enough since the #include line is a relative path
+         * and we are just trying to keep tmp from overflowing here anyway
+         */
+        if (snprintf(tmp, 3 * PATH_MAX,
+                     "  %s/log/libvirt/**/%s.log w,\n"
+                     "  %s/lib/libvirt/**/%s.monitor rw,\n"
+                     "  %s/run/libvirt/**/%s.pid rwk,\n",
+                     LOCAL_STATE_DIR, ctl->name, LOCAL_STATE_DIR, ctl->name,
+                     LOCAL_STATE_DIR, ctl->name) > 3 * PATH_MAX - 1) {
+            vah_error(ctl, 0, "added rules exceed maximum length");
+            goto clean;
+        }
+
+        if (strlen(included_files) + strlen(tmp) > ilen - 1) {
+            vah_error(ctl, 0, "added rules exceed maximum length");
+            goto clean;
+        }
+        strcat(included_files, tmp);
+
+        /* (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') {
+            tlen = strlen("  #include <libvirt/.files>\n");
+            tlen += PROFILE_NAME_SIZE;
+
+            /* While PATH_MAX + 14 is not accurate when thinking about
+             * PATH_MAX, it is good enough since the #include line is a
+             * relative path and we are just trying to keep tmp from
+             * overflowing here anyway
+             */
+            if (snprintf
+                (tmp, PATH_MAX + 14, "  #include <libvirt/%s.files>\n",
+                 ctl->uuid) > PATH_MAX - 13) {
+                vah_error(ctl, 0, "added include exceeds maximum length");
+                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);
+            }
+        }
+
+        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);
+}

Attachment: signature.asc
Description: Digital signature

--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]