# HG changeset patch
# User john.levon(a)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(a)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"));
}