diff -r a41065295371 configure.in --- a/configure.in Tue Sep 18 22:28:57 2007 -0400 +++ b/configure.in Tue Sep 18 22:30:19 2007 -0400 @@ -25,6 +25,7 @@ LIBXML_REQUIRED="2.5.0" LIBXML_REQUIRED="2.5.0" GNUTLS_REQUIRED="1.2.0" AVAHI_REQUIRED="0.6.0" +POLKIT_REQUIRED="0.5" dnl Checks for programs. AC_PROG_CC @@ -336,6 +337,32 @@ AM_CONDITIONAL(HAVE_AVAHI, [test "$with_ AM_CONDITIONAL(HAVE_AVAHI, [test "$with_avahi" = "yes"]) AC_SUBST(AVAHI_CFLAGS) AC_SUBST(AVAHI_LIBS) + +dnl PolicyKit library +AC_ARG_WITH(polkit, + [ --with-polkit use PolicyKit for UNIX socket access checks], + [], + [with_polkit=check]) + +if test "$with_polkit" = "check"; then + PKG_CHECK_EXISTS(polkit >= $POLKIT_REQUIRED, [with_polkit=yes], [with_polkit=no]) +fi + +if test "$with_polkit" = "yes"; then + PKG_CHECK_MODULES(POLKIT, polkit >= $POLKIT_REQUIRED) + PKG_CHECK_MODULES(POLKIT_DBUS, polkit-dbus >= $POLKIT_REQUIRED) + AC_DEFINE_UNQUOTED(HAVE_POLKIT, 1, [use PolicyKit for UNIX socket access checks]) +else + POLKIT_CFLAGS= + POLKIT_LIBS= + POLKIT_DBUS_CFLAGS= + POLKIT_DBUS_LIBS= +fi +AM_CONDITIONAL(HAVE_POLKIT, [test "$with_polkit" = "yes"]) +AC_SUBST(POLKIT_CFLAGS) +AC_SUBST(POLKIT_LIBS) +AC_SUBST(POLKIT_DBUS_CFLAGS) +AC_SUBST(POLKIT_DBUS_LIBS) dnl virsh libraries AC_CHECK_LIB(curses, initscr, @@ -528,6 +555,11 @@ else else AC_MSG_NOTICE([ avahi: no]) fi +if test "$with_polkit" = "yes" ; then +AC_MSG_NOTICE([ polkit: $POLKIT_CFLAGS $POLKIT_LIBS $POLKIT_DBUS_CFLAGS $POLKIT_DBUS_LIBS]) +else +AC_MSG_NOTICE([ polkit: no]) +fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Miscellaneous]) AC_MSG_NOTICE([]) diff -r a41065295371 qemud/Makefile.am --- a/qemud/Makefile.am Tue Sep 18 22:28:57 2007 -0400 +++ b/qemud/Makefile.am Tue Sep 18 22:30:19 2007 -0400 @@ -42,6 +42,16 @@ libvirtd_CFLAGS += $(AVAHI_CFLAGS) libvirtd_CFLAGS += $(AVAHI_CFLAGS) libvirtd_LDADD += $(AVAHI_LIBS) endif + +if HAVE_POLKIT +libvirtd_CFLAGS += $(POLKIT_CFLAGS) $(POLKIT_DBUS_CFLAGS) +libvirtd_LDADD += $(POLKIT_LIBS) $(POLKIT_DBUS_LIBS) + +policydir = $(datadir)/PolicyKit/policy +policy_DATA = libvirtd.policy +endif +EXTRA_DIST += libvirtd.policy + install-data-local: install-init mkdir -p $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart diff -r a41065295371 qemud/internal.h --- a/qemud/internal.h Tue Sep 18 22:28:57 2007 -0400 +++ b/qemud/internal.h Tue Sep 18 22:30:19 2007 -0400 @@ -28,6 +28,9 @@ #include #include #include "../src/gnutls_1_0_compat.h" +#ifdef HAVE_POLKIT +#include "dbus/dbus.h" +#endif #include "protocol.h" #include "remote_protocol.h" @@ -57,6 +60,13 @@ typedef enum { #endif } qemudLogPriority; +enum libvirtd_auth { + LIBVIRTD_AUTH_NONE, + LIBVIRTD_AUTH_TLS, +#ifdef HAVE_POLKIT + LIBVIRTD_AUTH_POLKIT, +#endif +}; enum qemud_mode { QEMUD_MODE_RX_HEADER, @@ -82,8 +92,8 @@ struct qemud_client { struct sockaddr_storage addr; socklen_t addrlen; - /* If set, TLS is required on this socket. */ - int tls; + /* Required auth mode for incoming connections */ + int auth; gnutls_session_t session; enum qemud_tls_direction direction; @@ -109,8 +119,8 @@ struct qemud_socket { struct qemud_socket { int fd; int readonly; - /* If set, TLS is required on this socket. */ - int tls; + /* Required auth mode for incoming connections */ + int auth; int port; struct qemud_socket *next; }; @@ -127,6 +137,9 @@ struct qemud_server { #ifdef HAVE_AVAHI struct libvirtd_mdns *mdns; #endif +#ifdef HAVE_POLKIT + DBusConnection *sysbus; +#endif }; void qemudLog(int priority, const char *fmt, ...) diff -r a41065295371 qemud/libvirtd.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qemud/libvirtd.policy Tue Sep 18 22:30:19 2007 -0400 @@ -0,0 +1,36 @@ + + + + + + + Monitor local virtualized systems + System policy prevents monitoring of local virtualized systems + + yes + yes + + + + + Manage local virtualized systems + System policy prevents management of local virtualized systems + + no + auth_self_keep_session + + + diff -r a41065295371 qemud/qemud.c --- a/qemud/qemud.c Tue Sep 18 22:28:57 2007 -0400 +++ b/qemud/qemud.c Tue Sep 18 22:30:19 2007 -0400 @@ -61,6 +61,11 @@ #include "mdns.h" #endif +#ifdef HAVE_POLKIT +#include +#include +#endif + static int godaemon = 0; /* -d: Be a daemon */ static int verbose = 0; /* -v: Verbose mode */ static int timeout = -1; /* -t: Shutdown timeout */ @@ -73,8 +78,15 @@ static const char *tls_port = LIBVIRTD_T static const char *tls_port = LIBVIRTD_TLS_PORT; static const char *tcp_port = LIBVIRTD_TCP_PORT; +#ifdef HAVE_POLKIT +static int unix_sock_polkit = 1; /* Use PolicyKit for UNIX auth */ +#endif static gid_t unix_sock_gid = 0; /* Only root by default */ +#ifdef HAVE_POLKIT +static int unix_sock_rw_perms = 0777; /* Allow world (ACL with PolicyKit) */ +#else static int unix_sock_rw_perms = 0700; /* Allow user only */ +#endif static int unix_sock_ro_perms = 0777; /* Allow world */ #ifdef HAVE_AVAHI @@ -463,6 +475,11 @@ static int qemudListenUnix(struct qemud_ sock->readonly = readonly; sock->port = -1; +#ifdef HAVE_POLKIT + sock->auth = unix_sock_polkit ? LIBVIRTD_AUTH_POLKIT : LIBVIRTD_AUTH_NONE; +#else + sock->auth = LIBVIRTD_AUTH_NONE; +#endif if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { qemudLog(QEMUD_ERR, "Failed to create socket: %s", @@ -577,7 +594,7 @@ static int static int remoteListenTCP (struct qemud_server *server, const char *port, - int tls) + int auth) { int fds[2]; int nfds = 0; @@ -605,7 +622,7 @@ remoteListenTCP (struct qemud_server *se server->nsockets++; sock->fd = fds[i]; - sock->tls = tls; + sock->auth = auth; if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0) return -1; @@ -719,6 +736,20 @@ static struct qemud_server *qemudInitial if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1) < 0) goto cleanup; +#ifdef HAVE_POLKIT + if (unix_sock_polkit) { + DBusError err; + dbus_error_init(&err); + server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (!(server->sysbus)) { + qemudLog(QEMUD_ERR, "Failed to connect to system bus for PolicyKit auth: %s", + err.message); + dbus_error_free(&err); + goto cleanup; + } + } +#endif + __virEventRegisterImpl(virEventAddHandleImpl, virEventUpdateHandleImpl, virEventRemoveHandleImpl, @@ -729,14 +760,14 @@ static struct qemud_server *qemudInitial virStateInitialize(); if (ipsock) { - if (listen_tcp && remoteListenTCP (server, tcp_port, 0) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, LIBVIRTD_AUTH_NONE) < 0) goto cleanup; if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, 1) < 0) + if (remoteListenTCP (server, tls_port, LIBVIRTD_AUTH_TLS) < 0) goto cleanup; } } @@ -769,7 +800,7 @@ static struct qemud_server *qemudInitial */ sock = server->sockets; while (sock) { - if (sock->port != -1 && sock->tls) { + if (sock->port != -1 && sock->auth == LIBVIRTD_AUTH_TLS) { port = sock->port; break; } @@ -795,6 +826,10 @@ static struct qemud_server *qemudInitial sock = sock->next; } +#ifdef HAVE_POLKIT + if (server->sysbus) + dbus_connection_unref(server->sysbus); +#endif free(server); } return NULL; @@ -1017,12 +1052,32 @@ remoteCheckAccess (struct qemud_client * return 0; } +static int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid) { +#ifdef SO_PEERCRED + struct ucred cr; + unsigned int cr_len = sizeof (cr); + + if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) { + qemudLog(QEMUD_ERR, "Failed to verify client credentials: %s", strerror(errno)); + return -1; + } + + *pid = cr.pid; + *uid = cr.uid; +#else + /* XXX Many more OS support UNIX socket credentials we could port to. See dbus ....*/ +#error "UNIX socket credentials not supported/implemented on this platform yet..." +#endif + return 0; +} + static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) { int fd; struct sockaddr_storage addr; socklen_t addrlen = (socklen_t) (sizeof addr); struct qemud_client *client; int no_slow_start = 1; + int ret; if ((fd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen)) < 0) { if (errno == EAGAIN) @@ -1045,19 +1100,105 @@ static int qemudDispatchServer(struct qe client->magic = QEMUD_CLIENT_MAGIC; client->fd = fd; client->readonly = sock->readonly; - client->tls = sock->tls; + client->auth = sock->auth; memcpy (&client->addr, &addr, sizeof addr); client->addrlen = addrlen; - if (!client->tls) { + qemudDebug("Authenticating new client on fd %d with mode %d", client->fd, client->auth); + + switch (client->auth) { +#ifdef HAVE_POLKIT + case LIBVIRTD_AUTH_POLKIT: + { + PolKitCaller *pkcaller = NULL; + PolKitAction *pkaction = NULL; + PolKitContext *pkcontext = NULL; + PolKitError *pkerr; + PolKitResult pkresult; + const char *action = sock->readonly ? + "org.libvirt.local.monitor" : + "org.libvirt.local.manage"; + pid_t callerPid; + uid_t callerUid; + DBusError err; + + if (qemudGetSocketIdentity(fd, &callerUid, &callerPid) < 0) { + close(fd); + free(client); + return -1; + } + + /* Only do policy checks for non-root - allow root user + through with no checks, so ssh sessions running as root + succeed - PolicyKit only works within X sessions */ + if (callerUid == 0) { + qemudLog(QEMUD_INFO, "Allowing PID %d running as root", callerPid); + } else { + qemudLog(QEMUD_INFO, "Checking PID %d running as %d", callerPid, callerUid); + dbus_error_init(&err); + if (!(pkcaller = polkit_caller_new_from_pid(server->sysbus, callerPid, &err))) { + qemudLog(QEMUD_ERR, "Failed to lookup policy kit caller: %s", err.message); + dbus_error_free(&err); + close(fd); + free(client); + return -1; + } + + if (!(pkaction = polkit_action_new())) { + qemudLog(QEMUD_ERR, "Failed to create polkit action %s\n", strerror(errno)); + polkit_caller_unref(pkcaller); + close(fd); + free(client); + return -1; + } + polkit_action_set_action_id(pkaction, action); + + if (!(pkcontext = polkit_context_new()) || + !polkit_context_init(pkcontext, &pkerr)) { + qemudLog(QEMUD_ERR, "Failed to create polkit context %s\n", + pkerr ? polkit_error_get_error_message(pkerr) : strerror(errno)); + if (pkerr) + polkit_error_free(pkerr); + polkit_caller_unref(pkcaller); + polkit_action_unref(pkaction); + dbus_error_free(&err); + close(fd); + free(client); + return -1; + } + + pkresult = polkit_context_can_caller_do_action(pkcontext, pkaction, pkcaller); + polkit_context_unref(pkcontext); + polkit_caller_unref(pkcaller); + polkit_action_unref(pkaction); + if (pkresult != POLKIT_RESULT_YES) { + qemudLog(QEMUD_ERR, "Policy kit denied action %s from pid %d, uid %d, result: %s\n", + action, callerPid, callerUid, polkit_result_to_string_representation(pkresult)); + close(fd); + free(client); + return -1; + } + qemudLog(QEMUD_INFO, "Policy allowed action %s from pid %d, uid %d, result %s", + action, callerPid, callerUid, polkit_result_to_string_representation(pkresult)); + } + } client->mode = QEMUD_MODE_RX_HEADER; client->bufferLength = QEMUD_PKT_HEADER_XDR_LEN; if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; - } else { - int ret; - + break; +#endif + + case LIBVIRTD_AUTH_NONE: + client->mode = QEMUD_MODE_RX_HEADER; + client->bufferLength = QEMUD_PKT_HEADER_XDR_LEN; + + if (qemudRegisterClientEvent (server, client, 0) < 0) + goto cleanup; + break; + + case LIBVIRTD_AUTH_TLS: client->session = remoteInitializeTLSSession (); if (client->session == NULL) goto cleanup; @@ -1087,6 +1228,12 @@ static int qemudDispatchServer(struct qe gnutls_strerror (ret)); goto cleanup; } + + default: + qemudLog(QEMUD_ERR, "Unknown/illegal authentication scheme configured: %d\n", sock->auth); + close(fd); + free(client); + return -1; } client->next = server->clients; @@ -1126,7 +1273,8 @@ static void qemudDispatchClientFailure(s if (client->conn) virConnectClose(client->conn); - if (client->tls && client->session) gnutls_deinit (client->session); + if (client->auth == LIBVIRTD_AUTH_TLS && client->session) + gnutls_deinit (client->session); close(client->fd); free(client); } @@ -1143,7 +1291,7 @@ static int qemudClientRead(struct qemud_ /*qemudDebug ("qemudClientRead: len = %d", len);*/ - if (!client->tls) { + if (client->auth != LIBVIRTD_AUTH_TLS) { if ((ret = read (client->fd, data, len)) <= 0) { if (ret == 0 || errno != EAGAIN) { if (ret != 0) @@ -1211,7 +1359,8 @@ static void qemudDispatchClientRead(stru client->mode = QEMUD_MODE_RX_PAYLOAD; client->bufferLength = h.length; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ; + if (client->auth == LIBVIRTD_AUTH_TLS) + client->direction = QEMUD_TLS_DIRECTION_READ; /* Note that we don't reset bufferOffset here because we want * to retain the whole message, including header. */ @@ -1299,7 +1448,7 @@ static int qemudClientWrite(struct qemud data = client->buffer + client->bufferOffset; len = client->bufferLength - client->bufferOffset; - if (!client->tls) { + if (client->auth != LIBVIRTD_AUTH_TLS) { if ((ret = write(client->fd, data, len)) == -1) { if (errno != EAGAIN) { qemudLog (QEMUD_ERR, "write: %s", strerror (errno)); @@ -1338,7 +1487,8 @@ static void qemudDispatchClientWrite(str client->mode = QEMUD_MODE_RX_HEADER; client->bufferLength = QEMUD_PKT_HEADER_XDR_LEN; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ; + if (client->auth == LIBVIRTD_AUTH_TLS) + client->direction = QEMUD_TLS_DIRECTION_READ; if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); @@ -1407,7 +1557,7 @@ static int qemudRegisterClientEvent(stru if (virEventRemoveHandleImpl(client->fd) < 0) return -1; - if (client->tls) { + if (client->auth == LIBVIRTD_AUTH_TLS) { if (virEventAddHandleImpl(client->fd, (client->direction ? POLLOUT : POLLIN) | POLLERR | POLLHUP, @@ -1564,6 +1714,16 @@ remoteReadConfigFile (const char *filena p = virConfGetValue (conf, "tcp_port"); CHECK_TYPE ("tcp_port", VIR_CONF_STRING); tcp_port = p ? strdup (p->str) : tcp_port; + +#ifdef HAVE_POLKIT + p = virConfGetValue (conf, "unix_sock_polkit"); + CHECK_TYPE ("unix_auth_polkit", VIR_CONF_LONG); + unix_sock_polkit = p ? p->l : unix_sock_polkit; + if (unix_sock_polkit) + unix_sock_rw_perms = 0777; + else + unix_sock_rw_perms = 0700; +#endif p = virConfGetValue (conf, "unix_sock_group"); CHECK_TYPE ("unix_sock_group", VIR_CONF_STRING); diff -r a41065295371 qemud/remote.c --- a/qemud/remote.c Tue Sep 18 22:28:57 2007 -0400 +++ b/qemud/remote.c Tue Sep 18 22:30:19 2007 -0400 @@ -266,7 +266,8 @@ remoteDispatchClientRequest (struct qemu client->mode = QEMUD_MODE_TX_PACKET; client->bufferLength = len; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; + if (client->auth == LIBVIRTD_AUTH_TLS) + client->direction = QEMUD_TLS_DIRECTION_WRITE; } /* An error occurred during the dispatching process itself (ie. not @@ -360,7 +361,8 @@ remoteDispatchError (struct qemud_client client->mode = QEMUD_MODE_TX_PACKET; client->bufferLength = len; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; + if (client->auth == LIBVIRTD_AUTH_TLS) + client->direction = QEMUD_TLS_DIRECTION_WRITE; } /*----- Functions. -----*/