In the interests of giving a 'heads-up' I'm posting this patch. It implements least-privilege on Solaris. The basic idea is that all libvirt clients are forced to go through libvirtd, which verifies a particular privilege. virtd itself runs with enough privilege to interact with Xen. This patch is: - not to be applied :) - only against 0.4.0 - subject to further change - not yet reviewed, not even by myself (properly) Nonetheless, comments are more than welcome. regards john --- libvirt-0.4.0/qemud/remote.c 2007-12-12 05:30:49.000000000 -0800 +++ libvirt-new/qemud/remote.c 2008-04-10 12:52:18.059618661 -0700 @@ -434,6 +434,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) --- libvirt-0.4.0/src/libvirt.c 2007-12-17 13:51:09.000000000 -0800 +++ libvirt-new/src/libvirt.c 2008-04-16 08:46:28.767087199 -0700 @@ -34,6 +34,7 @@ #include "uuid.h" #include "test.h" +#include "xen_internal.h" #include "xen_unified.h" #include "remote_internal.h" #include "qemu_driver.h" @@ -202,8 +203,16 @@ virInitialize(void) if (qemudRegister() == -1) return -1; #endif #ifdef WITH_XEN + /* + * On Solaris, only initialize Xen if we're libvirtd. + */ +#ifdef __sun + if (geteuid() != 0 && xenHavePrivilege() && + xenUnifiedRegister () == -1) return -1; +#else if (xenUnifiedRegister () == -1) return -1; #endif +#endif #ifdef WITH_OPENVZ if (openvzRegister() == -1) return -1; #endif @@ -525,6 +534,16 @@ do_open (const char *name, if (STREQ (name, "xen://")) name = "xen:///"; +#ifdef __sun + /* + * If we're not libvirtd, force us to go via the daemon. + */ + if (geteuid() == 0 || !xenHavePrivilege()) + name = "remote+unix:///"; + else if (STREQ (name, "xen:///") && xenUnifiedRegister () == -1) + return NULL; +#endif + if (!initialized) if (virInitialize() < 0) return NULL; --- libvirt-0.4.0/qemud/qemud.c 2007-12-12 05:30:49.000000000 -0800 +++ libvirt-new/qemud/qemud.c 2008-04-17 17:16:47.075258251 -0700 @@ -60,6 +60,25 @@ #include "mdns.h" #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 */ @@ -668,11 +687,13 @@ static int qemudInitPaths(struct qemud_s unlink(sockname); +#ifndef __sun if (snprintf (roSockname, maxlen, "%s/run/libvirt/libvirt-sock-ro", LOCAL_STATE_DIR) >= maxlen) goto snprintf_error; unlink(roSockname); +#endif if (snprintf(server->logDir, PATH_MAX, "%s/log/libvirt/", LOCAL_STATE_DIR) >= PATH_MAX) goto snprintf_error; @@ -1033,6 +1054,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); @@ -1864,6 +1908,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) return -1; if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0) @@ -1955,6 +2003,32 @@ remoteReadConfigFile (struct qemud_serve return -1; } +#ifdef __sun +static void +qemudSetupPrivs (struct qemud_server *server) +{ + chown ("/var/run/libvirt", 60, 60); + chmod ("/var/run/libvirt", 0755); + 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 +#endif + /* Print command-line usage. */ static void usage (const char *argv0) @@ -2139,6 +2213,8 @@ int main(int argc, char **argv) { goto error2; } + qemudSetupPrivs(server); + qemudRunLoop(server); qemudCleanup(server); --- libvirt-0.4.0/src/xs_internal.c 2007-12-14 07:33:11.000000000 -0800 +++ libvirt-new/src/xs_internal.c 2008-04-10 09:54:38.676077954 -0700 @@ -31,7 +31,7 @@ #include "driver.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" @@ -344,11 +344,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 - * remote) mechanism. + * 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, _("failed to connect to Xen Store")); } --- libvirt-0.4.0/src/xen_internal.c 2007-12-12 05:30:49.000000000 -0800 +++ libvirt-new/src/xen_internal.c 2008-04-10 10:28:41.363704244 -0700 @@ -28,6 +28,15 @@ #include <errno.h> #include <sys/utsname.h> +#ifdef __sun +#include <priv.h> + +#ifndef PRIV_XVM_CONTROL +#define PRIV_XVM_CONTROL ((const char *)"xvm_control") +#endif + +#endif + #include "xs_internal.h" #include "stats_linux.h" #include "xend_internal.h" @@ -3259,6 +3268,21 @@ 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 +} + #endif /* WITH_XEN */ /* * vim: set tabstop=4: --- libvirt-0.4.0/src/xen_internal.h 2007-12-05 12:33:02.000000000 -0800 +++ libvirt-new/src/xen_internal.h 2008-04-10 09:34:16.379186656 -0700 @@ -98,6 +98,9 @@ int xenHypervisorNodeGetCellsFreeMemory( unsigned long long *freeMems, int startCell, int maxCells); + +int xenHavePrivilege(void); + #ifdef __cplusplus } #endif --- libvirt-0.4.0/src/xen_unified.c 2007-12-12 05:30:49.000000000 -0800 +++ libvirt-new/src/xen_unified.c 2008-04-10 15:10:53.653298774 -0700 @@ -266,8 +266,8 @@ xenUnifiedOpen (virConnectPtr conn, xmlU priv->xendConfigVersion > 2) continue; - /* Ignore proxy for root */ - if (i == XEN_UNIFIED_PROXY_OFFSET && getuid() == 0) + /* Ignore proxy if we have privilege */ + if (i == XEN_UNIFIED_PROXY_OFFSET && xenHavePrivilege()) continue; if (drivers[i]->open) { @@ -282,10 +282,10 @@ xenUnifiedOpen (virConnectPtr conn, xmlU #endif } - /* If as root, then all drivers must succeed. - If non-root, then only proxy must succeed */ + /* If privileged, then all drivers must succeed. + If unprivileged, then only proxy must succeed */ if (!priv->opened[i] && - (getuid() == 0 || i == XEN_UNIFIED_PROXY_OFFSET)) { + (xenHavePrivilege() || i == XEN_UNIFIED_PROXY_OFFSET)) { for (j = 0; j < i; ++j) if (priv->opened[j]) drivers[j]->close (conn); free (priv); @@ -1266,6 +1266,12 @@ static virDriver xenUnifiedDriver = { int xenUnifiedRegister (void) { + static int driver_priority = 0; + static int xen_initialized = 0; + + if (xen_initialized) + return driver_priority; + /* Ignore failures here. */ (void) xenHypervisorInit (); (void) xenProxyInit (); @@ -1273,7 +1279,9 @@ xenUnifiedRegister (void) (void) xenStoreInit (); (void) xenXMInit (); - return virRegisterDriver (&xenUnifiedDriver); + driver_priority = virRegisterDriver (&xenUnifiedDriver); + xen_initialized = 1; + return driver_priority; } #endif /* WITH_XEN */ --- libvirt-0.4.0/src/xend_internal.c 2007-12-17 15:05:27.000000000 -0800 +++ libvirt-new/src/xend_internal.c 2008-04-10 09:45:10.262989682 -0700 @@ -42,7 +42,7 @@ #include "uuid.h" #include "xen_unified.h" #include "xend_internal.h" -#include "xen_internal.h" /* for DOM0_INTERFACE_VERSION */ +#include "xen_internal.h" #include "xs_internal.h" /* To extract VNC port & Serial console TTY */ /* required for cpumap_t */ @@ -235,7 +235,7 @@ do_connect(virConnectPtr xend) * is rather normal, this should fallback to the proxy (or * remote) mechanism. */ - if ((getuid() == 0) || (xend->flags & VIR_CONNECT_RO)) { + if (xenHavePrivilege() || (xend->flags & VIR_CONNECT_RO)) { virXendError(xend, VIR_ERR_INTERNAL_ERROR, "failed to connect to xend"); } --- libvirt-0.4.0/src/virsh.c 2007-12-07 07:00:48.000000000 -0800 +++ libvirt-new/src/virsh.c 2008-04-16 08:44:35.668718168 -0700 @@ -36,6 +36,7 @@ #include <sys/stat.h> #include <inttypes.h> #include <test.h> +#include <signal.h> #include <libxml/parser.h> #include <libxml/tree.h> @@ -50,6 +51,7 @@ #include "console.h" static char *progname; +static int sigpipe; #ifndef TRUE #define TRUE 1 @@ -202,9 +204,6 @@ typedef struct __vshControl { virConnectPtr conn; /* connection to hypervisor (MAY BE NULL) */ vshCmd *cmd; /* the current command */ char *cmdstr; /* string with command */ -#ifndef __MINGW32__ - uid_t uid; /* process owner */ -#endif /* __MINGW32__ */ int imode; /* interactive mode? */ int quiet; /* quiet mode */ int debug; /* print debug messages? */ @@ -464,12 +463,8 @@ static vshCmdOptDef opts_console[] = { static int cmdConsole(vshControl * ctl, vshCmd * cmd) { - xmlDocPtr xml = NULL; - xmlXPathObjectPtr obj = NULL; - xmlXPathContextPtr ctxt = NULL; virDomainPtr dom; int ret = FALSE; - char *doc; if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) return FALSE; @@ -477,37 +472,16 @@ cmdConsole(vshControl * ctl, vshCmd * cm if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) return FALSE; - doc = virDomainGetXMLDesc(dom, 0); - if (!doc) - goto cleanup; - - xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, - XML_PARSE_NOENT | XML_PARSE_NONET | - XML_PARSE_NOWARNING); - free(doc); - if (!xml) - goto cleanup; - ctxt = xmlXPathNewContext(xml); - if (!ctxt) - goto cleanup; + ret = vshConsole(dom); - obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt); - if ((obj != NULL) && ((obj->type == XPATH_STRING) && - (obj->stringval != NULL) && (obj->stringval[0] != 0))) { - if (vshRunConsole((const char *)obj->stringval) == 0) - ret = TRUE; - } else { + if (ret == 0) { vshPrintExtra(ctl, _("No console available for domain\n")); + } else if (ret == -1) { + fprintf(stderr, _("unable to open console: %s\n"), + strerror(errno)); } - xmlXPathFreeObject(obj); - cleanup: - if (ctxt) - xmlXPathFreeContext(ctxt); - if (xml) - xmlFreeDoc(xml); - virDomainFree(dom); - return ret; + return ret == 1; } #else /* __MINGW32__ */ @@ -4523,22 +4497,11 @@ vshInit(vshControl * ctl) if (ctl->conn) return FALSE; -#ifndef __MINGW32__ - ctl->uid = getuid(); -#endif - vshOpenLogFile(ctl); /* set up the library error handler */ virSetErrorFunc(NULL, virshErrorHandler); -#ifndef __MINGW32__ - /* Force a non-root, Xen connection to readonly */ - if ((ctl->name == NULL || - !strcasecmp(ctl->name, "xen")) && ctl->uid != 0) - ctl->readonly = 1; -#endif - ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault, ctl->readonly ? VIR_CONNECT_RO : 0); @@ -5021,12 +4984,21 @@ vshParseArgv(vshControl * ctl, int argc, return TRUE; } -int +static void sigpipe_handler(int sig) +{ + sigpipe = 1; + /* + * Force readline() to exit. + */ + close(STDIN_FILENO); +} + main(int argc, char **argv) { vshControl _ctl, *ctl = &_ctl; char *defaultConn; int ret = TRUE; + struct sigaction sig_action; if (!setlocale(LC_ALL, "")) { perror("setlocale"); @@ -5059,6 +5031,12 @@ main(int argc, char **argv) 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); exit(EXIT_FAILURE); @@ -5098,6 +5076,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); } --- libvirt-0.4.0/src/console.c 2007-12-07 07:00:48.000000000 -0800 +++ libvirt-new/src/console.c 2008-04-16 16:34:15.533322609 -0700 @@ -34,6 +34,11 @@ #include <errno.h> #include <unistd.h> #include <signal.h> +#include <stdlib.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> #include "console.h" #include "internal.h" @@ -59,8 +64,10 @@ cfmakeraw (struct termios *attr) } #endif /* !HAVE_CFMAKERAW */ -int vshRunConsole(const char *tty) { - int ttyfd, ret = -1; +static int +vshDirectConsole(int ttyfd) +{ + int ret = -1; struct termios ttyattr, rawattr; void (*old_sigquit)(int); void (*old_sigterm)(int); @@ -68,14 +75,6 @@ int vshRunConsole(const char *tty) { void (*old_sighup)(int); void (*old_sigpipe)(int); - - /* We do not want this to become the controlling TTY */ - if ((ttyfd = open(tty, O_NOCTTY | O_RDWR)) < 0) { - fprintf(stderr, _("unable to open tty %s: %s\n"), - tty, strerror(errno)); - return -1; - } - /* Put STDIN into raw mode so that stuff typed does not echo to the screen (the TTY reads will result in it being echoed back already), and @@ -174,7 +173,7 @@ int vshRunConsole(const char *tty) { } } done: - ret = 0; + ret = 1; cleanup: @@ -195,6 +194,106 @@ int vshRunConsole(const char *tty) { return ret; } +/* + * Attempt to access the domain via 'xenconsole', which may have + * additional privilege to reach the console. + */ +static int +vshXenConsole(int id) +{ + char arg[100]; + char *argv[3]; + pid_t pid; + + if ((pid = fork()) < 0) { + return -1; + } else if (pid) { + int wstat; + + if (waitpid(pid, &wstat, 0) < 0) + return -1; + + if (WIFSIGNALED(wstat)) + return -1; + if (WIFEXITED(wstat) && WEXITSTATUS(wstat)) + return -1; + + return 1; + } + + /* child */ + + if (snprintf(arg, 100, "%d", id) < 0) + return -1; + + argv[0] = "/usr/lib/xen/bin/xenconsole"; + argv[1] = arg; + argv[2] = NULL; + + if (execv("/usr/lib/xen/bin/xenconsole", argv)) + fprintf(stderr, _("failed to run xenconsole: %s\n"), + strerror(errno)); + _exit(1); +} + +/* + * Returns 0 if no console is available, 1 if the console was accessed + * successfully, or -1 on error. + */ +int vshConsole(virDomainPtr dom) +{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj = NULL; + xmlXPathContextPtr ctxt = NULL; + char *doc; + int ttyfd; + int ret = -1; + + doc = virDomainGetXMLDesc(dom, 0); + if (!doc) + goto cleanup; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + free(doc); + if (!xml) + goto cleanup; + ctxt = xmlXPathNewContext(xml); + if (!ctxt) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt); + if (obj == NULL || obj->type != XPATH_STRING || obj->stringval == NULL || + obj->stringval[0] == '\0') { + ret = 0; + goto cleanup; + } + + /* We do not want this to become the controlling TTY */ + ttyfd = open((char *)obj->stringval, O_NOCTTY | O_RDWR); + + if (ttyfd != -1) { + ret = vshDirectConsole(ttyfd); + goto cleanup; + } else if (errno != EACCES) { + fprintf(stderr, _("unable to open tty %s: %s\n"), + obj->stringval, strerror(errno)); + } else { + ret = vshXenConsole(virDomainGetID(dom)); + } + + cleanup: + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + virDomainFree(dom); + return ret; +} + #endif /* !__MINGW32__ */ /* --- libvirt-0.4.0/src/console.h 2007-12-07 07:00:48.000000000 -0800 +++ libvirt-new/src/console.h 2008-04-16 08:49:20.165071296 -0700 @@ -25,11 +25,13 @@ #ifndef __MINGW32__ +#include "libvirt/libvirt.h" + #ifdef __cplusplus extern "C" { #endif - int vshRunConsole(const char *tty); +int vshConsole(virDomainPtr dom); #ifdef __cplusplus } -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list