On Thu, Apr 24, 2008 at 01:48:08PM +0100, John Levon wrote:
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 is interesting work. Work around privilege levels / security is
probably one of the most interesting & important aspects for future
libvirt development. On Linux obviously this likely involves SELinux
and its interesting to see that Solaris is also working on a MAC
based on the FLASK model. Any work we can do in the realm of privilege
separation using existing UNIX apis is also useful.
--- 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
+
I'm pretty sure we can come up with a way todo this without having to
have the #ifdef __sun here. AFAICT, you're trying to stop the remote
driver in the daemon re-entrantly calling back into the daemon. If
so, then we can make this more generic, by just having a global flag
to disable the remote driver entirely when inside the daemon.
--- 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
As Daniel suggests, we should write some kind of generic helper function
in the util.c file, so we can isolate the #ifdef __sun in a single place
rather than repeating it.
--- 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 */
+
After the 0.4.0 release I refactored the damon code to have a generic function for
getting socket peer credentials. So we can move this code into the qemud.c file
in the qemudGetSocketIdentity() method. Its good to have a Solaris impl for this
API.
/* 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
+
We can probably use 0666 even on Linux - there's no compelling reason why we
need to have 0777 with execute permission - read & write should be sufficient
I believe.
+#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);
The thing that concerns me here, is that we're getting a bit of a disconnect
between the config file and the impl. The user can already set these things
via the cofnig file, so hardcoding specific values is not so pleasant. I'm
wondering if its practical to have this privilege separation stuff be toggled
via a config file setting and avoid hardcoding this soo much.
+/**
+ * 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
+}
+
As mentioned earlier, we probably want to move this into the util.c file
and have the privilege name passed in as a parameter.
+/*
+ * 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))
Clearly this has to die. The virsh console command implements exactly
the same functionality as xenconsole without being xen specific.
I'm guessing you're doing this because you need the console in a
separate process from virsh, so it can run with different privileges.
If so, we should probably split 'virsh console' out into a separate
standlone binary/command 'virt-console'. This will let you get the
privilege separation without relying on Xen specific commands.
So in summary, this is interesting work. I've no objections to the general
principle of what the patch is trying to achieve - we'll just need to
iterate over it a few times to try and better isolate the solaris specific
#ifdefs and/or make them more generically applicable and avoid the xen
specific console bits.
It'd be useful for other people's understanding if there was a short doc
giving the 1000ft overview of the different components and their relative
privileges
Regards,
Dan.
--
|: Red Hat, Engineering, Boston -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 :|