[libvirt] [PATCH] Solaris least privilege support

john.levon at sun.com john.levon at sun.com
Thu Jan 15 03:32:28 UTC 2009


# HG changeset patch
# User john.levon at sun.com
# Date 1231990064 28800
# Node ID 629c101c9ec11f3eb5cb56eb9548c96c33c8daf6
# Parent  0f488fb716b1ab0a1379509b8b3594f32f0ea980
Solaris least privilege support

On Solaris dom0, virtd runs as a privilege barrier: all libvirt
connections are routed through it, and it performs the relevant
privilege checks for any clients.

Signed-off-by: John Levon <john.levon at sun.com>

diff --git a/qemud/qemud.c b/qemud/qemud.c
--- a/qemud/qemud.c
+++ b/qemud/qemud.c
@@ -84,6 +84,25 @@
 #endif
 
 
+#ifdef __sun
+#include <ucred.h>
+#include <priv.h>
+
+#ifndef PRIV_VIRT_MANAGE
+#define PRIV_VIRT_MANAGE ((const char *)"virt_manage")
+#endif
+
+#ifndef PRIV_XVM_CONTROL
+#define PRIV_XVM_CONTROL ((const char *)"xvm_control")
+#endif
+
+#define PU_RESETGROUPS          0x0001  /* Remove supplemental groups */
+#define PU_CLEARLIMITSET        0x0008  /* L=0 */
+
+extern int __init_daemon_priv(int, uid_t, gid_t, ...);
+
+#endif
+
 static int godaemon = 0;        /* -d: Be a daemon */
 static int verbose = 0;         /* -v: Verbose mode */
 static int timeout = -1;        /* -t: Shutdown timeout */
