3 * AppArmor security driver for libvirt
4 * Copyright (C) 2009 Canonical Ltd.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
12 * Jamie Strandboge <jamie@canonical.com>
13 * Based on security_selinux.c by James Morris <jmorris@namei.org>
15 * AppArmor security driver.
20 #include <sys/types.h>
23 #include <sys/apparmor.h>
31 #include "security_driver.h"
32 #include "security_apparmor.h"
35 #include "virterror_internal.h"
36 #include "datatypes.h"
39 #define VIR_FROM_THIS VIR_FROM_SECURITY
40 #define SECURITY_APPARMOR_VOID_DOI "0"
41 #define SECURITY_APPARMOR_NAME "apparmor"
42 #define VIRT_AA_HELPER BINDIR "/virt-aa-helper"
45 * profile_status returns '-1' on error, '0' if loaded
47 * If check_enforcing is set to '1', then returns '-1' on error, '0' if
48 * loaded in complain mode, and '1' if loaded in enforcing mode.
51 profile_status(const char *str, const int check_enforcing)
58 /* create string that is '<str> \0' for accurate matching */
59 if (virAsprintf(&tmp, "%s ", str) == -1)
62 if (check_enforcing != 0) {
63 /* create string that is '<str> (enforce)\0' for accurate matching */
64 if (virAsprintf(&etmp, "%s (enforce)", str) == -1) {
70 if (virFileReadAll(APPARMOR_PROFILES_PATH, MAX_FILE_LEN, &content) < 0) {
71 virReportSystemError(NULL, errno,
72 _("Failed to read AppArmor profiles list "
73 "\'%s\'"), APPARMOR_PROFILES_PATH);
74 if (check_enforcing != 0)
79 if (strstr(content, tmp) != NULL)
81 if (check_enforcing != 0) {
82 if (rc == 0 && strstr(content, etmp) != NULL)
83 rc = 1; /* return '1' if loaded and enforcing */
95 profile_loaded(const char *str)
97 return profile_status(str, 0);
101 * profile_status_file returns '-1' on error, '0' if file on disk is in
102 * complain mode and '1' if file on disk is in enforcing mode
105 profile_status_file(const char *str)
107 char profile[PATH_MAX];
108 char *content = NULL;
113 if (snprintf(profile, PATH_MAX, "%s/%s", APPARMOR_DIR "/libvirt", str)
115 virSecurityReportError(NULL, VIR_ERR_ERROR,
116 "%s", _("profile name exceeds maximum length"));
119 if (!virFileExists(profile)) {
123 if ((len = virFileReadAll(profile, MAX_FILE_LEN, &content)) < 0) {
124 virReportSystemError(NULL, errno,
125 _("Failed to read \'%s\'"), profile);
129 /* create string that is ' <str> flags=(complain)\0' */
130 if (virAsprintf(&tmp, " %s flags=(complain)", str) == -1) {
131 virReportOOMError(NULL);
135 if (strstr(content, tmp) != NULL)
148 * load (add) a profile. Will create one if necessary
151 load_profile(virConnectPtr conn, const char *profile, virDomainObjPtr vm,
152 virDomainDiskDefPtr disk)
154 int rc = -1, status, ret;
160 if (pipe(pipefd) < -1) {
161 virReportSystemError(conn, errno, "%s", _("unable to create pipe"));
165 xml = virDomainDefFormat(conn, vm->def, VIR_DOMAIN_XML_SECURE);
169 if (profile_status_file(profile) >= 0)
173 const char *const argv[] = {
174 VIRT_AA_HELPER, "-c", "-u", profile, NULL
176 ret = virExec(conn, argv, NULL, NULL, &child,
177 pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
178 } else if (disk && disk->src) {
179 const char *const argv[] = {
180 VIRT_AA_HELPER, "-r", "-u", profile, "-f", disk->src, NULL
182 ret = virExec(conn, argv, NULL, NULL, &child,
183 pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
185 const char *const argv[] = {
186 VIRT_AA_HELPER, "-r", "-u", profile, NULL
188 ret = virExec(conn, argv, NULL, NULL, &child,
189 pipefd[0], NULL, NULL, VIR_EXEC_CLEAR_CAPS);
194 /* parent continues here */
195 if (safewrite(pipefd[1], xml, strlen(xml)) < 0) {
196 virReportSystemError(conn, errno, "%s", _("unable to write to pipe"));
203 if (waitpid(child, &status, 0) != child) {
207 virSecurityReportError(conn, VIR_ERR_ERROR,
208 _("Unexpected exit status from virt-aa-helper "
210 WEXITSTATUS(status), (unsigned long)child);
227 remove_profile(const char *profile)
230 const char * const argv[] = {
231 VIRT_AA_HELPER, "-R", "-u", profile, NULL
234 if (virRun(NULL, argv, NULL) == 0)
241 get_profile_name(virConnectPtr conn, virDomainObjPtr vm)
243 char uuidstr[VIR_UUID_STRING_BUFLEN];
246 virUUIDFormat(vm->def->uuid, uuidstr);
247 if (virAsprintf(&name, "%s%s", AA_PREFIX, uuidstr) < 0) {
248 virReportOOMError(conn);
255 /* returns -1 on error or profile for libvirtd is unconfined, 0 if complain
256 * mode and 1 if enforcing. This is required because at present you cannot
257 * aa_change_profile() from a process that is unconfined.
262 char libvirt_daemon[PATH_MAX];
266 if ((len = readlink("/proc/self/exe", libvirt_daemon,
267 PATH_MAX - 1)) < 0) {
268 virSecurityReportError(NULL, VIR_ERR_ERROR,
269 "%s", _("could not find libvirtd"));
272 libvirt_daemon[len] = '\0';
274 if (access(APPARMOR_PROFILES_PATH, R_OK) != 0)
277 return profile_status(libvirt_daemon, 1);
280 /* Called on libvirtd startup to see if AppArmor is available */
282 AppArmorSecurityDriverProbe(void)
284 char template[PATH_MAX];
286 if (use_apparmor() < 0)
287 return SECURITY_DRIVER_DISABLE;
289 /* see if template file exists */
290 if (snprintf(template, PATH_MAX, "%s/TEMPLATE",
291 APPARMOR_DIR "/libvirt") > PATH_MAX - 1) {
292 virSecurityReportError(NULL, VIR_ERR_ERROR,
293 "%s", _("template too large"));
294 return SECURITY_DRIVER_DISABLE;
297 if (!virFileExists(template)) {
298 virSecurityReportError(NULL, VIR_ERR_ERROR,
299 _("template \'%s\' does not exist"), template);
300 return SECURITY_DRIVER_DISABLE;
303 return SECURITY_DRIVER_ENABLE;
306 /* Security driver initialization. DOI is for 'Domain of Interpretation' and is
307 * currently not used.
310 AppArmorSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv)
312 virSecurityDriverSetDOI(conn, drv, SECURITY_APPARMOR_VOID_DOI);
316 /* Currently called in qemudStartVMDaemon to setup a 'label'. We look for and
317 * use a profile based on the UUID, otherwise create one based on a template.
318 * Keep in mind that this is called on 'start' with RestoreSecurityLabel being
319 * called on shutdown.
322 AppArmorGenSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
325 char *profile_name = NULL;
327 if ((vm->def->seclabel.label) ||
328 (vm->def->seclabel.model) || (vm->def->seclabel.imagelabel)) {
329 virSecurityReportError(conn, VIR_ERR_ERROR,
331 _("security label already defined for VM"));
335 if ((profile_name = get_profile_name(conn, vm)) == NULL)
338 /* if the profile is not already loaded, then load one */
339 if (profile_loaded(profile_name) < 0) {
340 if (load_profile(conn, profile_name, vm, NULL) < 0) {
341 virSecurityReportError(conn, VIR_ERR_ERROR,
342 _("cannot generate AppArmor profile "
343 "\'%s\'"), profile_name);
348 vm->def->seclabel.label = strndup(profile_name, strlen(profile_name));
349 if (!vm->def->seclabel.label) {
350 virReportOOMError(NULL);
354 /* set imagelabel the same as label (but we won't use it) */
355 vm->def->seclabel.imagelabel = strndup(profile_name,
356 strlen(profile_name));
357 if (!vm->def->seclabel.imagelabel) {
358 virReportOOMError(NULL);
362 vm->def->seclabel.model = strdup(SECURITY_APPARMOR_NAME);
363 if (!vm->def->seclabel.model) {
364 virReportOOMError(conn);
372 remove_profile(profile_name);
373 VIR_FREE(vm->def->seclabel.label);
374 VIR_FREE(vm->def->seclabel.imagelabel);
375 VIR_FREE(vm->def->seclabel.model);
378 VIR_FREE(profile_name);
383 /* Seen with 'virsh dominfo <vm>'. This function only called if the VM is
387 AppArmorGetSecurityLabel(virConnectPtr conn,
388 virDomainObjPtr vm, virSecurityLabelPtr sec)
391 char *profile_name = NULL;
393 if ((profile_name = get_profile_name(conn, vm)) == NULL)
396 if (virStrcpy(sec->label, profile_name,
397 VIR_SECURITY_LABEL_BUFLEN) == NULL) {
398 virSecurityReportError(conn, VIR_ERR_ERROR,
399 "%s", _("error copying profile name"));
403 if ((sec->enforcing = profile_status(profile_name, 1)) < 0) {
404 virSecurityReportError(conn, VIR_ERR_ERROR,
405 "%s", _("error calling profile_status()"));
411 VIR_FREE(profile_name);
416 /* Called on VM shutdown and destroy. See AppArmorGenSecurityLabel (above) for
417 * more details. Currently called via qemudShutdownVMDaemon.
420 AppArmorRestoreSecurityLabel(virConnectPtr conn, virDomainObjPtr vm)
422 const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
425 if (secdef->imagelabel) {
426 if ((rc = remove_profile(secdef->label)) != 0) {
427 virSecurityReportError(conn, VIR_ERR_ERROR,
428 _("could not remove profile for \'%s\'"),
431 VIR_FREE(secdef->model);
432 VIR_FREE(secdef->label);
433 VIR_FREE(secdef->imagelabel);
438 /* Called via virExecWithHook. Output goes to
439 * LOCAL_STATE_DIR/log/libvirt/qemu/<vm name>.log
442 AppArmorSetSecurityLabel(virConnectPtr conn,
443 virSecurityDriverPtr drv, virDomainObjPtr vm)
445 const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
447 char *profile_name = NULL;
449 if ((profile_name = get_profile_name(conn, vm)) == NULL)
452 if (STRNEQ(drv->name, secdef->model)) {
453 virSecurityReportError(conn, VIR_ERR_ERROR,
454 _("security label driver mismatch: "
455 "\'%s\' model configured for domain, but "
456 "hypervisor driver is \'%s\'."),
457 secdef->model, drv->name);
458 if (use_apparmor() > 0)
462 if (aa_change_profile(profile_name) < 0) {
463 virSecurityReportError(conn, VIR_ERR_ERROR,
464 _("error calling aa_change_profile()"));
470 VIR_FREE(profile_name);
476 /* Called when hotplugging */
478 AppArmorRestoreSecurityImageLabel(virConnectPtr conn,
480 virDomainDiskDefPtr disk ATTRIBUTE_UNUSED)
482 const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
484 char *profile_name = NULL;
486 if (secdef->imagelabel) {
487 if ((profile_name = get_profile_name(conn, vm)) == NULL)
490 /* Update the profile only if it is loaded */
491 if (profile_loaded(secdef->imagelabel) >= 0) {
492 if (load_profile(conn, secdef->imagelabel, vm, NULL) < 0) {
493 virSecurityReportError(conn, VIR_ERR_ERROR,
494 _("cannot update AppArmor profile "
503 VIR_FREE(profile_name);
508 /* Called when hotplugging */
510 AppArmorSetSecurityImageLabel(virConnectPtr conn,
511 virDomainObjPtr vm, virDomainDiskDefPtr disk)
513 const virSecurityLabelDefPtr secdef = &vm->def->seclabel;
520 if (secdef->imagelabel) {
521 /* if the device doesn't exist, error out */
522 if (!virFileExists(disk->src)) {
523 virSecurityReportError(conn, VIR_ERR_ERROR,
524 _("\'%s\' does not exist"), disk->src);
528 if ((profile_name = get_profile_name(conn, vm)) == NULL)
531 /* update the profile only if it is loaded */
532 if (profile_loaded(secdef->imagelabel) >= 0) {
533 if (load_profile(conn, secdef->imagelabel, vm, disk) < 0) {
534 virSecurityReportError(conn, VIR_ERR_ERROR,
535 _("cannot update AppArmor profile "
545 VIR_FREE(profile_name);
551 AppArmorSecurityVerify(virConnectPtr conn, virDomainDefPtr def)
553 const virSecurityLabelDefPtr secdef = &def->seclabel;
555 if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) {
556 if (use_apparmor() < 0 || profile_status(secdef->label, 0) < 0) {
557 virSecurityReportError(conn, VIR_ERR_XML_ERROR,
558 _("Invalid security label \'%s\'"),
567 AppArmorReserveSecurityLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
568 virDomainObjPtr vm ATTRIBUTE_UNUSED)
570 /* NOOP. Nothing to reserve with AppArmor */
575 AppArmorSetSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
576 virDomainObjPtr vm ATTRIBUTE_UNUSED,
577 virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
580 /* TODO: call load_profile with an update vm->def */
585 AppArmorRestoreSecurityHostdevLabel(virConnectPtr conn ATTRIBUTE_UNUSED,
586 virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED)
589 /* TODO: call load_profile (needs virDomainObjPtr vm) */
593 virSecurityDriver virAppArmorSecurityDriver = {
594 .name = SECURITY_APPARMOR_NAME,
595 .probe = AppArmorSecurityDriverProbe,
596 .open = AppArmorSecurityDriverOpen,
597 .domainSecurityVerify = AppArmorSecurityVerify,
598 .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel,
599 .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel,
600 .domainGenSecurityLabel = AppArmorGenSecurityLabel,
601 .domainReserveSecurityLabel = AppArmorReserveSecurityLabel,
602 .domainGetSecurityLabel = AppArmorGetSecurityLabel,
603 .domainRestoreSecurityLabel = AppArmorRestoreSecurityLabel,
604 .domainSetSecurityLabel = AppArmorSetSecurityLabel,
605 .domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel,
606 .domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel,