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/>.
26 #include <sys/types.h>
29 #include <sys/utsname.h>
32 #include "virbuffer.h"
34 #include "vircommand.h"
39 #include "security_driver.h"
40 #include "security_apparmor.h"
41 #include "domain_conf.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
;
58 char uuid
[PROFILE_NAME_SIZE
]; /* UUID of vm */
59 bool dryrun
; /* dry run */
60 char cmd
; /* 'c' create
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 */
76 vahDeinit(vahControl
* ctl
)
82 virObjectUnref(ctl
->caps
);
83 virObjectUnref(ctl
->xmlopt
);
85 VIR_FREE(ctl
->virtType
);
87 VIR_FREE(ctl
->newfile
);
98 printf(_("\n%s [options] [< def.xml]\n\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"
112 puts(_("This command is intended to be used by libvirtd "
113 "and not used directly.\n"));
118 vah_error(vahControl
* ctl
, int doexit
, const char *str
)
120 fprintf(stderr
, _("%s: error: %s%c"), progname
, str
, '\n');
130 vah_warning(const char *str
)
132 fprintf(stderr
, _("%s: warning: %s%c"), progname
, str
, '\n');
136 vah_info(const char *str
)
138 fprintf(stderr
, _("%s:\n%s%c"), progname
, str
, '\n');
142 * run an apparmor_parser command
145 parserCommand(const char *profile_name
, const char cmd
)
153 if (strchr("arR", cmd
) == NULL
) {
154 vah_error(NULL
, 0, _("invalid flag"));
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"));
166 if (!virFileExists(profile
)) {
167 vah_error(NULL
, 0, _("profile does not exist"));
170 const char * const argv
[] = {
171 "/sbin/apparmor_parser", flag
, profile
, NULL
173 VIR_AUTOPTR(virCommand
) command
= virCommandNewArgs(argv
);
175 if ((ret
= virCommandRun(command
, &status
)) != 0 ||
176 (WIFEXITED(status
) && WEXITSTATUS(status
) != 0)) {
178 vah_error(NULL
, 0, _("failed to run apparmor_parser"));
180 } else if (cmd
== 'R' && WIFEXITED(status
) &&
181 WEXITSTATUS(status
) == 234) {
182 vah_warning(_("unable to unload already unloaded profile"));
184 vah_error(NULL
, 0, _("apparmor_parser exited with error"));
199 * Update the dynamic files
202 update_include_file(const char *include_file
, const char *included_files
,
208 char *pcontent
= NULL
;
209 char *existing
= NULL
;
210 const char *warning
=
211 "# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
213 if (virFileExists(include_file
)) {
214 flen
= virFileReadAll(include_file
, MAX_FILE_LEN
, &existing
);
219 if (append
&& virFileExists(include_file
)) {
220 if (virAsprintfQuiet(&pcontent
, "%s%s", existing
, included_files
) == -1) {
221 vah_error(NULL
, 0, _("could not allocate memory for profile"));
225 if (virAsprintfQuiet(&pcontent
, "%s%s", warning
, included_files
) == -1) {
226 vah_error(NULL
, 0, _("could not allocate memory for profile"));
231 plen
= strlen(pcontent
);
232 if (plen
> MAX_FILE_LEN
) {
233 vah_error(NULL
, 0, _("invalid length for new profile"));
237 /* only update the disk profile if it is different */
238 if (flen
> 0 && flen
== plen
&& STREQLEN(existing
, pcontent
, plen
)) {
244 if ((fd
= open(include_file
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0644)) == -1) {
245 vah_error(NULL
, 0, _("failed to create include file"));
249 if (safewrite(fd
, pcontent
, plen
) < 0) { /* don't write the '\0' */
251 vah_error(NULL
, 0, _("failed to write to profile"));
255 if (VIR_CLOSE(fd
) != 0) {
256 vah_error(NULL
, 0, _("failed to close or write to profile"));
269 * Create a profile based on a template
272 create_profile(const char *profile
, const char *profile_name
,
273 const char *profile_files
, int virtType
)
276 char *tcontent
= NULL
;
277 char *pcontent
= NULL
;
278 char *replace_name
= NULL
;
279 char *replace_files
= NULL
;
281 const char *template_name
= "\nprofile LIBVIRT_TEMPLATE";
282 const char *template_end
= "\n}";
286 const char *driver_name
= NULL
;
288 if (virFileExists(profile
)) {
289 vah_error(NULL
, 0, _("profile exists"));
294 case VIR_DOMAIN_VIRT_QEMU
:
295 case VIR_DOMAIN_VIRT_KQEMU
:
296 case VIR_DOMAIN_VIRT_KVM
:
297 driver_name
= "qemu";
300 driver_name
= virDomainVirtTypeToString(virtType
);
303 if (virAsprintfQuiet(&template, "%s/TEMPLATE.%s", APPARMOR_DIR
"/libvirt",
305 vah_error(NULL
, 0, _("template name exceeds maximum length"));
309 if (!virFileExists(template)) {
310 vah_error(NULL
, 0, _("template does not exist"));
314 if ((tlen
= virFileReadAll(template, MAX_FILE_LEN
, &tcontent
)) < 0) {
315 vah_error(NULL
, 0, _("failed to read AppArmor template"));
319 if (strstr(tcontent
, template_name
) == NULL
) {
320 vah_error(NULL
, 0, _("no replacement string in template"));
324 if (strstr(tcontent
, template_end
) == NULL
) {
325 vah_error(NULL
, 0, _("no replacement string in template"));
329 /* '\nprofile <profile_name>\0' */
330 if (virAsprintfQuiet(&replace_name
, "\nprofile %s", profile_name
) == -1) {
331 vah_error(NULL
, 0, _("could not allocate memory for profile name"));
335 /* '\n<profile_files>\n}\0' */
336 if ((virtType
!= VIR_DOMAIN_VIRT_LXC
) &&
337 virAsprintfQuiet(&replace_files
, "\n%s\n}", profile_files
) == -1) {
338 vah_error(NULL
, 0, _("could not allocate memory for profile files"));
339 VIR_FREE(replace_name
);
343 plen
= tlen
+ strlen(replace_name
) - strlen(template_name
) + 1;
345 if (virtType
!= VIR_DOMAIN_VIRT_LXC
)
346 plen
+= strlen(replace_files
) - strlen(template_end
);
348 if (plen
> MAX_FILE_LEN
|| plen
< tlen
) {
349 vah_error(NULL
, 0, _("invalid length for new profile"));
353 if (!(pcontent
= virStringReplace(tcontent
, template_name
, replace_name
)))
356 if (virtType
!= VIR_DOMAIN_VIRT_LXC
) {
357 if (!(tmp
= virStringReplace(pcontent
, template_end
, replace_files
)))
360 VIR_STEAL_PTR(pcontent
, tmp
);
364 if ((fd
= open(profile
, O_CREAT
| O_EXCL
| O_WRONLY
, 0644)) == -1) {
365 vah_error(NULL
, 0, _("failed to create profile"));
369 if (safewrite(fd
, pcontent
, plen
- 1) < 0) { /* don't write the '\0' */
371 vah_error(NULL
, 0, _("failed to write to profile"));
375 if (VIR_CLOSE(fd
) != 0) {
376 vah_error(NULL
, 0, _("failed to close or write to profile"));
384 VIR_FREE(replace_name
);
385 VIR_FREE(replace_files
);
394 * Load an existing profile
397 parserLoad(const char *profile_name
)
399 return parserCommand(profile_name
, 'a');
403 * Remove an existing profile
406 parserRemove(const char *profile_name
)
408 return parserCommand(profile_name
, 'R');
412 * Replace an existing profile
415 parserReplace(const char *profile_name
)
417 return parserCommand(profile_name
, 'r');
421 valid_uuid(const char *uuid
)
423 unsigned char rawuuid
[VIR_UUID_BUFLEN
];
425 if (strlen(uuid
) != PROFILE_NAME_SIZE
- 1)
428 if (!STRPREFIX(uuid
, AA_PREFIX
))
431 if (virUUIDParse(uuid
+ strlen(AA_PREFIX
), rawuuid
) < 0)
438 valid_name(const char *name
)
440 /* just try to filter out any dangerous characters in the name that can be
441 * used to subvert the profile */
442 const char *bad
= "/[]{}?^,\"*";
444 if (strlen(name
) == 0)
447 if (strcspn(name
, bad
) != strlen(name
))
453 /* see if one of the strings in arr starts with str */
455 array_starts_with(const char *str
, const char * const *arr
, const long size
)
458 for (i
= 0; i
< size
; i
++) {
459 if (strlen(str
) < strlen(arr
[i
]))
462 if (STRPREFIX(str
, arr
[i
]))
469 * Don't allow access to special files or restricted paths such as /bin, /sbin,
470 * /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
471 * access to system files which could be used to elevate privileges. This is a
472 * safety measure in case libvirtd is under a restrictive profile and is
473 * subverted and trying to escape confinement.
475 * Note that we cannot exclude block devices because they are valid devices.
476 * The TEMPLATE file can be adjusted to explicitly disallow these if needed.
478 * RETURN: -1 on error, 0 if ok, 1 if blocked
481 valid_path(const char *path
, const bool readonly
)
483 const char * const restricted
[] = {
501 /* these paths are ok for readonly, but not read/write */
502 const char * const restricted_rw
[] = {
507 "/usr/share/OVMF/", /* for OVMF images */
508 "/usr/share/ovmf/", /* for OVMF images */
509 "/usr/share/AAVMF/", /* for AAVMF images */
510 "/usr/share/qemu-efi/", /* for AAVMF images */
511 "/usr/share/qemu-efi-aarch64/" /* for AAVMF images */
513 /* override the above with these */
514 const char * const override
[] = {
515 "/sys/devices/pci", /* for hostdev pci devices */
516 "/sys/kernel/config/target/vhost", /* for hostdev vhost_scsi devices */
517 "/etc/libvirt-sandbox/services/" /* for virt-sandbox service config */
520 const int nropaths
= ARRAY_CARDINALITY(restricted
);
521 const int nrwpaths
= ARRAY_CARDINALITY(restricted_rw
);
522 const int nopaths
= ARRAY_CARDINALITY(override
);
525 vah_error(NULL
, 0, _("bad pathname"));
529 /* Don't allow double quotes, since we use them to quote the filename
530 * and this will confuse the apparmor parser.
532 if (strchr(path
, '"') != NULL
)
535 /* Require an absolute path */
536 if (STRNEQLEN(path
, "/", 1))
539 if (!virFileExists(path
))
540 vah_warning(_("path does not exist, skipping file type checks"));
542 /* overrides are always allowed */
543 if (array_starts_with(path
, override
, nopaths
) == 0)
546 /* allow read only paths upfront */
548 if (array_starts_with(path
, restricted_rw
, nrwpaths
) == 0)
552 /* disallow RW access to all paths in restricted and restriced_rw */
553 if ((array_starts_with(path
, restricted
, nropaths
) == 0 ||
554 array_starts_with(path
, restricted_rw
, nrwpaths
) == 0))
561 verify_xpath_context(xmlXPathContextPtr ctxt
)
567 vah_warning(_("Invalid context"));
571 /* check if have <name> */
572 if (!(tmp
= virXPathString("string(./name[1])", ctxt
))) {
573 vah_warning(_("Could not find <name>"));
578 /* check if have <uuid> */
579 if (!(tmp
= virXPathString("string(./uuid[1])", ctxt
))) {
580 vah_warning(_("Could not find <uuid>"));
592 * Parse the xml we received to fill in the following:
597 * These are suitable for setting up a virCapsPtr
600 caps_mockup(vahControl
* ctl
, const char *xmlStr
)
603 xmlDocPtr xml
= NULL
;
604 xmlXPathContextPtr ctxt
= NULL
;
607 if (!(xml
= virXMLParseStringCtxt(xmlStr
, _("(domain_definition)"),
612 if (!virXMLNodeNameEqual(ctxt
->node
, "domain")) {
613 vah_error(NULL
, 0, _("unexpected root element, expecting <domain>"));
617 /* Quick sanity check for some required elements */
618 if (verify_xpath_context(ctxt
) != 0)
621 ctl
->virtType
= virXPathString("string(./@type)", ctxt
);
622 if (!ctl
->virtType
) {
623 vah_error(ctl
, 0, _("domain type is not defined"));
626 ctl
->os
= virXPathString("string(./os/type[1])", ctxt
);
628 vah_error(ctl
, 0, _("os.type is not defined"));
631 arch
= virXPathString("string(./os/type[1]/@arch)", ctxt
);
633 ctl
->arch
= virArchFromHost();
635 ctl
->arch
= virArchFromString(arch
);
643 xmlXPathFreeContext(ctxt
);
648 virDomainDefParserConfig virAAHelperDomainDefParserConfig
= {
649 .features
= VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG
|
650 VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN
|
651 VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS
|
652 VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING
,
656 get_definition(vahControl
* ctl
, const char *xmlStr
)
658 int rc
= -1, ostype
, virtType
;
659 virCapsGuestPtr guest
; /* this is freed when caps is freed */
662 * mock up some capabilities. We don't currently use these explicitly,
663 * but need them for virDomainDefParseString().
665 if (caps_mockup(ctl
, xmlStr
) != 0)
668 if ((ctl
->caps
= virCapabilitiesNew(ctl
->arch
, true, true)) == NULL
) {
669 vah_error(ctl
, 0, _("could not allocate memory"));
673 if (!(ctl
->xmlopt
= virDomainXMLOptionNew(&virAAHelperDomainDefParserConfig
,
674 NULL
, NULL
, NULL
, NULL
))) {
675 vah_error(ctl
, 0, _("Failed to create XML config object"));
679 if ((ostype
= virDomainOSTypeFromString(ctl
->os
)) < 0) {
680 vah_error(ctl
, 0, _("unknown OS type"));
684 if ((guest
= virCapabilitiesAddGuest(ctl
->caps
,
691 vah_error(ctl
, 0, _("could not allocate memory"));
695 if ((virtType
= virDomainVirtTypeFromString(ctl
->virtType
)) < 0) {
696 vah_error(ctl
, 0, _("unknown virtualization type"));
700 if (virCapabilitiesAddGuestDomain(guest
,
706 vah_error(ctl
, 0, _("could not allocate memory"));
710 ctl
->def
= virDomainDefParseString(xmlStr
,
711 ctl
->caps
, ctl
->xmlopt
, NULL
,
712 VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL
|
713 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE
);
715 if (ctl
->def
== NULL
) {
716 vah_error(ctl
, 0, _("could not parse XML"));
720 if (!ctl
->def
->name
) {
721 vah_error(ctl
, 0, _("could not find name in XML"));
725 if (valid_name(ctl
->def
->name
) != 0) {
726 vah_error(ctl
, 0, _("bad name"));
737 * The permissions allowed are apparmor valid permissions and 'R'. 'R' stands for
738 * read with no explicit deny rule.
741 vah_add_path(virBufferPtr buf
, const char *path
, const char *perms
, bool recursive
)
745 bool readonly
= true;
746 bool explicit_deny_rule
= true;
748 char *perms_new
= NULL
;
749 char *pathdir
= NULL
;
750 char *pathtmp
= NULL
;
751 char *pathreal
= NULL
;
756 /* Skip files without an absolute path. Not having one confuses the
757 * apparmor parser and this also ensures things like tcp consoles don't
758 * get added to the profile.
760 if (STRNEQLEN(path
, "/", 1)) {
762 vah_warning(_("skipped non-absolute path"));
766 /* files might be created by qemu later on and not exist right now.
767 * But realpath needs a valid path to work on, therefore:
768 * 1. walk the path to find longest valid path
769 * 2. get the realpath of that valid path
770 * 3. re-combine the realpath with the remaining suffix
771 * Note: A totally non existent path is used as-is
773 if (VIR_STRDUP_QUIET(pathdir
, path
) < 0)
775 while (!virFileExists(pathdir
)) {
776 if ((pathtmp
= mdir_name(pathdir
)) == NULL
)
779 VIR_STEAL_PTR(pathdir
, pathtmp
);
782 if (strlen(pathdir
) == 1) {
783 /* nothing of the path does exist yet */
784 if (VIR_STRDUP_QUIET(tmp
, path
) < 0)
787 if (VIR_STRDUP_QUIET(pathtmp
, path
+strlen(pathdir
)) < 0)
789 if ((pathreal
= realpath(pathdir
, NULL
)) == NULL
) {
790 vah_error(NULL
, 0, pathdir
);
791 vah_error(NULL
, 0, _("could not find realpath"));
794 if (virAsprintfQuiet(&tmp
, "%s%s", pathreal
, pathtmp
) < 0)
798 if (VIR_STRDUP_QUIET(perms_new
, perms
) < 0)
801 if (strchr(perms_new
, 'w') != NULL
) {
803 explicit_deny_rule
= false;
806 if ((sub
= strchr(perms_new
, 'R')) != NULL
) {
807 /* Don't write the invalid R permission, replace it with 'r' */
809 explicit_deny_rule
= false;
812 rc
= valid_path(tmp
, readonly
);
815 vah_error(NULL
, 0, path
);
816 vah_error(NULL
, 0, _("skipped restricted file"));
821 if (tmp
[strlen(tmp
) - 1] == '/')
822 tmp
[strlen(tmp
) - 1] = '\0';
824 virBufferAsprintf(buf
, " \"%s%s\" %s,\n", tmp
, recursive
? "/**" : "",
826 if (explicit_deny_rule
) {
827 virBufferAddLit(buf
, " # don't audit writes to readonly files\n");
828 virBufferAsprintf(buf
, " deny \"%s%s\" w,\n", tmp
, recursive
? "/**" : "");
831 /* allow reading (but not creating) the dir */
832 virBufferAsprintf(buf
, " \"%s/\" r,\n", tmp
);
846 vah_add_file(virBufferPtr buf
, const char *path
, const char *perms
)
848 return vah_add_path(buf
, path
, perms
, false);
852 vah_add_file_chardev(virBufferPtr buf
,
861 if (type
== VIR_DOMAIN_CHR_TYPE_PIPE
) {
862 /* add the pipe input */
863 if (virAsprintfQuiet(&pipe_in
, "%s.in", path
) == -1) {
864 vah_error(NULL
, 0, _("could not allocate memory"));
868 if (vah_add_file(buf
, pipe_in
, perms
) != 0)
871 /* add the pipe output */
872 if (virAsprintfQuiet(&pipe_out
, "%s.out", path
) == -1) {
873 vah_error(NULL
, 0, _("could not allocate memory"));
877 if (vah_add_file(buf
, pipe_out
, perms
) != 0)
887 if (vah_add_file(buf
, path
, perms
) != 0)
897 file_iterate_hostdev_cb(virUSBDevicePtr dev ATTRIBUTE_UNUSED
,
898 const char *file
, void *opaque
)
900 virBufferPtr buf
= opaque
;
901 return vah_add_file(buf
, file
, "rw");
905 file_iterate_pci_cb(virPCIDevicePtr dev ATTRIBUTE_UNUSED
,
906 const char *file
, void *opaque
)
908 virBufferPtr buf
= opaque
;
909 return vah_add_file(buf
, file
, "rw");
913 add_file_path(virDomainDiskDefPtr disk
,
918 virBufferPtr buf
= opaque
;
922 if (disk
->src
->readonly
)
923 ret
= vah_add_file(buf
, path
, "rk");
925 ret
= vah_add_file(buf
, path
, "rwk");
927 ret
= vah_add_file(buf
, path
, "rk");
937 get_files(vahControl
* ctl
)
939 virBuffer buf
= VIR_BUFFER_INITIALIZER
;
943 char uuidstr
[VIR_UUID_STRING_BUFLEN
];
944 bool needsVfio
= false, needsvhost
= false, needsgl
= false;
946 /* verify uuid is same as what we were given on the command line */
947 virUUIDFormat(ctl
->def
->uuid
, uuidstr
);
948 if (virAsprintfQuiet(&uuid
, "%s%s", AA_PREFIX
, uuidstr
) == -1) {
949 vah_error(ctl
, 0, _("could not allocate memory"));
953 if (STRNEQ(uuid
, ctl
->uuid
)) {
954 vah_error(ctl
, 0, _("given uuid does not match XML uuid"));
958 /* load the storage driver so that backing store can be accessed */
960 virDriverLoadModule("storage", "storageRegister", false);
963 for (i
= 0; i
< ctl
->def
->ndisks
; i
++) {
964 virDomainDiskDefPtr disk
= ctl
->def
->disks
[i
];
966 if (!virDomainDiskGetSource(disk
))
968 /* XXX - if we knew the qemu user:group here we could send it in
969 * so that the open could be re-tried as that user:group.
971 if (!virStorageSourceHasBacking(disk
->src
))
972 virStorageFileGetMetadata(disk
->src
, -1, -1, false);
974 /* XXX passing ignoreOpenFailure = true to get back to the behavior
975 * from before using virDomainDiskDefForeachPath. actually we should
976 * be passing ignoreOpenFailure = false and handle open errors more
977 * careful than just ignoring them.
979 if (virDomainDiskDefForeachPath(disk
, true, add_file_path
, &buf
) < 0)
983 for (i
= 0; i
< ctl
->def
->nserials
; i
++)
984 if (ctl
->def
->serials
[i
] &&
985 (ctl
->def
->serials
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PTY
||
986 ctl
->def
->serials
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_DEV
||
987 ctl
->def
->serials
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_FILE
||
988 ctl
->def
->serials
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_UNIX
||
989 ctl
->def
->serials
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PIPE
) &&
990 ctl
->def
->serials
[i
]->source
->data
.file
.path
&&
991 ctl
->def
->serials
[i
]->source
->data
.file
.path
[0] != '\0')
992 if (vah_add_file_chardev(&buf
,
993 ctl
->def
->serials
[i
]->source
->data
.file
.path
,
995 ctl
->def
->serials
[i
]->source
->type
) != 0)
998 for (i
= 0; i
< ctl
->def
->nconsoles
; i
++)
999 if (ctl
->def
->consoles
[i
] &&
1000 (ctl
->def
->consoles
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PTY
||
1001 ctl
->def
->consoles
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_DEV
||
1002 ctl
->def
->consoles
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_FILE
||
1003 ctl
->def
->consoles
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_UNIX
||
1004 ctl
->def
->consoles
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PIPE
) &&
1005 ctl
->def
->consoles
[i
]->source
->data
.file
.path
&&
1006 ctl
->def
->consoles
[i
]->source
->data
.file
.path
[0] != '\0')
1007 if (vah_add_file(&buf
,
1008 ctl
->def
->consoles
[i
]->source
->data
.file
.path
, "rw") != 0)
1011 for (i
= 0; i
< ctl
->def
->nparallels
; i
++)
1012 if (ctl
->def
->parallels
[i
] &&
1013 (ctl
->def
->parallels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PTY
||
1014 ctl
->def
->parallels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_DEV
||
1015 ctl
->def
->parallels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_FILE
||
1016 ctl
->def
->parallels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_UNIX
||
1017 ctl
->def
->parallels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PIPE
) &&
1018 ctl
->def
->parallels
[i
]->source
->data
.file
.path
&&
1019 ctl
->def
->parallels
[i
]->source
->data
.file
.path
[0] != '\0')
1020 if (vah_add_file_chardev(&buf
,
1021 ctl
->def
->parallels
[i
]->source
->data
.file
.path
,
1023 ctl
->def
->parallels
[i
]->source
->type
) != 0)
1026 for (i
= 0; i
< ctl
->def
->nchannels
; i
++)
1027 if (ctl
->def
->channels
[i
] &&
1028 (ctl
->def
->channels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PTY
||
1029 ctl
->def
->channels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_DEV
||
1030 ctl
->def
->channels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_FILE
||
1031 ctl
->def
->channels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_UNIX
||
1032 ctl
->def
->channels
[i
]->source
->type
== VIR_DOMAIN_CHR_TYPE_PIPE
) &&
1033 ctl
->def
->channels
[i
]->source
->data
.file
.path
&&
1034 ctl
->def
->channels
[i
]->source
->data
.file
.path
[0] != '\0')
1035 if (vah_add_file_chardev(&buf
,
1036 ctl
->def
->channels
[i
]->source
->data
.file
.path
,
1038 ctl
->def
->channels
[i
]->source
->type
) != 0)
1041 if (ctl
->def
->os
.kernel
)
1042 if (vah_add_file(&buf
, ctl
->def
->os
.kernel
, "r") != 0)
1045 if (ctl
->def
->os
.initrd
)
1046 if (vah_add_file(&buf
, ctl
->def
->os
.initrd
, "r") != 0)
1049 if (ctl
->def
->os
.dtb
)
1050 if (vah_add_file(&buf
, ctl
->def
->os
.dtb
, "r") != 0)
1053 if (ctl
->def
->os
.slic_table
)
1054 if (vah_add_file(&buf
, ctl
->def
->os
.slic_table
, "r") != 0)
1057 if (ctl
->def
->os
.loader
&& ctl
->def
->os
.loader
->path
)
1058 if (vah_add_file(&buf
, ctl
->def
->os
.loader
->path
, "rk") != 0)
1061 if (ctl
->def
->os
.loader
&& ctl
->def
->os
.loader
->nvram
)
1062 if (vah_add_file(&buf
, ctl
->def
->os
.loader
->nvram
, "rwk") != 0)
1065 for (i
= 0; i
< ctl
->def
->ngraphics
; i
++) {
1066 virDomainGraphicsDefPtr graphics
= ctl
->def
->graphics
[i
];
1068 const char *rendernode
= virDomainGraphicsGetRenderNode(graphics
);
1071 vah_add_file(&buf
, rendernode
, "rw");
1074 if (virDomainGraphicsNeedsAutoRenderNode(graphics
)) {
1075 char *defaultRenderNode
= virHostGetDRMRenderNode();
1078 if (defaultRenderNode
) {
1079 vah_add_file(&buf
, defaultRenderNode
, "rw");
1080 VIR_FREE(defaultRenderNode
);
1085 for (n
= 0; n
< graphics
->nListens
; n
++) {
1086 virDomainGraphicsListenDef listenObj
= graphics
->listens
[n
];
1088 if (listenObj
.type
== VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET
&&
1090 vah_add_file(&buf
, listenObj
.socket
, "rw"))
1095 if (ctl
->def
->ngraphics
== 1 &&
1096 ctl
->def
->graphics
[0]->type
== VIR_DOMAIN_GRAPHICS_TYPE_SDL
)
1097 if (vah_add_file(&buf
, ctl
->def
->graphics
[0]->data
.sdl
.xauth
,
1101 for (i
= 0; i
< ctl
->def
->nhostdevs
; i
++)
1102 if (ctl
->def
->hostdevs
[i
]) {
1103 virDomainHostdevDefPtr dev
= ctl
->def
->hostdevs
[i
];
1104 virDomainHostdevSubsysUSBPtr usbsrc
= &dev
->source
.subsys
.u
.usb
;
1105 switch (dev
->source
.subsys
.type
) {
1106 case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB
: {
1107 virUSBDevicePtr usb
=
1108 virUSBDeviceNew(usbsrc
->bus
, usbsrc
->device
, NULL
);
1113 if (virHostdevFindUSBDevice(dev
, true, &usb
) < 0)
1116 rc
= virUSBDeviceFileIterate(usb
, file_iterate_hostdev_cb
, &buf
);
1117 virUSBDeviceFree(usb
);
1123 case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV
: {
1124 virDomainHostdevSubsysMediatedDevPtr mdevsrc
= &dev
->source
.subsys
.u
.mdev
;
1125 switch ((virMediatedDeviceModelType
) mdevsrc
->model
) {
1126 case VIR_MDEV_MODEL_TYPE_VFIO_PCI
:
1127 case VIR_MDEV_MODEL_TYPE_VFIO_AP
:
1128 case VIR_MDEV_MODEL_TYPE_VFIO_CCW
:
1131 case VIR_MDEV_MODEL_TYPE_LAST
:
1133 virReportEnumRangeError(virMediatedDeviceModelType
,
1140 case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI
: {
1141 virPCIDevicePtr pci
= virPCIDeviceNew(
1142 dev
->source
.subsys
.u
.pci
.addr
.domain
,
1143 dev
->source
.subsys
.u
.pci
.addr
.bus
,
1144 dev
->source
.subsys
.u
.pci
.addr
.slot
,
1145 dev
->source
.subsys
.u
.pci
.addr
.function
);
1147 virDomainHostdevSubsysPCIBackendType backend
= dev
->source
.subsys
.u
.pci
.backend
;
1148 if (backend
== VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO
||
1149 backend
== VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT
) {
1156 rc
= virPCIDeviceFileIterate(pci
, file_iterate_pci_cb
, &buf
);
1157 virPCIDeviceFree(pci
);
1168 for (i
= 0; i
< ctl
->def
->nfss
; i
++) {
1169 if (ctl
->def
->fss
[i
] &&
1170 ctl
->def
->fss
[i
]->type
== VIR_DOMAIN_FS_TYPE_MOUNT
&&
1171 (ctl
->def
->fss
[i
]->fsdriver
== VIR_DOMAIN_FS_DRIVER_TYPE_PATH
||
1172 ctl
->def
->fss
[i
]->fsdriver
== VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT
) &&
1173 ctl
->def
->fss
[i
]->src
) {
1174 virDomainFSDefPtr fs
= ctl
->def
->fss
[i
];
1176 /* We don't need to add deny rw rules for readonly mounts,
1177 * this can only lead to troubles when mounting / readonly.
1179 if (vah_add_path(&buf
, fs
->src
->path
, fs
->readonly
? "R" : "rw", true) != 0)
1184 for (i
= 0; i
< ctl
->def
->ninputs
; i
++) {
1185 if (ctl
->def
->inputs
[i
] &&
1186 ctl
->def
->inputs
[i
]->type
== VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH
) {
1187 if (vah_add_file(&buf
, ctl
->def
->inputs
[i
]->source
.evdev
, "rw") != 0)
1192 for (i
= 0; i
< ctl
->def
->nnets
; i
++) {
1193 if (ctl
->def
->nets
[i
] &&
1194 ctl
->def
->nets
[i
]->type
== VIR_DOMAIN_NET_TYPE_VHOSTUSER
&&
1195 ctl
->def
->nets
[i
]->data
.vhostuser
) {
1196 virDomainChrSourceDefPtr vhu
= ctl
->def
->nets
[i
]->data
.vhostuser
;
1198 if (vah_add_file_chardev(&buf
, vhu
->data
.nix
.path
, "rw",
1204 for (i
= 0; i
< ctl
->def
->nmems
; i
++) {
1205 if (ctl
->def
->mems
[i
] &&
1206 ctl
->def
->mems
[i
]->model
== VIR_DOMAIN_MEMORY_MODEL_NVDIMM
) {
1207 if (vah_add_file(&buf
, ctl
->def
->mems
[i
]->nvdimmPath
, "rw") != 0)
1212 if (ctl
->def
->tpm
) {
1213 char *shortName
= NULL
;
1214 const char *tpmpath
= NULL
;
1216 switch (ctl
->def
->tpm
->type
) {
1217 case VIR_DOMAIN_TPM_TYPE_EMULATOR
:
1218 shortName
= virDomainDefGetShortName(ctl
->def
);
1220 switch (ctl
->def
->tpm
->version
) {
1221 case VIR_DOMAIN_TPM_VERSION_1_2
:
1224 case VIR_DOMAIN_TPM_VERSION_2_0
:
1227 case VIR_DOMAIN_TPM_VERSION_DEFAULT
:
1228 case VIR_DOMAIN_TPM_VERSION_LAST
:
1232 /* Unix socket for QEMU and swtpm to use */
1233 virBufferAsprintf(&buf
,
1234 " \"/run/libvirt/qemu/swtpm/%s-swtpm.sock\" rw,\n",
1236 /* Paths for swtpm to use: give it access to its state
1237 * directory, log, and PID files.
1239 virBufferAsprintf(&buf
,
1240 " \"%s/lib/libvirt/swtpm/%s/%s/**\" rw,\n",
1241 LOCALSTATEDIR
, uuidstr
, tpmpath
);
1242 virBufferAsprintf(&buf
,
1243 " \"%s/log/swtpm/libvirt/qemu/%s-swtpm.log\" a,\n",
1244 LOCALSTATEDIR
, ctl
->def
->name
);
1245 virBufferAsprintf(&buf
,
1246 " \"/run/libvirt/qemu/swtpm/%s-swtpm.pid\" rw,\n",
1249 VIR_FREE(shortName
);
1251 case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH
:
1252 case VIR_DOMAIN_TPM_TYPE_LAST
:
1257 if (ctl
->def
->virtType
== VIR_DOMAIN_VIRT_KVM
) {
1258 for (i
= 0; i
< ctl
->def
->nnets
; i
++) {
1259 virDomainNetDefPtr net
= ctl
->def
->nets
[i
];
1260 if (net
&& virDomainNetGetModelString(net
)) {
1261 if (net
->driver
.virtio
.name
== VIR_DOMAIN_NET_BACKEND_TYPE_QEMU
)
1263 if (!virDomainNetIsVirtioModel(net
))
1270 virBufferAddLit(&buf
, " \"/dev/vhost-net\" rw,\n");
1273 virBufferAddLit(&buf
, " \"/dev/vfio/vfio\" rw,\n");
1274 virBufferAddLit(&buf
, " \"/dev/vfio/[0-9]*\" rw,\n");
1277 /* if using gl all sorts of further dri related paths will be needed */
1278 virBufferAddLit(&buf
, " # DRI/Mesa/(e)GL config and driver paths\n");
1279 virBufferAddLit(&buf
, " \"/usr/lib{,32,64}/dri/*.so*\" mr,\n");
1280 virBufferAddLit(&buf
, " \"/usr/lib/@{multiarch}/dri/*.so*\" mr,\n");
1281 virBufferAddLit(&buf
, " \"/usr/lib/fglrx/dri/*.so*\" mr,\n");
1282 virBufferAddLit(&buf
, " \"/etc/drirc\" r,\n");
1283 virBufferAddLit(&buf
, " \"/usr/share/drirc.d/{,*.conf}\" r,\n");
1284 virBufferAddLit(&buf
, " \"/etc/glvnd/egl_vendor.d/{,*}\" r,\n");
1285 virBufferAddLit(&buf
, " \"/usr/share/glvnd/egl_vendor.d/{,*}\" r,\n");
1286 virBufferAddLit(&buf
, " \"/usr/share/egl/egl_external_platform.d/\" r,\n");
1287 virBufferAddLit(&buf
, " \"/usr/share/egl/egl_external_platform.d/*\" r,\n");
1288 virBufferAddLit(&buf
, " \"/proc/modules\" r,\n");
1289 virBufferAddLit(&buf
, " \"/proc/driver/nvidia/params\" r,\n");
1290 virBufferAddLit(&buf
, " \"/dev/nvidiactl\" rw,\n");
1291 virBufferAddLit(&buf
, " # Probe DRI device attributes\n");
1292 virBufferAddLit(&buf
, " \"/dev/dri/\" r,\n");
1293 virBufferAddLit(&buf
, " \"/sys/devices/**/{uevent,vendor,device,subsystem_vendor,subsystem_device}\" r,\n");
1294 virBufferAddLit(&buf
, " # dri libs will trigger that, but t is not requited and DAC would deny it anyway\n");
1295 virBufferAddLit(&buf
, " deny \"/var/lib/libvirt/.cache/\" w,\n");
1299 if (vah_add_file(&buf
, ctl
->newfile
, "rwk") != 0)
1302 if (virBufferError(&buf
)) {
1303 virBufferFreeAndReset(&buf
);
1304 vah_error(NULL
, 0, _("failed to allocate file buffer"));
1309 ctl
->files
= virBufferContentAndReset(&buf
);
1317 vahParseArgv(vahControl
* ctl
, int argc
, char **argv
)
1320 struct option opt
[] = {
1322 {"create", 0, 0, 'c'},
1323 {"dryrun", 0, 0, 'd'},
1324 {"delete", 0, 0, 'D'},
1325 {"add-file", 0, 0, 'f'},
1326 {"append-file", 0, 0, 'F'},
1327 {"help", 0, 0, 'h'},
1328 {"replace", 0, 0, 'r'},
1329 {"remove", 0, 0, 'R'},
1330 {"uuid", 1, 0, 'u'},
1334 while ((arg
= getopt_long(argc
, argv
, "acdDhrRH:b:u:p:f:F:", opt
,
1351 if (VIR_STRDUP_QUIET(ctl
->newfile
, optarg
) < 0)
1352 vah_error(ctl
, 1, _("could not allocate memory for disk"));
1353 ctl
->append
= arg
== 'F';
1366 if (strlen(optarg
) > PROFILE_NAME_SIZE
- 1)
1367 vah_error(ctl
, 1, _("invalid UUID"));
1368 if (virStrcpy((char *)ctl
->uuid
, optarg
,
1369 PROFILE_NAME_SIZE
) < 0)
1370 vah_error(ctl
, 1, _("error copying UUID"));
1373 vah_error(ctl
, 1, _("unsupported option"));
1377 if (strchr("acDrR", ctl
->cmd
) == NULL
)
1378 vah_error(ctl
, 1, _("bad command"));
1380 if (valid_uuid(ctl
->uuid
) != 0)
1381 vah_error(ctl
, 1, _("invalid UUID"));
1388 if (ctl
->cmd
== 'c' || ctl
->cmd
== 'r') {
1389 char *xmlStr
= NULL
;
1390 if (virFileReadLimFD(STDIN_FILENO
, MAX_FILE_LEN
, &xmlStr
) < 0)
1391 vah_error(ctl
, 1, _("could not read xml file"));
1393 if (get_definition(ctl
, xmlStr
) != 0 || ctl
->def
== NULL
) {
1395 vah_error(ctl
, 1, _("could not get VM definition"));
1399 if (get_files(ctl
) != 0)
1400 vah_error(ctl
, 1, _("invalid VM definition"));
1407 * virt-aa-helper -c -u UUID < file.xml
1408 * virt-aa-helper -r -u UUID [-f <file>] < file.xml
1409 * virt-aa-helper -a -u UUID
1410 * virt-aa-helper -R -u UUID
1411 * virt-aa-helper -D -u UUID
1414 main(int argc
, char **argv
)
1416 vahControl _ctl
, *ctl
= &_ctl
;
1417 virBuffer buf
= VIR_BUFFER_INITIALIZER
;
1419 char *profile
= NULL
;
1420 char *include_file
= NULL
;
1422 if (virGettextInitialize() < 0 ||
1423 virThreadInitialize() < 0 ||
1424 virErrorInitialize() < 0) {
1425 fprintf(stderr
, _("%s: initialization failed\n"), argv
[0]);
1429 virFileActivateDirOverride(argv
[0]);
1431 /* Initialize the log system */
1434 /* clear the environment */
1436 if (setenv("PATH", "/sbin:/usr/sbin", 1) != 0)
1437 vah_error(ctl
, 1, _("could not set PATH"));
1439 /* ensure the traditional IFS setting */
1440 if (setenv("IFS", " \t\n", 1) != 0)
1441 vah_error(ctl
, 1, _("could not set IFS"));
1443 if (!(progname
= strrchr(argv
[0], '/')))
1448 memset(ctl
, 0, sizeof(vahControl
));
1450 if (vahParseArgv(ctl
, argc
, argv
) != 0)
1451 vah_error(ctl
, 1, _("could not parse arguments"));
1453 if (virAsprintfQuiet(&profile
, "%s/%s",
1454 APPARMOR_DIR
"/libvirt", ctl
->uuid
) < 0)
1455 vah_error(ctl
, 0, _("could not allocate memory"));
1457 if (virAsprintfQuiet(&include_file
, "%s/%s.files",
1458 APPARMOR_DIR
"/libvirt", ctl
->uuid
) < 0)
1459 vah_error(ctl
, 0, _("could not allocate memory"));
1461 if (ctl
->cmd
== 'a') {
1462 rc
= parserLoad(ctl
->uuid
);
1463 } else if (ctl
->cmd
== 'R' || ctl
->cmd
== 'D') {
1464 rc
= parserRemove(ctl
->uuid
);
1465 if (ctl
->cmd
== 'D') {
1466 unlink(include_file
);
1469 } else if (ctl
->cmd
== 'c' || ctl
->cmd
== 'r') {
1470 char *included_files
= NULL
;
1472 if (ctl
->cmd
== 'c' && virFileExists(profile
))
1473 vah_error(ctl
, 1, _("profile exists"));
1475 if (ctl
->append
&& ctl
->newfile
) {
1476 if (vah_add_file(&buf
, ctl
->newfile
, "rwk") != 0)
1479 if (ctl
->def
->virtType
== VIR_DOMAIN_VIRT_QEMU
||
1480 ctl
->def
->virtType
== VIR_DOMAIN_VIRT_KQEMU
||
1481 ctl
->def
->virtType
== VIR_DOMAIN_VIRT_KVM
) {
1482 virBufferAsprintf(&buf
, " \"%s/log/libvirt/**/%s.log\" w,\n",
1483 LOCALSTATEDIR
, ctl
->def
->name
);
1484 virBufferAsprintf(&buf
, " \"%s/lib/libvirt/qemu/domain-%s/monitor.sock\" rw,\n",
1485 LOCALSTATEDIR
, ctl
->def
->name
);
1486 virBufferAsprintf(&buf
, " \"%s/lib/libvirt/qemu/domain-%d-%.*s/*\" rw,\n",
1487 LOCALSTATEDIR
, ctl
->def
->id
, 20, ctl
->def
->name
);
1488 virBufferAsprintf(&buf
, " \"%s/run/libvirt/**/%s.pid\" rwk,\n",
1489 LOCALSTATEDIR
, ctl
->def
->name
);
1490 virBufferAsprintf(&buf
, " \"/run/libvirt/**/%s.pid\" rwk,\n",
1492 virBufferAsprintf(&buf
, " \"%s/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
1493 LOCALSTATEDIR
, ctl
->def
->name
);
1494 virBufferAsprintf(&buf
, " \"/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
1498 virBufferAdd(&buf
, ctl
->files
, -1);
1501 if (virBufferError(&buf
)) {
1502 virBufferFreeAndReset(&buf
);
1503 vah_error(ctl
, 1, _("failed to allocate buffer"));
1506 included_files
= virBufferContentAndReset(&buf
);
1508 /* (re)create the include file using included_files */
1510 vah_info(include_file
);
1511 vah_info(included_files
);
1513 } else if (ctl
->def
->virtType
== VIR_DOMAIN_VIRT_LXC
) {
1515 } else if ((rc
= update_include_file(include_file
,
1517 ctl
->append
)) != 0) {
1522 /* create the profile from TEMPLATE */
1523 if (ctl
->cmd
== 'c') {
1525 if (virAsprintfQuiet(&tmp
, " #include <libvirt/%s.files>\n",
1527 vah_error(ctl
, 0, _("could not allocate memory"));
1533 vah_info(ctl
->uuid
);
1536 } else if ((rc
= create_profile(profile
, ctl
->uuid
, tmp
,
1537 ctl
->def
->virtType
)) != 0) {
1538 vah_error(ctl
, 0, _("could not create profile"));
1539 unlink(include_file
);
1544 if (rc
== 0 && !ctl
->dryrun
) {
1545 if (ctl
->cmd
== 'c')
1546 rc
= parserLoad(ctl
->uuid
);
1548 rc
= parserReplace(ctl
->uuid
);
1552 unlink(include_file
);
1553 if (ctl
->cmd
== 'c')
1558 VIR_FREE(included_files
);
1564 VIR_FREE(include_file
);
1566 exit(rc
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
);