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 virCommandRawStatus(command
);
176 if ((ret
= virCommandRun(command
, &status
)) != 0 ||
177 (WIFEXITED(status
) && WEXITSTATUS(status
) != 0)) {
179 vah_error(NULL
, 0, _("failed to run apparmor_parser"));
181 } else if (cmd
== 'R' && WIFEXITED(status
) &&
182 WEXITSTATUS(status
) == 234) {
183 vah_warning(_("unable to unload already unloaded profile"));
185 vah_error(NULL
, 0, _("apparmor_parser exited with error"));
200 * Update the dynamic files
203 update_include_file(const char *include_file
, const char *included_files
,
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
);
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"));
226 if (virAsprintfQuiet(&pcontent
, "%s%s", warning
, included_files
) == -1) {
227 vah_error(NULL
, 0, _("could not allocate memory for profile"));
232 plen
= strlen(pcontent
);
233 if (plen
> MAX_FILE_LEN
) {
234 vah_error(NULL
, 0, _("invalid length for new profile"));
238 /* only update the disk profile if it is different */
239 if (flen
> 0 && flen
== plen
&& STREQLEN(existing
, pcontent
, plen
)) {
245 if ((fd
= open(include_file
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0644)) == -1) {
246 vah_error(NULL
, 0, _("failed to create include file"));
250 if (safewrite(fd
, pcontent
, plen
) < 0) { /* don't write the '\0' */
252 vah_error(NULL
, 0, _("failed to write to profile"));
256 if (VIR_CLOSE(fd
) != 0) {
257 vah_error(NULL
, 0, _("failed to close or write to profile"));
270 * Create a profile based on a template
273 create_profile(const char *profile
, const char *profile_name
,
274 const char *profile_files
, int virtType
)
277 char *tcontent
= NULL
;
278 char *pcontent
= NULL
;
279 char *replace_name
= NULL
;
280 char *replace_files
= NULL
;
282 const char *template_name
= "\nprofile LIBVIRT_TEMPLATE";
283 const char *template_end
= "\n}";
287 const char *driver_name
= NULL
;
289 if (virFileExists(profile
)) {
290 vah_error(NULL
, 0, _("profile exists"));
295 case VIR_DOMAIN_VIRT_QEMU
:
296 case VIR_DOMAIN_VIRT_KQEMU
:
297 case VIR_DOMAIN_VIRT_KVM
:
298 driver_name
= "qemu";
301 driver_name
= virDomainVirtTypeToString(virtType
);
304 if (virAsprintfQuiet(&template, "%s/TEMPLATE.%s", APPARMOR_DIR
"/libvirt",
306 vah_error(NULL
, 0, _("template name exceeds maximum length"));
310 if (!virFileExists(template)) {
311 vah_error(NULL
, 0, _("template does not exist"));
315 if ((tlen
= virFileReadAll(template, MAX_FILE_LEN
, &tcontent
)) < 0) {
316 vah_error(NULL
, 0, _("failed to read AppArmor template"));
320 if (strstr(tcontent
, template_name
) == NULL
) {
321 vah_error(NULL
, 0, _("no replacement string in template"));
325 if (strstr(tcontent
, template_end
) == NULL
) {
326 vah_error(NULL
, 0, _("no replacement string in template"));
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"));
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
);
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"));
354 if (!(pcontent
= virStringReplace(tcontent
, template_name
, replace_name
)))
357 if (virtType
!= VIR_DOMAIN_VIRT_LXC
) {
358 if (!(tmp
= virStringReplace(pcontent
, template_end
, replace_files
)))
361 VIR_STEAL_PTR(pcontent
, tmp
);
365 if ((fd
= open(profile
, O_CREAT
| O_EXCL
| O_WRONLY
, 0644)) == -1) {
366 vah_error(NULL
, 0, _("failed to create profile"));
370 if (safewrite(fd
, pcontent
, plen
- 1) < 0) { /* don't write the '\0' */
372 vah_error(NULL
, 0, _("failed to write to profile"));
376 if (VIR_CLOSE(fd
) != 0) {
377 vah_error(NULL
, 0, _("failed to close or write to profile"));
385 VIR_FREE(replace_name
);
386 VIR_FREE(replace_files
);
395 * Load an existing profile
398 parserLoad(const char *profile_name
)
400 return parserCommand(profile_name
, 'a');
404 * Remove an existing profile
407 parserRemove(const char *profile_name
)
409 return parserCommand(profile_name
, 'R');
413 * Replace an existing profile
416 parserReplace(const char *profile_name
)
418 return parserCommand(profile_name
, 'r');
422 valid_uuid(const char *uuid
)
424 unsigned char rawuuid
[VIR_UUID_BUFLEN
];
426 if (strlen(uuid
) != PROFILE_NAME_SIZE
- 1)
429 if (!STRPREFIX(uuid
, AA_PREFIX
))
432 if (virUUIDParse(uuid
+ strlen(AA_PREFIX
), rawuuid
) < 0)
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)
448 if (strcspn(name
, bad
) != strlen(name
))
454 /* see if one of the strings in arr starts with str */
456 array_starts_with(const char *str
, const char * const *arr
, const long size
)
459 for (i
= 0; i
< size
; i
++) {
460 if (strlen(str
) < strlen(arr
[i
]))
463 if (STRPREFIX(str
, arr
[i
]))
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
482 valid_path(const char *path
, const bool readonly
)
484 const char * const restricted
[] = {
502 /* these paths are ok for readonly, but not read/write */
503 const char * const restricted_rw
[] = {
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
);
526 vah_error(NULL
, 0, _("bad pathname"));
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
)
536 /* Require an absolute path */
537 if (STRNEQLEN(path
, "/", 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)
547 /* allow read only paths upfront */
549 if (array_starts_with(path
, restricted_rw
, nrwpaths
) == 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))
562 verify_xpath_context(xmlXPathContextPtr ctxt
)
568 vah_warning(_("Invalid context"));
572 /* check if have <name> */
573 if (!(tmp
= virXPathString("string(./name[1])", ctxt
))) {
574 vah_warning(_("Could not find <name>"));
579 /* check if have <uuid> */
580 if (!(tmp
= virXPathString("string(./uuid[1])", ctxt
))) {
581 vah_warning(_("Could not find <uuid>"));
593 * Parse the xml we received to fill in the following:
598 * These are suitable for setting up a virCapsPtr
601 caps_mockup(vahControl
* ctl
, const char *xmlStr
)
604 xmlDocPtr xml
= NULL
;
605 xmlXPathContextPtr ctxt
= NULL
;
608 if (!(xml
= virXMLParseStringCtxt(xmlStr
, _("(domain_definition)"),
613 if (!virXMLNodeNameEqual(ctxt
->node
, "domain")) {
614 vah_error(NULL
, 0, _("unexpected root element, expecting <domain>"));
618 /* Quick sanity check for some required elements */
619 if (verify_xpath_context(ctxt
) != 0)
622 ctl
->virtType
= virXPathString("string(./@type)", ctxt
);
623 if (!ctl
->virtType
) {
624 vah_error(ctl
, 0, _("domain type is not defined"));
627 ctl
->os
= virXPathString("string(./os/type[1])", ctxt
);
629 vah_error(ctl
, 0, _("os.type is not defined"));
632 arch
= virXPathString("string(./os/type[1]/@arch)", ctxt
);
634 ctl
->arch
= virArchFromHost();
636 ctl
->arch
= virArchFromString(arch
);
644 xmlXPathFreeContext(ctxt
);
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
,
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)
669 if ((ctl
->caps
= virCapabilitiesNew(ctl
->arch
, true, true)) == NULL
) {
670 vah_error(ctl
, 0, _("could not allocate memory"));
674 if (!(ctl
->xmlopt
= virDomainXMLOptionNew(&virAAHelperDomainDefParserConfig
,
675 NULL
, NULL
, NULL
, NULL
))) {
676 vah_error(ctl
, 0, _("Failed to create XML config object"));
680 if ((ostype
= virDomainOSTypeFromString(ctl
->os
)) < 0) {
681 vah_error(ctl
, 0, _("unknown OS type"));
685 if ((guest
= virCapabilitiesAddGuest(ctl
->caps
,
692 vah_error(ctl
, 0, _("could not allocate memory"));
696 if ((virtType
= virDomainVirtTypeFromString(ctl
->virtType
)) < 0) {
697 vah_error(ctl
, 0, _("unknown virtualization type"));
701 if (virCapabilitiesAddGuestDomain(guest
,
707 vah_error(ctl
, 0, _("could not allocate memory"));
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"));
721 if (!ctl
->def
->name
) {
722 vah_error(ctl
, 0, _("could not find name in XML"));
726 if (valid_name(ctl
->def
->name
) != 0) {
727 vah_error(ctl
, 0, _("bad name"));
738 * The permissions allowed are apparmor valid permissions and 'R'. 'R' stands for
739 * read with no explicit deny rule.
742 vah_add_path(virBufferPtr buf
, const char *path
, const char *perms
, bool recursive
)
746 bool readonly
= true;
747 bool explicit_deny_rule
= true;
749 char *perms_new
= NULL
;
750 char *pathdir
= NULL
;
751 char *pathtmp
= NULL
;
752 char *pathreal
= NULL
;
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)) {
763 vah_warning(_("skipped non-absolute path"));
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)
776 while (!virFileExists(pathdir
)) {
777 if ((pathtmp
= mdir_name(pathdir
)) == NULL
)
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)
788 if (VIR_STRDUP_QUIET(pathtmp
, path
+strlen(pathdir
)) < 0)
790 if ((pathreal
= realpath(pathdir
, NULL
)) == NULL
) {
791 vah_error(NULL
, 0, pathdir
);
792 vah_error(NULL
, 0, _("could not find realpath"));
795 if (virAsprintfQuiet(&tmp
, "%s%s", pathreal
, pathtmp
) < 0)
799 if (VIR_STRDUP_QUIET(perms_new
, perms
) < 0)
802 if (strchr(perms_new
, 'w') != NULL
) {
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' */
810 explicit_deny_rule
= false;
813 rc
= valid_path(tmp
, readonly
);
816 vah_error(NULL
, 0, path
);
817 vah_error(NULL
, 0, _("skipped restricted file"));
822 if (tmp
[strlen(tmp
) - 1] == '/')
823 tmp
[strlen(tmp
) - 1] = '\0';
825 virBufferAsprintf(buf
, " \"%s%s\" %s,\n", tmp
, recursive
? "/**" : "",
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
? "/**" : "");
832 /* allow reading (but not creating) the dir */
833 virBufferAsprintf(buf
, " \"%s/\" r,\n", tmp
);
847 vah_add_file(virBufferPtr buf
, const char *path
, const char *perms
)
849 return vah_add_path(buf
, path
, perms
, false);
853 vah_add_file_chardev(virBufferPtr buf
,
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"));
869 if (vah_add_file(buf
, pipe_in
, perms
) != 0)
872 /* add the pipe output */
873 if (virAsprintfQuiet(&pipe_out
, "%s.out", path
) == -1) {
874 vah_error(NULL
, 0, _("could not allocate memory"));
878 if (vah_add_file(buf
, pipe_out
, perms
) != 0)
888 if (vah_add_file(buf
, path
, perms
) != 0)
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");
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");
914 add_file_path(virDomainDiskDefPtr disk
,
919 virBufferPtr buf
= opaque
;
923 if (disk
->src
->readonly
)
924 ret
= vah_add_file(buf
, path
, "rk");
926 ret
= vah_add_file(buf
, path
, "rwk");
928 ret
= vah_add_file(buf
, path
, "rk");
938 get_files(vahControl
* ctl
)
940 virBuffer buf
= VIR_BUFFER_INITIALIZER
;
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"));
954 if (STRNEQ(uuid
, ctl
->uuid
)) {
955 vah_error(ctl
, 0, _("given uuid does not match XML uuid"));
959 /* load the storage driver so that backing store can be accessed */
961 virDriverLoadModule("storage", "storageRegister", false);
964 for (i
= 0; i
< ctl
->def
->ndisks
; i
++) {
965 virDomainDiskDefPtr disk
= ctl
->def
->disks
[i
];
967 if (!virDomainDiskGetSource(disk
))
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)
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
,
996 ctl
->def
->serials
[i
]->source
->type
) != 0)
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)
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
,
1024 ctl
->def
->parallels
[i
]->source
->type
) != 0)
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
,
1039 ctl
->def
->channels
[i
]->source
->type
) != 0)
1042 if (ctl
->def
->os
.kernel
)
1043 if (vah_add_file(&buf
, ctl
->def
->os
.kernel
, "r") != 0)
1046 if (ctl
->def
->os
.initrd
)
1047 if (vah_add_file(&buf
, ctl
->def
->os
.initrd
, "r") != 0)
1050 if (ctl
->def
->os
.dtb
)
1051 if (vah_add_file(&buf
, ctl
->def
->os
.dtb
, "r") != 0)
1054 if (ctl
->def
->os
.slic_table
)
1055 if (vah_add_file(&buf
, ctl
->def
->os
.slic_table
, "r") != 0)
1058 if (ctl
->def
->os
.loader
&& ctl
->def
->os
.loader
->path
)
1059 if (vah_add_file(&buf
, ctl
->def
->os
.loader
->path
, "rk") != 0)
1062 if (ctl
->def
->os
.loader
&& ctl
->def
->os
.loader
->nvram
)
1063 if (vah_add_file(&buf
, ctl
->def
->os
.loader
->nvram
, "rwk") != 0)
1066 for (i
= 0; i
< ctl
->def
->ngraphics
; i
++) {
1067 virDomainGraphicsDefPtr graphics
= ctl
->def
->graphics
[i
];
1069 const char *rendernode
= virDomainGraphicsGetRenderNode(graphics
);
1072 vah_add_file(&buf
, rendernode
, "rw");
1075 if (virDomainGraphicsNeedsAutoRenderNode(graphics
)) {
1076 char *defaultRenderNode
= virHostGetDRMRenderNode();
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
&&
1091 vah_add_file(&buf
, listenObj
.socket
, "rw"))
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
,
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
);
1114 if (virHostdevFindUSBDevice(dev
, true, &usb
) < 0)
1117 rc
= virUSBDeviceFileIterate(usb
, file_iterate_hostdev_cb
, &buf
);
1118 virUSBDeviceFree(usb
);
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
:
1132 case VIR_MDEV_MODEL_TYPE_LAST
:
1134 virReportEnumRangeError(virMediatedDeviceModelType
,
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
) {
1157 rc
= virPCIDeviceFileIterate(pci
, file_iterate_pci_cb
, &buf
);
1158 virPCIDeviceFree(pci
);
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)
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)
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",
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)
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
:
1225 case VIR_DOMAIN_TPM_VERSION_2_0
:
1228 case VIR_DOMAIN_TPM_VERSION_DEFAULT
:
1229 case VIR_DOMAIN_TPM_VERSION_LAST
:
1233 /* Unix socket for QEMU and swtpm to use */
1234 virBufferAsprintf(&buf
,
1235 " \"/run/libvirt/qemu/swtpm/%s-swtpm.sock\" rw,\n",
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",
1250 VIR_FREE(shortName
);
1252 case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH
:
1253 case VIR_DOMAIN_TPM_TYPE_LAST
:
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
)
1264 if (!virDomainNetIsVirtioModel(net
))
1271 virBufferAddLit(&buf
, " \"/dev/vhost-net\" rw,\n");
1274 virBufferAddLit(&buf
, " \"/dev/vfio/vfio\" rw,\n");
1275 virBufferAddLit(&buf
, " \"/dev/vfio/[0-9]*\" rw,\n");
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");
1300 if (vah_add_file(&buf
, ctl
->newfile
, "rwk") != 0)
1303 if (virBufferError(&buf
)) {
1304 virBufferFreeAndReset(&buf
);
1305 vah_error(NULL
, 0, _("failed to allocate file buffer"));
1310 ctl
->files
= virBufferContentAndReset(&buf
);
1318 vahParseArgv(vahControl
* ctl
, int argc
, char **argv
)
1321 struct option opt
[] = {
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'},
1335 while ((arg
= getopt_long(argc
, argv
, "acdDhrRH:b:u:p:f:F:", opt
,
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';
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"));
1374 vah_error(ctl
, 1, _("unsupported option"));
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"));
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
) {
1396 vah_error(ctl
, 1, _("could not get VM definition"));
1400 if (get_files(ctl
) != 0)
1401 vah_error(ctl
, 1, _("invalid VM definition"));
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
;
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]);
1430 virFileActivateDirOverride(argv
[0]);
1432 /* Initialize the log system */
1435 /* clear the environment */
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], '/')))
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
);
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)
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",
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",
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 */
1511 vah_info(include_file
);
1512 vah_info(included_files
);
1514 } else if (ctl
->def
->virtType
== VIR_DOMAIN_VIRT_LXC
) {
1516 } else if ((rc
= update_include_file(include_file
,
1518 ctl
->append
)) != 0) {
1523 /* create the profile from TEMPLATE */
1524 if (ctl
->cmd
== 'c') {
1526 if (virAsprintfQuiet(&tmp
, " #include <libvirt/%s.files>\n",
1528 vah_error(ctl
, 0, _("could not allocate memory"));
1534 vah_info(ctl
->uuid
);
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
);
1545 if (rc
== 0 && !ctl
->dryrun
) {
1546 if (ctl
->cmd
== 'c')
1547 rc
= parserLoad(ctl
->uuid
);
1549 rc
= parserReplace(ctl
->uuid
);
1553 unlink(include_file
);
1554 if (ctl
->cmd
== 'c')
1559 VIR_FREE(included_files
);
1565 VIR_FREE(include_file
);
1567 exit(rc
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
);