@@ -638,10 +657,32 @@ static int qemudInitPaths(struct qemud_s
 static int qemudInitPaths(struct qemud_server *server,
                           char *sockname,
                           char *roSockname,
-                          int maxlen) {
+                          int maxlen)
+{
     uid_t uid = geteuid();
-
+#ifdef __sun
+    char *base = NULL;
+
+    if (virAsprintf (&base, "%s/run/libvirt", LOCAL_STATE_DIR) == -1) {
+        VIR_ERROR0(_("Out of memory"));
+        return -1;
+    }
+    if (mkdir (base, 0755)) {
+        if (errno != EEXIST) {
+            VIR_ERROR0(_("unable to create rundir"));
+            free (base);
+            exit(-1);
+        }
+    }
+
+    free (base);
+#endif
+
+#ifdef __sun
+    if (uid == 60) {
+#else
     if (!uid) {
+#endif
         if (snprintf (sockname, maxlen, "%s/run/libvirt/libvirt-sock",
                       LOCAL_STATE_DIR) >= maxlen)
             goto snprintf_error;
@@ -1105,6 +1146,29 @@ static int qemudDispatchServer(struct qe
         return -1;
     }
 
+#ifdef __sun
+    {
+        ucred_t *ucred = NULL;
+        const priv_set_t *privs;
+
+        if (getpeerucred (fd, &ucred) == -1 ||
+            (privs = ucred_getprivset (ucred, PRIV_EFFECTIVE)) == NULL) {
+            if (ucred != NULL)
+                ucred_free (ucred);
+            close (fd);
+            return -1;
+        }
+
+        if (!priv_ismember (privs, PRIV_VIRT_MANAGE)) {
+            ucred_free (ucred);
+            close (fd);
+            return -1;
+        }
+
+        ucred_free (ucred);
+    }
+#endif /* __sun */
+
     /* Disable Nagle.  Unix sockets will ignore this. */
     setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start,
                 sizeof no_slow_start);
@@ -2140,6 +2204,10 @@ remoteReadConfigFile (struct qemud_serve
     if (auth_unix_rw == REMOTE_AUTH_POLKIT)
         unix_sock_rw_mask = 0777;
 #endif
+#ifdef __sun
+    unix_sock_rw_mask = 0666;
+#endif
+
     if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0)
         goto free_and_fail;
     if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0)
@@ -2239,6 +2307,31 @@ version (const char *argv0)
 {
     printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
 }
+
+#ifdef __sun
+static void
+qemudSetupPrivs (struct qemud_server *server)
+{
+    chown ("/var/run/libvirt", 60, 60);
+    chown ("/var/run/libvirt/libvirt-sock", 60, 60);
+    chmod ("/var/run/libvirt/libvirt-sock", 0666);
+    chown (server->logDir, 60, 60);
+
+    if (__init_daemon_priv (PU_RESETGROUPS | PU_CLEARLIMITSET,
+        60, 60, PRIV_XVM_CONTROL, NULL)) {
+        fprintf (stderr, "additional privileges are required\n");
+        exit (1);
+    }
+
+    if (priv_set (PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_LINK_ANY, PRIV_PROC_INFO,
+        PRIV_PROC_SESSION, PRIV_PROC_EXEC, PRIV_PROC_FORK, NULL)) {
+        fprintf (stderr, "failed to set reduced privileges\n");
+        exit (1);
+    }
+}
+#else
+#define qemudSetupPrivs(a)
+#endif
 
 /* Print command-line usage. */
 static void
@@ -2417,6 +2510,8 @@ int main(int argc, char **argv) {
     sig_action.sa_handler = SIG_IGN;
     sigaction(SIGPIPE, &sig_action, NULL);
 
+    qemudSetupPrivs(server);
+
     if (!(server = qemudInitialize(sigpipe[0]))) {
         ret = 2;
         goto error2;
diff --git a/qemud/remote.c b/qemud/remote.c
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -424,6 +424,15 @@ remoteDispatchOpen (struct qemud_server 
     flags = args->flags;
     if (client->readonly) flags |= VIR_CONNECT_RO;
 
+#ifdef __sun
+    /*
+     * On Solaris, all clients are forced to go via virtd. As a result,
+     * virtd must indicate it really does want to connect to the
+     * hypervisor.
+     */
+    name = "xen:///";
+#endif
+
     client->conn =
         flags & VIR_CONNECT_RO
         ? virConnectOpenReadOnly (name)
diff --git a/src/libvirt.c b/src/libvirt.c
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -46,6 +46,7 @@
 #include "test.h"
 #endif
 #ifdef WITH_XEN
+#include "xen_internal.h"
 #include "xen_unified.h"
 #endif
 #ifdef WITH_REMOTE
@@ -825,6 +826,17 @@ do_open (const char *name,
         }
     }
 
+#ifdef __sun
+        /*
+         * If we're not libvirtd, force us to go via the daemon, unless we
+         * want the test hypervisor.
+         */
+        if (name == NULL || !STRCASEEQLEN (name, "test://", 7)) {
+            if (geteuid() == 0 || !xenHavePrivilege())
+                name = "remote+unix:///";
+        }
+#endif
+
     if (name) {
         /* Convert xen -> xen:/// for back compat */
         if (STRCASEEQ(name, "xen"))
diff --git a/src/remote_internal.c b/src/remote_internal.c
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -903,18 +903,21 @@ remoteOpen (virConnectPtr conn,
     }
 
     /*
-     * If URI is NULL, then do a UNIX connection
-     * possibly auto-spawning unprivileged server
-     * and probe remote server for URI
+     * If URI is NULL, then do a UNIX connection possibly auto-spawning
+     * unprivileged server and probe remote server for URI. On Solaris,
+     * this isn't supported, but we may be privileged enough to connect
+     * to the UNIX socket anyway.
      */
     if (!conn->uri) {
         DEBUG0("Auto-probe remote URI");
         rflags |= VIR_DRV_OPEN_REMOTE_UNIX;
+#ifndef __sun
         if (getuid() > 0) {
             DEBUG0("Auto-spawn user daemon instance");
             rflags |= VIR_DRV_OPEN_REMOTE_USER;
             rflags |= VIR_DRV_OPEN_REMOTE_AUTOSTART;
         }
+#endif
     }
 
     priv->magic = DEAD;
@@ -5086,8 +5089,7 @@ really_read_buf (virConnectPtr conn, str
             return -1;
         }
         if (err == 0) {
-            error (in_open ? NULL : conn,
-                   VIR_ERR_RPC, _("socket closed unexpectedly"));
+            DEBUG("conn %p: socket closed unexpectedly", conn);
             return -1;
         }
     } else {
@@ -5101,8 +5103,7 @@ really_read_buf (virConnectPtr conn, str
             return -1;
         }
         if (err == 0) {
-            error (in_open ? NULL : conn,
-                   VIR_ERR_RPC, _("socket closed unexpectedly"));
+            DEBUG("conn %p: socket closed unexpectedly", conn);
             return -1;
         }
     }
diff --git a/src/virsh.c b/src/virsh.c
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -28,6 +28,7 @@
 #include <limits.h>
 #include <assert.h>
 #include <errno.h>
+#include <signal.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <inttypes.h>
@@ -46,6 +47,7 @@
 #include "util.h"
 
 static char *progname;
+static int sigpipe;
 
 #ifndef TRUE
 #define TRUE 1
@@ -6984,12 +6986,22 @@ vshParseArgv(vshControl *ctl, int argc, 
     return TRUE;
 }
 
+static void sigpipe_handler(int sig ATTRIBUTE_UNUSED)
+{
+    sigpipe = 1;
+    /*
+     * Force readline() to exit.
+     */
+    close(STDIN_FILENO);
+}
+
 int
 main(int argc, char **argv)
 {
     vshControl _ctl, *ctl = &_ctl;
     char *defaultConn;
     int ret = TRUE;
+    struct sigaction sig_action;
 
     if (!setlocale(LC_ALL, "")) {
         perror("setlocale");
@@ -7021,6 +7033,12 @@ main(int argc, char **argv)
         vshDeinit(ctl);
         exit(EXIT_FAILURE);
     }
+
+    sig_action.sa_handler = sigpipe_handler;
+    sig_action.sa_flags = 0;
+    sigemptyset(&sig_action.sa_mask);
+
+    sigaction(SIGPIPE, &sig_action, NULL);
 
     if (!vshInit(ctl)) {
         vshDeinit(ctl);
@@ -7061,6 +7079,13 @@ main(int argc, char **argv)
             fputc('\n', stdout);        /* line break after alone prompt */
     }
 
+    /*
+     * If the connection over a socket failed abruptly, it's probably
+     * due to not having the right privileges.
+     */
+    if (sigpipe)
+        vshError(ctl, TRUE, _("failed to connect (insufficient privileges?)"));
+
     vshDeinit(ctl);
     exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
 }
diff --git a/src/xen_internal.c b/src/xen_internal.c
--- a/src/xen_internal.c
+++ b/src/xen_internal.c
@@ -26,6 +26,17 @@
 #include <errno.h>
 #include <sys/utsname.h>
 
+#ifdef __sun
+#include <sys/systeminfo.h>
+
+#include <priv.h>
+
+#ifndef PRIV_XVM_CONTROL
+#define PRIV_XVM_CONTROL ((const char *)"xvm_control")
+#endif
+
+#endif /* __sun */
+
 /* required for dom0_getdomaininfo_t */
 #include <xen/dom0_ops.h>
 #include <xen/version.h>
@@ -35,10 +46,6 @@
 #ifdef HAVE_XEN_SYS_PRIVCMD_H
 #include <xen/sys/privcmd.h>
 #endif
-#endif
-
-#ifdef __sun
-#include <sys/systeminfo.h>
 #endif
 
 /* required for shutdown flags */
@@ -3387,3 +3394,17 @@ xenHypervisorGetVcpuMax(virDomainPtr dom
     return maxcpu;
 }
 
+/**
+ * xenHavePrivilege()
+ *
+ * Return true if the current process should be able to connect to Xen.
+ */
+int
+xenHavePrivilege()
+{
+#ifdef __sun
+    return priv_ineffect(PRIV_XVM_CONTROL);
+#else
+    return getuid () == 0;
+#endif
+}
diff --git a/src/xen_internal.h b/src/xen_internal.h
--- a/src/xen_internal.h
+++ b/src/xen_internal.h
@@ -104,4 +104,6 @@ int     xenHypervisorNodeGetCellsFreeMem
                                           int startCell,
                                           int maxCells);
 
+int	xenHavePrivilege(void);
+
 #endif                          /* __VIR_XEN_INTERNAL_H__ */
diff --git a/src/xen_unified.c b/src/xen_unified.c
--- a/src/xen_unified.c
+++ b/src/xen_unified.c
@@ -283,8 +283,8 @@ xenUnifiedOpen (virConnectPtr conn, virC
     priv->proxy = -1;
 
 
-    /* Hypervisor is only run as root & required to succeed */
-    if (getuid() == 0) {
+    /* Hypervisor is only run with privilege & required to succeed */
+    if (xenHavePrivilege()) {
         DEBUG0("Trying hypervisor sub-driver");
         if (drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->open(conn, auth, flags) ==
             VIR_DRV_OPEN_SUCCESS) {
@@ -293,7 +293,7 @@ xenUnifiedOpen (virConnectPtr conn, virC
         }
     }
 
-    /* XenD is required to suceed if root.
+    /* XenD is required to succeed if privileged.
      * If it fails as non-root, then the proxy driver may take over
      */
     DEBUG0("Trying XenD sub-driver");
@@ -318,12 +318,12 @@ xenUnifiedOpen (virConnectPtr conn, virC
             DEBUG0("Activated XS sub-driver");
             priv->opened[XEN_UNIFIED_XS_OFFSET] = 1;
         } else {
-            if (getuid() == 0)
-                goto fail; /* XS is mandatory as root */
+            if (xenHavePrivilege())
+                goto fail; /* XS is mandatory when privileged */
         }
     } else {
-        if (getuid() == 0) {
-            goto fail; /* XenD is mandatory as root */
+        if (xenHavePrivilege()) {
+            goto fail; /* XenD is mandatory when privileged */
         } else {
 #if WITH_PROXY
             DEBUG0("Trying proxy sub-driver");
diff --git a/src/xend_internal.c b/src/xend_internal.c
--- a/src/xend_internal.c
+++ b/src/xend_internal.c
@@ -42,7 +42,7 @@
 #include "buf.h"
 #include "uuid.h"
 #include "xen_unified.h"
-#include "xen_internal.h" /* for DOM0_INTERFACE_VERSION */
+#include "xen_internal.h"
 #include "xs_internal.h" /* To extract VNC port & Serial console TTY */
 #include "memory.h"
 
@@ -151,9 +151,10 @@ do_connect(virConnectPtr xend)
         s = -1;
 
         /*
-         * Connecting to XenD as root is mandatory, so log this error
+         * Connecting to XenD when privileged is mandatory, so log this
+         * error
          */
-        if (getuid() == 0) {
+        if (xenHavePrivilege()) {
             virXendError(xend, VIR_ERR_INTERNAL_ERROR,
                          "%s", _("failed to connect to xend"));
         }
diff --git a/src/xs_internal.c b/src/xs_internal.c
--- a/src/xs_internal.c
+++ b/src/xs_internal.c
@@ -35,7 +35,7 @@
 #include "uuid.h"
 #include "xen_unified.h"
 #include "xs_internal.h"
-#include "xen_internal.h" /* for xenHypervisorCheckID */
+#include "xen_internal.h"
 
 #ifdef __linux__
 #define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd"
@@ -299,11 +299,11 @@ xenStoreOpen(virConnectPtr conn,
 
     if (priv->xshandle == NULL) {
         /*
-         * not being able to connect via the socket as a normal user
-         * is rather normal, this should fallback to the proxy (or
+         * not being able to connect via the socket as an unprivileged
+         * user is rather normal, this should fallback to the proxy (or
          * remote) mechanism.
          */
-        if (getuid() == 0) {
+        if (xenHavePrivilege()) {
             virXenStoreError(NULL, VIR_ERR_NO_XEN,
                                  "%s", _("failed to connect to Xen Store"));
         }




More information about the libvir-list mailing list