[libvirt] [PATCH] Solaris least privilege support

# HG changeset patch # User john.levon@sun.com # Date 1232039546 28800 # Node ID b9d4d60bca87633897cb133461e1415d1223c823 # Parent 25a0c46588d5de1653b16dfed6bc357abf11db77 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@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; @@ -1105,6 +1135,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); @@ -2239,6 +2292,29 @@ 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", SYSTEM_UID, SYSTEM_UID); + chown (server->logDir, 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"); + 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 +2493,20 @@ int main(int argc, char **argv) { sig_action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig_action, NULL); + /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ + 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); + } + } + } + + qemudSetupPrivs(server); + 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 @@ -90,7 +90,7 @@ #define MAGIC 999 /* private_data->magic if OK */ #define DEAD 998 /* private_data->magic if dead/closed */ -static int inside_daemon = 0; +int inside_daemon = 0; struct private_data { int magic; /* Should be MAGIC or DEAD. */ @@ -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; 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 @@ -231,6 +231,16 @@ xenUnifiedOpen (virConnectPtr conn, virC xenUnifiedPrivatePtr priv; virDomainEventCallbackListPtr cbList; +#ifdef __sun + extern int inside_daemon; + /* + * 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 +293,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 +303,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 +328,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")); }

john.levon@sun.com wrote:
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.
Hi John, When reposting a patch, please say a few words about what you've changed since the previous, the more the better ;-) That helps everyone involved.

On Thu, Jan 15, 2009 at 08:00:38PM +0100, Jim Meyering wrote:
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.
When reposting a patch, please say a few words about what you've changed since the previous, the more the better ;-) That helps everyone involved.
Sorry, I was being unnecessarily brief to say the least. I believe this addresses all Dan's review comments, and picks up his suggestion of declining the Xen driver to non-daemon users. An additional change I forgot to mention was the dropping of the sigpipe handling in virsh - I hadn't noticed that behaviour has changed since 0.4.0, and the connection failure happens much earlier now. regards, john

On Thu, Jan 15, 2009 at 09:19:39AM -0800, john.levon@sun.com wrote:
# HG changeset patch # User john.levon@sun.com # Date 1232039546 28800 # Node ID b9d4d60bca87633897cb133461e1415d1223c823 # Parent 25a0c46588d5de1653b16dfed6bc357abf11db77 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.
This looks fine to me except that chunk:
@@ -2417,6 +2493,20 @@ int main(int argc, char **argv) { sig_action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig_action, NULL);
+ /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ + 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); + } + } + } + + qemudSetupPrivs(server); +
The comment and the code don't seems to match, and it seems to me that this code would fail except in the first time the daemon is launched because mkdir /var/run/libvirt will return -1 and errno EEXIST in all following cases. I'm worried about this, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Jan 16, 2009 at 08:35:16AM +0100, Daniel Veillard wrote:
The comment and the code don't seems to match, and it seems to me
Oops, will fix the comment.
that this code would fail except in the first time the daemon is launched because mkdir /var/run/libvirt will return -1 and errno EEXIST in all following cases. I'm worried about this,
What do you mean? /var/run is a tmpfs on Solaris so must mkdir after every reboot. regards john

On Fri, Jan 16, 2009 at 12:59:59PM +0000, John Levon wrote:
On Fri, Jan 16, 2009 at 08:35:16AM +0100, Daniel Veillard wrote:
The comment and the code don't seems to match, and it seems to me
Oops, will fix the comment.
that this code would fail except in the first time the daemon is launched because mkdir /var/run/libvirt will return -1 and errno EEXIST in all following cases. I'm worried about this,
What do you mean? /var/run is a tmpfs on Solaris so must mkdir after every reboot.
I don't see any #ifdef __sun around that part of the patch, so I assume it need to be valid for all OSes. Moreover if you kill and restart the daemon, I would assume that chunk to fail too. So this need to be fixed IMHO before commit. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Mon, Jan 19, 2009 at 04:32:44PM +0100, Daniel Veillard wrote:
that this code would fail except in the first time the daemon is launched because mkdir /var/run/libvirt will return -1 and errno EEXIST in all following cases. I'm worried about this,
What do you mean? /var/run is a tmpfs on Solaris so must mkdir after every reboot.
I don't see any #ifdef __sun around that part of the patch, so I assume it need to be valid for all OSes. Moreover if you kill and restart the daemon, I would assume that chunk to fail too. So this need to be fixed IMHO before commit.
I don't think you've read the code correctly. A mkdir of an existing directory will not fail. regards john

On Mon, Jan 19, 2009 at 10:05:03PM +0000, John Levon wrote:
On Mon, Jan 19, 2009 at 04:32:44PM +0100, Daniel Veillard wrote:
that this code would fail except in the first time the daemon is launched because mkdir /var/run/libvirt will return -1 and errno EEXIST in all following cases. I'm worried about this,
What do you mean? /var/run is a tmpfs on Solaris so must mkdir after every reboot.
I don't see any #ifdef __sun around that part of the patch, so I assume it need to be valid for all OSes. Moreover if you kill and restart the daemon, I would assume that chunk to fail too. So this need to be fixed IMHO before commit.
I don't think you've read the code correctly. A mkdir of an existing directory will not fail.
Oops I was blind ! Weird ... Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Thu, Jan 15, 2009 at 09:19:39AM -0800, john.levon@sun.com wrote: [..snip..]
+ /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ + if (geteuid () == 0) { + const char *rundir = LOCAL_STATE_DIR "/run/libvirt"; + + if (mkdir (rundir, 0755)) { virFileMakePath? -- Guido

On Thu, Jan 15, 2009 at 09:19:39AM -0800, john.levon@sun.com wrote:
+#ifdef __sun +static void +qemudSetupPrivs (struct qemud_server *server) +{ + chown ("/var/run/libvirt", SYSTEM_UID, SYSTEM_UID); + chown (server->logDir, 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"); + 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
I think this function should be made to return int 0/-1 for failure, and let the caller propagate errors & do any cleanup it may desire, since that's the way most other failures in the daemon initialization are dealt with.
/* Print command-line usage. */ static void @@ -2417,6 +2493,20 @@ int main(int argc, char **argv) { sig_action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig_action, NULL);
+ /* Change the group ownership of /var/run/libvirt to unix_sock_gid */ + 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); + } + } + } + + qemudSetupPrivs(server);
This would need a "if(qemudSetupPrivs(server) < 0) return -1;"
diff --git a/src/remote_internal.c b/src/remote_internal.c --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -90,7 +90,7 @@ #define MAGIC 999 /* private_data->magic if OK */ #define DEAD 998 /* private_data->magic if dead/closed */
-static int inside_daemon = 0; +int inside_daemon = 0;
Eek no, the Xen code shouldn't reference this variable directly - it should track its own. We need to avoid compile dependancies inbetween any of the drivers, because all drivers need to be able to be turned on/off independantly, and may be dlopen'd at runtime, so you can't guarentee you'll have access to another driver's data.
diff --git a/src/xen_unified.c b/src/xen_unified.c --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -231,6 +231,16 @@ xenUnifiedOpen (virConnectPtr conn, virC xenUnifiedPrivatePtr priv; virDomainEventCallbackListPtr cbList;
+#ifdef __sun + extern int inside_daemon;
This flags needs to be tracked by the Xen driver explicitly & independantly of the daemon code. Aside from those two issues, I think this patch looks pretty good now. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (7)
-
Daniel P. Berrange
-
Daniel Veillard
-
Guido Günther
-
Jim Meyering
-
John Levon
-
John Levon
-
john.levon@sun.com