1 /*
2  * virt-aa-helper: wrapper program used by AppArmor security driver.
3  *
4  * Copyright (C) 2010-2014 Red Hat, Inc.
5  * Copyright (C) 2009-2011 Canonical Ltd.
6  *
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.
11  *
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.
16  *
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/>.
20  */
21 
22 #include <config.h>
23 
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>
30 
31 #include "internal.h"
32 #include "virbuffer.h"
33 #include "viralloc.h"
34 #include "vircommand.h"
35 #include "virlog.h"
36 #include "driver.h"
37 
38 #include "security_driver.h"
39 #include "security_apparmor.h"
40 #include "storage_source.h"
41 #include "domain_conf.h"
42 #include "virxml.h"
43 #include "viruuid.h"
44 #include "virusb.h"
45 #include "virutil.h"
46 #include "virpci.h"
47 #include "virfile.h"
48 #include "configmake.h"
49 #include "virrandom.h"
50 #include "virstring.h"
51 #include "virgettext.h"
52 #include "virhostdev.h"
53 
54 #define VIR_FROM_THIS VIR_FROM_SECURITY
55 
56 static char *progname;
57 
58 typedef struct {
59     char uuid[PROFILE_NAME_SIZE];       /* UUID of vm */
60     bool dryrun;                /* dry run */
61     char cmd;                   /* 'c'   create
62                                  * 'a'   add (load)
63                                  * 'r'   replace
64                                  * 'R'   remove */
65     char *files;                /* list of files */
66     virDomainDef *def;        /* VM definition */
67     virCaps *caps;            /* VM capabilities */
68     virDomainXMLOption *xmlopt; /* XML parser data */
69     char *virtType;                  /* type of hypervisor (eg qemu, xen, lxc) */
70     char *os;                   /* type of os (eg hvm, xen, exe) */
71     virArch arch;               /* machine architecture */
72     char *newfile;              /* newly added file */
73     bool append;                /* append to .files instead of rewrite */
74 } vahControl;
75 
76 static int
vahDeinit(vahControl * ctl)77 vahDeinit(vahControl * ctl)
78 {
79     if (ctl == NULL)
80         return -1;
81 
82     virDomainDefFree(ctl->def);
83     virObjectUnref(ctl->caps);
84     virObjectUnref(ctl->xmlopt);
85     VIR_FREE(ctl->files);
86     VIR_FREE(ctl->virtType);
87     VIR_FREE(ctl->os);
88     VIR_FREE(ctl->newfile);
89 
90     return 0;
91 }
92 
93 /*
94  * Print usage
95  */
96 static void
vah_usage(void)97 vah_usage(void)
98 {
99     printf(_("\n%s mode [options] [extra file] [< def.xml]\n\n"
100             "  Modes:\n"
101             "    -a | --add                     load profile\n"
102             "    -c | --create                  create profile from template\n"
103             "    -D | --delete                  unload profile and delete generated rules\n"
104             "    -r | --replace                 reload profile\n"
105             "    -R | --remove                  unload profile\n"
106             "  Options:\n"
107             "    -d | --dryrun                  dry run\n"
108             "    -u | --uuid <uuid>             uuid (profile name)\n"
109             "    -h | --help                    this help\n"
110             "  Extra File:\n"
111             "    -f | --add-file <file>         add file to a profile generated from XML\n"
112             "    -F | --append-file <file>      append file to an existing profile\n"
113             "\n"), progname);
114 
115     puts(_("This command is intended to be used by libvirtd "
116            "and not used directly.\n"));
117     return;
118 }
119 
120 static void
vah_error(vahControl * ctl,int doexit,const char * str)121 vah_error(vahControl * ctl, int doexit, const char *str)
122 {
123     fprintf(stderr, _("%s: error: %s%c"), progname, str, '\n');
124 
125     if (doexit) {
126         if (ctl != NULL)
127             vahDeinit(ctl);
128         exit(EXIT_FAILURE);
129     }
130 }
131 
132 static void
vah_warning(const char * str)133 vah_warning(const char *str)
134 {
135     fprintf(stderr, _("%s: warning: %s%c"), progname, str, '\n');
136 }
137 
138 static void
vah_info(const char * str)139 vah_info(const char *str)
140 {
141     fprintf(stderr, _("%s:\n%s%c"), progname, str, '\n');
142 }
143 
144 /*
145  * run an apparmor_parser command
146  */
147 static int
parserCommand(const char * profile_name,const char cmd)148 parserCommand(const char *profile_name, const char cmd)
149 {
150     int result = -1;
151     char flag[3];
152     char *profile;
153     int status;
154     int ret;
155 
156     if (strchr("arR", cmd) == NULL) {
157         vah_error(NULL, 0, _("invalid flag"));
158         return -1;
159     }
160 
161     g_snprintf(flag, 3, "-%c", cmd);
162 
163     profile = g_strdup_printf("%s/%s", APPARMOR_DIR "/libvirt", profile_name);
164 
165     if (!virFileExists(profile)) {
166         vah_error(NULL, 0, _("profile does not exist"));
167         goto cleanup;
168     } else {
169         const char * const argv[] = {
170             "/sbin/apparmor_parser", flag, profile, NULL
171         };
172         g_autoptr(virCommand) command = virCommandNewArgs(argv);
173 
174         virCommandRawStatus(command);
175         if ((ret = virCommandRun(command, &status)) != 0 ||
176             (WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
177             if (ret != 0) {
178                 vah_error(NULL, 0, _("failed to run apparmor_parser"));
179                 goto cleanup;
180             } else if (cmd == 'R' && WIFEXITED(status) &&
181                        WEXITSTATUS(status) == 234) {
182                 vah_warning(_("unable to unload already unloaded profile"));
183             } else {
184                 vah_error(NULL, 0, _("apparmor_parser exited with error"));
185                 goto cleanup;
186             }
187         }
188     }
189 
190     result = 0;
191 
192  cleanup:
193     VIR_FREE(profile);
194 
195     return result;
196 }
197 
198 /*
199  * Update the dynamic files
200  */
201 static int
update_include_file(const char * include_file,const char * included_files,bool append)202 update_include_file(const char *include_file, const char *included_files,
203                     bool append)
204 {
205     int rc = -1;
206     int plen, flen = 0;
207     int fd;
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";
212 
213     if (virFileExists(include_file)) {
214         flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
215         if (flen < 0)
216             return rc;
217     }
218 
219     if (append && virFileExists(include_file))
220         pcontent = g_strdup_printf("%s%s", existing, included_files);
221     else
222         pcontent = g_strdup_printf("%s%s", warning, included_files);
223 
224     plen = strlen(pcontent);
225     if (plen > MAX_FILE_LEN) {
226         vah_error(NULL, 0, _("invalid length for new profile"));
227         goto cleanup;
228     }
229 
230     /* only update the disk profile if it is different */
231     if (flen > 0 && flen == plen && STREQLEN(existing, pcontent, plen)) {
232         rc = 0;
233         goto cleanup;
234     }
235 
236     /* write the file */
237     if ((fd = open(include_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
238         vah_error(NULL, 0, _("failed to create include file"));
239         goto cleanup;
240     }
241 
242     if (safewrite(fd, pcontent, plen) < 0) { /* don't write the '\0' */
243         VIR_FORCE_CLOSE(fd);
244         vah_error(NULL, 0, _("failed to write to profile"));
245         goto cleanup;
246     }
247 
248     if (VIR_CLOSE(fd) != 0) {
249         vah_error(NULL, 0, _("failed to close or write to profile"));
250         goto cleanup;
251     }
252     rc = 0;
253 
254  cleanup:
255     VIR_FREE(pcontent);
256     VIR_FREE(existing);
257 
258     return rc;
259 }
260 
261 /*
262  * Create a profile based on a template
263  */
264 static int
create_profile(const char * profile,const char * profile_name,const char * profile_files,int virtType)265 create_profile(const char *profile, const char *profile_name,
266                const char *profile_files, int virtType)
267 {
268     g_autofree char *template = NULL;
269     g_autofree char *tcontent = NULL;
270     g_autofree char *pcontent = NULL;
271     g_autofree char *replace_name = NULL;
272     g_autofree char *replace_files = NULL;
273     char *tmp = NULL;
274     const char *template_name = "\nprofile LIBVIRT_TEMPLATE";
275     const char *template_end = "\n}";
276     int tlen, plen;
277     int fd;
278     const char *driver_name = NULL;
279 
280     if (virFileExists(profile)) {
281         vah_error(NULL, 0, _("profile exists"));
282         return -1;
283     }
284 
285     switch (virtType) {
286     case VIR_DOMAIN_VIRT_QEMU:
287     case VIR_DOMAIN_VIRT_KQEMU:
288     case VIR_DOMAIN_VIRT_KVM:
289         driver_name = "qemu";
290         break;
291     default:
292         driver_name = virDomainVirtTypeToString(virtType);
293     }
294 
295     template = g_strdup_printf("%s/TEMPLATE.%s", APPARMOR_DIR "/libvirt", driver_name);
296 
297     if (!virFileExists(template)) {
298         vah_error(NULL, 0, _("template does not exist"));
299         return -1;
300     }
301 
302     if ((tlen = virFileReadAll(template, MAX_FILE_LEN, &tcontent)) < 0) {
303         vah_error(NULL, 0, _("failed to read AppArmor template"));
304         return -1;
305     }
306 
307     if (strstr(tcontent, template_name) == NULL) {
308         vah_error(NULL, 0, _("no replacement string in template"));
309         return -1;
310     }
311 
312     if (strstr(tcontent, template_end) == NULL) {
313         vah_error(NULL, 0, _("no replacement string in template"));
314         return -1;
315     }
316 
317     /* '\nprofile <profile_name>\0' */
318     replace_name = g_strdup_printf("\nprofile %s", profile_name);
319 
320     /* '\n<profile_files>\n}\0' */
321     if (virtType != VIR_DOMAIN_VIRT_LXC)
322     replace_files = g_strdup_printf("\n%s\n}", profile_files);
323 
324     plen = tlen + strlen(replace_name) - strlen(template_name) + 1;
325 
326     if (virtType != VIR_DOMAIN_VIRT_LXC)
327         plen += strlen(replace_files) - strlen(template_end);
328 
329     if (plen > MAX_FILE_LEN || plen < tlen) {
330         vah_error(NULL, 0, _("invalid length for new profile"));
331         return -1;
332     }
333 
334     if (!(pcontent = virStringReplace(tcontent, template_name, replace_name)))
335         return -1;
336 
337     if (virtType != VIR_DOMAIN_VIRT_LXC) {
338         if (!(tmp = virStringReplace(pcontent, template_end, replace_files)))
339             return -1;
340         VIR_FREE(pcontent);
341         pcontent = g_steal_pointer(&tmp);
342     }
343 
344     /* write the file */
345     if ((fd = open(profile, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1) {
346         vah_error(NULL, 0, _("failed to create profile"));
347         return -1;
348     }
349 
350     if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
351         VIR_FORCE_CLOSE(fd);
352         vah_error(NULL, 0, _("failed to write to profile"));
353         return -1;
354     }
355 
356     if (VIR_CLOSE(fd) != 0) {
357         vah_error(NULL, 0, _("failed to close or write to profile"));
358         return -1;
359     }
360 
361     return 0;
362 }
363 
364 /*
365  * Load an existing profile
366  */
367 static int
parserLoad(const char * profile_name)368 parserLoad(const char *profile_name)
369 {
370     return parserCommand(profile_name, 'a');
371 }
372 
373 /*
374  * Remove an existing profile
375  */
376 static int
parserRemove(const char * profile_name)377 parserRemove(const char *profile_name)
378 {
379     return parserCommand(profile_name, 'R');
380 }
381 
382 /*
383  * Replace an existing profile
384  */
385 static int
parserReplace(const char * profile_name)386 parserReplace(const char *profile_name)
387 {
388     return parserCommand(profile_name, 'r');
389 }
390 
391 static int
valid_uuid(const char * uuid)392 valid_uuid(const char *uuid)
393 {
394     unsigned char rawuuid[VIR_UUID_BUFLEN];
395 
396     if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
397         return -1;
398 
399     if (!STRPREFIX(uuid, AA_PREFIX))
400         return -1;
401 
402     if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
403         return -1;
404 
405     return 0;
406 }
407 
408 static int
valid_name(const char * name)409 valid_name(const char *name)
410 {
411     /* just try to filter out any dangerous characters in the name that can be
412      * used to subvert the profile */
413     const char *bad = "/[]{}?^,\"*";
414 
415     if (strlen(name) == 0)
416         return -1;
417 
418     if (strcspn(name, bad) != strlen(name))
419         return -1;
420 
421     return 0;
422 }
423 
424 /* see if one of the strings in arr starts with str */
425 static int
array_starts_with(const char * str,const char * const * arr,const long size)426 array_starts_with(const char *str, const char * const *arr, const long size)
427 {
428     size_t i;
429     for (i = 0; i < size; i++) {
430         if (strlen(str) < strlen(arr[i]))
431             continue;
432 
433         if (STRPREFIX(str, arr[i]))
434             return 0;
435     }
436     return 1;
437 }
438 
439 /*
440  * Don't allow access to special files or restricted paths such as /bin, /sbin,
441  * /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
442  * access to system files which could be used to elevate privileges. This is a
443  * safety measure in case libvirtd is under a restrictive profile and is
444  * subverted and trying to escape confinement.
445  *
446  * Note that we cannot exclude block devices because they are valid devices.
447  * The TEMPLATE file can be adjusted to explicitly disallow these if needed.
448  *
449  * RETURN: -1 on error, 0 if ok, 1 if blocked
450  */
451 static int
valid_path(const char * path,const bool readonly)452 valid_path(const char *path, const bool readonly)
453 {
454     const char * const restricted[] = {
455         "/bin/",
456         "/etc/",
457         "/lib",
458         "/lost+found/",
459         "/proc/",
460         "/sbin/",
461         "/selinux/",
462         "/sys/",
463         "/usr/bin/",
464         "/usr/lib",
465         "/usr/sbin/",
466         "/usr/share/",
467         "/usr/local/bin/",
468         "/usr/local/etc/",
469         "/usr/local/lib",
470         "/usr/local/sbin/"
471     };
472     /* these paths are ok for readonly, but not read/write */
473     const char * const restricted_rw[] = {
474         "/boot/",
475         "/vmlinuz",
476         "/initrd",
477         "/initrd.img",
478         "/usr/share/edk2/",
479         "/usr/share/OVMF/",              /* for OVMF images */
480         "/usr/share/ovmf/",              /* for OVMF images */
481         "/usr/share/AAVMF/",             /* for AAVMF images */
482         "/usr/share/qemu-efi/",          /* for AAVMF images */
483         "/usr/share/qemu-efi-aarch64/"   /* for AAVMF images */
484     };
485     /* override the above with these */
486     const char * const override[] = {
487         "/sys/devices/pci",                /* for hostdev pci devices */
488         "/sys/kernel/config/target/vhost", /* for hostdev vhost_scsi devices */
489         "/etc/libvirt-sandbox/services/"   /* for virt-sandbox service config */
490     };
491 
492     const int nropaths = G_N_ELEMENTS(restricted);
493     const int nrwpaths = G_N_ELEMENTS(restricted_rw);
494     const int nopaths = G_N_ELEMENTS(override);
495 
496     if (path == NULL) {
497         vah_error(NULL, 0, _("bad pathname"));
498         return -1;
499     }
500 
501     /* Don't allow double quotes, since we use them to quote the filename
502      * and this will confuse the apparmor parser.
503      */
504     if (strchr(path, '"') != NULL)
505         return 1;
506 
507     /* Require an absolute path */
508     if (STRNEQLEN(path, "/", 1))
509         return 1;
510 
511     if (!virFileExists(path))
512         vah_warning(_("path does not exist, skipping file type checks"));
513 
514     /* overrides are always allowed */
515     if (array_starts_with(path, override, nopaths) == 0)
516         return 0;
517 
518     /* allow read only paths upfront */
519     if (readonly) {
520         if (array_starts_with(path, restricted_rw, nrwpaths) == 0)
521             return 0;
522     }
523 
524     /* disallow RW access to all paths in restricted and restriced_rw */
525     if ((array_starts_with(path, restricted, nropaths) == 0 ||
526          array_starts_with(path, restricted_rw, nrwpaths) == 0))
527         return 1;
528 
529     return 0;
530 }
531 
532 static int
verify_xpath_context(xmlXPathContextPtr ctxt)533 verify_xpath_context(xmlXPathContextPtr ctxt)
534 {
535     char *tmp = NULL;
536 
537     if (!ctxt) {
538         vah_warning(_("Invalid context"));
539         return -1;
540     }
541 
542     /* check if have <name> */
543     if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
544         vah_warning(_("Could not find <name>"));
545         return -1;
546     }
547     VIR_FREE(tmp);
548 
549     /* check if have <uuid> */
550     if (!(tmp = virXPathString("string(./uuid[1])", ctxt))) {
551         vah_warning(_("Could not find <uuid>"));
552         return -1;
553     }
554     VIR_FREE(tmp);
555 
556     return 0;
557 }
558 
559 /*
560  * Parse the xml we received to fill in the following:
561  * ctl->virtType
562  * ctl->os
563  * ctl->arch
564  *
565  * These are suitable for setting up a virCaps *
566  */
567 static int
caps_mockup(vahControl * ctl,const char * xmlStr)568 caps_mockup(vahControl * ctl, const char *xmlStr)
569 {
570     g_autoptr(xmlDoc) xml = NULL;
571     g_autoptr(xmlXPathContext) ctxt = NULL;
572     char *arch;
573 
574     if (!(xml = virXMLParseStringCtxt(xmlStr, _("(domain_definition)"),
575                                       &ctxt))) {
576         return -1;
577     }
578 
579     if (!virXMLNodeNameEqual(ctxt->node, "domain")) {
580         vah_error(NULL, 0, _("unexpected root element, expecting <domain>"));
581         return -1;
582     }
583 
584     /* Quick sanity check for some required elements */
585     if (verify_xpath_context(ctxt) != 0)
586         return -1;
587 
588     ctl->virtType = virXPathString("string(./@type)", ctxt);
589     if (!ctl->virtType) {
590         vah_error(ctl, 0, _("domain type is not defined"));
591         return -1;
592     }
593     ctl->os = virXPathString("string(./os/type[1])", ctxt);
594     if (!ctl->os) {
595         vah_error(ctl, 0, _("os.type is not defined"));
596         return -1;
597     }
598     arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
599     if (!arch) {
600         ctl->arch = virArchFromHost();
601     } else {
602         ctl->arch = virArchFromString(arch);
603         VIR_FREE(arch);
604     }
605 
606     return 0;
607 }
608 
609 virDomainDefParserConfig virAAHelperDomainDefParserConfig = {
610     .features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG |
611                 VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN |
612                 VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS |
613                 VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING,
614 };
615 
616 static int
get_definition(vahControl * ctl,const char * xmlStr)617 get_definition(vahControl * ctl, const char *xmlStr)
618 {
619     int ostype, virtType;
620     virCapsGuest *guest;  /* this is freed when caps is freed */
621 
622     /*
623      * mock up some capabilities. We don't currently use these explicitly,
624      * but need them for virDomainDefParseString().
625      */
626     if (caps_mockup(ctl, xmlStr) != 0)
627         return -1;
628 
629     if ((ctl->caps = virCapabilitiesNew(ctl->arch, true, true)) == NULL) {
630         vah_error(ctl, 0, _("could not allocate memory"));
631         return -1;
632     }
633 
634     if (!(ctl->xmlopt = virDomainXMLOptionNew(&virAAHelperDomainDefParserConfig,
635                                               NULL, NULL, NULL, NULL))) {
636         vah_error(ctl, 0, _("Failed to create XML config object"));
637         return -1;
638     }
639 
640     if ((ostype = virDomainOSTypeFromString(ctl->os)) < 0) {
641         vah_error(ctl, 0, _("unknown OS type"));
642         return -1;
643     }
644 
645     guest = virCapabilitiesAddGuest(ctl->caps, ostype, ctl->arch,
646                                     NULL, NULL, 0, NULL);
647 
648     if ((virtType = virDomainVirtTypeFromString(ctl->virtType)) < 0) {
649         vah_error(ctl, 0, _("unknown virtualization type"));
650         return -1;
651     }
652 
653     virCapabilitiesAddGuestDomain(guest, virtType,
654                                   NULL, NULL, 0, NULL);
655 
656     ctl->def = virDomainDefParseString(xmlStr,
657                                        ctl->xmlopt, NULL,
658                                        VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL |
659                                        VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
660 
661     if (ctl->def == NULL) {
662         vah_error(ctl, 0, _("could not parse XML"));
663         return -1;
664     }
665 
666     if (!ctl->def->name) {
667         vah_error(ctl, 0, _("could not find name in XML"));
668         return -1;
669     }
670 
671     if (valid_name(ctl->def->name) != 0) {
672         vah_error(ctl, 0, _("bad name"));
673         return -1;
674     }
675 
676     return 0;
677 }
678 
679 /**
680   * The permissions allowed are apparmor valid permissions and 'R'. 'R' stands for
681   * read with no explicit deny rule.
682   */
683 static int
vah_add_path(virBuffer * buf,const char * path,const char * perms,bool recursive)684 vah_add_path(virBuffer *buf, const char *path, const char *perms, bool recursive)
685 {
686     char *tmp = NULL;
687     int rc = -1;
688     bool readonly = true;
689     bool explicit_deny_rule = true;
690     char *sub = NULL;
691     char *perms_new = NULL;
692     char *pathdir = NULL;
693     char *pathtmp = NULL;
694     char *pathreal = NULL;
695 
696     if (path == NULL)
697         return rc;
698 
699     /* Skip files without an absolute path. Not having one confuses the
700      * apparmor parser and this also ensures things like tcp consoles don't
701      * get added to the profile.
702      */
703     if (STRNEQLEN(path, "/", 1)) {
704         vah_warning(path);
705         vah_warning(_("skipped non-absolute path"));
706         return 0;
707     }
708 
709     /* files might be created by qemu later on and not exist right now.
710      * But realpath needs a valid path to work on, therefore:
711      * 1. walk the path to find longest valid path
712      * 2. get the realpath of that valid path
713      * 3. re-combine the realpath with the remaining suffix
714      * Note: A totally non existent path is used as-is
715      */
716      pathdir = g_strdup(path);
717      while (!virFileExists(pathdir)) {
718          pathtmp = g_path_get_dirname(pathdir);
719          VIR_FREE(pathdir);
720          pathdir = g_steal_pointer(&pathtmp);
721      }
722 
723     if (strlen(pathdir) == 1) {
724         /* nothing of the path does exist yet */
725         tmp = g_strdup(path);
726     } else {
727         pathtmp = g_strdup(path + strlen(pathdir));
728         if ((pathreal = realpath(pathdir, NULL)) == NULL) {
729             vah_error(NULL, 0, pathdir);
730             vah_error(NULL, 0, _("could not find realpath"));
731             goto cleanup;
732         }
733         tmp = g_strdup_printf("%s%s", pathreal, pathtmp);
734     }
735 
736     perms_new = g_strdup(perms);
737 
738     if (strchr(perms_new, 'w') != NULL) {
739         readonly = false;
740         explicit_deny_rule = false;
741     }
742 
743     if ((sub = strchr(perms_new, 'R')) != NULL) {
744         /* Don't write the invalid R permission, replace it with 'r' */
745         sub[0] = 'r';
746         explicit_deny_rule = false;
747     }
748 
749     rc = valid_path(tmp, readonly);
750     if (rc != 0) {
751         if (rc > 0) {
752             vah_error(NULL, 0, path);
753             vah_error(NULL, 0, _("skipped restricted file"));
754         }
755         goto cleanup;
756     }
757 
758     if (tmp[strlen(tmp) - 1] == '/')
759         tmp[strlen(tmp) - 1] = '\0';
760 
761     virBufferAsprintf(buf, "  \"%s%s\" %s,\n", tmp, recursive ? "/**" : "",
762                       perms_new);
763     if (explicit_deny_rule) {
764         virBufferAddLit(buf, "  # don't audit writes to readonly files\n");
765         virBufferAsprintf(buf, "  deny \"%s%s\" w,\n", tmp, recursive ? "/**" : "");
766     }
767     if (recursive) {
768         /* allow reading (but not creating) the dir */
769         virBufferAsprintf(buf, "  \"%s/\" r,\n", tmp);
770     }
771 
772  cleanup:
773     VIR_FREE(pathdir);
774     VIR_FREE(pathtmp);
775     VIR_FREE(pathreal);
776     VIR_FREE(perms_new);
777     VIR_FREE(tmp);
778 
779     return rc;
780 }
781 
782 static int
vah_add_file(virBuffer * buf,const char * path,const char * perms)783 vah_add_file(virBuffer *buf, const char *path, const char *perms)
784 {
785     return vah_add_path(buf, path, perms, false);
786 }
787 
788 static int
vah_add_file_chardev(virBuffer * buf,const char * path,const char * perms,const int type)789 vah_add_file_chardev(virBuffer *buf,
790                      const char *path,
791                      const char *perms,
792                      const int type)
793 {
794     char *pipe_in;
795     char *pipe_out;
796     int rc = -1;
797 
798     if (type == VIR_DOMAIN_CHR_TYPE_PIPE) {
799         /* add the pipe input */
800         pipe_in = g_strdup_printf("%s.in", path);
801 
802         if (vah_add_file(buf, pipe_in, perms) != 0)
803             goto clean_pipe_in;
804 
805         /* add the pipe output */
806         pipe_out = g_strdup_printf("%s.out", path);
807 
808         if (vah_add_file(buf, pipe_out, perms) != 0)
809             goto clean_pipe_out;
810 
811         rc = 0;
812       clean_pipe_out:
813         VIR_FREE(pipe_out);
814       clean_pipe_in:
815         VIR_FREE(pipe_in);
816     } else {
817         /* add the file */
818         if (vah_add_file(buf, path, perms) != 0)
819             return -1;
820         rc = 0;
821     }
822 
823     return rc;
824 }
825 
826 static int
file_iterate_hostdev_cb(virUSBDevice * dev G_GNUC_UNUSED,const char * file,void * opaque)827 file_iterate_hostdev_cb(virUSBDevice *dev G_GNUC_UNUSED,
828                         const char *file, void *opaque)
829 {
830     virBuffer *buf = opaque;
831     return vah_add_file(buf, file, "rw");
832 }
833 
834 static int
file_iterate_pci_cb(virPCIDevice * dev G_GNUC_UNUSED,const char * file,void * opaque)835 file_iterate_pci_cb(virPCIDevice *dev G_GNUC_UNUSED,
836                     const char *file, void *opaque)
837 {
838     virBuffer *buf = opaque;
839     return vah_add_file(buf, file, "rw");
840 }
841 
842 static int
add_file_path(virStorageSource * src,size_t depth,virBuffer * buf)843 add_file_path(virStorageSource *src,
844               size_t depth,
845               virBuffer *buf)
846 {
847     int ret;
848 
849     /* execute the callback only for local storage */
850     if (!src->path || !virStorageSourceIsLocalStorage(src))
851         return 0;
852 
853     if (depth == 0) {
854         if (src->readonly)
855             ret = vah_add_file(buf, src->path, "rk");
856         else
857             ret = vah_add_file(buf, src->path, "rwk");
858     } else {
859         ret = vah_add_file(buf, src->path, "rk");
860     }
861 
862     if (ret != 0)
863         ret = -1;
864 
865     return ret;
866 }
867 
868 
869 static int
storage_source_add_files(virStorageSource * src,virBuffer * buf,size_t depth)870 storage_source_add_files(virStorageSource *src,
871                          virBuffer *buf,
872                          size_t depth)
873 {
874     virStorageSource *tmp;
875 
876     for (tmp = src; virStorageSourceIsBacking(tmp); tmp = tmp->backingStore) {
877         if (add_file_path(tmp, depth, buf) < 0)
878             return -1;
879 
880         depth++;
881     }
882 
883     return 0;
884 }
885 
886 static int
get_files(vahControl * ctl)887 get_files(vahControl * ctl)
888 {
889     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
890     int rc = -1;
891     size_t i;
892     char *uuid;
893     char *mem_path = NULL;
894     char uuidstr[VIR_UUID_STRING_BUFLEN];
895     bool needsVfio = false, needsvhost = false, needsgl = false;
896 
897     /* verify uuid is same as what we were given on the command line */
898     virUUIDFormat(ctl->def->uuid, uuidstr);
899     uuid = g_strdup_printf("%s%s", AA_PREFIX, uuidstr);
900 
901     if (STRNEQ(uuid, ctl->uuid)) {
902         vah_error(ctl, 0, _("given uuid does not match XML uuid"));
903         goto cleanup;
904     }
905 
906     /* load the storage driver so that backing store can be accessed */
907 #ifdef WITH_STORAGE
908     virDriverLoadModule("storage", "storageRegister", false);
909 #endif
910 
911     for (i = 0; i < ctl->def->ndisks; i++) {
912         virDomainDiskDef *disk = ctl->def->disks[i];
913 
914         if (virStorageSourceIsEmpty(disk->src))
915             continue;
916         /* XXX - if we knew the qemu user:group here we could send it in
917          *        so that the open could be re-tried as that user:group.
918          *
919          * The maximum depth is limited to 200 layers similarly to the qemu
920          * implementation.
921          */
922         if (!disk->src->backingStore)
923             virStorageSourceGetMetadata(disk->src, -1, -1, 200, false);
924 
925          /* XXX should handle open errors more careful than just ignoring them.
926          */
927         if (storage_source_add_files(disk->src, &buf, 0) < 0)
928             goto cleanup;
929     }
930 
931     for (i = 0; i < ctl->def->nserials; i++)
932         if (ctl->def->serials[i] &&
933             (ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
934              ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
935              ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
936              ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
937              ctl->def->serials[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
938             ctl->def->serials[i]->source->data.file.path &&
939             ctl->def->serials[i]->source->data.file.path[0] != '\0')
940             if (vah_add_file_chardev(&buf,
941                                      ctl->def->serials[i]->source->data.file.path,
942                                      "rw",
943                                      ctl->def->serials[i]->source->type) != 0)
944                 goto cleanup;
945 
946     for (i = 0; i < ctl->def->nconsoles; i++)
947         if (ctl->def->consoles[i] &&
948             (ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
949              ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
950              ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
951              ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
952              ctl->def->consoles[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
953             ctl->def->consoles[i]->source->data.file.path &&
954             ctl->def->consoles[i]->source->data.file.path[0] != '\0')
955             if (vah_add_file(&buf,
956                              ctl->def->consoles[i]->source->data.file.path, "rw") != 0)
957                 goto cleanup;
958 
959     for (i = 0; i < ctl->def->nparallels; i++)
960         if (ctl->def->parallels[i] &&
961             (ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
962              ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
963              ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
964              ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
965              ctl->def->parallels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
966             ctl->def->parallels[i]->source->data.file.path &&
967             ctl->def->parallels[i]->source->data.file.path[0] != '\0')
968             if (vah_add_file_chardev(&buf,
969                                      ctl->def->parallels[i]->source->data.file.path,
970                                      "rw",
971                                      ctl->def->parallels[i]->source->type) != 0)
972                 goto cleanup;
973 
974     for (i = 0; i < ctl->def->nchannels; i++)
975         if (ctl->def->channels[i] &&
976             (ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PTY ||
977              ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_DEV ||
978              ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_FILE ||
979              ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_UNIX ||
980              ctl->def->channels[i]->source->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
981             ctl->def->channels[i]->source->data.file.path &&
982             ctl->def->channels[i]->source->data.file.path[0] != '\0')
983             if (vah_add_file_chardev(&buf,
984                                      ctl->def->channels[i]->source->data.file.path,
985                                      "rw",
986                                      ctl->def->channels[i]->source->type) != 0)
987                 goto cleanup;
988 
989     if (ctl->def->os.kernel)
990         if (vah_add_file(&buf, ctl->def->os.kernel, "r") != 0)
991             goto cleanup;
992 
993     if (ctl->def->os.initrd)
994         if (vah_add_file(&buf, ctl->def->os.initrd, "r") != 0)
995             goto cleanup;
996 
997     if (ctl->def->os.dtb)
998         if (vah_add_file(&buf, ctl->def->os.dtb, "r") != 0)
999             goto cleanup;
1000 
1001     if (ctl->def->os.slic_table)
1002         if (vah_add_file(&buf, ctl->def->os.slic_table, "r") != 0)
1003             goto cleanup;
1004 
1005     if (ctl->def->os.loader && ctl->def->os.loader->path)
1006         if (vah_add_file(&buf, ctl->def->os.loader->path, "rk") != 0)
1007             goto cleanup;
1008 
1009     if (ctl->def->os.loader && ctl->def->os.loader->nvram)
1010         if (vah_add_file(&buf, ctl->def->os.loader->nvram, "rwk") != 0)
1011             goto cleanup;
1012 
1013     for (i = 0; i < ctl->def->ngraphics; i++) {
1014         virDomainGraphicsDef *graphics = ctl->def->graphics[i];
1015         size_t n;
1016         const char *rendernode = virDomainGraphicsGetRenderNode(graphics);
1017 
1018         if (rendernode) {
1019             vah_add_file(&buf, rendernode, "rw");
1020             needsgl = true;
1021         } else {
1022             if (virDomainGraphicsNeedsAutoRenderNode(graphics)) {
1023                 char *defaultRenderNode = virHostGetDRMRenderNode();
1024                 needsgl = true;
1025 
1026                 if (defaultRenderNode) {
1027                     vah_add_file(&buf, defaultRenderNode, "rw");
1028                     VIR_FREE(defaultRenderNode);
1029                 }
1030             }
1031         }
1032 
1033         for (n = 0; n < graphics->nListens; n++) {
1034             virDomainGraphicsListenDef listenObj = graphics->listens[n];
1035 
1036             if (listenObj.type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
1037                 listenObj.socket &&
1038                 vah_add_file(&buf, listenObj.socket, "rw"))
1039                 goto cleanup;
1040         }
1041     }
1042 
1043     if (ctl->def->ngraphics == 1 &&
1044         ctl->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL)
1045         if (vah_add_file(&buf, ctl->def->graphics[0]->data.sdl.xauth,
1046                          "r") != 0)
1047             goto cleanup;
1048 
1049     for (i = 0; i < ctl->def->nhostdevs; i++)
1050         if (ctl->def->hostdevs[i]) {
1051             virDomainHostdevDef *dev = ctl->def->hostdevs[i];
1052             virDomainHostdevSubsysUSB *usbsrc = &dev->source.subsys.u.usb;
1053             switch (dev->source.subsys.type) {
1054             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
1055                 virUSBDevice *usb =
1056                     virUSBDeviceNew(usbsrc->bus, usbsrc->device, NULL);
1057 
1058                 if (usb == NULL)
1059                     continue;
1060 
1061                 if (virHostdevFindUSBDevice(dev, true, &usb) < 0)
1062                     continue;
1063 
1064                 rc = virUSBDeviceFileIterate(usb, file_iterate_hostdev_cb, &buf);
1065                 virUSBDeviceFree(usb);
1066                 if (rc != 0)
1067                     goto cleanup;
1068                 break;
1069             }
1070 
1071             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: {
1072                 virDomainHostdevSubsysMediatedDev *mdevsrc = &dev->source.subsys.u.mdev;
1073                 switch ((virMediatedDeviceModelType) mdevsrc->model) {
1074                     case VIR_MDEV_MODEL_TYPE_VFIO_PCI:
1075                     case VIR_MDEV_MODEL_TYPE_VFIO_AP:
1076                     case VIR_MDEV_MODEL_TYPE_VFIO_CCW:
1077                         needsVfio = true;
1078                         break;
1079                     case VIR_MDEV_MODEL_TYPE_LAST:
1080                     default:
1081                         virReportEnumRangeError(virMediatedDeviceModelType,
1082                                                 mdevsrc->model);
1083                         break;
1084                 }
1085                 break;
1086             }
1087 
1088             case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
1089                 virPCIDevice *pci = virPCIDeviceNew(&dev->source.subsys.u.pci.addr);
1090 
1091                 virDomainHostdevSubsysPCIBackendType backend = dev->source.subsys.u.pci.backend;
1092                 if (backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO ||
1093                         backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) {
1094                     needsVfio = true;
1095                 }
1096 
1097                 if (pci == NULL)
1098                     continue;
1099 
1100                 rc = virPCIDeviceFileIterate(pci, file_iterate_pci_cb, &buf);
1101                 virPCIDeviceFree(pci);
1102 
1103                 break;
1104             }
1105 
1106             default:
1107                 rc = 0;
1108                 break;
1109             } /* switch */
1110         }
1111 
1112     for (i = 0; i < ctl->def->nfss; i++) {
1113         if (ctl->def->fss[i] &&
1114                 ctl->def->fss[i]->type == VIR_DOMAIN_FS_TYPE_MOUNT &&
1115                 (ctl->def->fss[i]->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH ||
1116                  ctl->def->fss[i]->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT) &&
1117                 ctl->def->fss[i]->src) {
1118             virDomainFSDef *fs = ctl->def->fss[i];
1119 
1120             /* We don't need to add deny rw rules for readonly mounts,
1121              * this can only lead to troubles when mounting / readonly.
1122              */
1123             if (vah_add_path(&buf, fs->src->path, fs->readonly ? "R" : "rwl", true) != 0)
1124                 goto cleanup;
1125         }
1126     }
1127 
1128     for (i = 0; i < ctl->def->ninputs; i++) {
1129         if (ctl->def->inputs[i] &&
1130                 (ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH ||
1131                  ctl->def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_EVDEV)) {
1132             if (vah_add_file(&buf, ctl->def->inputs[i]->source.evdev, "rw") != 0)
1133                 goto cleanup;
1134         }
1135     }
1136 
1137     for (i = 0; i < ctl->def->nnets; i++) {
1138         if (ctl->def->nets[i] &&
1139                 ctl->def->nets[i]->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER &&
1140                 ctl->def->nets[i]->data.vhostuser) {
1141             virDomainChrSourceDef *vhu = ctl->def->nets[i]->data.vhostuser;
1142 
1143             if (vah_add_file_chardev(&buf, vhu->data.nix.path, "rw",
1144                        vhu->type) != 0)
1145                 goto cleanup;
1146         }
1147     }
1148 
1149     for (i = 0; i < ctl->def->nmems; i++) {
1150         if (ctl->def->mems[i] &&
1151                 ctl->def->mems[i]->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
1152             if (vah_add_file(&buf, ctl->def->mems[i]->nvdimmPath, "rw") != 0)
1153                 goto cleanup;
1154         }
1155     }
1156 
1157     for (i = 0; i < ctl->def->nsysinfo; i++) {
1158         size_t j;
1159 
1160         for (j = 0; j < ctl->def->sysinfo[i]->nfw_cfgs; j++) {
1161             virSysinfoFWCfgDef *f = &ctl->def->sysinfo[i]->fw_cfgs[j];
1162 
1163             if (f->file &&
1164                 vah_add_file(&buf, f->file, "r") != 0)
1165                 goto cleanup;
1166         }
1167     }
1168 
1169     for (i = 0; i < ctl->def->nshmems; i++) {
1170         virDomainShmemDef *shmem = ctl->def->shmems[i];
1171         /* explicit server paths can be on any model to overwrites defaults.
1172          * When the server path is enabled, use it - otherwise fallback to
1173          * model dependent defaults. */
1174         if (shmem->server.enabled &&
1175             shmem->server.chr->data.nix.path) {
1176                 if (vah_add_file(&buf, shmem->server.chr->data.nix.path,
1177                         "rw") != 0)
1178                     goto cleanup;
1179         } else {
1180             switch (shmem->model) {
1181             case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
1182                 /* until exposed, recreate qemuBuildShmemBackendMemProps */
1183                 mem_path = g_strdup_printf("/dev/shm/%s", shmem->name);
1184                 break;
1185             case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL:
1186             case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM:
1187                  /* until exposed, recreate qemuDomainPrepareShmemChardev */
1188                 mem_path = g_strdup_printf("/var/lib/libvirt/shmem-%s-sock",
1189                                shmem->name);
1190                 break;
1191             case VIR_DOMAIN_SHMEM_MODEL_LAST:
1192                 virReportEnumRangeError(virDomainShmemModel,
1193                                         shmem->model);
1194                 break;
1195             }
1196             if (mem_path != NULL) {
1197                 if (vah_add_file(&buf, mem_path, "rw") != 0)
1198                     goto cleanup;
1199             }
1200         }
1201     }
1202 
1203 
1204     if (ctl->def->ntpms > 0) {
1205         char *shortName = NULL;
1206         const char *tpmpath = NULL;
1207 
1208         for (i = 0; i < ctl->def->ntpms; i++) {
1209             if (ctl->def->tpms[i]->type != VIR_DOMAIN_TPM_TYPE_EMULATOR)
1210                 continue;
1211 
1212             shortName = virDomainDefGetShortName(ctl->def);
1213 
1214             switch (ctl->def->tpms[i]->version) {
1215             case VIR_DOMAIN_TPM_VERSION_1_2:
1216                 tpmpath = "tpm1.2";
1217                 break;
1218             case VIR_DOMAIN_TPM_VERSION_2_0:
1219                 tpmpath = "tpm2";
1220                 break;
1221             case VIR_DOMAIN_TPM_VERSION_DEFAULT:
1222             case VIR_DOMAIN_TPM_VERSION_LAST:
1223                 break;
1224             }
1225 
1226             /* Unix socket for QEMU and swtpm to use */
1227             virBufferAsprintf(&buf,
1228                 "  \"%s/libvirt/qemu/swtpm/%s-swtpm.sock\" rw,\n",
1229                 RUNSTATEDIR, shortName);
1230             /* Paths for swtpm to use: give it access to its state
1231              * directory (state files and fsync on dir), log, and PID files.
1232              */
1233             virBufferAsprintf(&buf,
1234                 "  \"%s/lib/libvirt/swtpm/%s/%s/\" r,\n",
1235                 LOCALSTATEDIR, uuidstr, tpmpath);
1236             virBufferAsprintf(&buf,
1237                 "  \"%s/lib/libvirt/swtpm/%s/%s/**\" rwk,\n",
1238                 LOCALSTATEDIR, uuidstr, tpmpath);
1239             virBufferAsprintf(&buf,
1240                 "  \"%s/log/swtpm/libvirt/qemu/%s-swtpm.log\" w,\n",
1241                 LOCALSTATEDIR, ctl->def->name);
1242             virBufferAsprintf(&buf,
1243                 "  \"%s/libvirt/qemu/swtpm/%s-swtpm.pid\" rw,\n",
1244                 RUNSTATEDIR, shortName);
1245 
1246             VIR_FREE(shortName);
1247         }
1248     }
1249 
1250     for (i = 0; i < ctl->def->nsmartcards; i++) {
1251         virDomainSmartcardDef *sc = ctl->def->smartcards[i];
1252         virDomainSmartcardType sc_type = sc->type;
1253         char *sc_db = (char *)VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
1254         if (sc->data.cert.database)
1255             sc_db = sc->data.cert.database;
1256         switch (sc_type) {
1257             /*
1258              * Note: At time of writing, to get this working, qemu seccomp sandbox has
1259              * to be disabled or the host must be running QEMU with commit
1260              * 9a1565a03b79d80b236bc7cc2dbce52a2ef3a1b8.
1261              * It's possibly due to libcacard:vcard_emul_new_event_thread(), which calls
1262              * PR_CreateThread(), which calls {g,s}etpriority(). And resourcecontrol seccomp
1263              * filter forbids it (cf src/qemu/qemu_command.c which seems to always use
1264              * resourcecontrol=deny).
1265              */
1266             case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
1267                 virBufferAddLit(&buf, "  \"/etc/pki/nssdb/{,*}\" rk,\n");
1268                 break;
1269             case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
1270                 virBufferAsprintf(&buf, "  \"%s/{,*}\" rk,\n", sc_db);
1271                 break;
1272             /*
1273              * Nothing to do for passthrough, as the smartcard
1274              * access is done through TCP or Spice
1275              */
1276             case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
1277                 break;
1278             case VIR_DOMAIN_SMARTCARD_TYPE_LAST:
1279                 break;
1280         }
1281     }
1282 
1283     if (ctl->def->virtType == VIR_DOMAIN_VIRT_KVM) {
1284         for (i = 0; i < ctl->def->nnets; i++) {
1285             virDomainNetDef *net = ctl->def->nets[i];
1286             if (net && virDomainNetGetModelString(net)) {
1287                 if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_QEMU)
1288                     continue;
1289                 if (!virDomainNetIsVirtioModel(net))
1290                     continue;
1291             }
1292             needsvhost = true;
1293         }
1294     }
1295     if (needsvhost)
1296         virBufferAddLit(&buf, "  \"/dev/vhost-net\" rw,\n");
1297 
1298     if (needsVfio) {
1299         virBufferAddLit(&buf, "  \"/dev/vfio/vfio\" rw,\n");
1300         virBufferAddLit(&buf, "  \"/dev/vfio/[0-9]*\" rw,\n");
1301     }
1302     if (needsgl) {
1303         /* if using gl all sorts of further dri related paths will be needed */
1304         virBufferAddLit(&buf, "  # DRI/Mesa/(e)GL config and driver paths\n");
1305         virBufferAddLit(&buf, "  \"/usr/lib{,32,64}/dri/*.so*\" mr,\n");
1306         virBufferAddLit(&buf, "  \"/usr/lib/@{multiarch}/dri/*.so*\" mr,\n");
1307         virBufferAddLit(&buf, "  \"/usr/lib/fglrx/dri/*.so*\" mr,\n");
1308         virBufferAddLit(&buf, "  \"/etc/drirc\" r,\n");
1309         virBufferAddLit(&buf, "  \"/usr/share/drirc.d/{,*.conf}\" r,\n");
1310         virBufferAddLit(&buf, "  \"/etc/glvnd/egl_vendor.d/{,*}\" r,\n");
1311         virBufferAddLit(&buf, "  \"/usr/share/glvnd/egl_vendor.d/{,*}\" r,\n");
1312         virBufferAddLit(&buf, "  \"/usr/share/egl/egl_external_platform.d/\" r,\n");
1313         virBufferAddLit(&buf, "  \"/usr/share/egl/egl_external_platform.d/*\" r,\n");
1314         virBufferAddLit(&buf, "  \"/proc/modules\" r,\n");
1315         virBufferAddLit(&buf, "  \"/proc/driver/nvidia/params\" r,\n");
1316         virBufferAddLit(&buf, "  \"/dev/nvidiactl\" rw,\n");
1317         virBufferAddLit(&buf, "  # Probe DRI device attributes\n");
1318         virBufferAddLit(&buf, "  \"/dev/dri/\" r,\n");
1319         virBufferAddLit(&buf, "  \"/sys/devices/**/{uevent,vendor,device,subsystem_vendor,subsystem_device}\" r,\n");
1320         virBufferAddLit(&buf, "  # dri libs will trigger that, but t is not requited and DAC would deny it anyway\n");
1321         virBufferAddLit(&buf, "  deny \"/var/lib/libvirt/.cache/\" w,\n");
1322     }
1323 
1324     if (ctl->newfile)
1325         if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
1326             goto cleanup;
1327 
1328     rc = 0;
1329     ctl->files = virBufferContentAndReset(&buf);
1330 
1331  cleanup:
1332     VIR_FREE(mem_path);
1333     VIR_FREE(uuid);
1334     return rc;
1335 }
1336 
1337 static int
vahParseArgv(vahControl * ctl,int argc,char ** argv)1338 vahParseArgv(vahControl * ctl, int argc, char **argv)
1339 {
1340     int arg, idx = 0;
1341     struct option opt[] = {
1342         {"add", 0, 0, 'a'},
1343         {"create", 0, 0, 'c'},
1344         {"dryrun", 0, 0, 'd'},
1345         {"delete", 0, 0, 'D'},
1346         {"add-file", 0, 0, 'f'},
1347         {"append-file", 0, 0, 'F'},
1348         {"help", 0, 0, 'h'},
1349         {"replace", 0, 0, 'r'},
1350         {"remove", 0, 0, 'R'},
1351         {"uuid", 1, 0, 'u'},
1352         {0, 0, 0, 0}
1353     };
1354 
1355     while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:", opt,
1356             &idx)) != -1) {
1357         switch (arg) {
1358             case 'a':
1359                 ctl->cmd = 'a';
1360                 break;
1361             case 'c':
1362                 ctl->cmd = 'c';
1363                 break;
1364             case 'd':
1365                 ctl->dryrun = true;
1366                 break;
1367             case 'D':
1368                 ctl->cmd = 'D';
1369                 break;
1370             case 'f':
1371             case 'F':
1372                 ctl->newfile = g_strdup(optarg);
1373                 ctl->append = arg == 'F';
1374                 break;
1375             case 'h':
1376                 vah_usage();
1377                 exit(EXIT_SUCCESS);
1378                 break;
1379             case 'r':
1380                 ctl->cmd = 'r';
1381                 break;
1382             case 'R':
1383                 ctl->cmd = 'R';
1384                 break;
1385             case 'u':
1386                 if (strlen(optarg) > PROFILE_NAME_SIZE - 1)
1387                     vah_error(ctl, 1, _("invalid UUID"));
1388                 if (virStrcpy((char *)ctl->uuid, optarg,
1389                     PROFILE_NAME_SIZE) < 0)
1390                     vah_error(ctl, 1, _("error copying UUID"));
1391                 break;
1392             default:
1393                 vah_error(ctl, 1, _("unsupported option"));
1394                 break;
1395         }
1396     }
1397     if (strchr("acDrR", ctl->cmd) == NULL)
1398         vah_error(ctl, 1, _("bad command"));
1399 
1400     if (valid_uuid(ctl->uuid) != 0)
1401         vah_error(ctl, 1, _("invalid UUID"));
1402 
1403     if (!ctl->cmd) {
1404         vah_usage();
1405         exit(EXIT_FAILURE);
1406     }
1407 
1408     if (ctl->cmd == 'c' || ctl->cmd == 'r') {
1409         char *xmlStr = NULL;
1410         if (virFileReadLimFD(STDIN_FILENO, MAX_FILE_LEN, &xmlStr) < 0)
1411             vah_error(ctl, 1, _("could not read xml file"));
1412 
1413         if (get_definition(ctl, xmlStr) != 0 || ctl->def == NULL) {
1414             VIR_FREE(xmlStr);
1415             vah_error(ctl, 1, _("could not get VM definition"));
1416         }
1417         VIR_FREE(xmlStr);
1418 
1419         if (get_files(ctl) != 0)
1420             vah_error(ctl, 1, _("invalid VM definition"));
1421     }
1422     return 0;
1423 }
1424 
1425 
1426 /*
1427  * virt-aa-helper -c -u UUID < file.xml
1428  * virt-aa-helper -r -u UUID [-f <file>] < file.xml
1429  * virt-aa-helper -a -u UUID
1430  * virt-aa-helper -R -u UUID
1431  * virt-aa-helper -D -u UUID
1432  */
1433 int
main(int argc,char ** argv)1434 main(int argc, char **argv)
1435 {
1436     vahControl _ctl, *ctl = &_ctl;
1437     int rc = -1;
1438     char *profile = NULL;
1439     char *include_file = NULL;
1440     off_t size;
1441     bool purged = 0;
1442 
1443     if (virGettextInitialize() < 0 ||
1444         virErrorInitialize() < 0) {
1445         fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
1446         exit(EXIT_FAILURE);
1447     }
1448 
1449     virFileActivateDirOverrideForProg(argv[0]);
1450 
1451     /* Initialize the log system */
1452     virLogSetFromEnv();
1453 
1454     /* clear the environment */
1455     environ = NULL;
1456     if (g_setenv("PATH", "/sbin:/usr/sbin", TRUE) == FALSE)
1457         vah_error(ctl, 1, _("could not set PATH"));
1458 
1459     /* ensure the traditional IFS setting */
1460     if (g_setenv("IFS", " \t\n", TRUE) == FALSE)
1461         vah_error(ctl, 1, _("could not set IFS"));
1462 
1463     if (!(progname = strrchr(argv[0], '/')))
1464         progname = argv[0];
1465     else
1466         progname++;
1467 
1468     memset(ctl, 0, sizeof(vahControl));
1469 
1470     if (vahParseArgv(ctl, argc, argv) != 0)
1471         vah_error(ctl, 1, _("could not parse arguments"));
1472 
1473     profile = g_strdup_printf("%s/%s", APPARMOR_DIR "/libvirt", ctl->uuid);
1474     include_file = g_strdup_printf("%s/%s.files", APPARMOR_DIR "/libvirt", ctl->uuid);
1475 
1476     if (ctl->cmd == 'a') {
1477         rc = parserLoad(ctl->uuid);
1478     } else if (ctl->cmd == 'R' || ctl->cmd == 'D') {
1479         rc = parserRemove(ctl->uuid);
1480         if (ctl->cmd == 'D')
1481             unlink(include_file);
1482     } else if (ctl->cmd == 'c' || ctl->cmd == 'r') {
1483         char *included_files = NULL;
1484         g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
1485 
1486         if (ctl->cmd == 'c' && virFileExists(profile))
1487             vah_error(ctl, 1, _("profile exists"));
1488 
1489         /*
1490          * Rare cases can leave corrupted empty files behind breaking
1491          * the guest. An empty file is never correct as virt-aa-helper
1492          * would at least add the basic rules, therefore clean this up
1493          * for a proper refresh.
1494          */
1495         if (virFileExists(profile)) {
1496                 size = virFileLength(profile, -1);
1497                 if (size == 0) {
1498                         vah_warning(_("Profile of 0 size detected, will attempt to remove it"));
1499                         if ((rc = parserRemove(ctl->uuid) != 0))
1500                                 vah_error(ctl, 1, _("could not remove profile"));
1501                         unlink(profile);
1502                         purged = true;
1503                 }
1504         }
1505         if (ctl->append && ctl->newfile) {
1506             if (vah_add_file(&buf, ctl->newfile, "rwk") != 0)
1507                 goto cleanup;
1508         } else {
1509             if (ctl->def->virtType == VIR_DOMAIN_VIRT_QEMU ||
1510                 ctl->def->virtType == VIR_DOMAIN_VIRT_KQEMU ||
1511                 ctl->def->virtType == VIR_DOMAIN_VIRT_KVM) {
1512                 virBufferAsprintf(&buf, "  \"%s/log/libvirt/**/%s.log\" w,\n",
1513                                   LOCALSTATEDIR, ctl->def->name);
1514                 virBufferAsprintf(&buf, "  \"%s/lib/libvirt/qemu/domain-%s/monitor.sock\" rw,\n",
1515                                   LOCALSTATEDIR, ctl->def->name);
1516                 virBufferAsprintf(&buf, "  \"%s/lib/libvirt/qemu/domain-%d-%.*s/*\" rw,\n",
1517                                   LOCALSTATEDIR, ctl->def->id, 20, ctl->def->name);
1518                 virBufferAsprintf(&buf, "  \"%s/libvirt/**/%s.pid\" rwk,\n",
1519                                   RUNSTATEDIR, ctl->def->name);
1520                 virBufferAsprintf(&buf, "  \"%s/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
1521                                   RUNSTATEDIR, ctl->def->name);
1522             }
1523             if (ctl->files)
1524                 virBufferAdd(&buf, ctl->files, -1);
1525         }
1526 
1527         included_files = virBufferContentAndReset(&buf);
1528 
1529         /* (re)create the include file using included_files */
1530         if (ctl->dryrun) {
1531             vah_info(include_file);
1532             vah_info(included_files);
1533             rc = 0;
1534         } else if (ctl->def->virtType == VIR_DOMAIN_VIRT_LXC) {
1535             rc = 0;
1536         } else if ((rc = update_include_file(include_file,
1537                                              included_files,
1538                                              ctl->append)) != 0) {
1539             goto cleanup;
1540         }
1541 
1542 
1543         /* create the profile from TEMPLATE */
1544         if (ctl->cmd == 'c' || purged) {
1545             char *tmp = NULL;
1546             tmp = g_strdup_printf("  #include <libvirt/%s.files>\n", ctl->uuid);
1547 
1548             if (ctl->dryrun) {
1549                 vah_info(profile);
1550                 vah_info(ctl->uuid);
1551                 vah_info(tmp);
1552                 rc = 0;
1553             } else if ((rc = create_profile(profile, ctl->uuid, tmp,
1554                                             ctl->def->virtType)) != 0) {
1555                 vah_error(ctl, 0, _("could not create profile"));
1556                 unlink(include_file);
1557             }
1558             VIR_FREE(tmp);
1559         }
1560 
1561         if (rc == 0 && !ctl->dryrun) {
1562             if (ctl->cmd == 'c')
1563                 rc = parserLoad(ctl->uuid);
1564             else
1565                 rc = parserReplace(ctl->uuid);
1566 
1567             /* cleanup */
1568             if (rc != 0) {
1569                 unlink(include_file);
1570                 if (ctl->cmd == 'c')
1571                     unlink(profile);
1572             }
1573         }
1574       cleanup:
1575         VIR_FREE(included_files);
1576     }
1577 
1578     vahDeinit(ctl);
1579 
1580     VIR_FREE(profile);
1581     VIR_FREE(include_file);
1582 
1583     exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1584 }
1585