Merge lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009222219 into lp:ubuntu/maverick/libvirt

Proposed by James Westby
Status: Work in progress
Proposed branch: lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009222219
Merge into: lp:ubuntu/maverick/libvirt
Diff against target: 1271 lines (+1214/-0) (has conflicts)
4 files modified
.pc/.quilt_patches (+1/-0)
.pc/.quilt_series (+1/-0)
.pc/9028-lp628055.patch/src/security/virt-aa-helper.c (+1195/-0)
debian/patches/9028-lp628055.patch (+17/-0)
Conflict adding file .pc/9028-lp628055.patch.  Moved existing file to .pc/9028-lp628055.patch.moved.
Conflict adding file debian/patches/9028-lp628055.patch.  Moved existing file to debian/patches/9028-lp628055.patch.moved.
To merge this branch: bzr merge lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009222219

Description of the change

The package history in the archive and the history in the bzr branch differ. As the archive is authoritative the history of lp:ubuntu/maverick/libvirt now reflects that and the old bzr branch has been pushed to lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009222219. A merge should be performed if necessary.

To post a comment you must log in.

Unmerged revisions

106. By Jamie Strandboge

releasing version 0.8.3-1ubuntu13

105. By Jamie Strandboge

debian/patch/9028-lp628055.patch: include sys/stat.h to fix compiler
warning and stat() failure on 32bit architectures when calling stat() on
large files. This can be dropped in 0.8.5. (LP: #628055)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.pc/.quilt_patches'
2--- .pc/.quilt_patches 1970-01-01 00:00:00 +0000
3+++ .pc/.quilt_patches 2010-09-22 22:26:01 +0000
4@@ -0,0 +1,1 @@
5+debian/patches
6
7=== added file '.pc/.quilt_series'
8--- .pc/.quilt_series 1970-01-01 00:00:00 +0000
9+++ .pc/.quilt_series 2010-09-22 22:26:01 +0000
10@@ -0,0 +1,1 @@
11+series
12
13=== added file '.pc/0001-remove-RHism.diff.patch/.timestamp'
14=== added file '.pc/0003-allow-libvirt-group-to-access-the-socket.patch/.timestamp'
15=== added file '.pc/0004-fix-Debian-specific-path-to-hvm-loader.patch/.timestamp'
16=== added file '.pc/0006-patch-qemuMonitorTextGetMigrationStatus-to-intercept.patch/.timestamp'
17=== added file '.pc/9000-delayed_iff_up_bridge.patch/.timestamp'
18=== added file '.pc/9001-dont_clobber_existing_bridges.patch/.timestamp'
19=== added file '.pc/9002-better_default_uri_virsh.patch/.timestamp'
20=== added file '.pc/9003-better-default-arch.patch/.timestamp'
21=== added file '.pc/9004-libvirtd-group-name.patch/.timestamp'
22=== added file '.pc/9005-increase-unix-socket-timeout.patch/.timestamp'
23=== added file '.pc/9006-default-config-test-case.patch/.timestamp'
24=== added file '.pc/9007-fix-daemon-conf-ftbfs.patch/.timestamp'
25=== added file '.pc/9009-autodetect-nc-params.patch/.timestamp'
26=== added file '.pc/9010-dont-disable-ipv6.patch/.timestamp'
27=== added file '.pc/9011-move-ebtables-script.patch/.timestamp'
28=== added file '.pc/9012-apparmor-extra-tests.patch/.timestamp'
29=== added file '.pc/9013-apparmor-chardev.patch/.timestamp'
30=== added file '.pc/9014-skip-nodeinfotest.patch/.timestamp'
31=== added file '.pc/9015-Add-ubd-to-the-list-of-disk-prefixes.patch/.timestamp'
32=== added file '.pc/9016-Close-fd-s-of-persistent-tap-devices.patch/.timestamp'
33=== added file '.pc/9017-Make-sure-all-command-line-arguments-get-passed-to-U.patch/.timestamp'
34=== added file '.pc/9018-Make-umlConnectTapDevice-ask-brAddTap-for-a-persiste.patch/.timestamp'
35=== added file '.pc/9019-uml-fix-logic-bug-in-checking-reply-length.patch/.timestamp'
36=== added file '.pc/9020-lp545795.patch/.timestamp'
37=== added file '.pc/9021-Allow-chardev-of-type-file-for-UML-domains.patch/.timestamp'
38=== added file '.pc/9022-Rename-qemudShrinkDisks-to-virDomainDiskRemove-and-m.patch/.timestamp'
39=== added file '.pc/9023-Support-virDomainAttachDevice-and-virDomainDetachDev.patch/.timestamp'
40=== added file '.pc/9024-Explicitly-pass-uml_dir-argument-to-user-mode-linux.patch/.timestamp'
41=== added file '.pc/9025-Add-nwfilter-support-to-UML-driver.patch/.timestamp'
42=== added file '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/.timestamp'
43=== added file '.pc/9027-Make-newfilter-xml-transformations-endian-safe.patch/.timestamp'
44=== added directory '.pc/9028-lp628055.patch'
45=== renamed directory '.pc/9028-lp628055.patch' => '.pc/9028-lp628055.patch.moved'
46=== added file '.pc/9028-lp628055.patch/.timestamp'
47=== added directory '.pc/9028-lp628055.patch/src'
48=== added directory '.pc/9028-lp628055.patch/src/security'
49=== added file '.pc/9028-lp628055.patch/src/security/virt-aa-helper.c'
50--- .pc/9028-lp628055.patch/src/security/virt-aa-helper.c 1970-01-01 00:00:00 +0000
51+++ .pc/9028-lp628055.patch/src/security/virt-aa-helper.c 2010-09-22 22:26:01 +0000
52@@ -0,0 +1,1195 @@
53+
54+/*
55+ * virt-aa-helper: wrapper program used by AppArmor security driver.
56+ * Copyright (C) 2009 Canonical Ltd.
57+ *
58+ * See COPYING.LIB for the License of this software
59+ *
60+ * Author:
61+ * Jamie Strandboge <jamie@canonical.com>
62+ *
63+ */
64+
65+#include <config.h>
66+
67+#include <stdio.h>
68+#include <stdlib.h>
69+#include <string.h>
70+#include <stdarg.h>
71+#include <unistd.h>
72+#include <errno.h>
73+#include <sys/types.h>
74+#include <fcntl.h>
75+#include <getopt.h>
76+#include <stdbool.h>
77+#include <sys/utsname.h>
78+
79+#include "internal.h"
80+#include "buf.h"
81+#include "util.h"
82+#include "memory.h"
83+
84+#include "security_driver.h"
85+#include "security_apparmor.h"
86+#include "domain_conf.h"
87+#include "xml.h"
88+#include "uuid.h"
89+#include "hostusb.h"
90+#include "pci.h"
91+
92+static char *progname;
93+
94+typedef struct {
95+ bool allowDiskFormatProbing;
96+ char uuid[PROFILE_NAME_SIZE]; /* UUID of vm */
97+ bool dryrun; /* dry run */
98+ char cmd; /* 'c' create
99+ * 'a' add (load)
100+ * 'r' replace
101+ * 'R' remove */
102+ char *files; /* list of files */
103+ virDomainDefPtr def; /* VM definition */
104+ virCapsPtr caps; /* VM capabilities */
105+ char *hvm; /* type of hypervisor (eg hvm, xen) */
106+ char *arch; /* machine architecture */
107+ int bits; /* bits in the guest */
108+ char *newdisk; /* newly added disk */
109+} vahControl;
110+
111+static int
112+vahDeinit(vahControl * ctl)
113+{
114+ if (ctl == NULL)
115+ return -1;
116+
117+ VIR_FREE(ctl->def);
118+ virCapabilitiesFree(ctl->caps);
119+ VIR_FREE(ctl->files);
120+ VIR_FREE(ctl->hvm);
121+ VIR_FREE(ctl->arch);
122+ VIR_FREE(ctl->newdisk);
123+
124+ return 0;
125+}
126+
127+/*
128+ * Print usage
129+ */
130+static void
131+vah_usage(void)
132+{
133+ fprintf(stdout, "\n%s [options] [< def.xml]\n\n"
134+ " Options:\n"
135+ " -a | --add load profile\n"
136+ " -c | --create create profile from template\n"
137+ " -D | --delete unload and delete profile\n"
138+ " -r | --replace reload profile\n"
139+ " -R | --remove unload profile\n"
140+ " -h | --help this help\n"
141+ " -u | --uuid <uuid> uuid (profile name)\n"
142+ "\n", progname);
143+
144+ fprintf(stdout, "This command is intended to be used by libvirtd "
145+ "and not used directly.\n");
146+ return;
147+}
148+
149+static void
150+vah_error(vahControl * ctl, int doexit, const char *str)
151+{
152+ fprintf(stderr, _("%s: error: %s\n"), progname, str);
153+
154+ if (doexit) {
155+ if (ctl != NULL)
156+ vahDeinit(ctl);
157+ exit(EXIT_FAILURE);
158+ }
159+}
160+
161+static void
162+vah_warning(const char *str)
163+{
164+ fprintf(stderr, _("%s: warning: %s\n"), progname, str);
165+}
166+
167+static void
168+vah_info(const char *str)
169+{
170+ fprintf(stderr, _("%s:\n%s\n"), progname, str);
171+}
172+
173+/*
174+ * Replace @oldstr in @orig with @repstr
175+ * @len is number of bytes allocated for @orig. Assumes @orig, @oldstr and
176+ * @repstr are null terminated
177+ */
178+static int
179+replace_string(char *orig, const size_t len, const char *oldstr,
180+ const char *repstr)
181+{
182+ int idx;
183+ char *pos = NULL;
184+ char *tmp = NULL;
185+
186+ if ((pos = strstr(orig, oldstr)) == NULL) {
187+ vah_error(NULL, 0, "could not find replacement string");
188+ return -1;
189+ }
190+
191+ if (VIR_ALLOC_N(tmp, len) < 0) {
192+ vah_error(NULL, 0, "could not allocate memory for string");
193+ return -1;
194+ }
195+ tmp[0] = '\0';
196+
197+ idx = abs(pos - orig);
198+
199+ /* copy everything up to oldstr */
200+ strncat(tmp, orig, idx);
201+
202+ /* add the replacement string */
203+ if (strlen(tmp) + strlen(repstr) > len - 1) {
204+ vah_error(NULL, 0, "not enough space in target buffer");
205+ VIR_FREE(tmp);
206+ return -1;
207+ }
208+ strcat(tmp, repstr);
209+
210+ /* add everything after oldstr */
211+ if (strlen(tmp) + strlen(orig) - (idx + strlen(oldstr)) > len - 1) {
212+ vah_error(NULL, 0, "not enough space in target buffer");
213+ VIR_FREE(tmp);
214+ return -1;
215+ }
216+ strncat(tmp, orig + idx + strlen(oldstr),
217+ strlen(orig) - (idx + strlen(oldstr)));
218+
219+ if (virStrcpy(orig, tmp, len) == NULL) {
220+ vah_error(NULL, 0, "error replacing string");
221+ VIR_FREE(tmp);
222+ return -1;
223+ }
224+ VIR_FREE(tmp);
225+
226+ return 0;
227+}
228+
229+/*
230+ * run an apparmor_parser command
231+ */
232+static int
233+parserCommand(const char *profile_name, const char cmd)
234+{
235+ char flag[3];
236+ char profile[PATH_MAX];
237+ int status;
238+ int ret;
239+
240+ if (strchr("arR", cmd) == NULL) {
241+ vah_error(NULL, 0, "invalid flag");
242+ return -1;
243+ }
244+
245+ snprintf(flag, 3, "-%c", cmd);
246+
247+ if (snprintf(profile, PATH_MAX, "%s/%s",
248+ APPARMOR_DIR "/libvirt", profile_name) > PATH_MAX - 1) {
249+ vah_error(NULL, 0, "profile name exceeds maximum length");
250+ return -1;
251+ }
252+
253+ if (!virFileExists(profile)) {
254+ vah_error(NULL, 0, "profile does not exist");
255+ return -1;
256+ } else {
257+ const char * const argv[] = {
258+ "/sbin/apparmor_parser", flag, profile, NULL
259+ };
260+ if ((ret = virRun(argv, &status)) != 0 ||
261+ (WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
262+ if (ret != 0) {
263+ vah_error(NULL, 0, "failed to run apparmor_parser");
264+ return -1;
265+ } else if (cmd == 'R' && WIFEXITED(status) && WEXITSTATUS(status) == 234) {
266+ vah_warning("unable to unload already unloaded profile (non-fatal)");
267+ } else {
268+ vah_error(NULL, 0, "apparmor_parser exited with error");
269+ return -1;
270+ }
271+ }
272+ }
273+
274+ return 0;
275+}
276+
277+/*
278+ * Update the dynamic files
279+ */
280+static int
281+update_include_file(const char *include_file, const char *included_files)
282+{
283+ int rc = -1;
284+ int plen;
285+ int fd;
286+ char *pcontent = NULL;
287+ const char *warning =
288+ "# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
289+
290+ if (virAsprintf(&pcontent, "%s%s", warning, included_files) == -1) {
291+ vah_error(NULL, 0, "could not allocate memory for profile");
292+ return rc;
293+ }
294+
295+ plen = strlen(pcontent);
296+ if (plen > MAX_FILE_LEN) {
297+ vah_error(NULL, 0, "invalid length for new profile");
298+ goto clean;
299+ }
300+
301+ /* only update the disk profile if it is different */
302+ if (virFileExists(include_file)) {
303+ char *existing = NULL;
304+ int flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
305+ if (flen < 0)
306+ goto clean;
307+
308+ if (flen == plen) {
309+ if (STREQLEN(existing, pcontent, plen)) {
310+ rc = 0;
311+ VIR_FREE(existing);
312+ goto clean;
313+ }
314+ }
315+ VIR_FREE(existing);
316+ }
317+
318+ /* write the file */
319+ if ((fd = open(include_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
320+ vah_error(NULL, 0, "failed to create include file");
321+ goto clean;
322+ }
323+
324+ if (safewrite(fd, pcontent, plen) < 0) { /* don't write the '\0' */
325+ close(fd);
326+ vah_error(NULL, 0, "failed to write to profile");
327+ goto clean;
328+ }
329+
330+ if (close(fd) != 0) {
331+ vah_error(NULL, 0, "failed to close or write to profile");
332+ goto clean;
333+ }
334+ rc = 0;
335+
336+ clean:
337+ VIR_FREE(pcontent);
338+
339+ return rc;
340+}
341+
342+/*
343+ * Create a profile based on a template
344+ */
345+static int
346+create_profile(const char *profile, const char *profile_name,
347+ const char *profile_files)
348+{
349+ char template[PATH_MAX];
350+ char *tcontent = NULL;
351+ char *pcontent = NULL;
352+ char *replace_name = NULL;
353+ char *replace_files = NULL;
354+ const char *template_name = "\nprofile LIBVIRT_TEMPLATE";
355+ const char *template_end = "\n}";
356+ int tlen, plen;
357+ int fd;
358+ int rc = -1;
359+
360+ if (virFileExists(profile)) {
361+ vah_error(NULL, 0, "profile exists");
362+ goto end;
363+ }
364+
365+ if (snprintf(template, PATH_MAX, "%s/TEMPLATE",
366+ APPARMOR_DIR "/libvirt") > PATH_MAX - 1) {
367+ vah_error(NULL, 0, "template name exceeds maximum length");
368+ goto end;
369+ }
370+
371+ if (!virFileExists(template)) {
372+ vah_error(NULL, 0, "template does not exist");
373+ goto end;
374+ }
375+
376+ if ((tlen = virFileReadAll(template, MAX_FILE_LEN, &tcontent)) < 0) {
377+ vah_error(NULL, 0, "failed to read AppArmor template");
378+ goto end;
379+ }
380+
381+ if (strstr(tcontent, template_name) == NULL) {
382+ vah_error(NULL, 0, "no replacement string in template");
383+ goto clean_tcontent;
384+ }
385+
386+ if (strstr(tcontent, template_end) == NULL) {
387+ vah_error(NULL, 0, "no replacement string in template");
388+ goto clean_tcontent;
389+ }
390+
391+ /* '\nprofile <profile_name>\0' */
392+ if (virAsprintf(&replace_name, "\nprofile %s", profile_name) == -1) {
393+ vah_error(NULL, 0, "could not allocate memory for profile name");
394+ goto clean_tcontent;
395+ }
396+
397+ /* '\n<profile_files>\n}\0' */
398+ if (virAsprintf(&replace_files, "\n%s\n}", profile_files) == -1) {
399+ vah_error(NULL, 0, "could not allocate memory for profile files");
400+ VIR_FREE(replace_name);
401+ goto clean_tcontent;
402+ }
403+
404+ plen = tlen + strlen(replace_name) - strlen(template_name) +
405+ strlen(replace_files) - strlen(template_end) + 1;
406+ if (plen > MAX_FILE_LEN || plen < tlen) {
407+ vah_error(NULL, 0, "invalid length for new profile");
408+ goto clean_replace;
409+ }
410+
411+ if (VIR_ALLOC_N(pcontent, plen) < 0) {
412+ vah_error(NULL, 0, "could not allocate memory for profile");
413+ goto clean_replace;
414+ }
415+ pcontent[0] = '\0';
416+ strcpy(pcontent, tcontent);
417+
418+ if (replace_string(pcontent, plen, template_name, replace_name) < 0)
419+ goto clean_all;
420+
421+ if (replace_string(pcontent, plen, template_end, replace_files) < 0)
422+ goto clean_all;
423+
424+ /* write the file */
425+ if ((fd = open(profile, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1) {
426+ vah_error(NULL, 0, "failed to create profile");
427+ goto clean_all;
428+ }
429+
430+ if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
431+ close(fd);
432+ vah_error(NULL, 0, "failed to write to profile");
433+ goto clean_all;
434+ }
435+
436+ if (close(fd) != 0) {
437+ vah_error(NULL, 0, "failed to close or write to profile");
438+ goto clean_all;
439+ }
440+ rc = 0;
441+
442+ clean_all:
443+ VIR_FREE(pcontent);
444+ clean_replace:
445+ VIR_FREE(replace_name);
446+ VIR_FREE(replace_files);
447+ clean_tcontent:
448+ VIR_FREE(tcontent);
449+ end:
450+ return rc;
451+}
452+
453+/*
454+ * Load an existing profile
455+ */
456+static int
457+parserLoad(const char *profile_name)
458+{
459+ return parserCommand(profile_name, 'a');
460+}
461+
462+/*
463+ * Remove an existing profile
464+ */
465+static int
466+parserRemove(const char *profile_name)
467+{
468+ return parserCommand(profile_name, 'R');
469+}
470+
471+/*
472+ * Replace an existing profile
473+ */
474+static int
475+parserReplace(const char *profile_name)
476+{
477+ return parserCommand(profile_name, 'r');
478+}
479+
480+static int
481+valid_uuid(const char *uuid)
482+{
483+ unsigned char rawuuid[VIR_UUID_BUFLEN];
484+
485+ if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
486+ return -1;
487+
488+ if (!STRPREFIX(uuid, AA_PREFIX))
489+ return -1;
490+
491+ if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
492+ return -1;
493+
494+ return 0;
495+}
496+
497+static int
498+valid_name(const char *name)
499+{
500+ /* just try to filter out any dangerous characters in the name that can be
501+ * used to subvert the profile */
502+ const char *bad = " /[]*";
503+
504+ if (strlen(name) == 0 || strlen(name) > PATH_MAX - 1)
505+ return -1;
506+
507+ if (strcspn(name, bad) != strlen(name))
508+ return -1;
509+
510+ return 0;
511+}
512+
513+/* see if one of the strings in arr starts with str */
514+static int
515+array_starts_with(const char *str, const char * const *arr, const long size)
516+{
517+ int i;
518+ for (i = 0; i < size; i++) {
519+ if (strlen(str) < strlen(arr[i]))
520+ continue;
521+
522+ if (STRPREFIX(str, arr[i]))
523+ return 0;
524+ }
525+ return 1;
526+}
527+
528+/*
529+ * Don't allow access to special files or restricted paths such as /bin, /sbin,
530+ * /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
531+ * access to system files which could be used to elevate privileges. This is a
532+ * safety measure in case libvirtd is under a restrictive profile and is
533+ * subverted and trying to escape confinement.
534+ *
535+ * Note that we cannot exclude block devices because they are valid devices.
536+ * The TEMPLATE file can be adjusted to explicitly disallow these if needed.
537+ *
538+ * RETURN: -1 on error, 0 if ok, 1 if blocked
539+ */
540+static int
541+valid_path(const char *path, const bool readonly)
542+{
543+ struct stat sb;
544+ int npaths, opaths;
545+ const char * const restricted[] = {
546+ "/bin/",
547+ "/etc/",
548+ "/lib",
549+ "/lost+found/",
550+ "/proc/",
551+ "/sbin/",
552+ "/selinux/",
553+ "/sys/",
554+ "/usr/bin/",
555+ "/usr/lib",
556+ "/usr/sbin/",
557+ "/usr/share/",
558+ "/usr/local/bin/",
559+ "/usr/local/etc/",
560+ "/usr/local/lib",
561+ "/usr/local/sbin/"
562+ };
563+ /* these paths are ok for readonly, but not read/write */
564+ const char * const restricted_rw[] = {
565+ "/boot/",
566+ "/vmlinuz",
567+ "/initrd",
568+ "/initrd.img"
569+ };
570+ /* override the above with these */
571+ const char * const override[] = {
572+ "/sys/devices/pci" /* for hostdev pci devices */
573+ };
574+
575+ if (path == NULL || strlen(path) > PATH_MAX - 1) {
576+ vah_error(NULL, 0, "bad pathname");
577+ return -1;
578+ }
579+
580+ /* Don't allow double quotes, since we use them to quote the filename
581+ * and this will confuse the apparmor parser.
582+ */
583+ if (strchr(path, '"') != NULL)
584+ return 1;
585+
586+ /* Require an absolute path */
587+ if (STRNEQLEN(path, "/", 1))
588+ return 1;
589+
590+ if (!virFileExists(path))
591+ vah_warning("path does not exist, skipping file type checks");
592+ else {
593+ if (stat(path, &sb) == -1)
594+ return -1;
595+
596+ switch (sb.st_mode & S_IFMT) {
597+ case S_IFDIR:
598+ return 1;
599+ break;
600+ case S_IFIFO:
601+ return 1;
602+ break;
603+ case S_IFSOCK:
604+ return 1;
605+ break;
606+ default:
607+ break;
608+ }
609+ }
610+
611+ opaths = sizeof(override)/sizeof *(override);
612+
613+ npaths = sizeof(restricted)/sizeof *(restricted);
614+ if (array_starts_with(path, restricted, npaths) == 0 &&
615+ array_starts_with(path, override, opaths) != 0)
616+ return 1;
617+
618+ npaths = sizeof(restricted_rw)/sizeof *(restricted_rw);
619+ if (!readonly) {
620+ if (array_starts_with(path, restricted_rw, npaths) == 0)
621+ return 1;
622+ }
623+
624+ return 0;
625+}
626+
627+/* Called from SAX on parsing errors in the XML. */
628+static void
629+catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
630+{
631+ xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
632+
633+ if (ctxt) {
634+ if (virGetLastError() == NULL &&
635+ ctxt->lastError.level == XML_ERR_FATAL &&
636+ ctxt->lastError.message != NULL) {
637+ char *err_str = NULL;
638+ if (virAsprintf(&err_str, "XML error at line %d: %s",
639+ ctxt->lastError.line,
640+ ctxt->lastError.message) == -1)
641+ vah_error(NULL, 0, "Could not get XML error");
642+ else {
643+ vah_error(NULL, 0, err_str);
644+ VIR_FREE(err_str);
645+ }
646+ }
647+ }
648+}
649+
650+/*
651+ * Parse the xml we received to fill in the following:
652+ * ctl->hvm
653+ * ctl->arch
654+ * ctl->bits
655+ *
656+ * These are suitable for setting up a virCapsPtr
657+ */
658+static int
659+caps_mockup(vahControl * ctl, const char *xmlStr)
660+{
661+ int rc = -1;
662+ xmlParserCtxtPtr pctxt = NULL;
663+ xmlDocPtr xml = NULL;
664+ xmlXPathContextPtr ctxt = NULL;
665+ xmlNodePtr root;
666+
667+ /* Set up a parser context so we can catch the details of XML errors. */
668+ pctxt = xmlNewParserCtxt ();
669+ if (!pctxt || !pctxt->sax)
670+ goto cleanup;
671+ pctxt->sax->error = catchXMLError;
672+
673+ xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr, "domain.xml", NULL,
674+ XML_PARSE_NOENT | XML_PARSE_NONET |
675+ XML_PARSE_NOWARNING);
676+ if (!xml) {
677+ if (virGetLastError() == NULL)
678+ vah_error(NULL, 0, "failed to parse xml document");
679+ goto cleanup;
680+ }
681+
682+ if ((root = xmlDocGetRootElement(xml)) == NULL) {
683+ vah_error(NULL, 0, "missing root element");
684+ goto cleanup;
685+ }
686+
687+ if (!xmlStrEqual(root->name, BAD_CAST "domain")) {
688+ vah_error(NULL, 0, "incorrect root element");
689+ goto cleanup;
690+ }
691+
692+ if ((ctxt = xmlXPathNewContext(xml)) == NULL) {
693+ vah_error(ctl, 0, "could not allocate memory");
694+ goto cleanup;
695+ }
696+ ctxt->node = root;
697+
698+ ctl->hvm = virXPathString("string(./os/type[1])", ctxt);
699+ if (!ctl->hvm || STRNEQ(ctl->hvm, "hvm")) {
700+ vah_error(ctl, 0, "os.type is not 'hvm'");
701+ goto cleanup;
702+ }
703+ ctl->arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
704+ if (!ctl->arch) {
705+ /* The XML we are given should have an arch, but in case it doesn't,
706+ * just use the host's arch.
707+ */
708+ struct utsname utsname;
709+
710+ /* Really, this never fails - look at the man-page. */
711+ uname (&utsname);
712+ if ((ctl->arch = strdup(utsname.machine)) == NULL) {
713+ vah_error(ctl, 0, "could not allocate memory");
714+ goto cleanup;
715+ }
716+ }
717+ if (STREQ(ctl->arch, "x86_64"))
718+ ctl->bits = 64;
719+ else
720+ ctl->bits = 32;
721+
722+ rc = 0;
723+
724+ cleanup:
725+ xmlFreeParserCtxt (pctxt);
726+ xmlFreeDoc (xml);
727+ xmlXPathFreeContext(ctxt);
728+
729+ return rc;
730+}
731+
732+static int
733+get_definition(vahControl * ctl, const char *xmlStr)
734+{
735+ int rc = -1;
736+ virCapsGuestPtr guest; /* this is freed when caps is freed */
737+
738+ /*
739+ * mock up some capabilities. We don't currently use these explicitly,
740+ * but need them for virDomainDefParseString().
741+ */
742+ if (caps_mockup(ctl, xmlStr) != 0)
743+ goto exit;
744+
745+ if ((ctl->caps = virCapabilitiesNew(ctl->arch, 1, 1)) == NULL) {
746+ vah_error(ctl, 0, "could not allocate memory");
747+ goto exit;
748+ }
749+
750+ if ((guest = virCapabilitiesAddGuest(ctl->caps,
751+ ctl->hvm,
752+ ctl->arch,
753+ ctl->bits,
754+ NULL,
755+ NULL,
756+ 0,
757+ NULL)) == NULL) {
758+ vah_error(ctl, 0, "could not allocate memory");
759+ goto exit;
760+ }
761+
762+ ctl->def = virDomainDefParseString(ctl->caps, xmlStr,
763+ VIR_DOMAIN_XML_INACTIVE);
764+ if (ctl->def == NULL) {
765+ vah_error(ctl, 0, "could not parse XML");
766+ goto exit;
767+ }
768+
769+ if (!ctl->def->name) {
770+ vah_error(ctl, 0, "could not find name in XML");
771+ goto exit;
772+ }
773+
774+ if (valid_name(ctl->def->name) != 0) {
775+ vah_error(ctl, 0, "bad name");
776+ goto exit;
777+ }
778+
779+ rc = 0;
780+
781+ exit:
782+ return rc;
783+}
784+
785+static int
786+vah_add_file(virBufferPtr buf, const char *path, const char *perms)
787+{
788+ char *tmp = NULL;
789+ int rc = -1;
790+ bool readonly = true;
791+
792+ if (path == NULL)
793+ return rc;
794+
795+ /* Skip files without an absolute path. Not having one confuses the
796+ * apparmor parser and this also ensures things like tcp consoles don't
797+ * get added to the profile.
798+ */
799+ if (STRNEQLEN(path, "/", 1)) {
800+ vah_warning(path);
801+ vah_warning(" skipped non-absolute path");
802+ return 0;
803+ }
804+
805+ if (virFileExists(path)) {
806+ if ((tmp = realpath(path, NULL)) == NULL) {
807+ vah_error(NULL, 0, path);
808+ vah_error(NULL, 0, " could not find realpath for disk");
809+ return rc;
810+ }
811+ } else
812+ if ((tmp = strdup(path)) == NULL)
813+ return rc;
814+
815+ if (strchr(perms, 'w') != NULL)
816+ readonly = false;
817+
818+ rc = valid_path(tmp, readonly);
819+ if (rc != 0) {
820+ if (rc > 0) {
821+ vah_error(NULL, 0, path);
822+ vah_error(NULL, 0, " skipped restricted file");
823+ }
824+ goto clean;
825+ }
826+
827+ virBufferVSprintf(buf, " \"%s\" %s,\n", tmp, perms);
828+ if (readonly) {
829+ virBufferVSprintf(buf, " # don't audit writes to readonly files\n");
830+ virBufferVSprintf(buf, " deny \"%s\" w,\n", tmp);
831+ }
832+
833+ clean:
834+ VIR_FREE(tmp);
835+
836+ return rc;
837+}
838+
839+static int
840+file_iterate_hostdev_cb(usbDevice *dev ATTRIBUTE_UNUSED,
841+ const char *file, void *opaque)
842+{
843+ virBufferPtr buf = opaque;
844+ return vah_add_file(buf, file, "rw");
845+}
846+
847+static int
848+file_iterate_pci_cb(pciDevice *dev ATTRIBUTE_UNUSED,
849+ const char *file, void *opaque)
850+{
851+ virBufferPtr buf = opaque;
852+ return vah_add_file(buf, file, "rw");
853+}
854+
855+static int
856+add_file_path(virDomainDiskDefPtr disk,
857+ const char *path,
858+ size_t depth,
859+ void *opaque)
860+{
861+ virBufferPtr buf = opaque;
862+ int ret;
863+
864+ if (depth == 0) {
865+ if (disk->readonly)
866+ ret = vah_add_file(buf, path, "r");
867+ else
868+ ret = vah_add_file(buf, path, "rw");
869+ } else {
870+ ret = vah_add_file(buf, path, "r");
871+ }
872+
873+ if (ret != 0)
874+ ret = -1;
875+
876+ return ret;
877+}
878+
879+
880+static int
881+get_files(vahControl * ctl)
882+{
883+ virBuffer buf = VIR_BUFFER_INITIALIZER;
884+ int rc = -1;
885+ int i;
886+ char *uuid;
887+ char uuidstr[VIR_UUID_STRING_BUFLEN];
888+
889+ /* verify uuid is same as what we were given on the command line */
890+ virUUIDFormat(ctl->def->uuid, uuidstr);
891+ if (virAsprintf(&uuid, "%s%s", AA_PREFIX, uuidstr) == -1) {
892+ vah_error(ctl, 0, "could not allocate memory");
893+ return rc;
894+ }
895+
896+ if (STRNEQ(uuid, ctl->uuid)) {
897+ vah_error(ctl, 0, "given uuid does not match XML uuid");
898+ goto clean;
899+ }
900+
901+ for (i = 0; i < ctl->def->ndisks; i++) {
902+ /* XXX passing ignoreOpenFailure = true to get back to the behavior
903+ * from before using virDomainDiskDefForeachPath. actually we should
904+ * be passing ignoreOpenFailure = false and handle open errors more
905+ * careful than just ignoring them */
906+ int ret = virDomainDiskDefForeachPath(ctl->def->disks[i],
907+ ctl->allowDiskFormatProbing,
908+ true,
909+ add_file_path,
910+ &buf);
911+ if (ret != 0)
912+ goto clean;
913+ }
914+
915+ for (i = 0; i < ctl->def->nserials; i++)
916+ if (ctl->def->serials[i] &&
917+ (ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY ||
918+ ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_DEV ||
919+ ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_FILE ||
920+ ctl->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
921+ ctl->def->serials[i]->data.file.path)
922+ if (vah_add_file(&buf,
923+ ctl->def->serials[i]->data.file.path, "rw") != 0)
924+ goto clean;
925+
926+ if (ctl->def->console && ctl->def->console->data.file.path)
927+ if (vah_add_file(&buf, ctl->def->console->data.file.path, "rw") != 0)
928+ goto clean;
929+
930+ for (i = 0 ; i < ctl->def->nparallels; i++)
931+ if (ctl->def->parallels[i] &&
932+ (ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_PTY ||
933+ ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_DEV ||
934+ ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_FILE ||
935+ ctl->def->parallels[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
936+ ctl->def->parallels[i]->data.file.path)
937+ if (vah_add_file(&buf,
938+ ctl->def->parallels[i]->data.file.path,
939+ "rw") != 0)
940+ goto clean;
941+
942+ for (i = 0 ; i < ctl->def->nchannels; i++)
943+ if (ctl->def->channels[i] &&
944+ (ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_PTY ||
945+ ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_DEV ||
946+ ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_FILE ||
947+ ctl->def->channels[i]->type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
948+ ctl->def->channels[i]->data.file.path)
949+ if (vah_add_file(&buf,
950+ ctl->def->channels[i]->data.file.path,
951+ "rw") != 0)
952+ goto clean;
953+
954+ if (ctl->def->os.kernel)
955+ if (vah_add_file(&buf, ctl->def->os.kernel, "r") != 0)
956+ goto clean;
957+
958+ if (ctl->def->os.initrd)
959+ if (vah_add_file(&buf, ctl->def->os.initrd, "r") != 0)
960+ goto clean;
961+
962+ if (ctl->def->os.loader && ctl->def->os.loader)
963+ if (vah_add_file(&buf, ctl->def->os.loader, "r") != 0)
964+ goto clean;
965+
966+ if (ctl->def->ngraphics == 1 &&
967+ ctl->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL)
968+ if (vah_add_file(&buf, ctl->def->graphics[0]->data.sdl.xauth,
969+ "r") != 0)
970+ goto clean;
971+
972+ for (i = 0; i < ctl->def->nhostdevs; i++)
973+ if (ctl->def->hostdevs[i]) {
974+ virDomainHostdevDefPtr dev = ctl->def->hostdevs[i];
975+ switch (dev->source.subsys.type) {
976+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
977+ usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
978+ dev->source.subsys.u.usb.device);
979+
980+ if (usb == NULL)
981+ continue;
982+
983+ rc = usbDeviceFileIterate(usb, file_iterate_hostdev_cb, &buf);
984+ usbFreeDevice(usb);
985+ if (rc != 0)
986+ goto clean;
987+ break;
988+ }
989+
990+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
991+ pciDevice *pci = pciGetDevice(
992+ dev->source.subsys.u.pci.domain,
993+ dev->source.subsys.u.pci.bus,
994+ dev->source.subsys.u.pci.slot,
995+ dev->source.subsys.u.pci.function);
996+
997+ if (pci == NULL)
998+ continue;
999+
1000+ rc = pciDeviceFileIterate(pci, file_iterate_pci_cb, &buf);
1001+ pciFreeDevice(pci);
1002+
1003+ break;
1004+ }
1005+
1006+ default:
1007+ rc = 0;
1008+ break;
1009+ } /* switch */
1010+ }
1011+
1012+ if (ctl->newdisk)
1013+ if (vah_add_file(&buf, ctl->newdisk, "rw") != 0)
1014+ goto clean;
1015+
1016+ if (virBufferError(&buf)) {
1017+ virBufferFreeAndReset(&buf);
1018+ vah_error(NULL, 0, "failed to allocate file buffer");
1019+ goto clean;
1020+ }
1021+
1022+ rc = 0;
1023+ ctl->files = virBufferContentAndReset(&buf);
1024+
1025+ clean:
1026+ VIR_FREE(uuid);
1027+ return rc;
1028+}
1029+
1030+static int
1031+vahParseArgv(vahControl * ctl, int argc, char **argv)
1032+{
1033+ int arg, idx = 0;
1034+ struct option opt[] = {
1035+ {"probing", 1, 0, 'p' },
1036+ {"add", 0, 0, 'a'},
1037+ {"create", 0, 0, 'c'},
1038+ {"dryrun", 0, 0, 'd'},
1039+ {"delete", 0, 0, 'D'},
1040+ {"add-file", 0, 0, 'f'},
1041+ {"help", 0, 0, 'h'},
1042+ {"replace", 0, 0, 'r'},
1043+ {"remove", 0, 0, 'R'},
1044+ {"uuid", 1, 0, 'u'},
1045+ {0, 0, 0, 0}
1046+ };
1047+
1048+ while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:", opt,
1049+ &idx)) != -1) {
1050+ switch (arg) {
1051+ case 'a':
1052+ ctl->cmd = 'a';
1053+ break;
1054+ case 'c':
1055+ ctl->cmd = 'c';
1056+ break;
1057+ case 'd':
1058+ ctl->dryrun = true;
1059+ break;
1060+ case 'D':
1061+ ctl->cmd = 'D';
1062+ break;
1063+ case 'f':
1064+ if ((ctl->newdisk = strdup(optarg)) == NULL)
1065+ vah_error(ctl, 1, "could not allocate memory for disk");
1066+ break;
1067+ case 'h':
1068+ vah_usage();
1069+ exit(EXIT_SUCCESS);
1070+ break;
1071+ case 'r':
1072+ ctl->cmd = 'r';
1073+ break;
1074+ case 'R':
1075+ ctl->cmd = 'R';
1076+ break;
1077+ case 'u':
1078+ if (strlen(optarg) > PROFILE_NAME_SIZE - 1)
1079+ vah_error(ctl, 1, "invalid UUID");
1080+ if (virStrcpy((char *) ctl->uuid, optarg,
1081+ PROFILE_NAME_SIZE) == NULL)
1082+ vah_error(ctl, 1, "error copying UUID");
1083+ break;
1084+ case 'p':
1085+ if (STREQ(optarg, "1"))
1086+ ctl->allowDiskFormatProbing = true;
1087+ else
1088+ ctl->allowDiskFormatProbing = false;
1089+ break;
1090+ default:
1091+ vah_error(ctl, 1, "unsupported option");
1092+ break;
1093+ }
1094+ }
1095+ if (strchr("acDrR", ctl->cmd) == NULL)
1096+ vah_error(ctl, 1, "bad command");
1097+
1098+ if (valid_uuid(ctl->uuid) != 0)
1099+ vah_error(ctl, 1, "invalid UUID");
1100+
1101+ if (!ctl->cmd) {
1102+ vah_usage();
1103+ exit(EXIT_FAILURE);
1104+ }
1105+
1106+ if (ctl->cmd == 'c' || ctl->cmd == 'r') {
1107+ char *xmlStr = NULL;
1108+ if (virFileReadLimFD(STDIN_FILENO, MAX_FILE_LEN, &xmlStr) < 0)
1109+ vah_error(ctl, 1, "could not read xml file");
1110+
1111+ if (get_definition(ctl, xmlStr) != 0 || ctl->def == NULL) {
1112+ VIR_FREE(xmlStr);
1113+ vah_error(ctl, 1, "could not get VM definition");
1114+ }
1115+ VIR_FREE(xmlStr);
1116+
1117+ if (get_files(ctl) != 0)
1118+ vah_error(ctl, 1, "invalid VM definition");
1119+ }
1120+ return 0;
1121+}
1122+
1123+
1124+/*
1125+ * virt-aa-helper -c -u UUID < file.xml
1126+ * virt-aa-helper -r -u UUID [-f <file>] < file.xml
1127+ * virt-aa-helper -a -u UUID
1128+ * virt-aa-helper -R -u UUID
1129+ * virt-aa-helper -D -u UUID
1130+ */
1131+int
1132+main(int argc, char **argv)
1133+{
1134+ vahControl _ctl, *ctl = &_ctl;
1135+ virBuffer buf = VIR_BUFFER_INITIALIZER;
1136+ int rc = -1;
1137+ char profile[PATH_MAX];
1138+ char include_file[PATH_MAX];
1139+
1140+ /* clear the environment */
1141+ environ = NULL;
1142+ if (setenv("PATH", "/sbin:/usr/sbin", 1) != 0) {
1143+ vah_error(ctl, 1, "could not set PATH");
1144+ }
1145+ if (setenv("IFS", " \t\n", 1) != 0) {
1146+ vah_error(ctl, 1, "could not set IFS");
1147+ }
1148+
1149+ if (!(progname = strrchr(argv[0], '/')))
1150+ progname = argv[0];
1151+ else
1152+ progname++;
1153+
1154+ memset(ctl, 0, sizeof(vahControl));
1155+
1156+ if (vahParseArgv(ctl, argc, argv) != 0)
1157+ vah_error(ctl, 1, "could not parse arguments");
1158+
1159+ if (snprintf(profile, PATH_MAX, "%s/%s",
1160+ APPARMOR_DIR "/libvirt", ctl->uuid) > PATH_MAX - 1)
1161+ vah_error(ctl, 1, "profile name exceeds maximum length");
1162+
1163+ if (snprintf(include_file, PATH_MAX, "%s/%s.files",
1164+ APPARMOR_DIR "/libvirt", ctl->uuid) > PATH_MAX - 1)
1165+ vah_error(ctl, 1, "disk profile name exceeds maximum length");
1166+
1167+ if (ctl->cmd == 'a')
1168+ rc = parserLoad(ctl->uuid);
1169+ else if (ctl->cmd == 'R' || ctl->cmd == 'D') {
1170+ rc = parserRemove(ctl->uuid);
1171+ if (ctl->cmd == 'D') {
1172+ unlink(include_file);
1173+ unlink(profile);
1174+ }
1175+ } else if (ctl->cmd == 'c' || ctl->cmd == 'r') {
1176+ char *included_files = NULL;
1177+
1178+ if (ctl->cmd == 'c' && virFileExists(profile))
1179+ vah_error(ctl, 1, "profile exists");
1180+
1181+ virBufferVSprintf(&buf, " \"%s/log/libvirt/**/%s.log\" w,\n",
1182+ LOCAL_STATE_DIR, ctl->def->name);
1183+ virBufferVSprintf(&buf, " \"%s/lib/libvirt/**/%s.monitor\" rw,\n",
1184+ LOCAL_STATE_DIR, ctl->def->name);
1185+ virBufferVSprintf(&buf, " \"%s/run/libvirt/**/%s.pid\" rwk,\n",
1186+ LOCAL_STATE_DIR, ctl->def->name);
1187+ if (ctl->files)
1188+ virBufferVSprintf(&buf, "%s", ctl->files);
1189+
1190+ if (virBufferError(&buf)) {
1191+ virBufferFreeAndReset(&buf);
1192+ vah_error(ctl, 1, "failed to allocate buffer");
1193+ }
1194+
1195+ included_files = virBufferContentAndReset(&buf);
1196+
1197+ /* (re)create the include file using included_files */
1198+ if (ctl->dryrun) {
1199+ vah_info(include_file);
1200+ vah_info(included_files);
1201+ rc = 0;
1202+ } else if ((rc = update_include_file(include_file,
1203+ included_files)) != 0)
1204+ goto clean;
1205+
1206+
1207+ /* create the profile from TEMPLATE */
1208+ if (ctl->cmd == 'c') {
1209+ char *tmp = NULL;
1210+ if (virAsprintf(&tmp, " #include <libvirt/%s.files>\n",
1211+ ctl->uuid) == -1) {
1212+ vah_error(ctl, 0, "could not allocate memory");
1213+ goto clean;
1214+ }
1215+
1216+ if (ctl->dryrun) {
1217+ vah_info(profile);
1218+ vah_info(ctl->uuid);
1219+ vah_info(tmp);
1220+ rc = 0;
1221+ } else if ((rc = create_profile(profile, ctl->uuid, tmp)) != 0) {
1222+ vah_error(ctl, 0, "could not create profile");
1223+ unlink(include_file);
1224+ }
1225+ VIR_FREE(tmp);
1226+ }
1227+
1228+ if (rc == 0 && !ctl->dryrun) {
1229+ if (ctl->cmd == 'c')
1230+ rc = parserLoad(ctl->uuid);
1231+ else
1232+ rc = parserReplace(ctl->uuid);
1233+
1234+ /* cleanup */
1235+ if (rc != 0) {
1236+ unlink(include_file);
1237+ if (ctl->cmd == 'c')
1238+ unlink(profile);
1239+ }
1240+ }
1241+ clean:
1242+ VIR_FREE(included_files);
1243+ }
1244+
1245+ vahDeinit(ctl);
1246+ exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1247+}
1248
1249=== added file 'debian/patches/9028-lp628055.patch'
1250--- debian/patches/9028-lp628055.patch 1970-01-01 00:00:00 +0000
1251+++ debian/patches/9028-lp628055.patch 2010-09-22 22:26:01 +0000
1252@@ -0,0 +1,17 @@
1253+Author: Jamie Strandboge <jamie@canonical.com>
1254+Description: fix compiler warning and stat() failure on 32-bit machines when
1255+ stating large files. This can be dropped in 0.8.5.
1256+Bug-Ubuntu: https://launchpad.net/bugs/628055
1257+
1258+Index: libvirt-0.8.3/src/security/virt-aa-helper.c
1259+===================================================================
1260+--- libvirt-0.8.3.orig/src/security/virt-aa-helper.c 2010-09-22 15:15:14.000000000 -0500
1261++++ libvirt-0.8.3/src/security/virt-aa-helper.c 2010-09-22 15:20:52.000000000 -0500
1262+@@ -18,6 +18,7 @@
1263+ #include <stdarg.h>
1264+ #include <unistd.h>
1265+ #include <errno.h>
1266++#include <sys/stat.h>
1267+ #include <sys/types.h>
1268+ #include <fcntl.h>
1269+ #include <getopt.h>
1270
1271=== renamed file 'debian/patches/9028-lp628055.patch' => 'debian/patches/9028-lp628055.patch.moved'

Subscribers

People subscribed via source and target branches