/*
* AppArmor security driver for libvirt
- * Copyright (C) 2009 Canonical Ltd.
+ * Copyright (C) 2009-2010 Canonical Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "virterror_internal.h"
#include "datatypes.h"
#include "uuid.h"
+#include "pci.h"
+#include "hostusb.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"
+/* Data structure to pass to *FileIterate so we have everything we need */
+struct SDPDOP {
+ virSecurityDriverPtr drv;
+ virDomainObjPtr vm;
+};
+
/*
* profile_status returns '-1' on error, '0' if loaded
*
*/
static int
load_profile(virSecurityDriverPtr drv,
- const char *profile, virDomainObjPtr vm,
- const char *fn)
+ const char *profile,
+ virDomainObjPtr vm,
+ const char *fn,
+ bool append)
{
int rc = -1, status, ret;
bool create = true;
};
ret = virExec(argv, NULL, NULL, &child,
pipefd[0], NULL, NULL, VIR_EXEC_NONE);
+ } else if (fn && append) {
+ const char *const argv[] = {
+ VIRT_AA_HELPER, "-p", probe, "-r", "-u", profile, "-F", fn, NULL
+ };
+ ret = virExec(argv, NULL, NULL, &child,
+ pipefd[0], NULL, NULL, VIR_EXEC_NONE);
} else if (fn) {
const char *const argv[] = {
VIRT_AA_HELPER, "-p", probe, "-r", "-u", profile, "-f", fn, NULL
*/
static int
reload_profile(virSecurityDriverPtr drv,
- virDomainObjPtr vm, const char *fn)
+ virDomainObjPtr vm,
+ const char *fn,
+ bool append)
{
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
int rc = -1;
/* Update the profile only if it is loaded */
if (profile_loaded(secdef->imagelabel) >= 0) {
- if (load_profile(drv, secdef->imagelabel, vm, fn) < 0) {
+ if (load_profile(drv, secdef->imagelabel, vm, fn, append) < 0) {
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot update AppArmor profile "
"\'%s\'"),
return rc;
}
+static int
+AppArmorSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED,
+ const char *file, void *opaque)
+{
+ struct SDPDOP *ptr = opaque;
+ virDomainObjPtr vm = ptr->vm;
+
+ if (reload_profile(ptr->drv, vm, file, true) < 0) {
+ const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+ virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot update AppArmor profile "
+ "\'%s\'"),
+ secdef->imagelabel);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+AppArmorSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED,
+ const char *file, void *opaque)
+{
+ struct SDPDOP *ptr = opaque;
+ virDomainObjPtr vm = ptr->vm;
+
+ if (reload_profile(ptr->drv, vm, file, true) < 0) {
+ const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+ virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot update AppArmor profile "
+ "\'%s\'"),
+ secdef->imagelabel);
+ return -1;
+ }
+ return 0;
+}
+
/* Called on libvirtd startup to see if AppArmor is available */
static int
AppArmorSecurityDriverProbe(void)
/* if the profile is not already loaded, then load one */
if (profile_loaded(vm->def->seclabel.label) < 0) {
- if (load_profile(drv, vm->def->seclabel.label, vm, stdin_path) < 0) {
+ if (load_profile(drv, vm->def->seclabel.label, vm, stdin_path,
+ false) < 0) {
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot generate AppArmor profile "
"\'%s\'"), vm->def->seclabel.label);
virDomainObjPtr vm,
virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
{
- return reload_profile(drv, vm, NULL);
+ return reload_profile(drv, vm, NULL, false);
}
/* Called when hotplugging */
/* update the profile only if it is loaded */
if (profile_loaded(secdef->imagelabel) >= 0) {
- if (load_profile(drv, secdef->imagelabel, vm, disk->src) < 0) {
+ if (load_profile(drv, secdef->imagelabel, vm, disk->src,
+ false) < 0) {
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot update AppArmor profile "
"\'%s\'"),
}
static int
-AppArmorSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
+AppArmorSetSecurityHostdevLabel(virSecurityDriverPtr drv,
virDomainObjPtr vm,
- virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
+ virDomainHostdevDefPtr dev)
{
const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
+ struct SDPDOP *ptr;
+ int ret = -1;
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
return 0;
- /* TODO: call load_profile with an update vm->def */
- return 0;
+ if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ return 0;
+
+ if (profile_loaded(secdef->imagelabel) < 0)
+ return 0;
+
+ if (VIR_ALLOC(ptr) < 0)
+ return -1;
+ ptr->drv = drv;
+ ptr->vm = vm;
+
+ switch (dev->source.subsys.type) {
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
+ usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
+ dev->source.subsys.u.usb.device);
+
+ if (!usb)
+ goto done;
+
+ ret = usbDeviceFileIterate(usb, AppArmorSetSecurityUSBLabel, ptr);
+ usbFreeDevice(usb);
+ break;
+ }
+
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
+ pciDevice *pci = pciGetDevice(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)
+ goto done;
+
+ ret = pciDeviceFileIterate(pci, AppArmorSetSecurityPCILabel, ptr);
+ pciFreeDevice(pci);
+ break;
+ }
+
+ default:
+ ret = 0;
+ break;
+ }
+
+done:
+ VIR_FREE(ptr);
+ return ret;
}
+
static int
-AppArmorRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED,
+AppArmorRestoreSecurityHostdevLabel(virSecurityDriverPtr drv,
virDomainObjPtr vm,
virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC)
return 0;
- /* TODO: call load_profile (needs virDomainObjPtr vm) */
- return 0;
+ return reload_profile(drv, vm, NULL, false);
}
static int
virDomainObjPtr vm,
const char *savefile)
{
- return reload_profile(drv, vm, savefile);
+ return reload_profile(drv, vm, savefile, true);
}
virDomainObjPtr vm,
const char *savefile ATTRIBUTE_UNUSED)
{
- return reload_profile(drv, vm, NULL);
+ return reload_profile(drv, vm, NULL, false);
}
virSecurityDriver virAppArmorSecurityDriver = {
/*
* virt-aa-helper: wrapper program used by AppArmor security driver.
- * Copyright (C) 2009 Canonical Ltd.
+ * Copyright (C) 2009-2010 Canonical Ltd.
*
* See COPYING.LIB for the License of this software
*
char *hvm; /* type of hypervisor (eg hvm, xen) */
char *arch; /* machine architecture */
int bits; /* bits in the guest */
- char *newdisk; /* newly added disk */
+ char *newfile; /* newly added file */
+ bool append; /* append to .files instead of rewrite */
} vahControl;
static int
VIR_FREE(ctl->files);
VIR_FREE(ctl->hvm);
VIR_FREE(ctl->arch);
- VIR_FREE(ctl->newdisk);
+ VIR_FREE(ctl->newfile);
return 0;
}
" -a | --add load profile\n"
" -c | --create create profile from template\n"
" -D | --delete unload and delete profile\n"
+ " -f | --add-file <file> add file to profile\n"
+ " -F | --append-file <file> append file to profile\n"
" -r | --replace reload profile\n"
" -R | --remove unload profile\n"
" -h | --help this help\n"
* Update the dynamic files
*/
static int
-update_include_file(const char *include_file, const char *included_files)
+update_include_file(const char *include_file, const char *included_files,
+ bool append)
{
int rc = -1;
- int plen;
+ int plen, flen;
int fd;
char *pcontent = NULL;
+ char *existing = 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;
+ if (virFileExists(include_file)) {
+ flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
+ if (flen < 0)
+ return rc;
+ }
+
+ if (append && virFileExists(include_file)) {
+ if (virAsprintf(&pcontent, "%s%s", existing, included_files) == -1) {
+ vah_error(NULL, 0, "could not allocate memory for profile");
+ goto clean;
+ }
+ } else {
+ if (virAsprintf(&pcontent, "%s%s", warning, included_files) == -1) {
+ vah_error(NULL, 0, "could not allocate memory for profile");
+ goto clean;
+ }
}
plen = strlen(pcontent);
}
/* 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);
+ if (flen > 0 && flen == plen && STREQLEN(existing, pcontent, plen)) {
+ rc = 0;
+ goto clean;
}
/* write the file */
clean:
VIR_FREE(pcontent);
+ VIR_FREE(existing);
return rc;
}
} /* switch */
}
- if (ctl->newdisk)
- if (vah_add_file(&buf, ctl->newdisk, "rw") != 0)
+ if (ctl->newfile)
+ if (vah_add_file(&buf, ctl->newfile, "rw") != 0)
goto clean;
if (virBufferError(&buf)) {
{"dryrun", 0, 0, 'd'},
{"delete", 0, 0, 'D'},
{"add-file", 0, 0, 'f'},
+ {"append-file", 0, 0, 'F'},
{"help", 0, 0, 'h'},
{"replace", 0, 0, 'r'},
{"remove", 0, 0, 'R'},
{0, 0, 0, 0}
};
- while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:", opt,
+ while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:", opt,
&idx)) != -1) {
switch (arg) {
case 'a':
ctl->cmd = 'D';
break;
case 'f':
- if ((ctl->newdisk = strdup(optarg)) == NULL)
+ case 'F':
+ if ((ctl->newfile = strdup(optarg)) == NULL)
vah_error(ctl, 1, "could not allocate memory for disk");
+ ctl->append = arg == 'F';
break;
case 'h':
vah_usage();
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 (ctl->append && ctl->newfile) {
+ if (vah_add_file(&buf, ctl->newfile, "rw") != 0)
+ goto clean;
+ } else {
+ 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)) {
virBufferFreeAndReset(&buf);
vah_info(included_files);
rc = 0;
} else if ((rc = update_include_file(include_file,
- included_files)) != 0)
+ included_files,
+ ctl->append)) != 0)
goto clean;
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,/boot/initrd,g" > "$test_xml"
testme "1" "-r with invalid -f with probing" "-p 1 -r -u $valid_uuid -f $bad_disk" "$test_xml"
testme "1" "-r with invalid -f without probing" "-p 0 -r -u $valid_uuid -f $bad_disk" "$test_xml"
+ testme "1" "-r with invalid -F with probing" "-p 1 -r -u $valid_uuid -F $bad_disk" "$test_xml"
+ testme "1" "-r with invalid -F without probing" "-p 0 -r -u $valid_uuid -F $bad_disk" "$test_xml"
fi
cat "$template_xml" | sed "s,###UUID###,$uuid,g" | sed "s,###DISK###,$disk1</disk>,g" > "$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" > "$test_xml"
+testme "0" "replace (appending 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 (appending 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"