[libvirt] [PATCH] Solaris least privilege support

john.levon at sun.com john.levon at sun.com
Tue Jan 20 19:13:38 UTC 2009


# HG changeset patch
# User john.levon at sun.com
# Date 1232478815 28800
# Node ID 9c7ef175f3a797ecc3ffa6b1fed5a27d1814838c
# Parent  ce76623e857f5bb2cf3af8414335f891fd7902b7
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;
@@ -1110,6 +1140,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);
@@ -2257,6 +2310,30 @@ version (const char *argv0)
 {
     printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
 }
+
+#ifdef __sun
+static int
+qemudSetupPrivs (void)
+{
+    chown ("/var/run/libvirt", 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");
+        return -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");
+        return -1;
+    }
+
+    return 0;
+}
+#else
+#define qemudSetupPrivs() 0
+#endif
 
 /* Print command-line usage. */
 static void
@@ -2435,6 +2512,21 @@ int main(int argc, char **argv) {
     sig_action.sa_handler = SIG_IGN;
     sigaction(SIGPIPE, &sig_action, NULL);
 
+    /* Ensure the rundir exists (on tmpfs on some systems) */
+    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;
+            }
+        }
+    }
+
+    if (qemudSetupPrivs() < 0)
+        goto error2;
+
     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
@@ -885,18 +885,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->sock = -1;
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 */
@@ -3393,3 +3400,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
@@ -65,6 +65,8 @@ static struct xenUnifiedDriver *drivers[
 #endif
 };
 
+static int inside_daemon;
+
 #define xenUnifiedError(conn, code, fmt...)                                  \
         virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__,           \
                                __FUNCTION__, __LINE__, fmt)
@@ -195,6 +197,21 @@ done:
     return(res);
 }
 
+#ifdef WITH_LIBVIRTD
+
+static int
+xenInitialize (void)
+{
+    inside_daemon = 1;
+    return 0;
+}
+
+static virStateDriver state_driver = {
+    .initialize = xenInitialize,
+};
+
+#endif
+
 /*----- Dispatch functions. -----*/
 
 /* These dispatch functions follow the model used historically
@@ -231,6 +248,15 @@ xenUnifiedOpen (virConnectPtr conn, virC
     xenUnifiedPrivatePtr priv;
     virDomainEventCallbackListPtr cbList;
 
+#ifdef __sun
+    /*
+     * 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 +309,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 +319,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 +344,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");
@@ -1472,6 +1498,10 @@ xenRegister (void)
     (void) xenHypervisorInit ();
     (void) xenXMInit ();
 
+#ifdef WITH_LIBVIRTD
+    if (virRegisterStateDriver (&state_driver) == -1) return -1;
+#endif
+
     return virRegisterDriver (&xenUnifiedDriver);
 }
 
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"
 
@@ -159,9 +159,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