diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/launch-libvirt.c | 178 |
1 files changed, 147 insertions, 31 deletions
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c index c2e3a281..1e7542ca 100644 --- a/src/launch-libvirt.c +++ b/src/launch-libvirt.c @@ -79,20 +79,26 @@ xmlBufferDetach (xmlBufferPtr buf) static xmlChar *construct_libvirt_xml (guestfs_h *g, const char *capabilities_xml, const char *kernel, const char *initrd, const char *appliance, const char *guestfsd_sock, const char *console_sock); static char *autodetect_format (guestfs_h *g, const char *path); static void libvirt_error (guestfs_h *g, const char *fs, ...); +static void restorecon (const char *dir); +static int is_dir (const char *path); static int is_blk (const char *path); +static int random_chars (char *ret, size_t len); static int launch_libvirt (guestfs_h *g, const char *libvirt_uri) { unsigned long version; + int is_root = geteuid () == 0; virConnectPtr conn = NULL; virDomainPtr dom = NULL; char *capabilities = NULL; xmlChar *xml = NULL; char *kernel = NULL, *initrd = NULL, *appliance = NULL; - char guestfsd_sock[256]; + char *sockdir = NULL; + int rm_sockets = 0; + char *guestfsd_sock = NULL; + char *console_sock = NULL; struct sockaddr_un addr; - char console_sock[256]; int console = -1, r; /* At present you must add drives before starting the appliance. In @@ -151,10 +157,54 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) guestfs___launch_send_progress (g, 3); TRACE0 (launch_build_libvirt_appliance_end); + /* Choose a directory for the sockets. To get SELinux labelling + * right we have to choose a directory which is known about in the + * policy. See: https://bugzilla.redhat.com/show_bug.cgi?id=842307 + */ + if (is_root && is_dir ("/var/run/libguestfs")) + sockdir = safe_strdup (g, "/var/run/libguestfs"); + + if (!sockdir) { + const char *xdg = getenv ("XDG_RUNTIME_DIR"); + if (xdg && is_dir (xdg)) { + char *p = safe_asprintf (g, "%s/libguestfs", xdg); + if (mkdir (p, 0755) == 0) + sockdir = p; + } + } + + /* We want a private temporary directory underneath sockdir, if + * sockdir is set above. Else we want to use g->tmpdir which is + * already a private temporary directory that will be cleaned up by + * the parent code. Note that on modern systems /var/run and + * $XDG_RUNTIME_DIR will be cleaned up by the system when the user + * logs out and/or the machine is rebooted. + */ + if (sockdir) { + char *old_sockdir = sockdir; + char subdir[17]; + + if (random_chars (subdir, 16) == -1) { + perrorf (g, "/dev/urandom"); + goto cleanup; + } + + sockdir = safe_asprintf (g, "%s/%s", old_sockdir, subdir); + free (old_sockdir); + if (mkdir (sockdir, 0755) == -1) { + perrorf (g, "mkdir: %s", sockdir); + goto cleanup; + } + + rm_sockets = 1; + } + else + sockdir = safe_strdup (g, g->tmpdir); + /* Using virtio-serial, we need to create a local Unix domain socket * for qemu to connect to. */ - snprintf (guestfsd_sock, sizeof guestfsd_sock, "%s/guestfsd.sock", g->tmpdir); + guestfsd_sock = safe_asprintf (g, "%s/guestfsd.sock", sockdir); unlink (guestfsd_sock); g->sock = socket (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); @@ -183,7 +233,7 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) } /* For the serial console. */ - snprintf (console_sock, sizeof console_sock, "%s/console.sock", g->tmpdir); + console_sock = safe_asprintf (g, "%s/console.sock", sockdir); console = socket (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); if (console == -1) { perrorf (g, "socket"); @@ -210,9 +260,11 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) * (1) Permissions of the socket. * (2) Permissions of the parent directory(-ies). Remember this * if $TMPDIR is located in your home directory. - * (3) SELinux/sVirt will prevent access. + * (3) SELinux/sVirt will prevent access. If the SELinux policy + * has been set up right then the socket should be labeled + * when it is created. */ - if (geteuid () == 0) { + if (is_root) { struct group *grp; if (chmod (guestfsd_sock, 0775) == -1) { @@ -239,6 +291,9 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) debug (g, "cannot find group 'qemu'"); } + /* Set SELinux labels on sockets. */ + restorecon (sockdir); + /* Construct the libvirt XML. */ if (g->verbose) guestfs___print_timestamped_message (g, "create libvirt XML"); @@ -343,11 +398,17 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) g->virt.connv = conn; g->virt.domv = dom; + if (sockdir && rm_sockets) + guestfs___remove_tmpdir (sockdir); + free (kernel); free (initrd); free (appliance); free (xml); free (capabilities); + free (sockdir); + free (guestfsd_sock); + free (console_sock); return 0; @@ -366,12 +427,7 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) close (g->sock); g->sock = -1; } - g->state = CONFIG; - free (kernel); - free (initrd); - free (appliance); - free (capabilities); - free (xml); + if (dom) { virDomainDestroy (dom); virDomainFree (dom); @@ -379,6 +435,20 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri) if (conn) virConnectClose (conn); + if (sockdir && rm_sockets) + guestfs___remove_tmpdir (sockdir); + + free (kernel); + free (initrd); + free (appliance); + free (capabilities); + free (xml); + free (sockdir); + free (guestfsd_sock); + free (console_sock); + + g->state = CONFIG; + return -1; } @@ -483,28 +553,12 @@ construct_libvirt_xml (guestfs_h *g, const char *capabilities_xml, static int construct_libvirt_xml_name (guestfs_h *g, xmlTextWriterPtr xo) { - int fd; char name[DOMAIN_NAME_LEN+1]; - size_t i; - unsigned char c; - - fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC); - if (fd == -1) { - perrorf (g, "/dev/urandom: open"); - return -1; - } - for (i = 0; i < DOMAIN_NAME_LEN; ++i) { - if (read (fd, &c, 1) != 1) { - perrorf (g, "/dev/urandom: read"); - close (fd); - return -1; - } - name[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[c % 36]; + if (random_chars (name, DOMAIN_NAME_LEN) == -1) { + perrorf (g, "/dev/urandom"); + goto err; } - name[DOMAIN_NAME_LEN] = '\0'; - - close (fd); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "name")); XMLERROR (-1, xmlTextWriterWriteFormatString (xo, "guestfs-%s", name)); @@ -1140,6 +1194,39 @@ autodetect_format (guestfs_h *g, const char *path) return ret; /* caller frees */ } +/* Run external 'restorecon' command on the socket directory to set + * labels. This is not required in recent versions of SELinux, and if + * SELinux isn't available we'll just ignore errors. + */ +static void +restorecon (const char *dir) +{ + pid_t pid; + + pid = fork (); + if (pid == -1) + return; + + if (pid == 0) { /* child */ + close (2); + open ("/dev/null", O_RDWR); + execlp ("restorecon", "restorecon", dir, NULL); + _exit (EXIT_FAILURE); + } + + waitpid (pid, NULL, 0); +} + +static int +is_dir (const char *path) +{ + struct stat statbuf; + + if (stat (path, &statbuf) == -1) + return 0; + return S_ISDIR (statbuf.st_mode); +} + static int is_blk (const char *path) { @@ -1151,6 +1238,35 @@ is_blk (const char *path) } static int +random_chars (char *ret, size_t len) +{ + int fd; + size_t i; + unsigned char c; + int saved_errno; + + fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC); + if (fd == -1) + return -1; + + for (i = 0; i < len; ++i) { + if (read (fd, &c, 1) != 1) { + saved_errno = errno; + close (fd); + errno = saved_errno; + return -1; + } + ret[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[c % 36]; + } + ret[len] = '\0'; + + if (close (fd) == -1) + return -1; + + return 0; +} + +static int shutdown_libvirt (guestfs_h *g) { virConnectPtr conn = g->virt.connv; |