From 2b296983521a18df4197aadf95952cf8a2e1ffb3 Mon Sep 17 00:00:00 2001 From: John Levon Date: Thu, 22 Jan 2009 17:49:41 +0000 Subject: [PATCH] Least privilege support for Solaris --- ChangeLog | 12 +++++ qemud/qemud.c | 104 +++++++++++++++++++++++++++++++++++++++--- src/remote_internal.c | 9 ++-- src/xen_internal.c | 29 ++++++++++-- src/xen_internal.h | 2 + src/xen_unified.c | 44 +++++++++++++++--- src/xend_internal.c | 7 +-- src/xs_internal.c | 8 ++-- 8 files changed, 188 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index a717111c7e..cf7b0c0e8a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +Thu Jan 22 09:23:53 PST 2009 John Levon + + Least privilege support for Solaris + * qemud/qemud.c: init privs and refuse non-privileged + connections + * src/remote_internal.c: don't attempt user daemon instance on + Solaris + * src/xen_internal.c, src/xen_internal.h, src/xen_unified.c, + src/xend_internal.c, src/xs_internal.c: replace UID checks + with privilege checks. Refuse to load the Xen driver unless + we're libvirtd + Wed Jan 21 18:10:12 GMT 2009 Daniel P. Berrange Make Xen driver threadsafe diff --git a/qemud/qemud.c b/qemud/qemud.c index 85eece7bca..32902e8893 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -84,6 +84,39 @@ #endif +#ifdef __sun +#include +#include + +#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 */ @@ -102,10 +135,6 @@ 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; static int auth_unix_ro = REMOTE_AUTH_POLKIT; @@ -669,10 +698,11 @@ cleanup: 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; @@ -1155,6 +1185,29 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket 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); @@ -2524,6 +2577,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 usage (const char *argv0) @@ -2701,6 +2778,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 index 8c7dd7672b..51a40b8741 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -957,18 +957,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 index 3da5865d9d..1ef835ec67 100644 --- a/src/xen_internal.c +++ b/src/xen_internal.c @@ -26,6 +26,17 @@ #include #include +#ifdef __sun +#include + +#include + +#ifndef PRIV_XVM_CONTROL +#define PRIV_XVM_CONTROL ((const char *)"xvm_control") +#endif + +#endif /* __sun */ + /* required for dom0_getdomaininfo_t */ #include #include @@ -37,10 +48,6 @@ #endif #endif -#ifdef __sun -#include -#endif - /* required for shutdown flags */ #include @@ -3405,3 +3412,17 @@ xenHypervisorGetVcpuMax(virDomainPtr domain) 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 index 7e14e03c6b..766f676836 100644 --- a/src/xen_internal.h +++ b/src/xen_internal.h @@ -104,4 +104,6 @@ int xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, int startCell, int maxCells); +int xenHavePrivilege(void); + #endif /* __VIR_XEN_INTERNAL_H__ */ diff --git a/src/xen_unified.c b/src/xen_unified.c index 91718b7cd8..95968150be 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -67,6 +67,8 @@ static struct xenUnifiedDriver const * const drivers[XEN_UNIFIED_NR_DRIVERS] = { #endif }; +static int inside_daemon; + #define xenUnifiedError(conn, code, fmt...) \ virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) @@ -168,6 +170,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 @@ -204,6 +221,15 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) 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; @@ -265,8 +291,8 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) 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) { @@ -275,7 +301,7 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) } } - /* 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"); @@ -300,12 +326,12 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) 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) /* Ignore failures here. */ (void) xenHypervisorInit (); +#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 index e24b1a6d92..cd726599e8 100644 --- 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" @@ -161,9 +161,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 index 3c6e8aa9a3..55be057f99 100644 --- 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" #ifndef PROXY static char *xenStoreDomainGetOSType(virDomainPtr domain); @@ -290,11 +290,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")); }