# HG changeset patch # User john.levon@xxxxxxx # 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@xxxxxxx> 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")); } -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list