virt-aa-helper: Call virCommandRawStatus()
[libvirt/ericb.git] / src / security / virt-aa-helper.c
blob60c9b75980701c89cc19ca421523aaf96962556d
1 /*
2 * virt-aa-helper: wrapper program used by AppArmor security driver.
4 * Copyright (C) 2010-2014 Red Hat, Inc.
5 * Copyright (C) 2009-2011 Canonical Ltd.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
22 #include <config.h>
24 #include <stdarg.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include <sys/utsname.h>
31 #include "internal.h"
32 #include "virbuffer.h"
33 #include "viralloc.h"
34 #include "vircommand.h"
35 #include "virlog.h"
36 #include "dirname.h"
37 #include "driver.h"
39 #include "security_driver.h"
40 #include "security_apparmor.h"
41 #include "domain_conf.h"
42 #include "virxml.h"
43 #include "viruuid.h"
44 #include "virusb.h"
45 #include "virpci.h"
46 #include "virfile.h"
47 #include "configmake.h"
48 #include "virrandom.h"
49 #include "virstring.h"
50 #include "virgettext.h"
51 #include "virhostdev.h"
53 #define VIR_FROM_THIS VIR_FROM_SECURITY
55 static char *progname;
57 typedef struct {
58 char uuid[PROFILE_NAME_SIZE]; /* UUID of vm */
59 bool dryrun; /* dry run */
60 char cmd; /* 'c' create
61 * 'a' add (load)
62 * 'r' replace
63 * 'R' remove */
64 char *files; /* list of files */
65 virDomainDefPtr def; /* VM definition */
66 virCapsPtr caps; /* VM capabilities */
67 virDomainXMLOptionPtr xmlopt; /* XML parser data */
68 char *virtType; /* type of hypervisor (eg qemu, xen, lxc) */
69 char *os; /* type of os (eg hvm, xen, exe) */
70 virArch arch; /* machine architecture */
71 char *newfile; /* newly added file */
72 bool append; /* append to .files instead of rewrite */
73 } vahControl;
75 static int
76 vahDeinit(vahControl * ctl)
78 if (ctl == NULL)
79 return -1;
81 VIR_FREE(ctl->def);
82 virObjectUnref(ctl->caps);
83 virObjectUnref(ctl->xmlopt);
84 VIR_FREE(ctl->files);
85 VIR_FREE(ctl->virtType);
86 VIR_FREE(ctl->os);
87 VIR_FREE(ctl->newfile);
89 return 0;
93 * Print usage
95 static void
96 vah_usage(void)
98 printf(_("\n%s [options] [< def.xml]\n\n"
99 " Options:\n"
100 " -a | --add load profile\n"
101 " -c | --create create profile from template\n"
102 " -d | --dryrun dry run\n"
103 " -D | --delete unload and delete profile\n"
104 " -f | --add-file <file> add file to profile\n"
105 " -F | --append-file <file> append file to profile\n"
106 " -r | --replace reload profile\n"
107 " -R | --remove unload profile\n"
108 " -h | --help this help\n"
109 " -u | --uuid <uuid> uuid (profile name)\n"
110 "\n"), progname);
112 puts(_("This command is intended to be used by libvirtd "
113 "and not used directly.\n"));
114 return;
117 static void
118 vah_error(vahControl * ctl, int doexit, const char *str)
120 fprintf(stderr, _("%s: error: %s%c"), progname, str, '\n');
122 if (doexit) {
123 if (ctl != NULL)
124 vahDeinit(ctl);
125 exit(EXIT_FAILURE);
129 static void
130 vah_warning(const char *str)
132 fprintf(stderr, _("%s: warning: %s%c"), progname, str, '\n');
135 static void
136 vah_info(const char *str)
138 fprintf(stderr, _("%s:\n%s%c"), progname, str, '\n');
142 * run an apparmor_parser command
144 static int
145 parserCommand(const char *profile_name, const char cmd)
147 int result = -1;
148 char flag[3];
149 char *profile;
150 int status;
151 int ret;
153 if (strchr("arR", cmd) == NULL) {
154 vah_error(NULL, 0, _("invalid flag"));
155 return -1;
158 snprintf(flag, 3, "-%c", cmd);
160 if (virAsprintfQuiet(&profile, "%s/%s",
161 APPARMOR_DIR "/libvirt", profile_name) < 0) {
162 vah_error(NULL, 0, _("profile name exceeds maximum length"));
163 return -1;
166 if (!virFileExists(profile)) {
167 vah_error(NULL, 0, _("profile does not exist"));
168 goto cleanup;
169 } else {
170 const char * const argv[] = {
171 "/sbin/apparmor_parser", flag, profile, NULL
173 VIR_AUTOPTR(virCommand) command = virCommandNewArgs(argv);
175 virCommandRawStatus(command);
176 if ((ret = virCommandRun(command, &status)) != 0 ||
177 (WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
178 if (ret != 0) {
179 vah_error(NULL, 0, _("failed to run apparmor_parser"));
180 goto cleanup;
181 } else if (cmd == 'R' && WIFEXITED(status) &&
182 WEXITSTATUS(status) == 234) {
183 vah_warning(_("unable to unload already unloaded profile"));
184 } else {
185 vah_error(NULL, 0, _("apparmor_parser exited with error"));
186 goto cleanup;
191 result = 0;
193 cleanup:
194 VIR_FREE(profile);
196 return result;
200 * Update the dynamic files
202 static int
203 update_include_file(const char *include_file, const char *included_files,
204 bool append)
206 int rc = -1;
207 int plen, flen = 0;
208 int fd;
209 char *pcontent = NULL;
210 char *existing = NULL;
211 const char *warning =
212 "# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
214 if (virFileExists(include_file)) {
215 flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
216 if (flen < 0)
217 return rc;
220 if (append && virFileExists(include_file)) {
221 if (virAsprintfQuiet(&pcontent, "%s%s", existing, included_files) == -1) {
222 vah_error(NULL, 0, _("could not allocate memory for profile"));
223 goto cleanup;
225 } else {
226 if (virAsprintfQuiet(&pcontent, "%s%s", warning, included_files) == -1) {
227 vah_error(NULL, 0, _("could not allocate memory for profile"));
228 goto cleanup;
232 plen = strlen(pcontent);
233 if (plen > MAX_FILE_LEN) {
234 vah_error(NULL, 0, _("invalid length for new profile"));
235 goto cleanup;
238 /* only update the disk profile if it is different */
239 if (flen > 0 && flen == plen && STREQLEN(existing, pcontent, plen)) {
240 rc = 0;
241 goto cleanup;
244 /* write the file */
245 if ((fd = open(include_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
246 vah_error(NULL, 0, _("failed to create include file"));
247 goto cleanup;
250 if (safewrite(fd, pcontent, plen) < 0) { /* don't write the '\0' */
251 VIR_FORCE_CLOSE(fd);
252 vah_error(NULL, 0, _("failed to write to profile"));
253 goto cleanup;
256 if (VIR_CLOSE(fd) != 0) {
257 vah_error(NULL, 0, _("failed to close or write to profile"));
258 goto cleanup;
260 rc = 0;
262 cleanup:
263 VIR_FREE(pcontent);
264 VIR_FREE(existing);
266 return rc;
270 * Create a profile based on a template
272 static int
273 create_profile(const char *profile, const char *profile_name,
274 const char *profile_files, int virtType)
276 char *template;
277 char *tcontent = NULL;
278 char *pcontent = NULL;
279 char *replace_name = NULL;
280 char *replace_files = NULL;
281 char *tmp = NULL;
282 const char *template_name = "\nprofile LIBVIRT_TEMPLATE";
283 const char *template_end = "\n}";
284 int tlen, plen;
285 int fd;
286 int rc = -1;
287 const char *driver_name = NULL;
289 if (virFileExists(profile)) {
290 vah_error(NULL, 0, _("profile exists"));
291 goto end;
294 switch (virtType) {
295 case VIR_DOMAIN_VIRT_QEMU:
296 case VIR_DOMAIN_VIRT_KQEMU:
297 case VIR_DOMAIN_VIRT_KVM:
298 driver_name = "qemu";
299 break;
300 default:
301 driver_name = virDomainVirtTypeToString(virtType);
304 if (virAsprintfQuiet(&template, "%s/TEMPLATE.%s", APPARMOR_DIR "/libvirt",
305 driver_name) < 0) {
306 vah_error(NULL, 0, _("template name exceeds maximum length"));
307 goto end;
310 if (!virFileExists(template)) {
311 vah_error(NULL, 0, _("template does not exist"));
312 goto end;
315 if ((tlen = virFileReadAll(template, MAX_FILE_LEN, &tcontent)) < 0) {
316 vah_error(NULL, 0, _("failed to read AppArmor template"));
317 goto end;
320 if (strstr(tcontent, template_name) == NULL) {
321 vah_error(NULL, 0, _("no replacement string in template"));
322 goto clean_tcontent;
325 if (strstr(tcontent, template_end) == NULL) {
326 vah_error(NULL, 0, _("no replacement string in template"));
327 goto clean_tcontent;
330 /* '\nprofile <profile_name>\0' */
331 if (virAsprintfQuiet(&replace_name, "\nprofile %s", profile_name) == -1) {
332 vah_error(NULL, 0, _("could not allocate memory for profile name"));
333 goto clean_tcontent;
336 /* '\n<profile_files>\n}\0' */
337 if ((virtType != VIR_DOMAIN_VIRT_LXC) &&
338 virAsprintfQuiet(&replace_files, "\n%s\n}", profile_files) == -1) {
339 vah_error(NULL, 0, _("could not allocate memory for profile files"));
340 VIR_FREE(replace_name);
341 goto clean_tcontent;
344 plen = tlen + strlen(replace_name) - strlen(template_name) + 1;
346 if (virtType != VIR_DOMAIN_VIRT_LXC)
347 plen += strlen(replace_files) - strlen(template_end);
349 if (plen > MAX_FILE_LEN || plen < tlen) {
350 vah_error(NULL, 0, _("invalid length for new profile"));
351 goto clean_replace;
354 if (!(pcontent = virStringReplace(tcontent, template_name, replace_name)))
355 goto clean_all;
357 if (virtType != VIR_DOMAIN_VIRT_LXC) {
358 if (!(tmp = virStringReplace(pcontent, template_end, replace_files)))
359 goto clean_all;
360 VIR_FREE(pcontent);
361 VIR_STEAL_PTR(pcontent, tmp);
364 /* write the file */
365 if ((fd = open(profile, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1) {
366 vah_error(NULL, 0, _("failed to create profile"));
367 goto clean_all;
370 if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
371 VIR_FORCE_CLOSE(fd);
372 vah_error(NULL, 0, _("failed to write to profile"));
373 goto clean_all;
376 if (VIR_CLOSE(fd) != 0) {
377 vah_error(NULL, 0, _("failed to close or write to profile"));
378 goto clean_all;
380 rc = 0;
382 clean_all:
383 VIR_FREE(pcontent);
384 clean_replace:
385 VIR_FREE(replace_name);
386 VIR_FREE(replace_files);
387 clean_tcontent:
388 VIR_FREE(tcontent);
389 end:
390 VIR_FREE(template);
391 return rc;
395 * Load an existing profile
397 static int
398 parserLoad(const char *profile_name)
400 return parserCommand(profile_name, 'a');
404 * Remove an existing profile
406 static int
407 parserRemove(const char *profile_name)
409 return parserCommand(profile_name, 'R');
413 * Replace an existing profile
415 static int
416 parserReplace(const char *profile_name)
418 return parserCommand(profile_name, 'r');
421 static int
422 valid_uuid(const char *uuid)
424 unsigned char rawuuid[VIR_UUID_BUFLEN];
426 if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
427 return -1;
429 if (!STRPREFIX(uuid, AA_PREFIX))
430 return -1;
432 if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
433 return -1;
435 return 0;
438 static int
439 valid_name(const char *name)
441 /* just try to filter out any dangerous characters in the name that can be
442 * used to subvert the profile */
443 const char *bad = "/[]{}?^,\"*";
445 if (strlen(name) == 0)
446 return -1;
448 if (strcspn(name, bad) != strlen(name))
449 return -1;
451 return 0;
454 /* see if one of the strings in arr starts with str */
455 static int
456 array_starts_with(const char *str, const char * const *arr, const long size)
458 size_t i;
459 for (i = 0; i < size; i++) {
460 if (strlen(str) < strlen(arr[i]))
461 continue;
463 if (STRPREFIX(str, arr[i]))
464 return 0;
466 return 1;
470 * Don't allow access to special files or restricted paths such as /bin, /sbin,
471 * /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
472 * access to system files which could be used to elevate privileges. This is a
473 * safety measure in case libvirtd is under a restrictive profile and is
474 * subverted and trying to escape confinement.
476 * Note that we cannot exclude block devices because they are valid devices.
477 * The TEMPLATE file can be adjusted to explicitly disallow these if needed.
479 * RETURN: -1 on error, 0 if ok, 1 if blocked
481 static int
482 valid_path(const char *path, const bool readonly)
484 const char * const restricted[] = {
485 "/bin/",
486 "/etc/",
487 "/lib",
488 "/lost+found/",
489 "/proc/",
490 "/sbin/",
491 "/selinux/",
492 "/sys/",
493 "/usr/bin/",
494 "/usr/lib",
495 "/usr/sbin/",
496 "/usr/share/",
497 "/usr/local/bin/",
498 "/usr/local/etc/",
499 "/usr/local/lib",
500 "/usr/local/sbin/"
502 /* these paths are ok for readonly, but not read/write */
503 const char * const restricted_rw[] = {
504 "/boot/",
505 "/vmlinuz",
506 "/initrd",
507 "/initrd.img",
508 "/usr/share/OVMF/", /* for OVMF images */
509 "/usr/share/ovmf/", /* for OVMF images */
510 "/usr/share/AAVMF/", /* for AAVMF images */
511 "/usr/share/qemu-efi/", /* for AAVMF images */
512 "/usr/share/qemu-efi-aarch64/" /* for AAVMF images */
514 /* override the above with these */
515 const char * const override[] = {
516 "/sys/devices/pci", /* for hostdev pci devices */
517 "/sys/kernel/config/target/vhost", /* for hostdev vhost_scsi devices */
518 "/etc/libvirt-sandbox/services/" /* for virt-sandbox service config */
521 const int nropaths = ARRAY_CARDINALITY(restricted);
522 const int nrwpaths = ARRAY_CARDINALITY(restricted_rw);
523 const int nopaths = ARRAY_CARDINALITY(override);
525 if (path == NULL) {
526 vah_error(NULL, 0, _("bad pathname"));
527 return -1;
530 /* Don't allow double quotes, since we use them to quote the filename
531 * and this will confuse the apparmor parser.
533 if (strchr(path, '"') != NULL)
534 return 1;
536 /* Require an absolute path */
537 if (STRNEQLEN(path, "/", 1))
538 return 1;
540 if (!virFileExists(path))
541 vah_warning(_("path does not exist, skipping file type checks"));
543 /* overrides are always allowed */
544 if (array_starts_with(path, override, nopaths) == 0)
545 return 0;
547 /* allow read only paths upfront */
548 if (readonly) {
549 if (array_starts_with(path, restricted_rw, nrwpaths) == 0)
550 return 0;
553 /* disallow RW access to all paths in restricted and restriced_rw */
554 if ((array_starts_with(path, restricted, nropaths) == 0 ||
555 array_starts_with(path, restricted_rw, nrwpaths) == 0))
556 return 1;
558 return 0;
561 static int
562 verify_xpath_context(xmlXPathContextPtr ctxt)
564 int rc = -1;
565 char *tmp = NULL;
567 if (!ctxt) {
568 vah_warning(_("Invalid context"));
569 goto error;
572 /* check if have <name> */
573 if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
574 vah_warning(_("Could not find <name>"));
575 goto error;
577 VIR_FREE(tmp);
579 /* check if have <uuid> */
580 if (!(tmp = virXPathString("string(./uuid[1])", ctxt))) {
581 vah_warning(_("Could not find <uuid>"));
582 goto error;
584 VIR_FREE(tmp);
586 rc = 0;
588 error:
589 return rc;
593 * Parse the xml we received to fill in the following:
594 * ctl->virtType
595 * ctl->os
596 * ctl->arch
598 * These are suitable for setting up a virCapsPtr
600 static int
601 caps_mockup(vahControl * ctl, const char *xmlStr)
603 int rc = -1;
604 xmlDocPtr xml = NULL;
605 xmlXPathContextPtr ctxt = NULL;
606 char *arch;
608 if (!(xml = virXMLParseStringCtxt(xmlStr, _("(domain_definition)"),
609 &ctxt))) {
610 goto cleanup;
613 if (!virXMLNodeNameEqual(ctxt->node, "domain")) {
614 vah_error(NULL, 0, _("unexpected root element, expecting <domain>"));
615 goto cleanup;
618 /* Quick sanity check for some required elements */
619 if (verify_xpath_context(ctxt) != 0)
620 goto cleanup;
622 ctl->virtType = virXPathString("string(./@type)", ctxt);
623 if (!ctl->virtType) {
624 vah_error(ctl, 0, _("domain type is not defined"));
625 goto cleanup;
627 ctl->os = virXPathString("string(./os/type[1])", ctxt);
628 if (!ctl->os) {
629 vah_error(ctl, 0, _("os.type is not defined"));
630 goto cleanup;
632 arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
633 if (!arch) {
634 ctl->arch = virArchFromHost();
635 } else {
636 ctl->arch = virArchFromString(arch);
637 VIR_FREE(arch);
640 rc = 0;
642 cleanup:
643 xmlFreeDoc(xml);
644 xmlXPathFreeContext(ctxt);
646 return rc;
649 virDomainDefParserConfig virAAHelperDomainDefParserConfig = {
650 .features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG |
651 VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN |
652 VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS |
653 VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING,
656 static int
657 get_definition(vahControl * ctl, const char *xmlStr)
659 int rc = -1, ostype, virtType;
660 virCapsGuestPtr guest; /* this is freed when caps is freed */
663 * mock up some capabilities. We don't currently use these explicitly,
664 * but need them for virDomainDefParseString().
666 if (caps_mockup(ctl, xmlStr) != 0)
667 goto exit;
669 if ((ctl->caps = virCapabilitiesNew(ctl->arch, true, true)) == NULL) {
670 vah_error(ctl, 0, _("could not allocate memory"));
671 goto exit;
674 if (!(ctl->xmlopt = virDomainXMLOptionNew(&virAAHelperDomainDefParserConfig,
675 NULL, NULL, NULL, NULL))) {
676 vah_error(ctl, 0, _("Failed to create XML config object"));
677 goto exit;
680 if ((ostype = virDomainOSTypeFromString(ctl->os)) < 0) {
681 vah_error(ctl, 0, _("unknown OS type"));
682 goto exit;
685 if ((guest = virCapabilitiesAddGuest(ctl->caps,
686 ostype,
687 ctl->arch,
688 NULL,
689 NULL,
691 NULL)) == NULL) {
692 vah_error(ctl, 0, _("could not allocate memory"));
693 goto exit;
696 if ((virtType = virDomainVirtTypeFromString(ctl->virtType)) < 0) {
697 vah_error(ctl, 0, _("unknown virtualization type"));
698 goto exit;
701 if (virCapabilitiesAddGuestDomain(guest,
702 virtType,
703 NULL,
704 NULL,
706 NULL) == NULL) {
707 vah_error(ctl, 0, _("could not allocate memory"));
708 goto exit;
711 ctl->def = virDomainDefParseString(xmlStr,
712 ctl->caps, ctl->xmlopt, NULL,
713 VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL |
714 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
716 if (ctl->def == NULL) {
717 vah_error(ctl, 0, _("could not parse XML"));
718 goto exit;
721 if (!ctl->def->name) {
722 vah_error(ctl, 0, _("could not find name in XML"));
723 goto exit;
726 if (valid_name(ctl->def->name) != 0) {
727 vah_error(ctl, 0, _("bad name"));
728 goto exit;
731 rc = 0;
733 exit:
734 return rc;
738 * The permissions allowed are apparmor valid permissions and 'R'. 'R' stands for
739 * read with no explicit deny rule.
741 static int
742 vah_add_path(virBufferPtr buf, const char *path, const char *perms, bool recursive)
744 char *tmp = NULL;
745 int rc = -1;
746 bool readonly = true;
747 bool explicit_deny_rule = true;
748 char *sub = NULL;
749 char *perms_new = NULL;
750 char *pathdir = NULL;
751 char *pathtmp = NULL;
752 char *pathreal = NULL;
754 if (path == NULL)
755 return rc;
757 /* Skip files without an absolute path. Not having one confuses the
758 * apparmor parser and this also ensures things like tcp consoles don't
759 * get added to the profile.
761 if (STRNEQLEN(path, "/", 1)) {
762 vah_warning(path);
763 vah_warning(_("skipped non-absolute path"));
764 return 0;
767 /* files might be created by qemu later on and not exist right now.
768 * But realpath needs a valid path to work on, therefore:
769 * 1. walk the path to find longest valid path
770 * 2. get the realpath of that valid path
771 * 3. re-combine the realpath with the remaining suffix
772 * Note: A totally non existent path is used as-is
774 if (VIR_STRDUP_QUIET(pathdir, path) < 0)
775 goto cleanup;
776 while (!virFileExists(pathdir)) {
777 if ((pathtmp = mdir_name(pathdir)) == NULL)
778 goto cleanup;
779 VIR_FREE(pathdir);
780 VIR_STEAL_PTR(pathdir, pathtmp);
783 if (strlen(pathdir) == 1) {
784 /* nothing of the path does exist yet */
785 if (VIR_STRDUP_QUIET(tmp, path) < 0)
786 goto cleanup;
787 } else {
788 if (VIR_STRDUP_QUIET(pathtmp, path+strlen(pathdir)) < 0)
789 goto cleanup;
790 if ((pathreal = realpath(pathdir, NULL)) == NULL) {
791 vah_error(NULL, 0, pathdir);
792 vah_error(NULL, 0, _("could not find realpath"));
793 goto cleanup;
795 if (virAsprintfQuiet(&tmp, "%s%s", pathreal, pathtmp) < 0)
796 goto cleanup;
799 if (VIR_STRDUP_QUIET(perms_new, perms) < 0)
800 goto cleanup;
802 if (strchr(perms_new, 'w') != NULL) {
803 readonly = false;
804 explicit_deny_rule = false;
807 if ((sub = strchr(perms_new, 'R')) != NULL) {
808 /* Don't write the invalid R permission, replace it with 'r' */
809 sub[0] = 'r';
810 explicit_deny_rule = false;
813 rc = valid_path(tmp, readonly);
814 if (rc != 0) {
815 if (rc > 0) {
816 vah_error(NULL, 0, path);
817 vah_error(NULL, 0, _("skipped restricted file"));
819 goto cleanup;
822 if (tmp[strlen(tmp) - 1] == '/')
823 tmp[strlen(tmp) - 1] = '\0';
825 virBufferAsprintf(buf, " \"%s%s\" %s,\n", tmp, recursive ? "/**" : "",
826 perms_new);
827 if (explicit_deny_rule) {
828 virBufferAddLit(buf, " # don't audit writes to readonly files\n");
829 virBufferAsprintf(buf, " deny \"%s%s\" w,\n", tmp, recursive ? "/**" : "");
831 if (recursive) {
832 /* allow reading (but not creating) the dir */
833 virBufferAsprintf(buf, " \"%s/\" r,\n", tmp);
836 cleanup:
837 VIR_FREE(pathdir);
838 VIR_FREE(pathtmp);
839 VIR_FREE(pathreal);
840 VIR_FREE(perms_new);
841 VIR_FREE(tmp);
843 return rc;
846 static int
847 vah_add_file(virBufferPtr buf, const char *path, const char *perms)
849 return vah_add_path(buf, path, perms, false);
852 static int
853 vah_add_file_chardev(virBufferPtr buf,
854 const char *path,
855 const char *perms,
856 const int type)
858 char *pipe_in;
859 char *pipe_out;
860 int rc = -1;
862 if (type == VIR_DOMAIN_CHR_TYPE_PIPE) {
863 /* add the pipe input */
864 if (virAsprintfQuiet(&pipe_in, "%s.in", path) == -1) {
865 vah_error(NULL, 0, _("could not allocate memory"));
866 goto cleanup;
869 if (vah_add_file(buf, pipe_in, perms) != 0)
870 goto clean_pipe_in;
872 /* add the pipe output */
873 if (virAsprintfQuiet(&pipe_out, "%s.out", path) == -1) {
874 vah_error(NULL, 0, _("could not allocate memory"));
875 goto clean_pipe_in;
878 if (vah_add_file(buf, pipe_out, perms) != 0)
879 goto clean_pipe_out;
881 rc = 0;
882 clean_pipe_out:
883 VIR_FREE(pipe_out);
884 clean_pipe_in:
885 VIR_FREE(pipe_in);
886 } else {
887 /* add the file */
888 if (vah_add_file(buf, path, perms) != 0)
889 goto cleanup;
890 rc = 0;
893 cleanup:
894 return rc;
897 static int
898 file_iterate_hostdev_cb(virUSBDevicePtr dev ATTRIBUTE_UNUSED,
899 const char *file, void *opaque)
901 virBufferPtr buf = opaque;
902 return vah_add_file(buf, file, "rw");
905 static int
906 file_iterate_pci_cb(virPCIDevicePtr dev ATTRIBUTE_UNUSED,
907 const char *file, void *opaque)
909 virBufferPtr buf = opaque;
910 return vah_add_file(buf, file, "rw");
913 static int
914 add_file_path(virDomainDiskDefPtr disk,
915 const char *path,
916 size_t depth,
917 void *opaque)
919 virBufferPtr buf = opaque;
920 int ret;
922 if (depth == 0) {
923 if (disk->src->readonly)
924 ret = vah_add_file(buf, path, "rk");
925 else
926 ret = vah_add_file(buf, path, "rwk");
927 } else {
928 ret = vah_add_file(buf, path, "rk");
931 if (ret != 0)
932 ret = -1;
934 return ret;
937 static int
938 get_files(vahControl * ctl)
940 virBuffer buf = VIR_BUFFER_INITIALIZER;
941 int rc = -1;
942 size_t i;
943 char *uuid;
944 char uuidstr[VIR_UUID_STRING_BUFLEN];
945 bool needsVfio = false, needsvhost = false, needsgl = false;
947 /* verify uuid is same as what we were given on the command line */
948 virUUIDFormat(ctl->def->uuid, uuidstr);
949 if (virAsprintfQuiet(&uuid, "%s%s", AA_PREFIX, uuidstr) == -1) {
950 vah_error(ctl, 0, _("could not allocate memory"));
951 return rc;
954 if (STRNEQ(uuid, ctl->uuid)) {
955 vah_error(ctl, 0, _("given uuid does not match XML uuid"));
956 goto cleanup;
959 /* load the storage driver so that backing store can be accessed */
960 #ifdef WITH_STORAGE
961 virDriverLoadModule("storage", "storageRegister", false);
962 #endif
964 for (i = 0; i < ctl->def->ndisks; i++) {
965 virDomainDiskDefPtr disk = ctl->def->disks[i];
967 if (!virDomainDiskGetSource(disk))
968 continue;
969 /* XXX - if we knew the qemu user:group here we could send it in
970 * so that the open could be re-tried as that user:group.
972 if (!virStorageSourceHasBacking(disk->src))
973 virStorageFileGetMetadata(disk->src, -1, -1, false);
975 /* XXX passing ignoreOpenFailure = true to get back to the behavior
976 * from before using virDomainDiskDefForeachPath. actually we should
977 * be passing ignoreOpenFailure = false and handle open errors more
978 * careful than just ignoring them.
980 if (virDomainDiskDefForeachPath(disk, true, add_file_path, &buf) < 0)
981 goto cleanup;
984 for (i = 0; i < ctl->def->nserials; i++)
985 if (ctl->def->serials[i] &&
986 (ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
987 ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
988 ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
989 ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
990 ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
991 ctl->def->serials[i]->source->data.file.path &&
992 ctl->def->serials[i]->source->data.file.path[0] != '\0')
993 if (vah_add_file_chardev(&buf,
994 ctl->def->serials[i]->source->data.file.path,
995 "rw",
996 ctl->def->serials[i]->source->type) != 0)
997 goto cleanup;
999 for (i = 0; i < ctl->def->nconsoles; i++)
1000 if (ctl->def->consoles[i] &&
1001 (ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
1002 ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
1003 ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
1004 ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
1005 ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
1006 ctl->def->consoles[i]->source->data.file.path &&
1007 ctl->def->consoles[i]->source->data.file.path[0] != '\0')
1008 if (vah_add_file(&buf,
1009 ctl->def->consoles[i]->source->data.file.path, "rw") != 0)
1010 goto cleanup;
1012 for (i = 0; i < ctl->def->nparallels; i++)
1013 if (ctl->def->parallels[i] &&
1014 (ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
1015 ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
1016 ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
1017 ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
1018 ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
1019 ctl->def->parallels[i]->source->data.file.path &&
1020 ctl->def->parallels[i]->source->data.file.path[0] != '\0')
1021 if (vah_add_file_chardev(&buf,
1022 ctl->def->parallels[i]->source->data.file.path,
1023 "rw",
1024 ctl->def->parallels[i]->source->type) != 0)
1025 goto cleanup;
1027 for (i = 0; i < ctl->def->nchannels; i++)
1028 if (ctl->def->channels[i] &&
1029 (ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
1030 ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
1031 ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
1032 ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
1033 ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
1034 ctl->def->channels[i]->source->data.file.path &&
1035 ctl->def->channels[i]->source->data.file.path[0] != '\0')
1036 if (vah_add_file_chardev(&buf,
1037 ctl->def->channels[i]->source->data.file.path,
1038 "rw",
1039 ctl->def->channels[i]->source->type) != 0)
1040 goto cleanup;
1042 if (ctl->def->os.kernel)
1043 if (vah_add_file(&buf, ctl->def->os.kernel, "r") != 0)
1044 goto cleanup;
1046 if (ctl->def->os.initrd)
1047 if (vah_add_file(&buf, ctl->def->os.initrd, "r") != 0)
1048 goto cleanup;
1050 if (ctl->def->os.dtb)
1051 if (vah_add_file(&buf, ctl->def->os.dtb, "r") != 0)
1052 goto cleanup;
1054 if (ctl->def->os.slic_table)
1055 if (vah_add_file(&buf, ctl->def->os.slic_table, "r") != 0)
1056 goto cleanup;
1058 if (ctl->def->os.loader && ctl->def->os.loader->path)
1059 if (vah_add_file(&buf, ctl->def->os.loader->path, "rk") != 0)
1060 goto cleanup;
1062 if (ctl->def->os.loader && ctl->def->os.loader->nvram)
1063 if (vah_add_file(&buf, ctl->def->os.loader->nvram, "rwk") != 0)
1064 goto cleanup;
1066 for (i = 0; i < ctl->def->ngraphics; i++) {
1067 virDomainGraphicsDefPtr graphics = ctl->def->graphics[i];
1068 size_t n;
1069 const char *rendernode = virDomainGraphicsGetRenderNode(graphics);
1071 if (rendernode) {
1072 vah_add_file(&buf, rendernode, "rw");
1073 needsgl = true;
1074 } else {
1075 if (virDomainGraphicsNeedsAutoRenderNode(graphics)) {
1076 char *defaultRenderNode = virHostGetDRMRenderNode();
1077 needsgl = true;
1079 if (defaultRenderNode) {
1080 vah_add_file(&buf, defaultRenderNode, "rw");
1081 VIR_FREE(defaultRenderNode);
1086 for (n = 0; n < graphics->nListens; n++) {
1087 virDomainGraphicsListenDef listenObj = graphics->listens[n];
1089 if (listenObj.type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
1090 listenObj.socket &&
1091 vah_add_file(&buf, listenObj.socket, "rw"))
1092 goto cleanup;
1096 if (ctl->def->ngraphics == 1 &&
1097 ctl->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL)
1098 if (vah_add_file(&buf, ctl->def->graphics[0]->data.sdl.xauth,
1099 "r") != 0)
1100 goto cleanup;
1102 for (i = 0; i < ctl->def->nhostdevs; i++)
1103 if (ctl->def->hostdevs[i]) {
1104 virDomainHostdevDefPtr dev = ctl->def->hostdevs[i];
1105 virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
1106 switch (dev->source.subsys.type) {
1107 case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
1108 virUSBDevicePtr usb =
1109 virUSBDeviceNew(usbsrc->bus, usbsrc->device, NULL);
1111 if (usb == NULL)
1112 continue;
1114 if (virHostdevFindUSBDevice(dev, true, &usb) < 0)
1115 continue;
1117 rc = virUSBDeviceFileIterate(usb, file_iterate_hostdev_cb, &buf);
1118 virUSBDeviceFree(usb);
1119 if (rc != 0)
1120 goto cleanup;
1121 break;
1124 case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
1125 virDomainHostdevSubsysMediatedDevPtr mdevsrc = &dev->source.subsys.u.mdev;
1126 switch ((virMediatedDeviceModelType) mdevsrc->model) {
1127 case VIR_MDEV_MODEL_TYPE_VFIO_PCI:
1128 case VIR_MDEV_MODEL_TYPE_VFIO_AP:
1129 case VIR_MDEV_MODEL_TYPE_VFIO_CCW:
1130 needsVfio = true;
1131 break;
1132 case VIR_MDEV_MODEL_TYPE_LAST:
1133 default:
1134 virReportEnumRangeError(virMediatedDeviceModelType,
1135 mdevsrc->model);
1136 break;
1138 break;
1141 case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
1142 virPCIDevicePtr pci = virPCIDeviceNew(
1143 dev->source.subsys.u.pci.addr.domain,
1144 dev->source.subsys.u.pci.addr.bus,
1145 dev->source.subsys.u.pci.addr.slot,
1146 dev->source.subsys.u.pci.addr.function);
1148 virDomainHostdevSubsysPCIBackendType backend = dev->source.subsys.u.pci.backend;
1149 if (backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO ||
1150 backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) {
1151 needsVfio = true;
1154 if (pci == NULL)
1155 continue;
1157 rc = virPCIDeviceFileIterate(pci, file_iterate_pci_cb, &buf);
1158 virPCIDeviceFree(pci);
1160 break;
1163 default:
1164 rc = 0;
1165 break;
1166 } /* switch */
1169 for (i = 0; i < ctl->def->nfss; i++) {
1170 if (ctl->def->fss[i] &&
1171 ctl->def->fss[i]->type == VIR_DOMAIN_FS_TYPE_MOUNT &&
1172 (ctl->def->fss[i]->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH ||
1173 ctl->def->fss[i]->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT) &&
1174 ctl->def->fss[i]->src) {
1175 virDomainFSDefPtr fs = ctl->def->fss[i];
1177 /* We don't need to add deny rw rules for readonly mounts,
1178 * this can only lead to troubles when mounting / readonly.
1180 if (vah_add_path(&buf, fs->src->path, fs->readonly ? "R" : "rw", true) != 0)
1181 goto cleanup;
1185 for (i = 0; i < ctl->def->ninputs; i++) {
1186 if (ctl->def->inputs[i] &&
1187 ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH) {
1188 if (vah_add_file(&buf, ctl->def->inputs[i]->source.evdev, "rw") != 0)
1189 goto cleanup;
1193 for (i = 0; i < ctl->def->nnets; i++) {
1194 if (ctl->def->nets[i] &&
1195 ctl->def->nets[i]->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER &&
1196 ctl->def->nets[i]->data.vhostuser) {
1197 virDomainChrSourceDefPtr vhu = ctl->def->nets[i]->data.vhostuser;
1199 if (vah_add_file_chardev(&buf, vhu->data.nix.path, "rw",
1200 vhu->type) != 0)
1201 goto cleanup;
1205 for (i = 0; i < ctl->def->nmems; i++) {
1206 if (ctl->def->mems[i] &&
1207 ctl->def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
1208 if (vah_add_file(&buf, ctl->def->mems[i]->nvdimmPath, "rw") != 0)
1209 goto cleanup;
1213 if (ctl->def->tpm) {
1214 char *shortName = NULL;
1215 const char *tpmpath = NULL;
1217 switch (ctl->def->tpm->type) {
1218 case VIR_DOMAIN_TPM_TYPE_EMULATOR:
1219 shortName = virDomainDefGetShortName(ctl->def);
1221 switch (ctl->def->tpm->version) {
1222 case VIR_DOMAIN_TPM_VERSION_1_2:
1223 tpmpath = "tpm1.2";
1224 break;
1225 case VIR_DOMAIN_TPM_VERSION_2_0:
1226 tpmpath = "tpm2";
1227 break;
1228 case VIR_DOMAIN_TPM_VERSION_DEFAULT:
1229 case VIR_DOMAIN_TPM_VERSION_LAST:
1230 break;
1233 /* Unix socket for QEMU and swtpm to use */
1234 virBufferAsprintf(&buf,
1235 " \"/run/libvirt/qemu/swtpm/%s-swtpm.sock\" rw,\n",
1236 shortName);
1237 /* Paths for swtpm to use: give it access to its state
1238 * directory, log, and PID files.
1240 virBufferAsprintf(&buf,
1241 " \"%s/lib/libvirt/swtpm/%s/%s/**\" rw,\n",
1242 LOCALSTATEDIR, uuidstr, tpmpath);
1243 virBufferAsprintf(&buf,
1244 " \"%s/log/swtpm/libvirt/qemu/%s-swtpm.log\" a,\n",
1245 LOCALSTATEDIR, ctl->def->name);
1246 virBufferAsprintf(&buf,
1247 " \"/run/libvirt/qemu/swtpm/%s-swtpm.pid\" rw,\n",
1248 shortName);
1250 VIR_FREE(shortName);
1251 break;
1252 case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
1253 case VIR_DOMAIN_TPM_TYPE_LAST:
1254 break;
1258 if (ctl->def->virtType == VIR_DOMAIN_VIRT_KVM) {
1259 for (i = 0; i < ctl->def->nnets; i++) {
1260 virDomainNetDefPtr net = ctl->def->nets[i];
1261 if (net && virDomainNetGetModelString(net)) {
1262 if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_QEMU)
1263 continue;
1264 if (!virDomainNetIsVirtioModel(net))
1265 continue;
1267 needsvhost = true;
1270 if (needsvhost)
1271 virBufferAddLit(&buf, " \"/dev/vhost-net\" rw,\n");
1273 if (needsVfio) {
1274 virBufferAddLit(&buf, " \"/dev/vfio/vfio\" rw,\n");
1275 virBufferAddLit(&buf, " \"/dev/vfio/[0-9]*\" rw,\n");
1277 if (needsgl) {
1278 /* if using gl all sorts of further dri related paths will be needed */
1279 virBufferAddLit(&buf, " # DRI/Mesa/(e)GL config and driver paths\n");
1280 virBufferAddLit(&buf, " \"/usr/lib{,32,64}/dri/*.so*\" mr,\n");
1281 virBufferAddLit(&buf, " \"/usr/lib/@{multiarch}/dri/*.so*\" mr,\n");
1282 virBufferAddLit(&buf, " \"/usr/lib/fglrx/dri/*.so*\" mr,\n");
1283 virBufferAddLit(&buf, " \"/etc/drirc\" r,\n");
1284 virBufferAddLit(&buf, " \"/usr/share/drirc.d/{,*.conf}\" r,\n");
1285 virBufferAddLit(&buf, " \"/etc/glvnd/egl_vendor.d/{,*}\" r,\n");
1286 virBufferAddLit(&buf, " \"/usr/share/glvnd/egl_vendor.d/{,*}\" r,\n");
1287 virBufferAddLit(&buf, " \"/usr/share/egl/egl_external_platform.d/\" r,\n");
1288 virBufferAddLit(&buf, " \"/usr/share/egl/egl_external_platform.d/*\" r,\n");
1289 virBufferAddLit(&buf, " \"/proc/modules\" r,\n");
1290 virBufferAddLit(&buf, " \"/proc/driver/nvidia/params\" r,\n");
1291 virBufferAddLit(&buf, " \"/dev/nvidiactl\" rw,\n");
1292 virBufferAddLit(&buf, " # Probe DRI device attributes\n");
1293 virBufferAddLit(&buf, " \"/dev/dri/\" r,\n");
1294 virBufferAddLit(&buf, " \"/sys/devices/**/{uevent,vendor,device,subsystem_vendor,subsystem_device}\" r,\n");
1295 virBufferAddLit(&buf, " # dri libs will trigger that, but t is not requited and DAC would deny it anyway\n");
1296 virBufferAddLit(&buf, " deny \"/var/lib/libvirt/.cache/\" w,\n");
1299 if (ctl->newfile)
1300 if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
1301 goto cleanup;
1303 if (virBufferError(&buf)) {
1304 virBufferFreeAndReset(&buf);
1305 vah_error(NULL, 0, _("failed to allocate file buffer"));
1306 goto cleanup;
1309 rc = 0;
1310 ctl->files = virBufferContentAndReset(&buf);
1312 cleanup:
1313 VIR_FREE(uuid);
1314 return rc;
1317 static int
1318 vahParseArgv(vahControl * ctl, int argc, char **argv)
1320 int arg, idx = 0;
1321 struct option opt[] = {
1322 {"add", 0, 0, 'a'},
1323 {"create", 0, 0, 'c'},
1324 {"dryrun", 0, 0, 'd'},
1325 {"delete", 0, 0, 'D'},
1326 {"add-file", 0, 0, 'f'},
1327 {"append-file", 0, 0, 'F'},
1328 {"help", 0, 0, 'h'},
1329 {"replace", 0, 0, 'r'},
1330 {"remove", 0, 0, 'R'},
1331 {"uuid", 1, 0, 'u'},
1332 {0, 0, 0, 0}
1335 while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:", opt,
1336 &idx)) != -1) {
1337 switch (arg) {
1338 case 'a':
1339 ctl->cmd = 'a';
1340 break;
1341 case 'c':
1342 ctl->cmd = 'c';
1343 break;
1344 case 'd':
1345 ctl->dryrun = true;
1346 break;
1347 case 'D':
1348 ctl->cmd = 'D';
1349 break;
1350 case 'f':
1351 case 'F':
1352 if (VIR_STRDUP_QUIET(ctl->newfile, optarg) < 0)
1353 vah_error(ctl, 1, _("could not allocate memory for disk"));
1354 ctl->append = arg == 'F';
1355 break;
1356 case 'h':
1357 vah_usage();
1358 exit(EXIT_SUCCESS);
1359 break;
1360 case 'r':
1361 ctl->cmd = 'r';
1362 break;
1363 case 'R':
1364 ctl->cmd = 'R';
1365 break;
1366 case 'u':
1367 if (strlen(optarg) > PROFILE_NAME_SIZE - 1)
1368 vah_error(ctl, 1, _("invalid UUID"));
1369 if (virStrcpy((char *)ctl->uuid, optarg,
1370 PROFILE_NAME_SIZE) < 0)
1371 vah_error(ctl, 1, _("error copying UUID"));
1372 break;
1373 default:
1374 vah_error(ctl, 1, _("unsupported option"));
1375 break;
1378 if (strchr("acDrR", ctl->cmd) == NULL)
1379 vah_error(ctl, 1, _("bad command"));
1381 if (valid_uuid(ctl->uuid) != 0)
1382 vah_error(ctl, 1, _("invalid UUID"));
1384 if (!ctl->cmd) {
1385 vah_usage();
1386 exit(EXIT_FAILURE);
1389 if (ctl->cmd == 'c' || ctl->cmd == 'r') {
1390 char *xmlStr = NULL;
1391 if (virFileReadLimFD(STDIN_FILENO, MAX_FILE_LEN, &xmlStr) < 0)
1392 vah_error(ctl, 1, _("could not read xml file"));
1394 if (get_definition(ctl, xmlStr) != 0 || ctl->def == NULL) {
1395 VIR_FREE(xmlStr);
1396 vah_error(ctl, 1, _("could not get VM definition"));
1398 VIR_FREE(xmlStr);
1400 if (get_files(ctl) != 0)
1401 vah_error(ctl, 1, _("invalid VM definition"));
1403 return 0;
1408 * virt-aa-helper -c -u UUID < file.xml
1409 * virt-aa-helper -r -u UUID [-f <file>] < file.xml
1410 * virt-aa-helper -a -u UUID
1411 * virt-aa-helper -R -u UUID
1412 * virt-aa-helper -D -u UUID
1415 main(int argc, char **argv)
1417 vahControl _ctl, *ctl = &_ctl;
1418 virBuffer buf = VIR_BUFFER_INITIALIZER;
1419 int rc = -1;
1420 char *profile = NULL;
1421 char *include_file = NULL;
1423 if (virGettextInitialize() < 0 ||
1424 virThreadInitialize() < 0 ||
1425 virErrorInitialize() < 0) {
1426 fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
1427 exit(EXIT_FAILURE);
1430 virFileActivateDirOverride(argv[0]);
1432 /* Initialize the log system */
1433 virLogSetFromEnv();
1435 /* clear the environment */
1436 environ = NULL;
1437 if (setenv("PATH", "/sbin:/usr/sbin", 1) != 0)
1438 vah_error(ctl, 1, _("could not set PATH"));
1440 /* ensure the traditional IFS setting */
1441 if (setenv("IFS", " \t\n", 1) != 0)
1442 vah_error(ctl, 1, _("could not set IFS"));
1444 if (!(progname = strrchr(argv[0], '/')))
1445 progname = argv[0];
1446 else
1447 progname++;
1449 memset(ctl, 0, sizeof(vahControl));
1451 if (vahParseArgv(ctl, argc, argv) != 0)
1452 vah_error(ctl, 1, _("could not parse arguments"));
1454 if (virAsprintfQuiet(&profile, "%s/%s",
1455 APPARMOR_DIR "/libvirt", ctl->uuid) < 0)
1456 vah_error(ctl, 0, _("could not allocate memory"));
1458 if (virAsprintfQuiet(&include_file, "%s/%s.files",
1459 APPARMOR_DIR "/libvirt", ctl->uuid) < 0)
1460 vah_error(ctl, 0, _("could not allocate memory"));
1462 if (ctl->cmd == 'a') {
1463 rc = parserLoad(ctl->uuid);
1464 } else if (ctl->cmd == 'R' || ctl->cmd == 'D') {
1465 rc = parserRemove(ctl->uuid);
1466 if (ctl->cmd == 'D') {
1467 unlink(include_file);
1468 unlink(profile);
1470 } else if (ctl->cmd == 'c' || ctl->cmd == 'r') {
1471 char *included_files = NULL;
1473 if (ctl->cmd == 'c' && virFileExists(profile))
1474 vah_error(ctl, 1, _("profile exists"));
1476 if (ctl->append && ctl->newfile) {
1477 if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
1478 goto cleanup;
1479 } else {
1480 if (ctl->def->virtType == VIR_DOMAIN_VIRT_QEMU ||
1481 ctl->def->virtType == VIR_DOMAIN_VIRT_KQEMU ||
1482 ctl->def->virtType == VIR_DOMAIN_VIRT_KVM) {
1483 virBufferAsprintf(&buf, " \"%s/log/libvirt/**/%s.log\" w,\n",
1484 LOCALSTATEDIR, ctl->def->name);
1485 virBufferAsprintf(&buf, " \"%s/lib/libvirt/qemu/domain-%s/monitor.sock\" rw,\n",
1486 LOCALSTATEDIR, ctl->def->name);
1487 virBufferAsprintf(&buf, " \"%s/lib/libvirt/qemu/domain-%d-%.*s/*\" rw,\n",
1488 LOCALSTATEDIR, ctl->def->id, 20, ctl->def->name);
1489 virBufferAsprintf(&buf, " \"%s/run/libvirt/**/%s.pid\" rwk,\n",
1490 LOCALSTATEDIR, ctl->def->name);
1491 virBufferAsprintf(&buf, " \"/run/libvirt/**/%s.pid\" rwk,\n",
1492 ctl->def->name);
1493 virBufferAsprintf(&buf, " \"%s/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
1494 LOCALSTATEDIR, ctl->def->name);
1495 virBufferAsprintf(&buf, " \"/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
1496 ctl->def->name);
1498 if (ctl->files)
1499 virBufferAdd(&buf, ctl->files, -1);
1502 if (virBufferError(&buf)) {
1503 virBufferFreeAndReset(&buf);
1504 vah_error(ctl, 1, _("failed to allocate buffer"));
1507 included_files = virBufferContentAndReset(&buf);
1509 /* (re)create the include file using included_files */
1510 if (ctl->dryrun) {
1511 vah_info(include_file);
1512 vah_info(included_files);
1513 rc = 0;
1514 } else if (ctl->def->virtType == VIR_DOMAIN_VIRT_LXC) {
1515 rc = 0;
1516 } else if ((rc = update_include_file(include_file,
1517 included_files,
1518 ctl->append)) != 0) {
1519 goto cleanup;
1523 /* create the profile from TEMPLATE */
1524 if (ctl->cmd == 'c') {
1525 char *tmp = NULL;
1526 if (virAsprintfQuiet(&tmp, " #include <libvirt/%s.files>\n",
1527 ctl->uuid) == -1) {
1528 vah_error(ctl, 0, _("could not allocate memory"));
1529 goto cleanup;
1532 if (ctl->dryrun) {
1533 vah_info(profile);
1534 vah_info(ctl->uuid);
1535 vah_info(tmp);
1536 rc = 0;
1537 } else if ((rc = create_profile(profile, ctl->uuid, tmp,
1538 ctl->def->virtType)) != 0) {
1539 vah_error(ctl, 0, _("could not create profile"));
1540 unlink(include_file);
1542 VIR_FREE(tmp);
1545 if (rc == 0 && !ctl->dryrun) {
1546 if (ctl->cmd == 'c')
1547 rc = parserLoad(ctl->uuid);
1548 else
1549 rc = parserReplace(ctl->uuid);
1551 /* cleanup */
1552 if (rc != 0) {
1553 unlink(include_file);
1554 if (ctl->cmd == 'c')
1555 unlink(profile);
1558 cleanup:
1559 VIR_FREE(included_files);
1562 vahDeinit(ctl);
1564 VIR_FREE(profile);
1565 VIR_FREE(include_file);
1567 exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);