Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

launch: libvirt: Use SELinux-mandated directory for sockets (RHBZ#842…

…307).

Note this does not yet enable sVirt confinement of the appliance.
That requires at least that SELinux policy goes upstream, plus there
may still be bugs.
  • Loading branch information...
commit 493e80ab4e7199bdbb44b16535448d65944eca66 1 parent 97aeaa8
@rwmjones rwmjones authored
Showing with 147 additions and 31 deletions.
  1. +147 −31 src/launch-libvirt.c
View
178 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;
Please sign in to comment.
Something went wrong with that request. Please try again.