[libvirt] [PATCH] Solaris least privilege support

john.levon at sun.com john.levon at sun.com
Thu Jan 15 17:19:39 UTC 2009


# HG changeset patch
# User john.levon at sun.com
# Date 1232039546 28800
# Node ID b9d4d60bca87633897cb133461e1415d1223c823
# Parent  25a0c46588d5de1653b16dfed6bc357abf11db77
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,39 @@
 #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, ...);
+
+#define SYSTEM_UID 60
+
+static gid_t unix_sock_gid = 60; /* Not used */
+static int unix_sock_rw_mask = 0666;
+static int unix_sock_ro_mask = 0666;
+
+#else
+
+#define SYSTEM_UID 0
+
+static gid_t unix_sock_gid = 0; /* Only root by default */
+static int unix_sock_rw_mask = 0700; /* Allow user only */
+static int unix_sock_ro_mask = 0777; /* Allow world */
+
+#endif /* __sun */
+
 static int godaemon = 0;        /* -d: Be a daemon */
 static int verbose = 0;         /* -v: Verbose mode */
 static int timeout = -1;        /* -t: Shutdown timeout */
@@ -101,10 +134,6 @@ static char *listen_addr  = (char *) LIB
 static char *listen_addr  = (char *) LIBVIRTD_LISTEN_ADDR;
 static char *tls_port = (char *) LIBVIRTD_TLS_PORT;
 static char *tcp_port = (char *) LIBVIRTD_TCP_PORT;
-
-static gid_t unix_sock_gid = 0; /* Only root by default */
-static int unix_sock_rw_mask = 0700; /* Allow user only */
-static int unix_sock_ro_mask = 0777; /* Allow world */
 
 #if HAVE_POLKIT
 static int auth_unix_rw = REMOTE_AUTH_POLKIT;
@@ -638,10 +667,11 @@ 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();
 
-    if (!uid) {
+    if (uid == SYSTEM_UID) {
         if (snprintf (sockname, maxlen, "%s/run/libvirt/libvirt-sock",
                       LOCAL_STATE_DIR) >= maxlen)
             goto snprintf_error;
@@ -1105,6 +1135,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);
@@ -2239,6 +2292,29 @@ 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", SYSTEM_UID, SYSTEM_UID);
+    chown (server->logDir, SYSTEM_UID, SYSTEM_UID);
+
+    if (__init_daemon_priv (PU_RESETGROUPS | PU_CLEARLIMITSET,
+        SYSTEM_UID, SYSTEM_UID, 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 +2493,20 @@ int main(int argc, char **argv) {
     sig_action.sa_handler = SIG_IGN;
     sigaction(SIGPIPE, &sig_action, NULL);
 
+    /* Change the group ownership of /var/run/libvirt to unix_sock_gid */
+    if (geteuid () == 0) {
+        const char *rundir = LOCAL_STATE_DIR "/run/libvirt";
+
+        if (mkdir (rundir, 0755)) {
+            if (errno != EEXIST) {
+                VIR_ERROR0 (_("unable to create rundir"));
+                return (-1);
+            }
+        }
+    }
+
+    qemudSetupPrivs(server);
+
     if (!(server = qemudInitialize(sigpipe[0]))) {
         ret = 2;
         goto error2;
diff --git a/src/remote_internal.c b/src/remote_internal.c
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -90,7 +90,7 @@
 #define MAGIC 999               /* private_data->magic if OK */
 #define DEAD 998                /* private_data->magic if dead/closed */
 
-static int inside_daemon = 0;
+int inside_daemon = 0;
 
 struct private_data {
     int magic;                  /* Should be MAGIC or DEAD. */
@@ -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;
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
@@ -231,6 +231,16 @@ xenUnifiedOpen (virConnectPtr conn, virC
     xenUnifiedPrivatePtr priv;
     virDomainEventCallbackListPtr cbList;
 
+#ifdef __sun
+    extern int inside_daemon;
+    /*
+     * Only the libvirtd instance can open this driver.
+     * Everything else falls back to the remote driver.
+     */
+    if (!inside_daemon)
+        return VIR_DRV_OPEN_DECLINED;
+#endif
+
     if (conn->uri == NULL) {
         if (!xenUnifiedProbe())
             return VIR_DRV_OPEN_DECLINED;
@@ -283,8 +293,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 +303,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 +328,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