[Libvir] PATCH: 0/10: remote authentication support

This the 3rd iteration of the remote authentication / SASL patches previously provided here: v1: http://www.redhat.com/archives/libvir-list/2007-October/msg00131.html v2: http://www.redhat.com/archives/libvir-list/2007-November/msg00005.html This iteration has incorporate the feedback /bugs identified from previous iterations. It also adds in an API for a callback to collect credentials from the client applications, meaning username+password based authentication is now also supported alongside Kerberos/GSSAPI. I have also re-worked my previous PolicyKit patches: http://www.redhat.com/archives/libvir-list/2007-September/msg00168.html To fit into the generic authentication RPC mechanisms I added for SASL. The resulting incremental patch for PolicyKit is much simpler, and integrates better with client applications. Finally, I have exposed the authentication callback support to python and prototyped its use in virt-manager as a sanity check for the whole API style. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch hooks up the basic authentication RPC calls, and the specific SASL implementation. The SASL impl can be enabled/disable via the configurre script with --without-sasl / --with-sasl - it'll auto-enable it if it finds the headers & libs OK. The sample /etc/sasl2/libvirt.conf file enables the DIGEST-MD5 mechanism by default, since it is by far the easiest to setup for admins. No need for a Kerberos server, or certificates - it just uses username/password which can be set with 'saslpasswd2 -a libvirt [username]' and a list of all active users viewed with 'sasldblistusers2 -a libvirt' There are also example settings for enabling Kerberos (GSSAPI) but this is disabled by default. It requires a file /etc/libvirt/krb5.tab containing a service principle. On some distros you need to set KRB5_KTNAME to point to this file when starting the daemon, so our init script does that. Other distros, the 'keytab' config param in /etc/sasl2/libvirt.conf is actually honoured. With this patch you can successfully authentication client <-> server for any authentication mechansim which doesn't need to prompt the user for credentials. In effect this means it only works for GSSAPI/Kerberos, but the later patches in this series will enable callbacks making the default DIGEST-MD5 auth work. The way auth is controlled, is that if the 'auth' parameter is set on the struct qemud_client object, *NO* rpc call will be processed except for the REMOTE_PROC_AUTH_LIST, SASL_AUTH_INIT, SASL_AUTH_START & SASL_AUTH_STEP calls. If SASL is not compiled in, the latter 3 will send errors back to the caller. Only once authentication is complete, are the other calls allowed. It currently hardcodes use of SASL on the TCP socket. The TLS & UNIX sockets are unchanged. A subsequent patch will make it configurable. b/qemud/libvirtd.sasl | 28 ++ configure.in | 39 ++ include/libvirt/virterror.h | 1 libvirt.spec.in | 4 qemud/Makefile.am | 21 + qemud/internal.h | 8 qemud/libvirtd.init.in | 3 qemud/libvirtd.sysconf | 3 qemud/qemud.c | 28 +- qemud/remote.c | 392 +++++++++++++++++++++++++++- qemud/remote_dispatch_localvars.h | 6 qemud/remote_dispatch_proc_switch.h | 30 ++ qemud/remote_dispatch_prototypes.h | 4 qemud/remote_protocol.c | 87 ++++++ qemud/remote_protocol.h | 78 +++++ qemud/remote_protocol.x | 50 +++ src/Makefile.am | 3 src/remote_internal.c | 498 +++++++++++++++++++++++++++++++----- src/virsh.c | 4 src/virterror.c | 6 tests/Makefile.am | 2 21 files changed, 1205 insertions(+), 90 deletions(-) diff -r 1c3780349e89 configure.in --- a/configure.in Wed Nov 28 12:02:28 2007 -0500 +++ b/configure.in Thu Nov 29 09:24:10 2007 -0500 @@ -353,6 +353,40 @@ AC_CHECK_TYPE(gnutls_session, [#include <gnutls/gnutls.h>]) CFLAGS="$old_cflags" LDFLAGS="$old_ldflags" + + +dnl Cyrus SASL +AC_ARG_WITH(sasl, + [ --with-sasl use cyrus SASL for authentication], + [], + [with_sasl=yes]) + +SASL_CFLAGS= +SASL_LIBS= +if test "$with_sasl" != "no"; then + if test "$with_sasl" != "yes"; then + SASL_CFLAGS="-I$with_sasl" + SASL_LIBS="-L$with_sasl" + fi + old_cflags="$CFLAGS" + old_libs="$LIBS" + CFLAGS="$CFLAGS $SASL_CFLAGS" + LIBS="$LIBS $SASL_LIBS" + AC_CHECK_HEADER([sasl/sasl.h], + [], + AC_MSG_ERROR([You must install the Cyrus SASL development package in order to compile libvirt])) + AC_CHECK_LIB(sasl2, sasl_client_init, + [], + [AC_MSG_ERROR([You must install the Cyrus SASL library in order to compile and run libvirt])]) + CFLAGS="$old_cflags" + LIBS="$old_libs" + SASL_LIBS="$SASL_LIBS -lsasl2" + AC_DEFINE_UNQUOTED(HAVE_SASL, 1, [whether Cyrus SASL is available for authentication]) +fi +AM_CONDITIONAL(HAVE_SASL, [test "$with_sasl" != "no"]) +AC_SUBST(SASL_CFLAGS) +AC_SUBST(SASL_LIBS) + dnl Avahi library @@ -564,6 +598,11 @@ AC_MSG_NOTICE([]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) AC_MSG_NOTICE([ gnutls: $GNUTLS_CFLAGS $GNUTLS_LIBS]) +if test "$with_sasl" != "no" ; then +AC_MSG_NOTICE([ sasl: $SASL_CFLAGS $SASL_LIBS]) +else +AC_MSG_NOTICE([ sasl: no]) +fi if test "$with_avahi" = "yes" ; then AC_MSG_NOTICE([ avahi: $AVAHI_CFLAGS $AVAHI_LIBS]) else diff -r 1c3780349e89 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Wed Nov 28 12:02:28 2007 -0500 +++ b/include/libvirt/virterror.h Thu Nov 29 09:24:10 2007 -0500 @@ -131,6 +131,7 @@ typedef enum { VIR_ERR_NO_DOMAIN, /* domain not found or unexpectedly disappeared */ VIR_ERR_NO_NETWORK, /* network not found */ VIR_ERR_INVALID_MAC, /* invalid MAC adress */ + VIR_ERR_AUTH_FAILED, /* authentication failed */ } virErrorNumber; /** diff -r 1c3780349e89 libvirt.spec.in --- a/libvirt.spec.in Wed Nov 28 12:02:28 2007 -0500 +++ b/libvirt.spec.in Thu Nov 29 09:24:10 2007 -0500 @@ -16,6 +16,8 @@ Requires: dnsmasq Requires: dnsmasq Requires: bridge-utils Requires: iptables +Requires: cyrus-sasl +Requires: cyrus-sasl-gssapi BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -26,6 +28,7 @@ BuildRequires: dnsmasq BuildRequires: dnsmasq BuildRequires: bridge-utils BuildRequires: qemu +BuildRequires: cyrus-sasl-devel Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -132,6 +135,7 @@ fi %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd %config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf %config(noreplace) %{_sysconfdir}/libvirt/qemu.conf +%config(noreplace) %{_sysconfdir}/sasl2/libvirt.conf %dir %{_datadir}/libvirt/ %dir %{_datadir}/libvirt/networks/ %{_datadir}/libvirt/networks/default.xml diff -r 1c3780349e89 qemud/Makefile.am --- a/qemud/Makefile.am Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/Makefile.am Thu Nov 29 09:24:10 2007 -0500 @@ -17,6 +17,7 @@ EXTRA_DIST = libvirtd.init.in libvirtd.s remote_dispatch_localvars.h \ remote_dispatch_proc_switch.h \ mdns.c mdns.h \ + libvirtd.sasl \ $(conf_DATA) libvirtd_SOURCES = \ @@ -28,14 +29,14 @@ libvirtd_SOURCES = \ #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L libvirtd_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include \ - $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) \ + $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \ $(WARN_CFLAGS) -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ -DQEMUD_PID_FILE="\"$(QEMUD_PID_FILE)\"" \ -DREMOTE_PID_FILE="\"$(REMOTE_PID_FILE)\"" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" -libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) +libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) libvirtd_DEPENDENCIES = ../src/libvirt.la libvirtd_LDADD = ../src/libvirt.la @@ -46,7 +47,7 @@ endif endif default_xml_dest = libvirt/qemu/networks/default.xml -install-data-local: install-init +install-data-local: install-init install-data-sasl mkdir -p $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart $(INSTALL_DATA) $(srcdir)/default-network.xml \ $(DESTDIR)$(sysconfdir)/$(default_xml_dest) @@ -59,7 +60,7 @@ install-data-local: install-init mkdir -p $(DESTDIR)$(localstatedir)/run/libvirt mkdir -p $(DESTDIR)$(localstatedir)/lib/libvirt -uninstall-local: uninstall-init +uninstall-local:: uninstall-init uninstall-data-sasl rm -f $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml rm -f $(DESTDIR)$(sysconfdir)/$(default_xml_dest) rmdir $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart || : @@ -67,6 +68,18 @@ uninstall-local: uninstall-init rmdir $(DESTDIR)$(localstatedir)/run/libvirt || : rmdir $(DESTDIR)$(localstatedir)/lib/libvirt || : +if HAVE_SASL +install-data-sasl:: install-init + mkdir -p $(DESTDIR)$(sysconfdir)/sasl2/ + $(INSTALL_DATA) $(srcdir)/libvirtd.sasl $(DESTDIR)$(sysconfdir)/sasl2/libvirt.conf + +uninstall-data-sasl:: install-init + rm -f $(DESTDIR)$(sysconfdir)/sasl2/libvirt.conf + rmdir $(DESTDIR)$(sysconfdir)/sasl2/ +else +install-data-sasl: +uninstall-data-sasl: +endif if RPCGEN .x.c: diff -r 1c3780349e89 qemud/internal.h --- a/qemud/internal.h Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/internal.h Thu Nov 29 09:24:10 2007 -0500 @@ -28,6 +28,9 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include "../src/gnutls_1_0_compat.h" +#if HAVE_SASL +#include <sasl/sasl.h> +#endif #ifdef HAVE_SYS_SYSLIMITS_H #include <sys/syslimits.h> @@ -91,6 +94,10 @@ struct qemud_client { int tls; gnutls_session_t session; enum qemud_tls_direction direction; + int auth; +#if HAVE_SASL + sasl_conn_t *saslconn; +#endif unsigned int incomingSerial; unsigned int outgoingSerial; @@ -116,6 +123,7 @@ struct qemud_socket { int readonly; /* If set, TLS is required on this socket. */ int tls; + int auth; int port; struct qemud_socket *next; }; diff -r 1c3780349e89 qemud/libvirtd.init.in --- a/qemud/libvirtd.init.in Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/libvirtd.init.in Thu Nov 29 09:24:10 2007 -0500 @@ -37,6 +37,7 @@ PROCESS=libvirtd LIBVIRTD_CONFIG= LIBVIRTD_ARGS= +KRB5_KTNAME=/etc/libvirt/krb5.tab test -f @sysconfdir@/sysconfig/libvirtd && . @sysconfdir@/sysconfig/libvirtd @@ -50,7 +51,7 @@ RETVAL=0 start() { echo -n $"Starting $SERVICE daemon: " - daemon --check $SERVICE $PROCESS --daemon $LIBVIRTD_CONFIG_ARGS $LIBVIRTD_ARGS + KRB5_KTNAME=$KRB5_KTNAME daemon --check $SERVICE $PROCESS --daemon $LIBVIRTD_CONFIG_ARGS $LIBVIRTD_ARGS RETVAL=$? echo [ $RETVAL -eq 0 ] && touch @localstatedir@/lock/subsys/$SERVICE diff -r 1c3780349e89 qemud/libvirtd.sasl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qemud/libvirtd.sasl Thu Nov 29 09:24:10 2007 -0500 @@ -0,0 +1,28 @@ +# If you want to use the non-TLS socket, then you *must* include +# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only +# ones that can offer session encryption as well as authentication. +# +# If you're only using TLS, then you can turn on any mechanisms +# you like for authentication, because TLS provides the encryption +# +# Default to a simple username+password mechanism +mech_list: digest-md5 + +# Before you can use GSSAPI, you need a service principle on the +# KDC server for libvirt, and that to be exported to the keytab +# file listed below +#mech_list: gssapi +# +# You can also list many mechanisms at once, then the user can choose +# by adding '?auth=sasl.gssapi' to their libvirt URI, eg +# qemu+tcp://hostname/system?auth=sasl.gssapi +#mech_list: digest-md5 gssapi + +# MIT kerberos ignores this option & needs KRB5_KTNAME env var. +# May be useful for other non-Linux OS though.... +keytab: /etc/libvirt/krb5.tab + +# If using digest-md5 for username/passwds, then this is the file +# containing the passwds. Use 'saslpasswd2 -a libvirt [username]' +# to add entries, and 'sasldblistusers2 -a libvirt' to browse it +sasldb_path: /etc/libvirt/passwd.db diff -r 1c3780349e89 qemud/libvirtd.sysconf --- a/qemud/libvirtd.sysconf Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/libvirtd.sysconf Thu Nov 29 09:24:10 2007 -0500 @@ -4,3 +4,6 @@ # Listen for TCP/IP connections # NB. must setup TLS/SSL keys prior to using this #LIBVIRTD_ARGS="--listen" + +# Override Kerberos service keytab for SASL/GSSAPI +#KRB5_KTNAME=/etc/libvirt/krb5.tab diff -r 1c3780349e89 qemud/qemud.c --- a/qemud/qemud.c Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/qemud.c Thu Nov 29 09:24:10 2007 -0500 @@ -577,7 +577,8 @@ static int static int remoteListenTCP (struct qemud_server *server, const char *port, - int tls) + int tls, + int auth) { int fds[2]; int nfds = 0; @@ -606,6 +607,7 @@ remoteListenTCP (struct qemud_server *se sock->fd = fds[i]; sock->tls = tls; + sock->auth = auth; if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0) return -1; @@ -701,6 +703,9 @@ static struct qemud_server *qemudInitial struct qemud_socket *sock; char sockname[PATH_MAX]; char roSockname[PATH_MAX]; +#if HAVE_SASL + int err; +#endif /* HAVE_SASL */ if (!(server = calloc(1, sizeof(struct qemud_server)))) { qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); @@ -730,15 +735,28 @@ static struct qemud_server *qemudInitial virStateInitialize(); +#if HAVE_SASL + if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { + qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", + sasl_errstring(err, NULL, NULL)); + goto cleanup; + } +#endif + if (ipsock) { - if (listen_tcp && remoteListenTCP (server, tcp_port, 0) < 0) +#if HAVE_SASL + if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_SASL) < 0) goto cleanup; +#else + if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_NONE) < 0) + goto cleanup; +#endif if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, 1) < 0) + if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0) goto cleanup; } } @@ -1048,6 +1066,7 @@ static int qemudDispatchServer(struct qe client->fd = fd; client->readonly = sock->readonly; client->tls = sock->tls; + client->auth = sock->auth; memcpy (&client->addr, &addr, sizeof addr); client->addrlen = addrlen; @@ -1128,6 +1147,9 @@ static void qemudDispatchClientFailure(s if (client->conn) virConnectClose(client->conn); +#if HAVE_SASL + if (client->saslconn) sasl_dispose(&client->saslconn); +#endif if (client->tls && client->session) gnutls_deinit (client->session); close(client->fd); free(client); diff -r 1c3780349e89 qemud/remote.c --- a/qemud/remote.c Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote.c Thu Nov 29 09:24:10 2007 -0500 @@ -52,6 +52,8 @@ #define DEBUG 0 +#define REMOTE_DEBUG(fmt,...) qemudDebug("REMOTE: " fmt, __VA_ARGS__) + static void remoteDispatchError (struct qemud_client *client, remote_message_header *req, const char *fmt, ...) @@ -116,6 +118,21 @@ remoteDispatchClientRequest (struct qemu (int) req.status); xdr_destroy (&xdr); return; + } + + /* If client is marked as needing auth, don't allow any RPC ops, + * except for authentication ones + */ + if (client->auth) { + if (req.proc != REMOTE_PROC_AUTH_LIST && + req.proc != REMOTE_PROC_AUTH_SASL_INIT && + req.proc != REMOTE_PROC_AUTH_SASL_START && + req.proc != REMOTE_PROC_AUTH_SASL_STEP + ) { + remoteDispatchError (client, &req, "authentication required"); + xdr_destroy (&xdr); + return; + } } /* Based on the procedure number, dispatch. In future we may base @@ -275,23 +292,14 @@ remoteDispatchClientRequest (struct qemu * reply. */ static void -remoteDispatchError (struct qemud_client *client, - remote_message_header *req, - const char *fmt, ...) +remoteDispatchSendError (struct qemud_client *client, + remote_message_header *req, + int code, const char *msg) { remote_message_header rep; remote_error error; - va_list args; - char msgbuf[1024]; - char *msg = msgbuf; XDR xdr; int len; - - va_start (args, fmt); - vsnprintf (msgbuf, sizeof msgbuf, fmt, args); - va_end (args); - - qemudDebug ("%s", msgbuf); /* Future versions of the protocol may use different vers or prog. Try * our hardest to send back a message that such clients could see. @@ -313,12 +321,12 @@ remoteDispatchError (struct qemud_client } /* Construct the error. */ - error.code = VIR_ERR_RPC; + error.code = code; error.domain = VIR_FROM_REMOTE; - error.message = &msg; + error.message = (char**)&msg; error.level = VIR_ERR_ERROR; error.dom = NULL; - error.str1 = &msg; + error.str1 = (char**)&msg; error.str2 = NULL; error.str3 = NULL; error.int1 = 0; @@ -363,6 +371,31 @@ remoteDispatchError (struct qemud_client client->bufferOffset = 0; if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; } + +static void +remoteDispatchFailAuth (struct qemud_client *client, + remote_message_header *req) +{ + remoteDispatchSendError (client, req, VIR_ERR_AUTH_FAILED, "authentication failed"); +} + +static void +remoteDispatchError (struct qemud_client *client, + remote_message_header *req, + const char *fmt, ...) +{ + va_list args; + char msgbuf[1024]; + char *msg = msgbuf; + + va_start (args, fmt); + vsnprintf (msgbuf, sizeof msgbuf, fmt, args); + va_end (args); + + remoteDispatchSendError (client, req, VIR_ERR_RPC, msg); +} + + /*----- Functions. -----*/ @@ -1946,6 +1979,335 @@ remoteDispatchNumOfNetworks (struct qemu return 0; } + +static int +remoteDispatchAuthList (struct qemud_client *client, + remote_message_header *req ATTRIBUTE_UNUSED, + void *args ATTRIBUTE_UNUSED, + remote_auth_list_ret *ret) +{ + ret->types.types_len = 1; + if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (remote_auth_type))) == NULL) { + remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, "auth types"); + return -2; + } + ret->types.types_val[0] = client->auth; + return 0; +} + + +#if HAVE_SASL +/* + * NB, keep in sync with similar method in src/remote_internal.c + */ +static char *addrToString(struct qemud_client *client, + remote_message_header *req, + struct sockaddr_storage *sa, socklen_t salen) { + char host[1024], port[20]; + char *addr; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + remoteDispatchError(client, req, + "Cannot resolve address %d: %s", err, gai_strerror(err)); + return NULL; + } + + addr = malloc(strlen(host) + 1 + strlen(port) + 1); + if (!addr) { + remoteDispatchError(client, req, "cannot allocate address"); + return NULL; + } + + strcpy(addr, host); + strcat(addr, ";"); + strcat(addr, port); + return addr; +} + + +/* + * Initializes the SASL session in prepare for authentication + * and gives the client a list of allowed mechansims to choose + * + * XXX callbacks for stuff like password verification ? + */ +static int +remoteDispatchAuthSaslInit (struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_sasl_init_ret *ret) +{ + const char *mechlist = NULL; + int err; + struct sockaddr_storage sa; + socklen_t salen; + char *localAddr, *remoteAddr; + + REMOTE_DEBUG("Initialize SASL auth %d", client->fd); + if (client->auth != REMOTE_AUTH_SASL || + client->saslconn != NULL) { + qemudLog(QEMUD_ERR, "client tried invalid SASL init request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + /* Get local address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getsockname(client->fd, (struct sockaddr*)&sa, &salen) < 0) { + remoteDispatchError(client, req, "failed to get sock address %d (%s)", + errno, strerror(errno)); + return -2; + } + if ((localAddr = addrToString(client, req, &sa, salen)) == NULL) { + return -2; + } + + /* Get remote address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getpeername(client->fd, (struct sockaddr*)&sa, &salen) < 0) { + remoteDispatchError(client, req, "failed to get peer address %d (%s)", + errno, strerror(errno)); + free(localAddr); + return -2; + } + if ((remoteAddr = addrToString(client, req, &sa, salen)) == NULL) { + free(localAddr); + return -2; + } + + err = sasl_server_new("libvirt", + NULL, /* FQDN - just delegates to gethostname */ + NULL, /* User realm */ + localAddr, + remoteAddr, + NULL, /* XXX Callbacks */ + SASL_SUCCESS_DATA, + &client->saslconn); + free(localAddr); + free(remoteAddr); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "sasl context setup failed %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + client->saslconn = NULL; + return -2; + } + + err = sasl_listmech(client->saslconn, + NULL, /* Don't need to set user */ + "", /* Prefix */ + ",", /* Separator */ + "", /* Suffix */ + &mechlist, + NULL, + NULL); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot list SASL mechanisms %d (%s)", + err, sasl_errdetail(client->saslconn)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + REMOTE_DEBUG("Available mechanisms for client: '%s'", mechlist); + ret->mechlist = strdup(mechlist); + if (!ret->mechlist) { + qemudLog(QEMUD_ERR, "cannot allocate mechlist"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + + return 0; +} + + +/* + * This starts the SASL authentication negotiation. + */ +static int +remoteDispatchAuthSaslStart (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_start_args *args, + remote_auth_sasl_start_ret *ret) +{ + const char *serverout; + unsigned int serveroutlen; + int err; + + REMOTE_DEBUG("Start SASL auth %d", client->fd); + if (client->auth != REMOTE_AUTH_SASL || + client->saslconn == NULL) { + qemudLog(QEMUD_ERR, "client tried invalid SASL start request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + REMOTE_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d", + args->mech, args->data.data_len, args->nil); + err = sasl_server_start(client->saslconn, + args->mech, + /* NB, distinction of NULL vs "" is *critical* in SASL */ + args->nil ? NULL : args->data.data_val, + args->data.data_len, + &serverout, + &serveroutlen); + if (err != SASL_OK && + err != SASL_CONTINUE) { + qemudLog(QEMUD_ERR, "sasl start failed %d (%s)", + err, sasl_errdetail(client->saslconn)); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) { + qemudLog(QEMUD_ERR, "sasl start reply data too long %d", serveroutlen); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverout) { + ret->data.data_val = malloc(serveroutlen); + if (!ret->data.data_val) { + remoteDispatchError (client, req, "out of memory allocating array"); + return -2; + } + memcpy(ret->data.data_val, serverout, serveroutlen); + } else { + ret->data.data_val = NULL; + } + ret->nil = serverout ? 0 : 1; + ret->data.data_len = serveroutlen; + + REMOTE_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil); + if (err == SASL_CONTINUE) { + ret->complete = 0; + } else { + REMOTE_DEBUG("Authentication successful %d", client->fd); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } + + return 0; +} + + +static int +remoteDispatchAuthSaslStep (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_step_args *args, + remote_auth_sasl_step_ret *ret) +{ + const char *serverout; + unsigned int serveroutlen; + int err; + + REMOTE_DEBUG("Step SASL auth %d", client->fd); + if (client->auth != REMOTE_AUTH_SASL || + client->saslconn == NULL) { + qemudLog(QEMUD_ERR, "client tried invalid SASL start request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + REMOTE_DEBUG("Using SASL Data %d bytes, nil: %d", + args->data.data_len, args->nil); + err = sasl_server_step(client->saslconn, + /* NB, distinction of NULL vs "" is *critical* in SASL */ + args->nil ? NULL : args->data.data_val, + args->data.data_len, + &serverout, + &serveroutlen); + if (err != SASL_OK && + err != SASL_CONTINUE) { + qemudLog(QEMUD_ERR, "sasl step failed %d (%s)", + err, sasl_errdetail(client->saslconn)); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + + if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) { + qemudLog(QEMUD_ERR, "sasl step reply data too long %d", serveroutlen); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverout) { + ret->data.data_val = malloc(serveroutlen); + if (!ret->data.data_val) { + remoteDispatchError (client, req, "out of memory allocating array"); + return -2; + } + memcpy(ret->data.data_val, serverout, serveroutlen); + } else { + ret->data.data_val = NULL; + } + ret->nil = serverout ? 0 : 1; + ret->data.data_len = serveroutlen; + + REMOTE_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil); + if (err == SASL_CONTINUE) { + ret->complete = 0; + } else { + REMOTE_DEBUG("Authentication successful %d", client->fd); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } + + return 0; +} + + +#else /* HAVE_SASL */ +static int +remoteDispatchAuthSaslInit (struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported SASL init request"); + remoteDispatchFailAuth(client, req); + return -1; +} + +static int +remoteDispatchAuthSaslStart (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, + remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported SASL start request"); + remoteDispatchFailAuth(client, req); + return -1; +} + +static int +remoteDispatchAuthSaslStep (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, + remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported SASL step request"); + remoteDispatchFailAuth(client, req); + return -1; +} +#endif /* HAVE_SASL */ + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -r 1c3780349e89 qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote_dispatch_localvars.h Thu Nov 29 09:24:10 2007 -0500 @@ -9,6 +9,7 @@ remote_list_defined_domains_ret lv_remot remote_list_defined_domains_ret lv_remote_list_defined_domains_ret; remote_get_capabilities_ret lv_remote_get_capabilities_ret; remote_domain_set_max_memory_args lv_remote_domain_set_max_memory_args; +remote_auth_sasl_init_ret lv_remote_auth_sasl_init_ret; remote_domain_get_os_type_args lv_remote_domain_get_os_type_args; remote_domain_get_os_type_ret lv_remote_domain_get_os_type_ret; remote_domain_get_autostart_args lv_remote_domain_get_autostart_args; @@ -36,6 +37,8 @@ remote_domain_create_linux_args lv_remot remote_domain_create_linux_args lv_remote_domain_create_linux_args; remote_domain_create_linux_ret lv_remote_domain_create_linux_ret; remote_domain_set_scheduler_parameters_args lv_remote_domain_set_scheduler_parameters_args; +remote_auth_sasl_start_args lv_remote_auth_sasl_start_args; +remote_auth_sasl_start_ret lv_remote_auth_sasl_start_ret; remote_domain_interface_stats_args lv_remote_domain_interface_stats_args; remote_domain_interface_stats_ret lv_remote_domain_interface_stats_ret; remote_domain_get_max_vcpus_args lv_remote_domain_get_max_vcpus_args; @@ -50,6 +53,8 @@ remote_network_get_bridge_name_args lv_r remote_network_get_bridge_name_args lv_remote_network_get_bridge_name_args; remote_network_get_bridge_name_ret lv_remote_network_get_bridge_name_ret; remote_domain_destroy_args lv_remote_domain_destroy_args; +remote_auth_sasl_step_args lv_remote_auth_sasl_step_args; +remote_auth_sasl_step_ret lv_remote_auth_sasl_step_ret; remote_domain_migrate_finish_args lv_remote_domain_migrate_finish_args; remote_domain_migrate_finish_ret lv_remote_domain_migrate_finish_ret; remote_domain_get_vcpus_args lv_remote_domain_get_vcpus_args; @@ -74,6 +79,7 @@ remote_network_set_autostart_args lv_rem remote_network_set_autostart_args lv_remote_network_set_autostart_args; remote_network_get_autostart_args lv_remote_network_get_autostart_args; remote_network_get_autostart_ret lv_remote_network_get_autostart_ret; +remote_auth_list_ret lv_remote_auth_list_ret; remote_domain_core_dump_args lv_remote_domain_core_dump_args; remote_domain_get_max_memory_args lv_remote_domain_get_max_memory_args; remote_domain_get_max_memory_ret lv_remote_domain_get_max_memory_ret; diff -r 1c3780349e89 qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote_dispatch_proc_switch.h Thu Nov 29 09:24:10 2007 -0500 @@ -2,6 +2,36 @@ * Do not edit this file. Any changes you make will be lost. */ +case REMOTE_PROC_AUTH_LIST: + fn = (dispatch_fn) remoteDispatchAuthList; + ret_filter = (xdrproc_t) xdr_remote_auth_list_ret; + ret = (char *) &lv_remote_auth_list_ret; + memset (&lv_remote_auth_list_ret, 0, sizeof lv_remote_auth_list_ret); + break; +case REMOTE_PROC_AUTH_SASL_INIT: + fn = (dispatch_fn) remoteDispatchAuthSaslInit; + ret_filter = (xdrproc_t) xdr_remote_auth_sasl_init_ret; + ret = (char *) &lv_remote_auth_sasl_init_ret; + memset (&lv_remote_auth_sasl_init_ret, 0, sizeof lv_remote_auth_sasl_init_ret); + break; +case REMOTE_PROC_AUTH_SASL_START: + fn = (dispatch_fn) remoteDispatchAuthSaslStart; + args_filter = (xdrproc_t) xdr_remote_auth_sasl_start_args; + args = (char *) &lv_remote_auth_sasl_start_args; + memset (&lv_remote_auth_sasl_start_args, 0, sizeof lv_remote_auth_sasl_start_args); + ret_filter = (xdrproc_t) xdr_remote_auth_sasl_start_ret; + ret = (char *) &lv_remote_auth_sasl_start_ret; + memset (&lv_remote_auth_sasl_start_ret, 0, sizeof lv_remote_auth_sasl_start_ret); + break; +case REMOTE_PROC_AUTH_SASL_STEP: + fn = (dispatch_fn) remoteDispatchAuthSaslStep; + args_filter = (xdrproc_t) xdr_remote_auth_sasl_step_args; + args = (char *) &lv_remote_auth_sasl_step_args; + memset (&lv_remote_auth_sasl_step_args, 0, sizeof lv_remote_auth_sasl_step_args); + ret_filter = (xdrproc_t) xdr_remote_auth_sasl_step_ret; + ret = (char *) &lv_remote_auth_sasl_step_ret; + memset (&lv_remote_auth_sasl_step_ret, 0, sizeof lv_remote_auth_sasl_step_ret); + break; case REMOTE_PROC_CLOSE: fn = (dispatch_fn) remoteDispatchClose; break; diff -r 1c3780349e89 qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote_dispatch_prototypes.h Thu Nov 29 09:24:10 2007 -0500 @@ -2,6 +2,10 @@ * Do not edit this file. Any changes you make will be lost. */ +static int remoteDispatchAuthList (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); +static int remoteDispatchAuthSaslInit (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); +static int remoteDispatchAuthSaslStart (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); +static int remoteDispatchAuthSaslStep (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); static int remoteDispatchClose (struct qemud_client *client, remote_message_header *req, void *args, void *ret); static int remoteDispatchDomainAttachDevice (struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); static int remoteDispatchDomainBlockStats (struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); diff -r 1c3780349e89 qemud/remote_protocol.c --- a/qemud/remote_protocol.c Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote_protocol.c Thu Nov 29 09:24:10 2007 -0500 @@ -100,6 +100,15 @@ xdr_remote_error (XDR *xdrs, remote_erro if (!xdr_int (xdrs, &objp->int2)) return FALSE; if (!xdr_remote_network (xdrs, &objp->net)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_type (XDR *xdrs, remote_auth_type *objp) +{ + + if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } @@ -1222,6 +1231,84 @@ xdr_remote_network_set_autostart_args (X } bool_t +xdr_remote_auth_list_ret (XDR *xdrs, remote_auth_list_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->types.types_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->types.types_len, REMOTE_AUTH_TYPE_LIST_MAX, + sizeof (remote_auth_type), (xdrproc_t) xdr_remote_auth_type)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_init_ret (XDR *xdrs, remote_auth_sasl_init_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->mechlist)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_start_args (XDR *xdrs, remote_auth_sasl_start_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_remote_nonnull_string (xdrs, &objp->mech)) + return FALSE; + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_start_ret (XDR *xdrs, remote_auth_sasl_start_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_int (xdrs, &objp->complete)) + return FALSE; + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_step_args (XDR *xdrs, remote_auth_sasl_step_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_step_ret (XDR *xdrs, remote_auth_sasl_step_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_int (xdrs, &objp->complete)) + return FALSE; + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff -r 1c3780349e89 qemud/remote_protocol.h --- a/qemud/remote_protocol.h Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote_protocol.h Thu Nov 29 09:24:10 2007 -0500 @@ -28,6 +28,8 @@ typedef remote_nonnull_string *remote_st #define REMOTE_MIGRATE_COOKIE_MAX 256 #define REMOTE_NETWORK_NAME_LIST_MAX 256 #define REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX 16 +#define REMOTE_AUTH_SASL_DATA_MAX 65536 +#define REMOTE_AUTH_TYPE_LIST_MAX 20 typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -63,6 +65,12 @@ struct remote_error { }; typedef struct remote_error remote_error; +enum remote_auth_type { + REMOTE_AUTH_NONE = 0, + REMOTE_AUTH_SASL = 1, +}; +typedef enum remote_auth_type remote_auth_type; + struct remote_vcpu_info { u_int number; int state; @@ -659,6 +667,58 @@ struct remote_network_set_autostart_args int autostart; }; typedef struct remote_network_set_autostart_args remote_network_set_autostart_args; + +struct remote_auth_list_ret { + struct { + u_int types_len; + remote_auth_type *types_val; + } types; +}; +typedef struct remote_auth_list_ret remote_auth_list_ret; + +struct remote_auth_sasl_init_ret { + remote_nonnull_string mechlist; +}; +typedef struct remote_auth_sasl_init_ret remote_auth_sasl_init_ret; + +struct remote_auth_sasl_start_args { + remote_nonnull_string mech; + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_start_args remote_auth_sasl_start_args; + +struct remote_auth_sasl_start_ret { + int complete; + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_start_ret remote_auth_sasl_start_ret; + +struct remote_auth_sasl_step_args { + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_step_args remote_auth_sasl_step_args; + +struct remote_auth_sasl_step_ret { + int complete; + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_step_ret remote_auth_sasl_step_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -728,6 +788,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63, REMOTE_PROC_DOMAIN_BLOCK_STATS = 64, REMOTE_PROC_DOMAIN_INTERFACE_STATS = 65, + REMOTE_PROC_AUTH_LIST = 66, + REMOTE_PROC_AUTH_SASL_INIT = 67, + REMOTE_PROC_AUTH_SASL_START = 68, + REMOTE_PROC_AUTH_SASL_STEP = 69, }; typedef enum remote_procedure remote_procedure; @@ -766,6 +830,7 @@ extern bool_t xdr_remote_domain (XDR *, extern bool_t xdr_remote_domain (XDR *, remote_domain*); extern bool_t xdr_remote_network (XDR *, remote_network*); extern bool_t xdr_remote_error (XDR *, remote_error*); +extern bool_t xdr_remote_auth_type (XDR *, remote_auth_type*); extern bool_t xdr_remote_vcpu_info (XDR *, remote_vcpu_info*); extern bool_t xdr_remote_sched_param_value (XDR *, remote_sched_param_value*); extern bool_t xdr_remote_sched_param (XDR *, remote_sched_param*); @@ -864,6 +929,12 @@ extern bool_t xdr_remote_network_get_au extern bool_t xdr_remote_network_get_autostart_args (XDR *, remote_network_get_autostart_args*); extern bool_t xdr_remote_network_get_autostart_ret (XDR *, remote_network_get_autostart_ret*); extern bool_t xdr_remote_network_set_autostart_args (XDR *, remote_network_set_autostart_args*); +extern bool_t xdr_remote_auth_list_ret (XDR *, remote_auth_list_ret*); +extern bool_t xdr_remote_auth_sasl_init_ret (XDR *, remote_auth_sasl_init_ret*); +extern bool_t xdr_remote_auth_sasl_start_args (XDR *, remote_auth_sasl_start_args*); +extern bool_t xdr_remote_auth_sasl_start_ret (XDR *, remote_auth_sasl_start_ret*); +extern bool_t xdr_remote_auth_sasl_step_args (XDR *, remote_auth_sasl_step_args*); +extern bool_t xdr_remote_auth_sasl_step_ret (XDR *, remote_auth_sasl_step_ret*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_direction (XDR *, remote_message_direction*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -878,6 +949,7 @@ extern bool_t xdr_remote_domain (); extern bool_t xdr_remote_domain (); extern bool_t xdr_remote_network (); extern bool_t xdr_remote_error (); +extern bool_t xdr_remote_auth_type (); extern bool_t xdr_remote_vcpu_info (); extern bool_t xdr_remote_sched_param_value (); extern bool_t xdr_remote_sched_param (); @@ -976,6 +1048,12 @@ extern bool_t xdr_remote_network_get_aut extern bool_t xdr_remote_network_get_autostart_args (); extern bool_t xdr_remote_network_get_autostart_ret (); extern bool_t xdr_remote_network_set_autostart_args (); +extern bool_t xdr_remote_auth_list_ret (); +extern bool_t xdr_remote_auth_sasl_init_ret (); +extern bool_t xdr_remote_auth_sasl_start_args (); +extern bool_t xdr_remote_auth_sasl_start_ret (); +extern bool_t xdr_remote_auth_sasl_step_args (); +extern bool_t xdr_remote_auth_sasl_step_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_direction (); extern bool_t xdr_remote_message_status (); diff -r 1c3780349e89 qemud/remote_protocol.x --- a/qemud/remote_protocol.x Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote_protocol.x Thu Nov 29 09:24:10 2007 -0500 @@ -80,6 +80,12 @@ const REMOTE_NETWORK_NAME_LIST_MAX = 256 /* Upper limit on list of scheduler parameters. */ const REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX = 16; + +/* Upper limit on SASL auth negotiation packet */ +const REMOTE_AUTH_SASL_DATA_MAX = 65536; + +/* Maximum number of auth types */ +const REMOTE_AUTH_TYPE_LIST_MAX = 20; /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -122,6 +128,13 @@ struct remote_error { int int2; remote_network net; }; + +/* Authentication types available thus far.... */ +enum remote_auth_type { + REMOTE_AUTH_NONE = 0, + REMOTE_AUTH_SASL = 1 +}; + /* Wire encoding of virVcpuInfo. */ struct remote_vcpu_info { @@ -610,6 +623,37 @@ struct remote_network_set_autostart_args struct remote_network_set_autostart_args { remote_nonnull_network net; int autostart; +}; + +struct remote_auth_list_ret { + remote_auth_type types<REMOTE_AUTH_TYPE_LIST_MAX>; +}; + +struct remote_auth_sasl_init_ret { + remote_nonnull_string mechlist; +}; + +struct remote_auth_sasl_start_args { + remote_nonnull_string mech; + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_sasl_start_ret { + int complete; + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_sasl_step_args { + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_sasl_step_ret { + int complete; + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; }; /*----- Protocol. -----*/ @@ -683,7 +727,11 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM = 62, REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63, REMOTE_PROC_DOMAIN_BLOCK_STATS = 64, - REMOTE_PROC_DOMAIN_INTERFACE_STATS = 65 + REMOTE_PROC_DOMAIN_INTERFACE_STATS = 65, + REMOTE_PROC_AUTH_LIST = 66, + REMOTE_PROC_AUTH_SASL_INIT = 67, + REMOTE_PROC_AUTH_SASL_START = 68, + REMOTE_PROC_AUTH_SASL_STEP = 69 }; /* Custom RPC structure. */ diff -r 1c3780349e89 src/Makefile.am --- a/src/Makefile.am Wed Nov 28 12:02:28 2007 -0500 +++ b/src/Makefile.am Thu Nov 29 09:24:10 2007 -0500 @@ -5,6 +5,7 @@ INCLUDES = -I$(top_builddir)/include \ -I@top_srcdir@/qemud \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ + $(SASL_CFLAGS) \ -DBINDIR=\""$(libexecdir)"\" \ -DSBINDIR=\""$(sbindir)"\" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ @@ -24,7 +25,7 @@ EXTRA_DIST = libvirt_sym.version $(conf_ EXTRA_DIST = libvirt_sym.version $(conf_DATA) lib_LTLIBRARIES = libvirt.la -libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) +libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \ -version-info @LIBVIRT_VERSION_INFO@ \ $(COVERAGE_CFLAGS:-f%=-Wc,-f%) diff -r 1c3780349e89 src/remote_internal.c --- a/src/remote_internal.c Wed Nov 28 12:02:28 2007 -0500 +++ b/src/remote_internal.c Thu Nov 29 09:24:10 2007 -0500 @@ -48,6 +48,9 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include "gnutls_1_0_compat.h" +#if HAVE_SASL +#include <sasl/sasl.h> +#endif #include <libxml/uri.h> #include "internal.h" @@ -72,6 +75,11 @@ struct private_data { char *type; /* Cached return from remoteType. */ int counter; /* Generates serial numbers for RPC. */ int networkOnly; /* Only used for network API */ + char *hostname; /* Original hostname */ + FILE *debugLog; /* Debug remote protocol */ +#if HAVE_SASL + sasl_conn_t *saslconn; /* SASL context */ +#endif }; #define GET_PRIVATE(conn,retcode) \ @@ -90,7 +98,20 @@ struct private_data { return (retcode); \ } -static int call (virConnectPtr conn, struct private_data *priv, int in_open, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret); +enum { + REMOTE_CALL_IN_OPEN = 1, + REMOTE_CALL_QUIET_MISSING_RPC = 2, +}; + + +static int call (virConnectPtr conn, struct private_data *priv, + int flags, int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret); +static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open); +#if HAVE_SASL +static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open); +#endif static void error (virConnectPtr conn, virErrorNumber code, const char *info); static void server_error (virConnectPtr conn, remote_error *err); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); @@ -121,7 +142,7 @@ static void query_free (struct query_fie /* GnuTLS functions used by remoteOpen. */ static int initialise_gnutls (virConnectPtr conn); -static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, int sock, int no_verify, const char *hostname); +static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, struct private_data *priv, int no_verify); static int remoteStartup(void) @@ -132,6 +153,22 @@ remoteStartup(void) inside_daemon = 1; return 0; } + +#if HAVE_SASL +static void +remoteDebug(struct private_data *priv, const char *msg,...) +{ + va_list args; + if (priv->debugLog == NULL) + return; + + va_start(args, msg); + vfprintf(priv->debugLog, msg, args); + va_end(args); + fprintf(priv->debugLog, "\n"); +} +#endif /* HAVE_SASL */ + /** * remoteFindServerPath: @@ -297,7 +334,7 @@ doRemoteOpen (virConnectPtr conn, struct * get freed in the failed: path. */ char *name = 0, *command = 0, *sockname = 0, *netcat = 0, *username = 0; - char *server = 0, *port = 0; + char *port = 0; int no_verify = 0, no_tty = 0; char **cmd_argv = 0; @@ -305,12 +342,6 @@ doRemoteOpen (virConnectPtr conn, struct int retcode = VIR_DRV_OPEN_ERROR; /* Remote server defaults to "localhost" if not specified. */ - server = strdup (uri->server ? uri->server : "localhost"); - if (!server) { - out_of_memory: - error (conn, VIR_ERR_NO_MEMORY, "duplicating server name"); - goto failed; - } if (uri->port != 0) { if (asprintf (&port, "%d", uri->port) == -1) goto out_of_memory; } else if (transport == trans_tls) { @@ -325,6 +356,12 @@ doRemoteOpen (virConnectPtr conn, struct } else port = NULL; /* Port not used for unix, ext. */ + + priv->hostname = strdup (uri->server ? uri->server : "localhost"); + if (!priv->hostname) { + error (NULL, VIR_ERR_NO_MEMORY, "allocating priv->hostname"); + goto failed; + } if (uri->user) { username = strdup (uri->user); if (!username) goto out_of_memory; @@ -367,6 +404,12 @@ doRemoteOpen (virConnectPtr conn, struct } else if (strcasecmp (var->name, "no_tty") == 0) { no_tty = atoi (var->value); var->ignore = 1; + } else if (strcasecmp (var->name, "debug") == 0) { + if (var->value && + strcasecmp(var->value, "stdout") == 0) + priv->debugLog = stdout; + else + priv->debugLog = stderr; } #if DEBUG else @@ -436,7 +479,7 @@ doRemoteOpen (virConnectPtr conn, struct memset (&hints, 0, sizeof hints); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; - int e = getaddrinfo (server, port, &hints, &res); + int e = getaddrinfo (priv->hostname, port, &hints, &res); if (e != 0) { error (conn, VIR_ERR_INVALID_ARG, gai_strerror (e)); goto failed; @@ -476,7 +519,7 @@ doRemoteOpen (virConnectPtr conn, struct if (priv->uses_tls) { priv->session = negotiate_gnutls_on_connection - (conn, priv->sock, no_verify, server); + (conn, priv, no_verify); if (!priv->session) { close (priv->sock); priv->sock = -1; @@ -603,7 +646,7 @@ doRemoteOpen (virConnectPtr conn, struct cmd_argv[j++] = strdup ("-e"); cmd_argv[j++] = strdup ("none"); } - cmd_argv[j++] = strdup (server); + cmd_argv[j++] = strdup (priv->hostname); cmd_argv[j++] = strdup (netcat ? netcat : "nc"); cmd_argv[j++] = strdup ("-U"); cmd_argv[j++] = strdup (sockname ? sockname : LIBVIRTD_PRIV_UNIX_SOCKET); @@ -665,10 +708,15 @@ doRemoteOpen (virConnectPtr conn, struct } } /* switch (transport) */ + + /* Try and authenticate with server */ + if (remoteAuthenticate(conn, priv, 1) == -1) + goto failed; + /* Finally we can call the remote side's open function. */ remote_open_args args = { &name, flags }; - if (call (conn, priv, 1, REMOTE_PROC_OPEN, + if (call (conn, priv, REMOTE_CALL_IN_OPEN, REMOTE_PROC_OPEN, (xdrproc_t) xdr_remote_open_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) goto failed; @@ -676,12 +724,35 @@ doRemoteOpen (virConnectPtr conn, struct /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; - /*FALLTHROUGH*/ + cleanup: + /* Free up the URL and strings. */ + if (name) free (name); + if (command) free (command); + if (sockname) free (sockname); + if (netcat) free (netcat); + if (username) free (username); + if (port) free (port); + if (cmd_argv) { + char **cmd_argv_ptr = cmd_argv; + while (*cmd_argv_ptr) { + free (*cmd_argv_ptr); + cmd_argv_ptr++; + } + free (cmd_argv); + } + + return retcode; + + out_of_memory: + error (NULL, VIR_ERR_NO_MEMORY, "uri params"); + failed: /* Close the socket if we failed. */ - if (retcode != VIR_DRV_OPEN_SUCCESS && priv->sock >= 0) { - if (priv->uses_tls && priv->session) + if (priv->sock >= 0) { + if (priv->uses_tls && priv->session) { gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); + gnutls_deinit (priv->session); + } close (priv->sock); if (priv->pid > 0) { pid_t reap; @@ -693,24 +764,12 @@ doRemoteOpen (virConnectPtr conn, struct } } - /* Free up the URL and strings. */ - if (name) free (name); - if (command) free (command); - if (sockname) free (sockname); - if (netcat) free (netcat); - if (username) free (username); - if (server) free (server); - if (port) free (port); - if (cmd_argv) { - char **cmd_argv_ptr = cmd_argv; - while (*cmd_argv_ptr) { - free (*cmd_argv_ptr); - cmd_argv_ptr++; - } - free (cmd_argv); - } - - return retcode; + if (priv->hostname) { + free (priv->hostname); + priv->hostname = NULL; + } + + goto cleanup; } static int @@ -1017,11 +1076,12 @@ initialise_gnutls (virConnectPtr conn) return 0; } -static int verify_certificate (virConnectPtr conn, gnutls_session_t session, const char *hostname); +static int verify_certificate (virConnectPtr conn, struct private_data *priv, gnutls_session_t session); static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, - int sock, int no_verify, const char *hostname) + struct private_data *priv, + int no_verify) { const int cert_type_priority[3] = { GNUTLS_CRT_X509, @@ -1062,7 +1122,7 @@ negotiate_gnutls_on_connection (virConne } gnutls_transport_set_ptr (session, - (gnutls_transport_ptr_t) (long) sock); + (gnutls_transport_ptr_t) (long) priv->sock); /* Perform the TLS handshake. */ again: @@ -1075,7 +1135,7 @@ negotiate_gnutls_on_connection (virConne } /* Verify certificate. */ - if (verify_certificate (conn, session, hostname) == -1) { + if (verify_certificate (conn, priv, session) == -1) { fprintf (stderr, "remote_internal: failed to verify peer's certificate\n"); if (!no_verify) return NULL; @@ -1110,8 +1170,8 @@ negotiate_gnutls_on_connection (virConne static int verify_certificate (virConnectPtr conn ATTRIBUTE_UNUSED, - gnutls_session_t session, - const char *hostname) + struct private_data *priv, + gnutls_session_t session) { int ret; unsigned int status; @@ -1189,14 +1249,14 @@ verify_certificate (virConnectPtr conn A } if (i == 0) { - if (!gnutls_x509_crt_check_hostname (cert, hostname)) { + if (!gnutls_x509_crt_check_hostname (cert, priv->hostname)) { __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, - VIR_ERR_ERROR, hostname, NULL, NULL, + VIR_ERR_ERROR, priv->hostname, NULL, NULL, 0, 0, "Certificate's owner does not match the hostname (%s)", - hostname); + priv->hostname); gnutls_x509_crt_deinit (cert); return -1; } @@ -1218,8 +1278,14 @@ doRemoteClose (virConnectPtr conn, struc return -1; /* Close socket. */ - if (priv->uses_tls && priv->session) + if (priv->uses_tls && priv->session) { gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); + gnutls_deinit (priv->session); + } +#if HAVE_SASL + if (priv->saslconn) + sasl_dispose (&priv->saslconn); +#endif close (priv->sock); if (priv->pid > 0) { @@ -1230,6 +1296,9 @@ doRemoteClose (virConnectPtr conn, struc continue; } while (reap != -1 && reap != priv->pid); } + + /* Free hostname copy */ + if (priv->hostname) free (priv->hostname); /* See comment for remoteType. */ if (priv->type) free (priv->type); @@ -2751,6 +2820,298 @@ remoteNetworkSetAutostart (virNetworkPtr /*----------------------------------------------------------------------*/ +static int +remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open) +{ + struct remote_auth_list_ret ret; + int err; + + memset(&ret, 0, sizeof ret); + err = call (conn, priv, + REMOTE_CALL_IN_OPEN | REMOTE_CALL_QUIET_MISSING_RPC, + REMOTE_PROC_AUTH_LIST, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_auth_list_ret, (char *) &ret); + if (err == -2) /* Missing RPC - old server - ignore */ + return 0; + + if (err < 0) + return -1; + + if (ret.types.types_len == 0) + return 0; + + switch (ret.types.types_val[0]) { +#if HAVE_SASL + case REMOTE_AUTH_SASL: + if (remoteAuthSASL(conn, priv, in_open) < 0) { + free(ret.types.types_val); + return -1; + } + break; +#endif + + case REMOTE_AUTH_NONE: + /* Nothing todo, hurrah ! */ + break; + + default: + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "unsupported authentication type %d", ret.types.types_val[0]); + free(ret.types.types_val); + return -1; + } + + free(ret.types.types_val); + + return 0; +} + + + +#if HAVE_SASL +/* + * NB, keep in sync with similar method in qemud/remote.c + */ +static char *addrToString(struct sockaddr_storage *sa, socklen_t salen) +{ + char host[1024], port[20]; + char *addr; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + __virRaiseError (NULL, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_NO_MEMORY, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Cannot resolve address %d: %s", err, gai_strerror(err)); + return NULL; + } + + addr = malloc(strlen(host) + 1 + strlen(port) + 1); + if (!addr) { + __virRaiseError (NULL, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_NO_MEMORY, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "address"); + return NULL; + } + + strcpy(addr, host); + strcat(addr, ";"); + strcat(addr, port); + return addr; +} + + +/* Perform the SASL authentication process + * + * XXX negotiate a session encryption layer for non-TLS sockets + * XXX fetch credentials from a libvirt client app callback + * XXX max packet size spec + * XXX better mechanism negotiation ? Ask client app ? + */ +static int +remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open) +{ + sasl_conn_t *saslconn = NULL; + remote_auth_sasl_init_ret iret; + remote_auth_sasl_start_args sargs; + remote_auth_sasl_start_ret sret; + remote_auth_sasl_step_args pargs; + remote_auth_sasl_step_ret pret; + const char *clientout; + char *serverin; + unsigned int clientoutlen, serverinlen; + const char *mech; + int err, complete; + struct sockaddr_storage sa; + socklen_t salen; + char *localAddr, *remoteAddr; + + remoteDebug(priv, "Client initialize SASL authentication"); + /* Sets up the SASL library as a whole */ + err = sasl_client_init(NULL); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "failed to initialize SASL library: %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + return -1; + } + + /* Get local address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getsockname(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "failed to get sock address %d (%s)", + errno, strerror(errno)); + return -1; + } + if ((localAddr = addrToString(&sa, salen)) == NULL) { + return -1; + } + + /* Get remote address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getpeername(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "failed to get peer address %d (%s)", + errno, strerror(errno)); + free(localAddr); + return -1; + } + if ((remoteAddr = addrToString(&sa, salen)) == NULL) { + free(localAddr); + return -1; + } + + /* Setup a handle for being a client */ + err = sasl_client_new("libvirt", + priv->hostname, + localAddr, + remoteAddr, + NULL, /* XXX callbacks */ + SASL_SUCCESS_DATA, + &saslconn); + free(localAddr); + free(remoteAddr); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to create SASL client context: %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + return -1; + } + + /* First call is to inquire about supported mechanisms in the server */ + memset (&iret, 0, sizeof iret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_INIT, + (xdrproc_t) xdr_void, (char *)NULL, + (xdrproc_t) xdr_remote_auth_sasl_init_ret, (char *) &iret) != 0) { + sasl_dispose(&saslconn); + return -1; /* virError already set by call */ + } + + + /* Start the auth negotiation on the client end first */ + remoteDebug(priv, "Client start negotiation mechlist '%s'", iret.mechlist); + err = sasl_client_start(saslconn, + iret.mechlist, + NULL, /* XXX interactions */ + &clientout, + &clientoutlen, + &mech); + if (err != SASL_OK && err != SASL_CONTINUE) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to start SASL negotiation: %d (%s)", + err, sasl_errdetail(saslconn)); + free(iret.mechlist); + sasl_dispose(&saslconn); + return -1; + } + free(iret.mechlist); + + if (clientoutlen > REMOTE_AUTH_SASL_DATA_MAX) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "SASL negotiation data too long: %d bytes", clientoutlen); + sasl_dispose(&saslconn); + return -1; + } + /* NB, distinction of NULL vs "" is *critical* in SASL */ + memset(&sargs, 0, sizeof sargs); + sargs.nil = clientout ? 0 : 1; + sargs.data.data_val = (char*)clientout; + sargs.data.data_len = clientoutlen; + sargs.mech = (char*)mech; + remoteDebug(priv, "Server start negotiation with mech %s. Data %d bytes %p", mech, clientoutlen, clientout); + + /* Now send the initial auth data to the server */ + memset (&sret, 0, sizeof sret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_START, + (xdrproc_t) xdr_remote_auth_sasl_start_args, (char *) &sargs, + (xdrproc_t) xdr_remote_auth_sasl_start_ret, (char *) &sret) != 0) { + sasl_dispose(&saslconn); + return -1; /* virError already set by call */ + } + + complete = sret.complete; + /* NB, distinction of NULL vs "" is *critical* in SASL */ + serverin = sret.nil ? NULL : sret.data.data_val; + serverinlen = sret.data.data_len; + remoteDebug(priv, "Client step result complete: %d. Data %d bytes %p", + complete, serverinlen, serverin); + + /* Loop-the-loop... + * Even if the server has completed, the client must *always* do at least one step + * in this loop to verify the server isn't lieing about something. Mutual auth */ + for (;;) { + err = sasl_client_step(saslconn, + serverin, + serverinlen, + NULL, /* XXX interactions */ + &clientout, + &clientoutlen); + if (serverin) free(serverin); + if (err != SASL_OK && err != SASL_CONTINUE) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed SASL step: %d (%s)", + err, sasl_errdetail(saslconn)); + sasl_dispose(&saslconn); + return -1; + } + remoteDebug(priv, "Client step result %d. Data %d bytes %p", err, clientoutlen, clientout); + + /* Previous server call showed completion & we're now locally complete too */ + if (complete && err == SASL_OK) + break; + + /* Not done, prepare to talk with the server for another iteration */ + /* NB, distinction of NULL vs "" is *critical* in SASL */ + memset(&pargs, 0, sizeof pargs); + pargs.nil = clientout ? 0 : 1; + pargs.data.data_val = (char*)clientout; + pargs.data.data_len = clientoutlen; + remoteDebug(priv, "Server step with %d bytes %p", clientoutlen, clientout); + + memset (&pret, 0, sizeof pret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_STEP, + (xdrproc_t) xdr_remote_auth_sasl_step_args, (char *) &pargs, + (xdrproc_t) xdr_remote_auth_sasl_step_ret, (char *) &pret) != 0) { + sasl_dispose(&saslconn); + return -1; /* virError already set by call */ + } + + complete = pret.complete; + /* NB, distinction of NULL vs "" is *critical* in SASL */ + serverin = pret.nil ? NULL : pret.data.data_val; + serverinlen = pret.data.data_len; + + remoteDebug(priv, "Client step result complete: %d. Data %d bytes %p", + complete, serverinlen, serverin); + + /* This server call shows complete, and earlier client step was OK */ + if (complete && err == SASL_OK) { + if (serverin) free(serverin); + break; + } + } + + remoteDebug(priv, "SASL authentication complete"); + /* XXX keep this around for wire encoding */ + sasl_dispose(&saslconn); + return 0; +} +#endif /* HAVE_SASL */ + +/*----------------------------------------------------------------------*/ + static int really_write (virConnectPtr conn, struct private_data *priv, int in_open, char *bytes, int len); static int really_read (virConnectPtr conn, struct private_data *priv, @@ -2768,7 +3129,7 @@ static int really_read (virConnectPtr co */ static int call (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, + int flags /* if we are in virConnectOpen */, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret) @@ -2793,13 +3154,13 @@ call (virConnectPtr conn, struct private /* Serialise header followed by args. */ xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE); if (!xdr_remote_message_header (&xdr, &hdr)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_remote_message_header"); return -1; } if (!(*args_filter) (&xdr, args)) { - error (in_open ? NULL : conn, VIR_ERR_RPC, "marshalling args"); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "marshalling args"); return -1; } @@ -2815,23 +3176,23 @@ call (virConnectPtr conn, struct private /* Encode the length word. */ xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_ENCODE); if (!xdr_int (&xdr, &len)) { - error (in_open ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word)"); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word)"); return -1; } xdr_destroy (&xdr); /* Send length word followed by header+args. */ - if (really_write (conn, priv, in_open, buffer2, sizeof buffer2) == -1 || - really_write (conn, priv, in_open, buffer, len-4) == -1) + if (really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1 || + really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len-4) == -1) return -1; /* Read and deserialise length word. */ - if (really_read (conn, priv, in_open, buffer2, sizeof buffer2) == -1) + if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1) return -1; xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); if (!xdr_int (&xdr, &len)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word, reply)"); return -1; } @@ -2841,33 +3202,33 @@ call (virConnectPtr conn, struct private len -= 4; if (len < 0 || len > REMOTE_MESSAGE_MAX) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "packet received from server too large"); return -1; } /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, in_open, buffer, len) == -1) + if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len) == -1) return -1; /* Deserialise reply header. */ xdrmem_create (&xdr, buffer, len, XDR_DECODE); if (!xdr_remote_message_header (&xdr, &hdr)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_remote_message_header (reply)"); return -1; } /* Check program, version, etc. are what we expect. */ if (hdr.prog != REMOTE_PROGRAM) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown program (received %x, expected %x)", hdr.prog, REMOTE_PROGRAM); return -1; } if (hdr.vers != REMOTE_PROTOCOL_VERSION) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown protocol version (received %x, expected %x)", hdr.vers, REMOTE_PROTOCOL_VERSION); @@ -2879,21 +3240,21 @@ call (virConnectPtr conn, struct private * message being received at this point. */ if (hdr.proc != proc_nr) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown procedure (received %x, expected %x)", hdr.proc, proc_nr); return -1; } if (hdr.direction != REMOTE_REPLY) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown direction (received %x, expected %x)", hdr.direction, REMOTE_REPLY); return -1; } if (hdr.serial != serial) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown serial (received %x, expected %x)", hdr.serial, serial); @@ -2907,7 +3268,7 @@ call (virConnectPtr conn, struct private switch (hdr.status) { case REMOTE_OK: if (!(*ret_filter) (&xdr, ret)) { - error (in_open ? NULL : conn, VIR_ERR_RPC, "unmarshalling ret"); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "unmarshalling ret"); return -1; } xdr_destroy (&xdr); @@ -2916,17 +3277,26 @@ call (virConnectPtr conn, struct private case REMOTE_ERROR: memset (&rerror, 0, sizeof rerror); if (!xdr_remote_error (&xdr, &rerror)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "unmarshalling remote_error"); return -1; } xdr_destroy (&xdr); - server_error (in_open ? NULL : conn, &rerror); + /* See if caller asked us to keep quiet about missing RPCs + * eg for interop with older servers */ + if (flags & REMOTE_CALL_QUIET_MISSING_RPC && + rerror.domain == VIR_FROM_REMOTE && + rerror.code == VIR_ERR_RPC && + rerror.level == VIR_ERR_ERROR && + STREQLEN(*rerror.message, "unknown procedure", 17)) { + return -2; + } + server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, &rerror); xdr_free ((xdrproc_t) xdr_remote_error, (char *) &rerror); return -1; default: - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown status (received %x)", hdr.status); diff -r 1c3780349e89 src/virsh.c --- a/src/virsh.c Wed Nov 28 12:02:28 2007 -0500 +++ b/src/virsh.c Thu Nov 29 09:24:10 2007 -0500 @@ -4525,8 +4525,10 @@ vshInit(vshControl * ctl) * vshConnectionUsability, except ones which don't need a connection * such as "help". */ - if (!ctl->conn) + if (!ctl->conn) { vshError(ctl, FALSE, _("failed to connect to the hypervisor")); + return FALSE; + } return TRUE; } diff -r 1c3780349e89 src/virterror.c --- a/src/virterror.c Wed Nov 28 12:02:28 2007 -0500 +++ b/src/virterror.c Thu Nov 29 09:24:10 2007 -0500 @@ -671,6 +671,12 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("invalid MAC adress: %s"); break; + case VIR_ERR_AUTH_FAILED: + if (info == NULL) + errmsg = _("authentication failed"); + else + errmsg = _("authentication failed: %s"); + break; } return (errmsg); } diff -r 1c3780349e89 tests/Makefile.am --- a/tests/Makefile.am Wed Nov 28 12:02:28 2007 -0500 +++ b/tests/Makefile.am Thu Nov 29 09:24:10 2007 -0500 @@ -17,6 +17,7 @@ INCLUDES = \ -I$(top_srcdir)/src \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ + $(SASL_CFLAGS) \ -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=199506L \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \ $(COVERAGE_CFLAGS) \ @@ -27,6 +28,7 @@ LDADDS = \ @STATIC_BINARIES@ \ $(LIBXML_LIBS) \ $(GNUTLS_LIBS) \ + $(SASL_LIBS) \ $(WARN_CFLAGS) \ $(LIBVIRT) \ $(COVERAGE_LDFLAGS) -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:16:34PM +0000, Daniel P. Berrange wrote:
This patch hooks up the basic authentication RPC calls, and the specific SASL implementation. The SASL impl can be enabled/disable via the configurre script with --without-sasl / --with-sasl - it'll auto-enable it if it finds the headers & libs OK.
The sample /etc/sasl2/libvirt.conf file enables the DIGEST-MD5 mechanism by default, since it is by far the easiest to setup for admins. No need for a Kerberos server, or certificates - it just uses username/password which can be set with 'saslpasswd2 -a libvirt [username]' and a list of all active users viewed with 'sasldblistusers2 -a libvirt'
Hum, shouldn't the spec file then Requires: cyrus-sasl-md5 to make sure this works ?
There are also example settings for enabling Kerberos (GSSAPI) but this is disabled by default. It requires a file /etc/libvirt/krb5.tab containing a service principle. On some distros you need to set KRB5_KTNAME to point to this file when starting the daemon, so our init script does that. Other distros, the 'keytab' config param in /etc/sasl2/libvirt.conf is actually honoured.
With this patch you can successfully authentication client <-> server for any authentication mechansim which doesn't need to prompt the user for credentials. In effect this means it only works for GSSAPI/Kerberos, but the later patches in this series will enable callbacks making the default DIGEST-MD5 auth work.
The way auth is controlled, is that if the 'auth' parameter is set on the struct qemud_client object, *NO* rpc call will be processed except for the REMOTE_PROC_AUTH_LIST, SASL_AUTH_INIT, SASL_AUTH_START & SASL_AUTH_STEP calls. If SASL is not compiled in, the latter 3 will send errors back to the caller.
Only once authentication is complete, are the other calls allowed. It currently hardcodes use of SASL on the TCP socket. The TLS & UNIX sockets are unchanged. A subsequent patch will make it configurable.
[...]
diff -r 1c3780349e89 libvirt.spec.in --- a/libvirt.spec.in Wed Nov 28 12:02:28 2007 -0500 +++ b/libvirt.spec.in Thu Nov 29 09:24:10 2007 -0500 @@ -16,6 +16,8 @@ Requires: dnsmasq Requires: dnsmasq Requires: bridge-utils Requires: iptables +Requires: cyrus-sasl +Requires: cyrus-sasl-gssapi
Requires: cyrus-sasl-md5 ?
BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel
[...]
@@ -730,15 +735,28 @@ static struct qemud_server *qemudInitial
virStateInitialize();
+#if HAVE_SASL + if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { + qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", + sasl_errstring(err, NULL, NULL)); + goto cleanup; + } +#endif +
So if libvirt was configured/compiled with SASL but for some reason we fail to initialize SASL we can't start the daemon at all ? Isn't that a bit excessive ? [...]
if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup;
- if (remoteListenTCP (server, tls_port, 1) < 0) + if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0) goto cleanup; } }
Looks to me that as long as TLS can still work we should not fail on sasl_server_init error, right ? [...]
@@ -325,6 +356,12 @@ doRemoteOpen (virConnectPtr conn, struct } else port = NULL; /* Port not used for unix, ext. */
+ + priv->hostname = strdup (uri->server ? uri->server : "localhost"); + if (!priv->hostname) { + error (NULL, VIR_ERR_NO_MEMORY, "allocating priv->hostname"); + goto failed; + } if (uri->user) { username = strdup (uri->user); if (!username) goto out_of_memory;
Actually there we should looks for a password and store it, that's very common and convenient, e.g. use xen://foo:bar@server/ as the connection URI, libxml2 will just return the user as 'foo:bar' which could subsequently be split here to store the password (bar). [...] Looks fine to me, +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Nov 29, 2007 at 01:08:52PM -0500, Daniel Veillard wrote:
On Thu, Nov 29, 2007 at 05:16:34PM +0000, Daniel P. Berrange wrote:
This patch hooks up the basic authentication RPC calls, and the specific SASL implementation. The SASL impl can be enabled/disable via the configurre script with --without-sasl / --with-sasl - it'll auto-enable it if it finds the headers & libs OK.
The sample /etc/sasl2/libvirt.conf file enables the DIGEST-MD5 mechanism by default, since it is by far the easiest to setup for admins. No need for a Kerberos server, or certificates - it just uses username/password which can be set with 'saslpasswd2 -a libvirt [username]' and a list of all active users viewed with 'sasldblistusers2 -a libvirt'
Hum, shouldn't the spec file then Requires: cyrus-sasl-md5 to make sure this works ?
Yes & no. Technically we only require the base library. Anything else is an admin choice. I'm inclined to add a dep on cyrus-sasl-md5 though because it will be our default auth mechanism, so it'll safe plenty of BZ reports if we just always require it.
[...]
diff -r 1c3780349e89 libvirt.spec.in --- a/libvirt.spec.in Wed Nov 28 12:02:28 2007 -0500 +++ b/libvirt.spec.in Thu Nov 29 09:24:10 2007 -0500 @@ -16,6 +16,8 @@ Requires: dnsmasq Requires: dnsmasq Requires: bridge-utils Requires: iptables +Requires: cyrus-sasl +Requires: cyrus-sasl-gssapi
Requires: cyrus-sasl-md5 ?
Yes, and I'm going to remove the gssapi one, since that's not eenabled by default in my latest patches & only a minority of people will use it.
@@ -730,15 +735,28 @@ static struct qemud_server *qemudInitial
virStateInitialize();
+#if HAVE_SASL + if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { + qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", + sasl_errstring(err, NULL, NULL)); + goto cleanup; + } +#endif +
So if libvirt was configured/compiled with SASL but for some reason we fail to initialize SASL we can't start the daemon at all ? Isn't that a bit excessive ?
Failure of the sasl_server_init function is a pretty serious problem - something is majorly messed up in your OS install. We shouldn't pretend we can safely continue. In any case, the 4th patch in this serieschanges this call so that sasl_server_init is only run, if the SASL auth is actually configured on one of the sockets. So if someone did have a problem with this call failing, they could simply edit the config file & turn off SASL auth (or fix the root problem).
[...]
if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup;
- if (remoteListenTCP (server, tls_port, 1) < 0) + if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0) goto cleanup; } }
Looks to me that as long as TLS can still work we should not fail on sasl_server_init error, right ?
The 4th patch will make it possible to run SASL on all the sockets - UNIX, TCP and TLS.
@@ -325,6 +356,12 @@ doRemoteOpen (virConnectPtr conn, struct } else port = NULL; /* Port not used for unix, ext. */
+ + priv->hostname = strdup (uri->server ? uri->server : "localhost"); + if (!priv->hostname) { + error (NULL, VIR_ERR_NO_MEMORY, "allocating priv->hostname"); + goto failed; + } if (uri->user) { username = strdup (uri->user); if (!username) goto out_of_memory;
Actually there we should looks for a password and store it, that's very common and convenient, e.g. use xen://foo:bar@server/
as the connection URI, libxml2 will just return the user as 'foo:bar' which could subsequently be split here to store the password (bar).
The virConnectCredentialPtr struct which is populated for the auth callback function contains a 'defresult' field where the default value of the credential should go. I intended to populate this value with the username part of the URI for VIR_CRED_AUTHNAME credentials, but forgot. Will add that in.... Using passwords in URIs is seriously frowned upon. URIs get into log files, in the command line ARGV, into gconf, into bug reports. We absolutely do not want passwords visible in any of those places. RFC 2396 explicitly recommends against using passwords in URIs "Some URL schemes use the format "user:password" in the userinfo field. This practice is NOT RECOMMENDED, because the passing of authentication information in clear text (such as URI) has proven to be a security risk in almost every case where it has been used." Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 07:20:08PM +0000, Daniel P. Berrange wrote:
Actually there we should looks for a password and store it, that's very common and convenient, e.g. use xen://foo:bar@server/
as the connection URI, libxml2 will just return the user as 'foo:bar' which could subsequently be split here to store the password (bar).
The virConnectCredentialPtr struct which is populated for the auth callback function contains a 'defresult' field where the default value of the credential should go. I intended to populate this value with the username part of the URI for VIR_CRED_AUTHNAME credentials, but forgot. Will add that in....
Using passwords in URIs is seriously frowned upon. URIs get into log files, in the command line ARGV, into gconf, into bug reports. We absolutely do not want passwords visible in any of those places.
RFC 2396 explicitly recommends against using passwords in URIs
"Some URL schemes use the format "user:password" in the userinfo field. This practice is NOT RECOMMENDED, because the passing of authentication information in clear text (such as URI) has proven to be a security risk in almost every case where it has been used."
I know, I have also argued against it (and that's why libxml2 doesn't parse it), but this can be way more convenient at times, and also has the potential to remove asynchronous interaction for example when using scripts. Anyway not a big deal, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Nov 29, 2007 at 02:43:09PM -0500, Daniel Veillard wrote:
On Thu, Nov 29, 2007 at 07:20:08PM +0000, Daniel P. Berrange wrote:
Actually there we should looks for a password and store it, that's very common and convenient, e.g. use xen://foo:bar@server/
as the connection URI, libxml2 will just return the user as 'foo:bar' which could subsequently be split here to store the password (bar).
The virConnectCredentialPtr struct which is populated for the auth callback function contains a 'defresult' field where the default value of the credential should go. I intended to populate this value with the username part of the URI for VIR_CRED_AUTHNAME credentials, but forgot. Will add that in....
Using passwords in URIs is seriously frowned upon. URIs get into log files, in the command line ARGV, into gconf, into bug reports. We absolutely do not want passwords visible in any of those places.
RFC 2396 explicitly recommends against using passwords in URIs
"Some URL schemes use the format "user:password" in the userinfo field. This practice is NOT RECOMMENDED, because the passing of authentication information in clear text (such as URI) has proven to be a security risk in almost every case where it has been used."
I know, I have also argued against it (and that's why libxml2 doesn't parse it), but this can be way more convenient at times, and also has the potential to remove asynchronous interaction for example when using scripts.
There's better ways to deal with scripting. eg, we could add a flag to virsh '--auth /path/to/file' where the file contained key,value pairs for each credential. Or could have an env var VIR_AUTH_FILE pointing to such a file, which can be processed by the default callback I aded. That lets you automate login, without leaking the confidential data anywhere. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 02:43:09PM -0500, Daniel Veillard wrote: ...
I know, I have also argued against it (and that's why libxml2 doesn't parse it), but this can be way more convenient at times, and also has the potential to remove asynchronous interaction for example when using scripts.
There's better ways to deal with scripting. eg, we could add a flag to virsh '--auth /path/to/file' where the file contained key,value pairs for each credential. Or could have an env var VIR_AUTH_FILE pointing to such a file, which can be processed by the default callback I aded. That lets you automate login, without leaking the confidential data anywhere.
Or, you could do what gpg does and use a gpg-agent-style envvar to specify socket and PID: GPG_AGENT_INFO=/path/to/socket:NNNNN:1 Then you'd use the path and pid used by your existing gpg-agent. When I started gpg-signing things regularly, I switched from ssh-agent to gpg-agent, and it takes care of auth for both gpg signing and ssh connections.

Hi Dan I haven't used getnameinfo much, so looked it up and found this: In order to assist the programmer in choosing reasonable sizes for the supplied buffers, <netdb.h> defines the constants # define NI_MAXHOST 1025 # define NI_MAXSERV 32 "Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 1c3780349e89 qemud/remote.c --- a/qemud/remote.c Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote.c Thu Nov 29 09:24:10 2007 -0500 ... +static char *addrToString(struct qemud_client *client, + remote_message_header *req, + struct sockaddr_storage *sa, socklen_t salen) { + char host[1024], port[20]; + char *addr; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
Since it's always good to avoid such literals, how about declaring those locals like this: char host[NI_MAXHOST]; port[NI_MAXSERV]; I see that qemud/remote.c already includes <netdb.h>, but if you're extra cautious, maybe you'd want this: (though NI_MAXHOST is already used in qemud.c, so maybe not): #ifndef NI_MAXHOST # define NI_MAXHOST 1025 #endif #ifndef NI_MAXSERV # define NI_MAXSERV 32 #endif

On Thu, Nov 29, 2007 at 09:31:50PM +0100, Jim Meyering wrote:
Hi Dan
I haven't used getnameinfo much, so looked it up and found this:
In order to assist the programmer in choosing reasonable sizes for the supplied buffers, <netdb.h> defines the constants
# define NI_MAXHOST 1025 # define NI_MAXSERV 32
"Daniel P. Berrange" <berrange@redhat.com> wrote: ...
diff -r 1c3780349e89 qemud/remote.c --- a/qemud/remote.c Wed Nov 28 12:02:28 2007 -0500 +++ b/qemud/remote.c Thu Nov 29 09:24:10 2007 -0500 ... +static char *addrToString(struct qemud_client *client, + remote_message_header *req, + struct sockaddr_storage *sa, socklen_t salen) { + char host[1024], port[20]; + char *addr; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
Since it's always good to avoid such literals, how about declaring those locals like this:
char host[NI_MAXHOST]; port[NI_MAXSERV];
Yep, looks like a good idea. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:16:34PM +0000, Daniel P. Berrange wrote:
This patch hooks up the basic authentication RPC calls, and the specific SASL implementation. The SASL impl can be enabled/disable via the configurre script with --without-sasl / --with-sasl - it'll auto-enable it if it finds the headers & libs OK.
The sample /etc/sasl2/libvirt.conf file enables the DIGEST-MD5 mechanism by default, since it is by far the easiest to setup for admins. No need for a Kerberos server, or certificates - it just uses username/password which can be set with 'saslpasswd2 -a libvirt [username]' and a list of all active users viewed with 'sasldblistusers2 -a libvirt'
There are also example settings for enabling Kerberos (GSSAPI) but this is disabled by default. It requires a file /etc/libvirt/krb5.tab containing a service principle. On some distros you need to set KRB5_KTNAME to point to this file when starting the daemon, so our init script does that. Other distros, the 'keytab' config param in /etc/sasl2/libvirt.conf is actually honoured.
With this patch you can successfully authentication client <-> server for any authentication mechansim which doesn't need to prompt the user for credentials. In effect this means it only works for GSSAPI/Kerberos, but the later patches in this series will enable callbacks making the default DIGEST-MD5 auth work.
The way auth is controlled, is that if the 'auth' parameter is set on the struct qemud_client object, *NO* rpc call will be processed except for the REMOTE_PROC_AUTH_LIST, SASL_AUTH_INIT, SASL_AUTH_START & SASL_AUTH_STEP calls. If SASL is not compiled in, the latter 3 will send errors back to the caller.
Only once authentication is complete, are the other calls allowed. It currently hardcodes use of SASL on the TCP socket. The TLS & UNIX sockets are unchanged. A subsequent patch will make it configurable.
Updated to add constants for getnameinfo() params as Jim suggested. Also remove the dep on cyrus-sasl-gssapi, and add one on cyrus-sasl-md5 diff -r e24d542af4fb configure.in --- a/configure.in Fri Nov 30 14:31:10 2007 -0500 +++ b/configure.in Fri Nov 30 15:32:16 2007 -0500 @@ -360,6 +360,40 @@ AC_CHECK_TYPE(gnutls_session, [#include <gnutls/gnutls.h>]) CFLAGS="$old_cflags" LDFLAGS="$old_ldflags" + + +dnl Cyrus SASL +AC_ARG_WITH(sasl, + [ --with-sasl use cyrus SASL for authentication], + [], + [with_sasl=yes]) + +SASL_CFLAGS= +SASL_LIBS= +if test "$with_sasl" != "no"; then + if test "$with_sasl" != "yes"; then + SASL_CFLAGS="-I$with_sasl" + SASL_LIBS="-L$with_sasl" + fi + old_cflags="$CFLAGS" + old_libs="$LIBS" + CFLAGS="$CFLAGS $SASL_CFLAGS" + LIBS="$LIBS $SASL_LIBS" + AC_CHECK_HEADER([sasl/sasl.h], + [], + AC_MSG_ERROR([You must install the Cyrus SASL development package in order to compile libvirt])) + AC_CHECK_LIB(sasl2, sasl_client_init, + [], + [AC_MSG_ERROR([You must install the Cyrus SASL library in order to compile and run libvirt])]) + CFLAGS="$old_cflags" + LIBS="$old_libs" + SASL_LIBS="$SASL_LIBS -lsasl2" + AC_DEFINE_UNQUOTED(HAVE_SASL, 1, [whether Cyrus SASL is available for authentication]) +fi +AM_CONDITIONAL(HAVE_SASL, [test "$with_sasl" != "no"]) +AC_SUBST(SASL_CFLAGS) +AC_SUBST(SASL_LIBS) + dnl Avahi library @@ -590,6 +624,11 @@ AC_MSG_NOTICE([]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) AC_MSG_NOTICE([ gnutls: $GNUTLS_CFLAGS $GNUTLS_LIBS]) +if test "$with_sasl" != "no" ; then +AC_MSG_NOTICE([ sasl: $SASL_CFLAGS $SASL_LIBS]) +else +AC_MSG_NOTICE([ sasl: no]) +fi if test "$with_avahi" = "yes" ; then AC_MSG_NOTICE([ avahi: $AVAHI_CFLAGS $AVAHI_LIBS]) else diff -r e24d542af4fb include/libvirt/virterror.h --- a/include/libvirt/virterror.h Fri Nov 30 14:31:10 2007 -0500 +++ b/include/libvirt/virterror.h Fri Nov 30 15:32:16 2007 -0500 @@ -131,6 +131,7 @@ typedef enum { VIR_ERR_NO_DOMAIN, /* domain not found or unexpectedly disappeared */ VIR_ERR_NO_NETWORK, /* network not found */ VIR_ERR_INVALID_MAC, /* invalid MAC adress */ + VIR_ERR_AUTH_FAILED, /* authentication failed */ } virErrorNumber; /** diff -r e24d542af4fb libvirt.spec.in --- a/libvirt.spec.in Fri Nov 30 14:31:10 2007 -0500 +++ b/libvirt.spec.in Fri Nov 30 15:32:16 2007 -0500 @@ -16,6 +16,10 @@ Requires: dnsmasq Requires: dnsmasq Requires: bridge-utils Requires: iptables +Requires: cyrus-sasl +# Not technically required, but makes 'out-of-box' config +# work correctly & doesn't have onerous dependancies +Requires: cyrus-sasl-md5 BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -26,6 +30,7 @@ BuildRequires: dnsmasq BuildRequires: dnsmasq BuildRequires: bridge-utils BuildRequires: qemu +BuildRequires: cyrus-sasl-devel Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -132,6 +137,7 @@ fi %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd %config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf %config(noreplace) %{_sysconfdir}/libvirt/qemu.conf +%config(noreplace) %{_sysconfdir}/sasl2/libvirt.conf %dir %{_datadir}/libvirt/ %dir %{_datadir}/libvirt/networks/ %{_datadir}/libvirt/networks/default.xml diff -r e24d542af4fb qemud/Makefile.am --- a/qemud/Makefile.am Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/Makefile.am Fri Nov 30 15:32:16 2007 -0500 @@ -17,6 +17,7 @@ EXTRA_DIST = libvirtd.init.in libvirtd.s remote_dispatch_localvars.h \ remote_dispatch_proc_switch.h \ mdns.c mdns.h \ + libvirtd.sasl \ $(conf_DATA) libvirtd_SOURCES = \ @@ -28,14 +29,14 @@ libvirtd_SOURCES = \ #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L libvirtd_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include \ - $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) \ + $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \ $(WARN_CFLAGS) -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ -DQEMUD_PID_FILE="\"$(QEMUD_PID_FILE)\"" \ -DREMOTE_PID_FILE="\"$(REMOTE_PID_FILE)\"" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" -libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) +libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) libvirtd_DEPENDENCIES = ../src/libvirt.la libvirtd_LDADD = ../src/libvirt.la @@ -46,7 +47,7 @@ endif endif default_xml_dest = libvirt/qemu/networks/default.xml -install-data-local: install-init +install-data-local: install-init install-data-sasl mkdir -p $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart $(INSTALL_DATA) $(srcdir)/default-network.xml \ $(DESTDIR)$(sysconfdir)/$(default_xml_dest) @@ -59,7 +60,7 @@ install-data-local: install-init mkdir -p $(DESTDIR)$(localstatedir)/run/libvirt mkdir -p $(DESTDIR)$(localstatedir)/lib/libvirt -uninstall-local: uninstall-init +uninstall-local:: uninstall-init uninstall-data-sasl rm -f $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml rm -f $(DESTDIR)$(sysconfdir)/$(default_xml_dest) rmdir $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart || : @@ -67,6 +68,18 @@ uninstall-local: uninstall-init rmdir $(DESTDIR)$(localstatedir)/run/libvirt || : rmdir $(DESTDIR)$(localstatedir)/lib/libvirt || : +if HAVE_SASL +install-data-sasl:: install-init + mkdir -p $(DESTDIR)$(sysconfdir)/sasl2/ + $(INSTALL_DATA) $(srcdir)/libvirtd.sasl $(DESTDIR)$(sysconfdir)/sasl2/libvirt.conf + +uninstall-data-sasl:: install-init + rm -f $(DESTDIR)$(sysconfdir)/sasl2/libvirt.conf + rmdir $(DESTDIR)$(sysconfdir)/sasl2/ +else +install-data-sasl: +uninstall-data-sasl: +endif if RPCGEN .x.c: diff -r e24d542af4fb qemud/internal.h --- a/qemud/internal.h Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/internal.h Fri Nov 30 15:32:16 2007 -0500 @@ -28,6 +28,9 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include "../src/gnutls_1_0_compat.h" +#if HAVE_SASL +#include <sasl/sasl.h> +#endif #ifdef HAVE_SYS_SYSLIMITS_H #include <sys/syslimits.h> @@ -91,6 +94,10 @@ struct qemud_client { int tls; gnutls_session_t session; enum qemud_tls_direction direction; + int auth; +#if HAVE_SASL + sasl_conn_t *saslconn; +#endif unsigned int incomingSerial; unsigned int outgoingSerial; @@ -116,6 +123,7 @@ struct qemud_socket { int readonly; /* If set, TLS is required on this socket. */ int tls; + int auth; int port; struct qemud_socket *next; }; diff -r e24d542af4fb qemud/libvirtd.init.in --- a/qemud/libvirtd.init.in Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/libvirtd.init.in Fri Nov 30 15:32:16 2007 -0500 @@ -37,6 +37,7 @@ PROCESS=libvirtd LIBVIRTD_CONFIG= LIBVIRTD_ARGS= +KRB5_KTNAME=/etc/libvirt/krb5.tab test -f @sysconfdir@/sysconfig/libvirtd && . @sysconfdir@/sysconfig/libvirtd @@ -50,7 +51,7 @@ RETVAL=0 start() { echo -n $"Starting $SERVICE daemon: " - daemon --check $SERVICE $PROCESS --daemon $LIBVIRTD_CONFIG_ARGS $LIBVIRTD_ARGS + KRB5_KTNAME=$KRB5_KTNAME daemon --check $SERVICE $PROCESS --daemon $LIBVIRTD_CONFIG_ARGS $LIBVIRTD_ARGS RETVAL=$? echo [ $RETVAL -eq 0 ] && touch @localstatedir@/lock/subsys/$SERVICE diff -r e24d542af4fb qemud/libvirtd.sasl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qemud/libvirtd.sasl Fri Nov 30 15:32:16 2007 -0500 @@ -0,0 +1,28 @@ +# If you want to use the non-TLS socket, then you *must* include +# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only +# ones that can offer session encryption as well as authentication. +# +# If you're only using TLS, then you can turn on any mechanisms +# you like for authentication, because TLS provides the encryption +# +# Default to a simple username+password mechanism +mech_list: digest-md5 + +# Before you can use GSSAPI, you need a service principle on the +# KDC server for libvirt, and that to be exported to the keytab +# file listed below +#mech_list: gssapi +# +# You can also list many mechanisms at once, then the user can choose +# by adding '?auth=sasl.gssapi' to their libvirt URI, eg +# qemu+tcp://hostname/system?auth=sasl.gssapi +#mech_list: digest-md5 gssapi + +# MIT kerberos ignores this option & needs KRB5_KTNAME env var. +# May be useful for other non-Linux OS though.... +keytab: /etc/libvirt/krb5.tab + +# If using digest-md5 for username/passwds, then this is the file +# containing the passwds. Use 'saslpasswd2 -a libvirt [username]' +# to add entries, and 'sasldblistusers2 -a libvirt' to browse it +sasldb_path: /etc/libvirt/passwd.db diff -r e24d542af4fb qemud/libvirtd.sysconf --- a/qemud/libvirtd.sysconf Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/libvirtd.sysconf Fri Nov 30 15:32:16 2007 -0500 @@ -4,3 +4,6 @@ # Listen for TCP/IP connections # NB. must setup TLS/SSL keys prior to using this #LIBVIRTD_ARGS="--listen" + +# Override Kerberos service keytab for SASL/GSSAPI +#KRB5_KTNAME=/etc/libvirt/krb5.tab diff -r e24d542af4fb qemud/qemud.c --- a/qemud/qemud.c Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/qemud.c Fri Nov 30 15:32:16 2007 -0500 @@ -577,7 +577,8 @@ static int static int remoteListenTCP (struct qemud_server *server, const char *port, - int tls) + int tls, + int auth) { int fds[2]; int nfds = 0; @@ -606,6 +607,7 @@ remoteListenTCP (struct qemud_server *se sock->fd = fds[i]; sock->tls = tls; + sock->auth = auth; if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0) return -1; @@ -701,6 +703,9 @@ static struct qemud_server *qemudInitial struct qemud_socket *sock; char sockname[PATH_MAX]; char roSockname[PATH_MAX]; +#if HAVE_SASL + int err; +#endif /* HAVE_SASL */ if (!(server = calloc(1, sizeof(struct qemud_server)))) { qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); @@ -730,15 +735,28 @@ static struct qemud_server *qemudInitial virStateInitialize(); +#if HAVE_SASL + if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { + qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", + sasl_errstring(err, NULL, NULL)); + goto cleanup; + } +#endif + if (ipsock) { - if (listen_tcp && remoteListenTCP (server, tcp_port, 0) < 0) +#if HAVE_SASL + if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_SASL) < 0) goto cleanup; +#else + if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_NONE) < 0) + goto cleanup; +#endif if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, 1) < 0) + if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0) goto cleanup; } } @@ -1050,6 +1068,7 @@ static int qemudDispatchServer(struct qe client->fd = fd; client->readonly = sock->readonly; client->tls = sock->tls; + client->auth = sock->auth; memcpy (&client->addr, &addr, sizeof addr); client->addrlen = addrlen; @@ -1130,6 +1149,9 @@ static void qemudDispatchClientFailure(s if (client->conn) virConnectClose(client->conn); +#if HAVE_SASL + if (client->saslconn) sasl_dispose(&client->saslconn); +#endif if (client->tls && client->session) gnutls_deinit (client->session); close(client->fd); free(client); diff -r e24d542af4fb qemud/remote.c --- a/qemud/remote.c Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote.c Fri Nov 30 15:32:16 2007 -0500 @@ -52,6 +52,8 @@ #define DEBUG 0 +#define REMOTE_DEBUG(fmt,...) qemudDebug("REMOTE: " fmt, __VA_ARGS__) + static void remoteDispatchError (struct qemud_client *client, remote_message_header *req, const char *fmt, ...) @@ -116,6 +118,21 @@ remoteDispatchClientRequest (struct qemu (int) req.status); xdr_destroy (&xdr); return; + } + + /* If client is marked as needing auth, don't allow any RPC ops, + * except for authentication ones + */ + if (client->auth) { + if (req.proc != REMOTE_PROC_AUTH_LIST && + req.proc != REMOTE_PROC_AUTH_SASL_INIT && + req.proc != REMOTE_PROC_AUTH_SASL_START && + req.proc != REMOTE_PROC_AUTH_SASL_STEP + ) { + remoteDispatchError (client, &req, "authentication required"); + xdr_destroy (&xdr); + return; + } } /* Based on the procedure number, dispatch. In future we may base @@ -275,23 +292,14 @@ remoteDispatchClientRequest (struct qemu * reply. */ static void -remoteDispatchError (struct qemud_client *client, - remote_message_header *req, - const char *fmt, ...) +remoteDispatchSendError (struct qemud_client *client, + remote_message_header *req, + int code, const char *msg) { remote_message_header rep; remote_error error; - va_list args; - char msgbuf[1024]; - char *msg = msgbuf; XDR xdr; int len; - - va_start (args, fmt); - vsnprintf (msgbuf, sizeof msgbuf, fmt, args); - va_end (args); - - qemudDebug ("%s", msgbuf); /* Future versions of the protocol may use different vers or prog. Try * our hardest to send back a message that such clients could see. @@ -313,12 +321,12 @@ remoteDispatchError (struct qemud_client } /* Construct the error. */ - error.code = VIR_ERR_RPC; + error.code = code; error.domain = VIR_FROM_REMOTE; - error.message = &msg; + error.message = (char**)&msg; error.level = VIR_ERR_ERROR; error.dom = NULL; - error.str1 = &msg; + error.str1 = (char**)&msg; error.str2 = NULL; error.str3 = NULL; error.int1 = 0; @@ -363,6 +371,31 @@ remoteDispatchError (struct qemud_client client->bufferOffset = 0; if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; } + +static void +remoteDispatchFailAuth (struct qemud_client *client, + remote_message_header *req) +{ + remoteDispatchSendError (client, req, VIR_ERR_AUTH_FAILED, "authentication failed"); +} + +static void +remoteDispatchError (struct qemud_client *client, + remote_message_header *req, + const char *fmt, ...) +{ + va_list args; + char msgbuf[1024]; + char *msg = msgbuf; + + va_start (args, fmt); + vsnprintf (msgbuf, sizeof msgbuf, fmt, args); + va_end (args); + + remoteDispatchSendError (client, req, VIR_ERR_RPC, msg); +} + + /*----- Functions. -----*/ @@ -1946,6 +1979,335 @@ remoteDispatchNumOfNetworks (struct qemu return 0; } + +static int +remoteDispatchAuthList (struct qemud_client *client, + remote_message_header *req ATTRIBUTE_UNUSED, + void *args ATTRIBUTE_UNUSED, + remote_auth_list_ret *ret) +{ + ret->types.types_len = 1; + if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (remote_auth_type))) == NULL) { + remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, "auth types"); + return -2; + } + ret->types.types_val[0] = client->auth; + return 0; +} + + +#if HAVE_SASL +/* + * NB, keep in sync with similar method in src/remote_internal.c + */ +static char *addrToString(struct qemud_client *client, + remote_message_header *req, + struct sockaddr_storage *sa, socklen_t salen) { + char host[1024], port[20]; + char *addr; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + remoteDispatchError(client, req, + "Cannot resolve address %d: %s", err, gai_strerror(err)); + return NULL; + } + + addr = malloc(strlen(host) + 1 + strlen(port) + 1); + if (!addr) { + remoteDispatchError(client, req, "cannot allocate address"); + return NULL; + } + + strcpy(addr, host); + strcat(addr, ";"); + strcat(addr, port); + return addr; +} + + +/* + * Initializes the SASL session in prepare for authentication + * and gives the client a list of allowed mechansims to choose + * + * XXX callbacks for stuff like password verification ? + */ +static int +remoteDispatchAuthSaslInit (struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_sasl_init_ret *ret) +{ + const char *mechlist = NULL; + int err; + struct sockaddr_storage sa; + socklen_t salen; + char *localAddr, *remoteAddr; + + REMOTE_DEBUG("Initialize SASL auth %d", client->fd); + if (client->auth != REMOTE_AUTH_SASL || + client->saslconn != NULL) { + qemudLog(QEMUD_ERR, "client tried invalid SASL init request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + /* Get local address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getsockname(client->fd, (struct sockaddr*)&sa, &salen) < 0) { + remoteDispatchError(client, req, "failed to get sock address %d (%s)", + errno, strerror(errno)); + return -2; + } + if ((localAddr = addrToString(client, req, &sa, salen)) == NULL) { + return -2; + } + + /* Get remote address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getpeername(client->fd, (struct sockaddr*)&sa, &salen) < 0) { + remoteDispatchError(client, req, "failed to get peer address %d (%s)", + errno, strerror(errno)); + free(localAddr); + return -2; + } + if ((remoteAddr = addrToString(client, req, &sa, salen)) == NULL) { + free(localAddr); + return -2; + } + + err = sasl_server_new("libvirt", + NULL, /* FQDN - just delegates to gethostname */ + NULL, /* User realm */ + localAddr, + remoteAddr, + NULL, /* XXX Callbacks */ + SASL_SUCCESS_DATA, + &client->saslconn); + free(localAddr); + free(remoteAddr); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "sasl context setup failed %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + client->saslconn = NULL; + return -2; + } + + err = sasl_listmech(client->saslconn, + NULL, /* Don't need to set user */ + "", /* Prefix */ + ",", /* Separator */ + "", /* Suffix */ + &mechlist, + NULL, + NULL); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot list SASL mechanisms %d (%s)", + err, sasl_errdetail(client->saslconn)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + REMOTE_DEBUG("Available mechanisms for client: '%s'", mechlist); + ret->mechlist = strdup(mechlist); + if (!ret->mechlist) { + qemudLog(QEMUD_ERR, "cannot allocate mechlist"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + + return 0; +} + + +/* + * This starts the SASL authentication negotiation. + */ +static int +remoteDispatchAuthSaslStart (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_start_args *args, + remote_auth_sasl_start_ret *ret) +{ + const char *serverout; + unsigned int serveroutlen; + int err; + + REMOTE_DEBUG("Start SASL auth %d", client->fd); + if (client->auth != REMOTE_AUTH_SASL || + client->saslconn == NULL) { + qemudLog(QEMUD_ERR, "client tried invalid SASL start request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + REMOTE_DEBUG("Using SASL mechanism %s. Data %d bytes, nil: %d", + args->mech, args->data.data_len, args->nil); + err = sasl_server_start(client->saslconn, + args->mech, + /* NB, distinction of NULL vs "" is *critical* in SASL */ + args->nil ? NULL : args->data.data_val, + args->data.data_len, + &serverout, + &serveroutlen); + if (err != SASL_OK && + err != SASL_CONTINUE) { + qemudLog(QEMUD_ERR, "sasl start failed %d (%s)", + err, sasl_errdetail(client->saslconn)); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) { + qemudLog(QEMUD_ERR, "sasl start reply data too long %d", serveroutlen); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverout) { + ret->data.data_val = malloc(serveroutlen); + if (!ret->data.data_val) { + remoteDispatchError (client, req, "out of memory allocating array"); + return -2; + } + memcpy(ret->data.data_val, serverout, serveroutlen); + } else { + ret->data.data_val = NULL; + } + ret->nil = serverout ? 0 : 1; + ret->data.data_len = serveroutlen; + + REMOTE_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil); + if (err == SASL_CONTINUE) { + ret->complete = 0; + } else { + REMOTE_DEBUG("Authentication successful %d", client->fd); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } + + return 0; +} + + +static int +remoteDispatchAuthSaslStep (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_step_args *args, + remote_auth_sasl_step_ret *ret) +{ + const char *serverout; + unsigned int serveroutlen; + int err; + + REMOTE_DEBUG("Step SASL auth %d", client->fd); + if (client->auth != REMOTE_AUTH_SASL || + client->saslconn == NULL) { + qemudLog(QEMUD_ERR, "client tried invalid SASL start request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + REMOTE_DEBUG("Using SASL Data %d bytes, nil: %d", + args->data.data_len, args->nil); + err = sasl_server_step(client->saslconn, + /* NB, distinction of NULL vs "" is *critical* in SASL */ + args->nil ? NULL : args->data.data_val, + args->data.data_len, + &serverout, + &serveroutlen); + if (err != SASL_OK && + err != SASL_CONTINUE) { + qemudLog(QEMUD_ERR, "sasl step failed %d (%s)", + err, sasl_errdetail(client->saslconn)); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + + if (serveroutlen > REMOTE_AUTH_SASL_DATA_MAX) { + qemudLog(QEMUD_ERR, "sasl step reply data too long %d", serveroutlen); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + remoteDispatchFailAuth(client, req); + return -2; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverout) { + ret->data.data_val = malloc(serveroutlen); + if (!ret->data.data_val) { + remoteDispatchError (client, req, "out of memory allocating array"); + return -2; + } + memcpy(ret->data.data_val, serverout, serveroutlen); + } else { + ret->data.data_val = NULL; + } + ret->nil = serverout ? 0 : 1; + ret->data.data_len = serveroutlen; + + REMOTE_DEBUG("SASL return data %d bytes, nil; %d", ret->data.data_len, ret->nil); + if (err == SASL_CONTINUE) { + ret->complete = 0; + } else { + REMOTE_DEBUG("Authentication successful %d", client->fd); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } + + return 0; +} + + +#else /* HAVE_SASL */ +static int +remoteDispatchAuthSaslInit (struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported SASL init request"); + remoteDispatchFailAuth(client, req); + return -1; +} + +static int +remoteDispatchAuthSaslStart (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, + remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported SASL start request"); + remoteDispatchFailAuth(client, req); + return -1; +} + +static int +remoteDispatchAuthSaslStep (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, + remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported SASL step request"); + remoteDispatchFailAuth(client, req); + return -1; +} +#endif /* HAVE_SASL */ + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -r e24d542af4fb qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote_dispatch_localvars.h Fri Nov 30 15:32:16 2007 -0500 @@ -9,6 +9,7 @@ remote_list_defined_domains_ret lv_remot remote_list_defined_domains_ret lv_remote_list_defined_domains_ret; remote_get_capabilities_ret lv_remote_get_capabilities_ret; remote_domain_set_max_memory_args lv_remote_domain_set_max_memory_args; +remote_auth_sasl_init_ret lv_remote_auth_sasl_init_ret; remote_domain_get_os_type_args lv_remote_domain_get_os_type_args; remote_domain_get_os_type_ret lv_remote_domain_get_os_type_ret; remote_domain_get_autostart_args lv_remote_domain_get_autostart_args; @@ -36,6 +37,8 @@ remote_domain_create_linux_args lv_remot remote_domain_create_linux_args lv_remote_domain_create_linux_args; remote_domain_create_linux_ret lv_remote_domain_create_linux_ret; remote_domain_set_scheduler_parameters_args lv_remote_domain_set_scheduler_parameters_args; +remote_auth_sasl_start_args lv_remote_auth_sasl_start_args; +remote_auth_sasl_start_ret lv_remote_auth_sasl_start_ret; remote_domain_interface_stats_args lv_remote_domain_interface_stats_args; remote_domain_interface_stats_ret lv_remote_domain_interface_stats_ret; remote_domain_get_max_vcpus_args lv_remote_domain_get_max_vcpus_args; @@ -50,6 +53,8 @@ remote_network_get_bridge_name_args lv_r remote_network_get_bridge_name_args lv_remote_network_get_bridge_name_args; remote_network_get_bridge_name_ret lv_remote_network_get_bridge_name_ret; remote_domain_destroy_args lv_remote_domain_destroy_args; +remote_auth_sasl_step_args lv_remote_auth_sasl_step_args; +remote_auth_sasl_step_ret lv_remote_auth_sasl_step_ret; remote_domain_migrate_finish_args lv_remote_domain_migrate_finish_args; remote_domain_migrate_finish_ret lv_remote_domain_migrate_finish_ret; remote_domain_get_vcpus_args lv_remote_domain_get_vcpus_args; @@ -74,6 +79,7 @@ remote_network_set_autostart_args lv_rem remote_network_set_autostart_args lv_remote_network_set_autostart_args; remote_network_get_autostart_args lv_remote_network_get_autostart_args; remote_network_get_autostart_ret lv_remote_network_get_autostart_ret; +remote_auth_list_ret lv_remote_auth_list_ret; remote_domain_core_dump_args lv_remote_domain_core_dump_args; remote_domain_get_max_memory_args lv_remote_domain_get_max_memory_args; remote_domain_get_max_memory_ret lv_remote_domain_get_max_memory_ret; diff -r e24d542af4fb qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote_dispatch_proc_switch.h Fri Nov 30 15:32:16 2007 -0500 @@ -2,6 +2,36 @@ * Do not edit this file. Any changes you make will be lost. */ +case REMOTE_PROC_AUTH_LIST: + fn = (dispatch_fn) remoteDispatchAuthList; + ret_filter = (xdrproc_t) xdr_remote_auth_list_ret; + ret = (char *) &lv_remote_auth_list_ret; + memset (&lv_remote_auth_list_ret, 0, sizeof lv_remote_auth_list_ret); + break; +case REMOTE_PROC_AUTH_SASL_INIT: + fn = (dispatch_fn) remoteDispatchAuthSaslInit; + ret_filter = (xdrproc_t) xdr_remote_auth_sasl_init_ret; + ret = (char *) &lv_remote_auth_sasl_init_ret; + memset (&lv_remote_auth_sasl_init_ret, 0, sizeof lv_remote_auth_sasl_init_ret); + break; +case REMOTE_PROC_AUTH_SASL_START: + fn = (dispatch_fn) remoteDispatchAuthSaslStart; + args_filter = (xdrproc_t) xdr_remote_auth_sasl_start_args; + args = (char *) &lv_remote_auth_sasl_start_args; + memset (&lv_remote_auth_sasl_start_args, 0, sizeof lv_remote_auth_sasl_start_args); + ret_filter = (xdrproc_t) xdr_remote_auth_sasl_start_ret; + ret = (char *) &lv_remote_auth_sasl_start_ret; + memset (&lv_remote_auth_sasl_start_ret, 0, sizeof lv_remote_auth_sasl_start_ret); + break; +case REMOTE_PROC_AUTH_SASL_STEP: + fn = (dispatch_fn) remoteDispatchAuthSaslStep; + args_filter = (xdrproc_t) xdr_remote_auth_sasl_step_args; + args = (char *) &lv_remote_auth_sasl_step_args; + memset (&lv_remote_auth_sasl_step_args, 0, sizeof lv_remote_auth_sasl_step_args); + ret_filter = (xdrproc_t) xdr_remote_auth_sasl_step_ret; + ret = (char *) &lv_remote_auth_sasl_step_ret; + memset (&lv_remote_auth_sasl_step_ret, 0, sizeof lv_remote_auth_sasl_step_ret); + break; case REMOTE_PROC_CLOSE: fn = (dispatch_fn) remoteDispatchClose; break; diff -r e24d542af4fb qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote_dispatch_prototypes.h Fri Nov 30 15:32:16 2007 -0500 @@ -2,6 +2,10 @@ * Do not edit this file. Any changes you make will be lost. */ +static int remoteDispatchAuthList (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); +static int remoteDispatchAuthSaslInit (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); +static int remoteDispatchAuthSaslStart (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); +static int remoteDispatchAuthSaslStep (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); static int remoteDispatchClose (struct qemud_client *client, remote_message_header *req, void *args, void *ret); static int remoteDispatchDomainAttachDevice (struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); static int remoteDispatchDomainBlockStats (struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); diff -r e24d542af4fb qemud/remote_protocol.c --- a/qemud/remote_protocol.c Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote_protocol.c Fri Nov 30 15:32:16 2007 -0500 @@ -100,6 +100,15 @@ xdr_remote_error (XDR *xdrs, remote_erro if (!xdr_int (xdrs, &objp->int2)) return FALSE; if (!xdr_remote_network (xdrs, &objp->net)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_type (XDR *xdrs, remote_auth_type *objp) +{ + + if (!xdr_enum (xdrs, (enum_t *) objp)) return FALSE; return TRUE; } @@ -1222,6 +1231,84 @@ xdr_remote_network_set_autostart_args (X } bool_t +xdr_remote_auth_list_ret (XDR *xdrs, remote_auth_list_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->types.types_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->types.types_len, REMOTE_AUTH_TYPE_LIST_MAX, + sizeof (remote_auth_type), (xdrproc_t) xdr_remote_auth_type)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_init_ret (XDR *xdrs, remote_auth_sasl_init_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->mechlist)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_start_args (XDR *xdrs, remote_auth_sasl_start_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_remote_nonnull_string (xdrs, &objp->mech)) + return FALSE; + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_start_ret (XDR *xdrs, remote_auth_sasl_start_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_int (xdrs, &objp->complete)) + return FALSE; + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_step_args (XDR *xdrs, remote_auth_sasl_step_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_auth_sasl_step_ret (XDR *xdrs, remote_auth_sasl_step_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->data.data_val; + + if (!xdr_int (xdrs, &objp->complete)) + return FALSE; + if (!xdr_int (xdrs, &objp->nil)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->data.data_len, REMOTE_AUTH_SASL_DATA_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff -r e24d542af4fb qemud/remote_protocol.h --- a/qemud/remote_protocol.h Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote_protocol.h Fri Nov 30 15:32:16 2007 -0500 @@ -28,6 +28,8 @@ typedef remote_nonnull_string *remote_st #define REMOTE_MIGRATE_COOKIE_MAX 256 #define REMOTE_NETWORK_NAME_LIST_MAX 256 #define REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX 16 +#define REMOTE_AUTH_SASL_DATA_MAX 65536 +#define REMOTE_AUTH_TYPE_LIST_MAX 20 typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -63,6 +65,12 @@ struct remote_error { }; typedef struct remote_error remote_error; +enum remote_auth_type { + REMOTE_AUTH_NONE = 0, + REMOTE_AUTH_SASL = 1, +}; +typedef enum remote_auth_type remote_auth_type; + struct remote_vcpu_info { u_int number; int state; @@ -659,6 +667,58 @@ struct remote_network_set_autostart_args int autostart; }; typedef struct remote_network_set_autostart_args remote_network_set_autostart_args; + +struct remote_auth_list_ret { + struct { + u_int types_len; + remote_auth_type *types_val; + } types; +}; +typedef struct remote_auth_list_ret remote_auth_list_ret; + +struct remote_auth_sasl_init_ret { + remote_nonnull_string mechlist; +}; +typedef struct remote_auth_sasl_init_ret remote_auth_sasl_init_ret; + +struct remote_auth_sasl_start_args { + remote_nonnull_string mech; + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_start_args remote_auth_sasl_start_args; + +struct remote_auth_sasl_start_ret { + int complete; + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_start_ret remote_auth_sasl_start_ret; + +struct remote_auth_sasl_step_args { + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_step_args remote_auth_sasl_step_args; + +struct remote_auth_sasl_step_ret { + int complete; + int nil; + struct { + u_int data_len; + char *data_val; + } data; +}; +typedef struct remote_auth_sasl_step_ret remote_auth_sasl_step_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -728,6 +788,10 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63, REMOTE_PROC_DOMAIN_BLOCK_STATS = 64, REMOTE_PROC_DOMAIN_INTERFACE_STATS = 65, + REMOTE_PROC_AUTH_LIST = 66, + REMOTE_PROC_AUTH_SASL_INIT = 67, + REMOTE_PROC_AUTH_SASL_START = 68, + REMOTE_PROC_AUTH_SASL_STEP = 69, }; typedef enum remote_procedure remote_procedure; @@ -766,6 +830,7 @@ extern bool_t xdr_remote_domain (XDR *, extern bool_t xdr_remote_domain (XDR *, remote_domain*); extern bool_t xdr_remote_network (XDR *, remote_network*); extern bool_t xdr_remote_error (XDR *, remote_error*); +extern bool_t xdr_remote_auth_type (XDR *, remote_auth_type*); extern bool_t xdr_remote_vcpu_info (XDR *, remote_vcpu_info*); extern bool_t xdr_remote_sched_param_value (XDR *, remote_sched_param_value*); extern bool_t xdr_remote_sched_param (XDR *, remote_sched_param*); @@ -864,6 +929,12 @@ extern bool_t xdr_remote_network_get_au extern bool_t xdr_remote_network_get_autostart_args (XDR *, remote_network_get_autostart_args*); extern bool_t xdr_remote_network_get_autostart_ret (XDR *, remote_network_get_autostart_ret*); extern bool_t xdr_remote_network_set_autostart_args (XDR *, remote_network_set_autostart_args*); +extern bool_t xdr_remote_auth_list_ret (XDR *, remote_auth_list_ret*); +extern bool_t xdr_remote_auth_sasl_init_ret (XDR *, remote_auth_sasl_init_ret*); +extern bool_t xdr_remote_auth_sasl_start_args (XDR *, remote_auth_sasl_start_args*); +extern bool_t xdr_remote_auth_sasl_start_ret (XDR *, remote_auth_sasl_start_ret*); +extern bool_t xdr_remote_auth_sasl_step_args (XDR *, remote_auth_sasl_step_args*); +extern bool_t xdr_remote_auth_sasl_step_ret (XDR *, remote_auth_sasl_step_ret*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_direction (XDR *, remote_message_direction*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -878,6 +949,7 @@ extern bool_t xdr_remote_domain (); extern bool_t xdr_remote_domain (); extern bool_t xdr_remote_network (); extern bool_t xdr_remote_error (); +extern bool_t xdr_remote_auth_type (); extern bool_t xdr_remote_vcpu_info (); extern bool_t xdr_remote_sched_param_value (); extern bool_t xdr_remote_sched_param (); @@ -976,6 +1048,12 @@ extern bool_t xdr_remote_network_get_aut extern bool_t xdr_remote_network_get_autostart_args (); extern bool_t xdr_remote_network_get_autostart_ret (); extern bool_t xdr_remote_network_set_autostart_args (); +extern bool_t xdr_remote_auth_list_ret (); +extern bool_t xdr_remote_auth_sasl_init_ret (); +extern bool_t xdr_remote_auth_sasl_start_args (); +extern bool_t xdr_remote_auth_sasl_start_ret (); +extern bool_t xdr_remote_auth_sasl_step_args (); +extern bool_t xdr_remote_auth_sasl_step_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_direction (); extern bool_t xdr_remote_message_status (); diff -r e24d542af4fb qemud/remote_protocol.x --- a/qemud/remote_protocol.x Fri Nov 30 14:31:10 2007 -0500 +++ b/qemud/remote_protocol.x Fri Nov 30 15:32:16 2007 -0500 @@ -80,6 +80,12 @@ const REMOTE_NETWORK_NAME_LIST_MAX = 256 /* Upper limit on list of scheduler parameters. */ const REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX = 16; + +/* Upper limit on SASL auth negotiation packet */ +const REMOTE_AUTH_SASL_DATA_MAX = 65536; + +/* Maximum number of auth types */ +const REMOTE_AUTH_TYPE_LIST_MAX = 20; /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -122,6 +128,13 @@ struct remote_error { int int2; remote_network net; }; + +/* Authentication types available thus far.... */ +enum remote_auth_type { + REMOTE_AUTH_NONE = 0, + REMOTE_AUTH_SASL = 1 +}; + /* Wire encoding of virVcpuInfo. */ struct remote_vcpu_info { @@ -610,6 +623,37 @@ struct remote_network_set_autostart_args struct remote_network_set_autostart_args { remote_nonnull_network net; int autostart; +}; + +struct remote_auth_list_ret { + remote_auth_type types<REMOTE_AUTH_TYPE_LIST_MAX>; +}; + +struct remote_auth_sasl_init_ret { + remote_nonnull_string mechlist; +}; + +struct remote_auth_sasl_start_args { + remote_nonnull_string mech; + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_sasl_start_ret { + int complete; + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_sasl_step_args { + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_sasl_step_ret { + int complete; + int nil; + char data<REMOTE_AUTH_SASL_DATA_MAX>; }; /*----- Protocol. -----*/ @@ -683,7 +727,11 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM = 62, REMOTE_PROC_DOMAIN_MIGRATE_FINISH = 63, REMOTE_PROC_DOMAIN_BLOCK_STATS = 64, - REMOTE_PROC_DOMAIN_INTERFACE_STATS = 65 + REMOTE_PROC_DOMAIN_INTERFACE_STATS = 65, + REMOTE_PROC_AUTH_LIST = 66, + REMOTE_PROC_AUTH_SASL_INIT = 67, + REMOTE_PROC_AUTH_SASL_START = 68, + REMOTE_PROC_AUTH_SASL_STEP = 69 }; /* Custom RPC structure. */ diff -r e24d542af4fb src/Makefile.am --- a/src/Makefile.am Fri Nov 30 14:31:10 2007 -0500 +++ b/src/Makefile.am Fri Nov 30 15:32:16 2007 -0500 @@ -5,6 +5,7 @@ INCLUDES = -I$(top_builddir)/include \ -I@top_srcdir@/qemud \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ + $(SASL_CFLAGS) \ -DBINDIR=\""$(libexecdir)"\" \ -DSBINDIR=\""$(sbindir)"\" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ @@ -59,7 +60,7 @@ SERVER_SOURCES = \ ../qemud/remote_protocol.c ../qemud/remote_protocol.h libvirt_la_SOURCES = $(CLIENT_SOURCES) $(SERVER_SOURCES) -libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(LTLIBOBJS) \ +libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(LTLIBOBJS) \ @CYGWIN_EXTRA_LIBADD@ libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \ -version-info @LIBVIRT_VERSION_INFO@ \ diff -r e24d542af4fb src/remote_internal.c --- a/src/remote_internal.c Fri Nov 30 14:31:10 2007 -0500 +++ b/src/remote_internal.c Fri Nov 30 15:32:16 2007 -0500 @@ -48,6 +48,9 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include "gnutls_1_0_compat.h" +#if HAVE_SASL +#include <sasl/sasl.h> +#endif #include <libxml/uri.h> #include "internal.h" @@ -72,6 +75,11 @@ struct private_data { char *type; /* Cached return from remoteType. */ int counter; /* Generates serial numbers for RPC. */ int networkOnly; /* Only used for network API */ + char *hostname; /* Original hostname */ + FILE *debugLog; /* Debug remote protocol */ +#if HAVE_SASL + sasl_conn_t *saslconn; /* SASL context */ +#endif }; #define GET_PRIVATE(conn,retcode) \ @@ -90,7 +98,20 @@ struct private_data { return (retcode); \ } -static int call (virConnectPtr conn, struct private_data *priv, int in_open, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret); +enum { + REMOTE_CALL_IN_OPEN = 1, + REMOTE_CALL_QUIET_MISSING_RPC = 2, +}; + + +static int call (virConnectPtr conn, struct private_data *priv, + int flags, int proc_nr, + xdrproc_t args_filter, char *args, + xdrproc_t ret_filter, char *ret); +static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open); +#if HAVE_SASL +static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open); +#endif static void error (virConnectPtr conn, virErrorNumber code, const char *info); static void server_error (virConnectPtr conn, remote_error *err); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); @@ -121,7 +142,7 @@ static void query_free (struct query_fie /* GnuTLS functions used by remoteOpen. */ static int initialise_gnutls (virConnectPtr conn); -static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, int sock, int no_verify, const char *hostname); +static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, struct private_data *priv, int no_verify); static int remoteStartup(void) @@ -132,6 +153,22 @@ remoteStartup(void) inside_daemon = 1; return 0; } + +#if HAVE_SASL +static void +remoteDebug(struct private_data *priv, const char *msg,...) +{ + va_list args; + if (priv->debugLog == NULL) + return; + + va_start(args, msg); + vfprintf(priv->debugLog, msg, args); + va_end(args); + fprintf(priv->debugLog, "\n"); +} +#endif /* HAVE_SASL */ + /** * remoteFindServerPath: @@ -297,7 +334,7 @@ doRemoteOpen (virConnectPtr conn, struct * get freed in the failed: path. */ char *name = 0, *command = 0, *sockname = 0, *netcat = 0, *username = 0; - char *server = 0, *port = 0; + char *port = 0; int no_verify = 0, no_tty = 0; char **cmd_argv = 0; @@ -305,12 +342,6 @@ doRemoteOpen (virConnectPtr conn, struct int retcode = VIR_DRV_OPEN_ERROR; /* Remote server defaults to "localhost" if not specified. */ - server = strdup (uri->server ? uri->server : "localhost"); - if (!server) { - out_of_memory: - error (conn, VIR_ERR_NO_MEMORY, "duplicating server name"); - goto failed; - } if (uri->port != 0) { if (asprintf (&port, "%d", uri->port) == -1) goto out_of_memory; } else if (transport == trans_tls) { @@ -325,6 +356,12 @@ doRemoteOpen (virConnectPtr conn, struct } else port = NULL; /* Port not used for unix, ext. */ + + priv->hostname = strdup (uri->server ? uri->server : "localhost"); + if (!priv->hostname) { + error (NULL, VIR_ERR_NO_MEMORY, "allocating priv->hostname"); + goto failed; + } if (uri->user) { username = strdup (uri->user); if (!username) goto out_of_memory; @@ -367,6 +404,12 @@ doRemoteOpen (virConnectPtr conn, struct } else if (strcasecmp (var->name, "no_tty") == 0) { no_tty = atoi (var->value); var->ignore = 1; + } else if (strcasecmp (var->name, "debug") == 0) { + if (var->value && + strcasecmp(var->value, "stdout") == 0) + priv->debugLog = stdout; + else + priv->debugLog = stderr; } #if DEBUG else @@ -436,7 +479,7 @@ doRemoteOpen (virConnectPtr conn, struct memset (&hints, 0, sizeof hints); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; - int e = getaddrinfo (server, port, &hints, &res); + int e = getaddrinfo (priv->hostname, port, &hints, &res); if (e != 0) { error (conn, VIR_ERR_INVALID_ARG, gai_strerror (e)); goto failed; @@ -476,7 +519,7 @@ doRemoteOpen (virConnectPtr conn, struct if (priv->uses_tls) { priv->session = negotiate_gnutls_on_connection - (conn, priv->sock, no_verify, server); + (conn, priv, no_verify); if (!priv->session) { close (priv->sock); priv->sock = -1; @@ -603,7 +646,7 @@ doRemoteOpen (virConnectPtr conn, struct cmd_argv[j++] = strdup ("-e"); cmd_argv[j++] = strdup ("none"); } - cmd_argv[j++] = strdup (server); + cmd_argv[j++] = strdup (priv->hostname); cmd_argv[j++] = strdup (netcat ? netcat : "nc"); cmd_argv[j++] = strdup ("-U"); cmd_argv[j++] = strdup (sockname ? sockname : LIBVIRTD_PRIV_UNIX_SOCKET); @@ -665,10 +708,15 @@ doRemoteOpen (virConnectPtr conn, struct } } /* switch (transport) */ + + /* Try and authenticate with server */ + if (remoteAuthenticate(conn, priv, 1) == -1) + goto failed; + /* Finally we can call the remote side's open function. */ remote_open_args args = { &name, flags }; - if (call (conn, priv, 1, REMOTE_PROC_OPEN, + if (call (conn, priv, REMOTE_CALL_IN_OPEN, REMOTE_PROC_OPEN, (xdrproc_t) xdr_remote_open_args, (char *) &args, (xdrproc_t) xdr_void, (char *) NULL) == -1) goto failed; @@ -676,12 +724,35 @@ doRemoteOpen (virConnectPtr conn, struct /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; - /*FALLTHROUGH*/ + cleanup: + /* Free up the URL and strings. */ + if (name) free (name); + if (command) free (command); + if (sockname) free (sockname); + if (netcat) free (netcat); + if (username) free (username); + if (port) free (port); + if (cmd_argv) { + char **cmd_argv_ptr = cmd_argv; + while (*cmd_argv_ptr) { + free (*cmd_argv_ptr); + cmd_argv_ptr++; + } + free (cmd_argv); + } + + return retcode; + + out_of_memory: + error (NULL, VIR_ERR_NO_MEMORY, "uri params"); + failed: /* Close the socket if we failed. */ - if (retcode != VIR_DRV_OPEN_SUCCESS && priv->sock >= 0) { - if (priv->uses_tls && priv->session) + if (priv->sock >= 0) { + if (priv->uses_tls && priv->session) { gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); + gnutls_deinit (priv->session); + } close (priv->sock); if (priv->pid > 0) { pid_t reap; @@ -693,24 +764,12 @@ doRemoteOpen (virConnectPtr conn, struct } } - /* Free up the URL and strings. */ - if (name) free (name); - if (command) free (command); - if (sockname) free (sockname); - if (netcat) free (netcat); - if (username) free (username); - if (server) free (server); - if (port) free (port); - if (cmd_argv) { - char **cmd_argv_ptr = cmd_argv; - while (*cmd_argv_ptr) { - free (*cmd_argv_ptr); - cmd_argv_ptr++; - } - free (cmd_argv); - } - - return retcode; + if (priv->hostname) { + free (priv->hostname); + priv->hostname = NULL; + } + + goto cleanup; } static int @@ -1017,11 +1076,12 @@ initialise_gnutls (virConnectPtr conn) return 0; } -static int verify_certificate (virConnectPtr conn, gnutls_session_t session, const char *hostname); +static int verify_certificate (virConnectPtr conn, struct private_data *priv, gnutls_session_t session); static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, - int sock, int no_verify, const char *hostname) + struct private_data *priv, + int no_verify) { const int cert_type_priority[3] = { GNUTLS_CRT_X509, @@ -1062,7 +1122,7 @@ negotiate_gnutls_on_connection (virConne } gnutls_transport_set_ptr (session, - (gnutls_transport_ptr_t) (long) sock); + (gnutls_transport_ptr_t) (long) priv->sock); /* Perform the TLS handshake. */ again: @@ -1075,7 +1135,7 @@ negotiate_gnutls_on_connection (virConne } /* Verify certificate. */ - if (verify_certificate (conn, session, hostname) == -1) { + if (verify_certificate (conn, priv, session) == -1) { fprintf (stderr, "remote_internal: failed to verify peer's certificate\n"); if (!no_verify) return NULL; @@ -1110,8 +1170,8 @@ negotiate_gnutls_on_connection (virConne static int verify_certificate (virConnectPtr conn ATTRIBUTE_UNUSED, - gnutls_session_t session, - const char *hostname) + struct private_data *priv, + gnutls_session_t session) { int ret; unsigned int status; @@ -1189,14 +1249,14 @@ verify_certificate (virConnectPtr conn A } if (i == 0) { - if (!gnutls_x509_crt_check_hostname (cert, hostname)) { + if (!gnutls_x509_crt_check_hostname (cert, priv->hostname)) { __virRaiseError (conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, - VIR_ERR_ERROR, hostname, NULL, NULL, + VIR_ERR_ERROR, priv->hostname, NULL, NULL, 0, 0, "Certificate's owner does not match the hostname (%s)", - hostname); + priv->hostname); gnutls_x509_crt_deinit (cert); return -1; } @@ -1218,8 +1278,14 @@ doRemoteClose (virConnectPtr conn, struc return -1; /* Close socket. */ - if (priv->uses_tls && priv->session) + if (priv->uses_tls && priv->session) { gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); + gnutls_deinit (priv->session); + } +#if HAVE_SASL + if (priv->saslconn) + sasl_dispose (&priv->saslconn); +#endif close (priv->sock); if (priv->pid > 0) { @@ -1230,6 +1296,9 @@ doRemoteClose (virConnectPtr conn, struc continue; } while (reap != -1 && reap != priv->pid); } + + /* Free hostname copy */ + if (priv->hostname) free (priv->hostname); /* See comment for remoteType. */ if (priv->type) free (priv->type); @@ -2751,6 +2820,298 @@ remoteNetworkSetAutostart (virNetworkPtr /*----------------------------------------------------------------------*/ +static int +remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open) +{ + struct remote_auth_list_ret ret; + int err; + + memset(&ret, 0, sizeof ret); + err = call (conn, priv, + REMOTE_CALL_IN_OPEN | REMOTE_CALL_QUIET_MISSING_RPC, + REMOTE_PROC_AUTH_LIST, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_auth_list_ret, (char *) &ret); + if (err == -2) /* Missing RPC - old server - ignore */ + return 0; + + if (err < 0) + return -1; + + if (ret.types.types_len == 0) + return 0; + + switch (ret.types.types_val[0]) { +#if HAVE_SASL + case REMOTE_AUTH_SASL: + if (remoteAuthSASL(conn, priv, in_open) < 0) { + free(ret.types.types_val); + return -1; + } + break; +#endif + + case REMOTE_AUTH_NONE: + /* Nothing todo, hurrah ! */ + break; + + default: + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "unsupported authentication type %d", ret.types.types_val[0]); + free(ret.types.types_val); + return -1; + } + + free(ret.types.types_val); + + return 0; +} + + + +#if HAVE_SASL +/* + * NB, keep in sync with similar method in qemud/remote.c + */ +static char *addrToString(struct sockaddr_storage *sa, socklen_t salen) +{ + char host[NI_MAXHOST], port[NI_MAXSERV]; + char *addr; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + __virRaiseError (NULL, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_NO_MEMORY, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Cannot resolve address %d: %s", err, gai_strerror(err)); + return NULL; + } + + addr = malloc(strlen(host) + 1 + strlen(port) + 1); + if (!addr) { + __virRaiseError (NULL, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_NO_MEMORY, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "address"); + return NULL; + } + + strcpy(addr, host); + strcat(addr, ";"); + strcat(addr, port); + return addr; +} + + +/* Perform the SASL authentication process + * + * XXX negotiate a session encryption layer for non-TLS sockets + * XXX fetch credentials from a libvirt client app callback + * XXX max packet size spec + * XXX better mechanism negotiation ? Ask client app ? + */ +static int +remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open) +{ + sasl_conn_t *saslconn = NULL; + remote_auth_sasl_init_ret iret; + remote_auth_sasl_start_args sargs; + remote_auth_sasl_start_ret sret; + remote_auth_sasl_step_args pargs; + remote_auth_sasl_step_ret pret; + const char *clientout; + char *serverin; + unsigned int clientoutlen, serverinlen; + const char *mech; + int err, complete; + struct sockaddr_storage sa; + socklen_t salen; + char *localAddr, *remoteAddr; + + remoteDebug(priv, "Client initialize SASL authentication"); + /* Sets up the SASL library as a whole */ + err = sasl_client_init(NULL); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "failed to initialize SASL library: %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + return -1; + } + + /* Get local address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getsockname(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "failed to get sock address %d (%s)", + errno, strerror(errno)); + return -1; + } + if ((localAddr = addrToString(&sa, salen)) == NULL) { + return -1; + } + + /* Get remote address in form IPADDR:PORT */ + salen = sizeof(sa); + if (getpeername(priv->sock, (struct sockaddr*)&sa, &salen) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "failed to get peer address %d (%s)", + errno, strerror(errno)); + free(localAddr); + return -1; + } + if ((remoteAddr = addrToString(&sa, salen)) == NULL) { + free(localAddr); + return -1; + } + + /* Setup a handle for being a client */ + err = sasl_client_new("libvirt", + priv->hostname, + localAddr, + remoteAddr, + NULL, /* XXX callbacks */ + SASL_SUCCESS_DATA, + &saslconn); + free(localAddr); + free(remoteAddr); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to create SASL client context: %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + return -1; + } + + /* First call is to inquire about supported mechanisms in the server */ + memset (&iret, 0, sizeof iret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_INIT, + (xdrproc_t) xdr_void, (char *)NULL, + (xdrproc_t) xdr_remote_auth_sasl_init_ret, (char *) &iret) != 0) { + sasl_dispose(&saslconn); + return -1; /* virError already set by call */ + } + + + /* Start the auth negotiation on the client end first */ + remoteDebug(priv, "Client start negotiation mechlist '%s'", iret.mechlist); + err = sasl_client_start(saslconn, + iret.mechlist, + NULL, /* XXX interactions */ + &clientout, + &clientoutlen, + &mech); + if (err != SASL_OK && err != SASL_CONTINUE) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to start SASL negotiation: %d (%s)", + err, sasl_errdetail(saslconn)); + free(iret.mechlist); + sasl_dispose(&saslconn); + return -1; + } + free(iret.mechlist); + + if (clientoutlen > REMOTE_AUTH_SASL_DATA_MAX) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "SASL negotiation data too long: %d bytes", clientoutlen); + sasl_dispose(&saslconn); + return -1; + } + /* NB, distinction of NULL vs "" is *critical* in SASL */ + memset(&sargs, 0, sizeof sargs); + sargs.nil = clientout ? 0 : 1; + sargs.data.data_val = (char*)clientout; + sargs.data.data_len = clientoutlen; + sargs.mech = (char*)mech; + remoteDebug(priv, "Server start negotiation with mech %s. Data %d bytes %p", mech, clientoutlen, clientout); + + /* Now send the initial auth data to the server */ + memset (&sret, 0, sizeof sret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_START, + (xdrproc_t) xdr_remote_auth_sasl_start_args, (char *) &sargs, + (xdrproc_t) xdr_remote_auth_sasl_start_ret, (char *) &sret) != 0) { + sasl_dispose(&saslconn); + return -1; /* virError already set by call */ + } + + complete = sret.complete; + /* NB, distinction of NULL vs "" is *critical* in SASL */ + serverin = sret.nil ? NULL : sret.data.data_val; + serverinlen = sret.data.data_len; + remoteDebug(priv, "Client step result complete: %d. Data %d bytes %p", + complete, serverinlen, serverin); + + /* Loop-the-loop... + * Even if the server has completed, the client must *always* do at least one step + * in this loop to verify the server isn't lieing about something. Mutual auth */ + for (;;) { + err = sasl_client_step(saslconn, + serverin, + serverinlen, + NULL, /* XXX interactions */ + &clientout, + &clientoutlen); + if (serverin) free(serverin); + if (err != SASL_OK && err != SASL_CONTINUE) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed SASL step: %d (%s)", + err, sasl_errdetail(saslconn)); + sasl_dispose(&saslconn); + return -1; + } + remoteDebug(priv, "Client step result %d. Data %d bytes %p", err, clientoutlen, clientout); + + /* Previous server call showed completion & we're now locally complete too */ + if (complete && err == SASL_OK) + break; + + /* Not done, prepare to talk with the server for another iteration */ + /* NB, distinction of NULL vs "" is *critical* in SASL */ + memset(&pargs, 0, sizeof pargs); + pargs.nil = clientout ? 0 : 1; + pargs.data.data_val = (char*)clientout; + pargs.data.data_len = clientoutlen; + remoteDebug(priv, "Server step with %d bytes %p", clientoutlen, clientout); + + memset (&pret, 0, sizeof pret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_STEP, + (xdrproc_t) xdr_remote_auth_sasl_step_args, (char *) &pargs, + (xdrproc_t) xdr_remote_auth_sasl_step_ret, (char *) &pret) != 0) { + sasl_dispose(&saslconn); + return -1; /* virError already set by call */ + } + + complete = pret.complete; + /* NB, distinction of NULL vs "" is *critical* in SASL */ + serverin = pret.nil ? NULL : pret.data.data_val; + serverinlen = pret.data.data_len; + + remoteDebug(priv, "Client step result complete: %d. Data %d bytes %p", + complete, serverinlen, serverin); + + /* This server call shows complete, and earlier client step was OK */ + if (complete && err == SASL_OK) { + if (serverin) free(serverin); + break; + } + } + + remoteDebug(priv, "SASL authentication complete"); + /* XXX keep this around for wire encoding */ + sasl_dispose(&saslconn); + return 0; +} +#endif /* HAVE_SASL */ + +/*----------------------------------------------------------------------*/ + static int really_write (virConnectPtr conn, struct private_data *priv, int in_open, char *bytes, int len); static int really_read (virConnectPtr conn, struct private_data *priv, @@ -2768,7 +3129,7 @@ static int really_read (virConnectPtr co */ static int call (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, + int flags /* if we are in virConnectOpen */, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret) @@ -2793,13 +3154,13 @@ call (virConnectPtr conn, struct private /* Serialise header followed by args. */ xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE); if (!xdr_remote_message_header (&xdr, &hdr)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_remote_message_header"); return -1; } if (!(*args_filter) (&xdr, args)) { - error (in_open ? NULL : conn, VIR_ERR_RPC, "marshalling args"); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "marshalling args"); return -1; } @@ -2815,23 +3176,23 @@ call (virConnectPtr conn, struct private /* Encode the length word. */ xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_ENCODE); if (!xdr_int (&xdr, &len)) { - error (in_open ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word)"); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word)"); return -1; } xdr_destroy (&xdr); /* Send length word followed by header+args. */ - if (really_write (conn, priv, in_open, buffer2, sizeof buffer2) == -1 || - really_write (conn, priv, in_open, buffer, len-4) == -1) + if (really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1 || + really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len-4) == -1) return -1; /* Read and deserialise length word. */ - if (really_read (conn, priv, in_open, buffer2, sizeof buffer2) == -1) + if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1) return -1; xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); if (!xdr_int (&xdr, &len)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_int (length word, reply)"); return -1; } @@ -2841,33 +3202,33 @@ call (virConnectPtr conn, struct private len -= 4; if (len < 0 || len > REMOTE_MESSAGE_MAX) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "packet received from server too large"); return -1; } /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, in_open, buffer, len) == -1) + if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len) == -1) return -1; /* Deserialise reply header. */ xdrmem_create (&xdr, buffer, len, XDR_DECODE); if (!xdr_remote_message_header (&xdr, &hdr)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "xdr_remote_message_header (reply)"); return -1; } /* Check program, version, etc. are what we expect. */ if (hdr.prog != REMOTE_PROGRAM) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown program (received %x, expected %x)", hdr.prog, REMOTE_PROGRAM); return -1; } if (hdr.vers != REMOTE_PROTOCOL_VERSION) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown protocol version (received %x, expected %x)", hdr.vers, REMOTE_PROTOCOL_VERSION); @@ -2879,21 +3240,21 @@ call (virConnectPtr conn, struct private * message being received at this point. */ if (hdr.proc != proc_nr) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown procedure (received %x, expected %x)", hdr.proc, proc_nr); return -1; } if (hdr.direction != REMOTE_REPLY) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown direction (received %x, expected %x)", hdr.direction, REMOTE_REPLY); return -1; } if (hdr.serial != serial) { - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown serial (received %x, expected %x)", hdr.serial, serial); @@ -2907,7 +3268,7 @@ call (virConnectPtr conn, struct private switch (hdr.status) { case REMOTE_OK: if (!(*ret_filter) (&xdr, ret)) { - error (in_open ? NULL : conn, VIR_ERR_RPC, "unmarshalling ret"); + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "unmarshalling ret"); return -1; } xdr_destroy (&xdr); @@ -2916,17 +3277,26 @@ call (virConnectPtr conn, struct private case REMOTE_ERROR: memset (&rerror, 0, sizeof rerror); if (!xdr_remote_error (&xdr, &rerror)) { - error (in_open ? NULL : conn, + error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, "unmarshalling remote_error"); return -1; } xdr_destroy (&xdr); - server_error (in_open ? NULL : conn, &rerror); + /* See if caller asked us to keep quiet about missing RPCs + * eg for interop with older servers */ + if (flags & REMOTE_CALL_QUIET_MISSING_RPC && + rerror.domain == VIR_FROM_REMOTE && + rerror.code == VIR_ERR_RPC && + rerror.level == VIR_ERR_ERROR && + STREQLEN(*rerror.message, "unknown procedure", 17)) { + return -2; + } + server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, &rerror); xdr_free ((xdrproc_t) xdr_remote_error, (char *) &rerror); return -1; default: - __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "unknown status (received %x)", hdr.status); diff -r e24d542af4fb src/virsh.c --- a/src/virsh.c Fri Nov 30 14:31:10 2007 -0500 +++ b/src/virsh.c Fri Nov 30 15:32:16 2007 -0500 @@ -4526,8 +4526,10 @@ vshInit(vshControl * ctl) * vshConnectionUsability, except ones which don't need a connection * such as "help". */ - if (!ctl->conn) + if (!ctl->conn) { vshError(ctl, FALSE, _("failed to connect to the hypervisor")); + return FALSE; + } return TRUE; } diff -r e24d542af4fb src/virterror.c --- a/src/virterror.c Fri Nov 30 14:31:10 2007 -0500 +++ b/src/virterror.c Fri Nov 30 15:32:16 2007 -0500 @@ -671,6 +671,12 @@ __virErrorMsg(virErrorNumber error, cons else errmsg = _("invalid MAC adress: %s"); break; + case VIR_ERR_AUTH_FAILED: + if (info == NULL) + errmsg = _("authentication failed"); + else + errmsg = _("authentication failed: %s"); + break; } return (errmsg); } diff -r e24d542af4fb tests/Makefile.am --- a/tests/Makefile.am Fri Nov 30 14:31:10 2007 -0500 +++ b/tests/Makefile.am Fri Nov 30 15:32:16 2007 -0500 @@ -17,6 +17,7 @@ INCLUDES = \ -I$(top_srcdir)/src \ $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) \ + $(SASL_CFLAGS) \ -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=199506L \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" \ $(COVERAGE_CFLAGS) \ @@ -27,6 +28,7 @@ LDADDS = \ @STATIC_BINARIES@ \ $(LIBXML_LIBS) \ $(GNUTLS_LIBS) \ + $(SASL_LIBS) \ $(WARN_CFLAGS) \ $(LIBVIRT) \ $(COVERAGE_LDFLAGS) Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Fri, Nov 30, 2007 at 10:32:40PM +0000, Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:16:34PM +0000, Daniel P. Berrange wrote:
This patch hooks up the basic authentication RPC calls, and the specific SASL implementation. The SASL impl can be enabled/disable via the configurre script with --without-sasl / --with-sasl - it'll auto-enable it if it finds the headers & libs OK.
The sample /etc/sasl2/libvirt.conf file enables the DIGEST-MD5 mechanism by default, since it is by far the easiest to setup for admins. No need for a Kerberos server, or certificates - it just uses username/password which can be set with 'saslpasswd2 -a libvirt [username]' and a list of all active users viewed with 'sasldblistusers2 -a libvirt'
There are also example settings for enabling Kerberos (GSSAPI) but this is disabled by default. It requires a file /etc/libvirt/krb5.tab containing a service principle. On some distros you need to set KRB5_KTNAME to point to this file when starting the daemon, so our init script does that. Other distros, the 'keytab' config param in /etc/sasl2/libvirt.conf is actually honoured.
With this patch you can successfully authentication client <-> server for any authentication mechansim which doesn't need to prompt the user for credentials. In effect this means it only works for GSSAPI/Kerberos, but the later patches in this series will enable callbacks making the default DIGEST-MD5 auth work.
The way auth is controlled, is that if the 'auth' parameter is set on the struct qemud_client object, *NO* rpc call will be processed except for the REMOTE_PROC_AUTH_LIST, SASL_AUTH_INIT, SASL_AUTH_START & SASL_AUTH_STEP calls. If SASL is not compiled in, the latter 3 will send errors back to the caller.
Only once authentication is complete, are the other calls allowed. It currently hardcodes use of SASL on the TCP socket. The TLS & UNIX sockets are unchanged. A subsequent patch will make it configurable.
Updated to add constants for getnameinfo() params as Jim suggested. Also remove the dep on cyrus-sasl-gssapi, and add one on cyrus-sasl-md5
Okay looks good to me, +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Dec 03, 2007 at 04:24:51AM -0500, Daniel Veillard wrote:
On Fri, Nov 30, 2007 at 10:32:40PM +0000, Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:16:34PM +0000, Daniel P. Berrange wrote:
This patch hooks up the basic authentication RPC calls, and the specific SASL implementation. The SASL impl can be enabled/disable via the configurre script with --without-sasl / --with-sasl - it'll auto-enable it if it finds the headers & libs OK.
The sample /etc/sasl2/libvirt.conf file enables the DIGEST-MD5 mechanism by default, since it is by far the easiest to setup for admins. No need for a Kerberos server, or certificates - it just uses username/password which can be set with 'saslpasswd2 -a libvirt [username]' and a list of all active users viewed with 'sasldblistusers2 -a libvirt'
There are also example settings for enabling Kerberos (GSSAPI) but this is disabled by default. It requires a file /etc/libvirt/krb5.tab containing a service principle. On some distros you need to set KRB5_KTNAME to point to this file when starting the daemon, so our init script does that. Other distros, the 'keytab' config param in /etc/sasl2/libvirt.conf is actually honoured.
With this patch you can successfully authentication client <-> server for any authentication mechansim which doesn't need to prompt the user for credentials. In effect this means it only works for GSSAPI/Kerberos, but the later patches in this series will enable callbacks making the default DIGEST-MD5 auth work.
The way auth is controlled, is that if the 'auth' parameter is set on the struct qemud_client object, *NO* rpc call will be processed except for the REMOTE_PROC_AUTH_LIST, SASL_AUTH_INIT, SASL_AUTH_START & SASL_AUTH_STEP calls. If SASL is not compiled in, the latter 3 will send errors back to the caller.
Only once authentication is complete, are the other calls allowed. It currently hardcodes use of SASL on the TCP socket. The TLS & UNIX sockets are unchanged. A subsequent patch will make it configurable.
Updated to add constants for getnameinfo() params as Jim suggested. Also remove the dep on cyrus-sasl-gssapi, and add one on cyrus-sasl-md5
Okay looks good to me, +1
This patch is now committed. Regards, Dan -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

With the TLS socket, all data is encrypted on the wire. The TCP socket though is still clear text. Fortunately some SASL authentication mechanism can also supply encryption capabilities. This is called SSF in SASL terminology. This patch mandates the use of an SSF capable SASL authentiction mechanism on the TCP socket. This in effect restricts you to a choise between GSSAPI and DIGEST-MD5 as your SASL auth methods (the latter is a user/password based scheme). We also disallow anonymous & plaintext auth methods. If you really want to run the TCP socket in clear text & with anonymous auth, simply turn off SASL altogether. Since TLS already provides encryptiuon, if you run SASL over the TLS socket, we don't place any restrictions on the choice of SASL auth mechanism. On the server side I have removed the 'direction' field of the client object. This was only used on the TLS socket & was intended to track whether the handshake process was waiting to receive or send. Rather than try to set this in various places throughout the daemon code, we simply query the neccessary direction at the one point where we register a FD event handle with poll(). This makes the code clearer to follow & reduces the chance of accidentally messing up the state. The send & receive functions previously would call read vs gnutls_record_recv and write vs gnutls_record_send depending on the type of socket. If there is a SASL SSF layer enabled, we have to first pass the outgoing data through the sasl_encode() API, and pass incoming data through sasl_decode(). So the send/recive APIs have changed a little, to deal with this codec need and thus there is also some more state being tracked per connection - we may have to cache the output for sasl_decode for future calls depending on how much data we need in short term. NB, the SSF layer lets you choose a strength factor from 0 -> a large number and the docs all talk about * 0 = no protection * 1 = integrity protection only * 40 = 40-bit DES or 40-bit RC2/RC4 * 56 = DES * 112 = triple-DES * 128 = 128-bit RC2/RC4/BLOWFISH * 256 = baseline AES This is incredibly misleading. The GSSAPI mechanism in SASL will never report a strength of anything other than 56. Even if it is using triple-DES. The true strength of the GSSAPI/Kerberos impl is impossible to figure out from the SASL apis. To ensure that Kerberos uses strong encryption, you need to make sure that the Kerberos principles issued only have the 3-DES cipher/keys present. If you are truely paranoid though, you always have the option of using TLS (which gives at least 128 bit ciphers, often 256 bit), and then just using Kerberos for auth and ignore the SASL SSF layer. A subsequent patch will make it possible to configure this stuff. qemud/internal.h | 31 +++-- qemud/qemud.c | 248 +++++++++++++++++++++++++++++++++----------- qemud/remote.c | 106 ++++++++++++++++++ src/remote_internal.c | 279 ++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 538 insertions(+), 126 deletions(-) diff -r 5a37498017ac qemud/internal.h --- a/qemud/internal.h Wed Nov 28 23:00:04 2007 -0500 +++ b/qemud/internal.h Wed Nov 28 23:00:47 2007 -0500 @@ -73,10 +73,17 @@ enum qemud_mode { QEMUD_MODE_TLS_HANDSHAKE, }; -/* These have to remain compatible with gnutls_record_get_direction. */ -enum qemud_tls_direction { - QEMUD_TLS_DIRECTION_READ = 0, - QEMUD_TLS_DIRECTION_WRITE = 1, +/* Whether we're passing reads & writes through a sasl SSF */ +enum qemud_sasl_ssf { + QEMUD_SASL_SSF_NONE = 0, + QEMUD_SASL_SSF_READ = 1, + QEMUD_SASL_SSF_WRITE = 2, +}; + +enum qemud_sock_type { + QEMUD_SOCK_TYPE_UNIX = 0, + QEMUD_SOCK_TYPE_TCP = 1, + QEMUD_SOCK_TYPE_TLS = 2, }; /* Stores the per-client connection state */ @@ -90,13 +97,18 @@ struct qemud_client { struct sockaddr_storage addr; socklen_t addrlen; - /* If set, TLS is required on this socket. */ - int tls; - gnutls_session_t session; - enum qemud_tls_direction direction; + int type; /* qemud_sock_type */ + gnutls_session_t tlssession; int auth; #if HAVE_SASL sasl_conn_t *saslconn; + int saslSSF; + const char *saslDecoded; + unsigned int saslDecodedLength; + unsigned int saslDecodedOffset; + const char *saslEncoded; + unsigned int saslEncodedLength; + unsigned int saslEncodedOffset; #endif unsigned int incomingSerial; @@ -121,8 +133,7 @@ struct qemud_socket { struct qemud_socket { int fd; int readonly; - /* If set, TLS is required on this socket. */ - int tls; + int type; /* qemud_sock_type */ int auth; int port; struct qemud_socket *next; diff -r 5a37498017ac qemud/qemud.c --- a/qemud/qemud.c Wed Nov 28 23:00:04 2007 -0500 +++ b/qemud/qemud.c Wed Nov 28 23:00:47 2007 -0500 @@ -463,6 +463,7 @@ static int qemudListenUnix(struct qemud_ sock->readonly = readonly; sock->port = -1; + sock->type = QEMUD_SOCK_TYPE_UNIX; if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { qemudLog(QEMUD_ERR, "Failed to create socket: %s", @@ -577,7 +578,7 @@ static int static int remoteListenTCP (struct qemud_server *server, const char *port, - int tls, + int type, int auth) { int fds[2]; @@ -606,7 +607,7 @@ remoteListenTCP (struct qemud_server *se server->nsockets++; sock->fd = fds[i]; - sock->tls = tls; + sock->type = type; sock->auth = auth; if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0) @@ -745,10 +746,10 @@ static struct qemud_server *qemudInitial if (ipsock) { #if HAVE_SASL - if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_SASL) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_SASL) < 0) goto cleanup; #else - if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_NONE) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_NONE) < 0) goto cleanup; #endif @@ -756,7 +757,7 @@ static struct qemud_server *qemudInitial if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0) + if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, REMOTE_AUTH_NONE) < 0) goto cleanup; } } @@ -789,7 +790,7 @@ static struct qemud_server *qemudInitial */ sock = server->sockets; while (sock) { - if (sock->port != -1 && sock->tls) { + if (sock->port != -1 && sock->type == QEMUD_SOCK_TYPE_TLS) { port = sock->port; break; } @@ -981,7 +982,7 @@ remoteCheckAccess (struct qemud_client * int found, err; /* Verify client certificate. */ - if (remoteCheckCertificate (client->session) == -1) { + if (remoteCheckCertificate (client->tlssession) == -1) { qemudLog (QEMUD_ERR, "remoteCheckCertificate: failed to verify client's certificate"); if (!tls_no_verify_certificate) return -1; else qemudLog (QEMUD_INFO, "remoteCheckCertificate: tls_no_verify_certificate is set so the bad certificate is ignored"); @@ -1033,7 +1034,6 @@ remoteCheckAccess (struct qemud_client * client->bufferOffset = 0; client->buffer[0] = '\1'; client->mode = QEMUD_MODE_TX_PACKET; - client->direction = QEMUD_TLS_DIRECTION_WRITE; return 0; } @@ -1065,12 +1065,12 @@ static int qemudDispatchServer(struct qe client->magic = QEMUD_CLIENT_MAGIC; client->fd = fd; client->readonly = sock->readonly; - client->tls = sock->tls; + client->type = sock->type; client->auth = sock->auth; memcpy (&client->addr, &addr, sizeof addr); client->addrlen = addrlen; - if (!client->tls) { + if (client->type != QEMUD_SOCK_TYPE_TLS) { client->mode = QEMUD_MODE_RX_HEADER; client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; @@ -1079,15 +1079,15 @@ static int qemudDispatchServer(struct qe } else { int ret; - client->session = remoteInitializeTLSSession (); - if (client->session == NULL) + client->tlssession = remoteInitializeTLSSession (); + if (client->tlssession == NULL) goto cleanup; - gnutls_transport_set_ptr (client->session, + gnutls_transport_set_ptr (client->tlssession, (gnutls_transport_ptr_t) (long) fd); /* Begin the TLS handshake. */ - ret = gnutls_handshake (client->session); + ret = gnutls_handshake (client->tlssession); if (ret == 0) { /* Unlikely, but ... Next step is to check the certificate. */ if (remoteCheckAccess (client) == -1) @@ -1099,7 +1099,6 @@ static int qemudDispatchServer(struct qe /* Most likely. */ client->mode = QEMUD_MODE_TLS_HANDSHAKE; client->bufferLength = -1; - client->direction = gnutls_record_get_direction (client->session); if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; @@ -1117,7 +1116,7 @@ static int qemudDispatchServer(struct qe return 0; cleanup: - if (client->session) gnutls_deinit (client->session); + if (client->tlssession) gnutls_deinit (client->tlssession); close (fd); free (client); return -1; @@ -1150,24 +1149,21 @@ static void qemudDispatchClientFailure(s #if HAVE_SASL if (client->saslconn) sasl_dispose(&client->saslconn); #endif - if (client->tls && client->session) gnutls_deinit (client->session); + if (client->tlssession) gnutls_deinit (client->tlssession); close(client->fd); free(client); } -static int qemudClientRead(struct qemud_server *server, - struct qemud_client *client) { - int ret, len; - char *data; - - data = client->buffer + client->bufferOffset; - len = client->bufferLength - client->bufferOffset; +static int qemudClientReadBuf(struct qemud_server *server, + struct qemud_client *client, + char *data, unsigned len) { + int ret; /*qemudDebug ("qemudClientRead: len = %d", len);*/ - if (!client->tls) { + if (!client->tlssession) { if ((ret = read (client->fd, data, len)) <= 0) { if (ret == 0 || errno != EAGAIN) { if (ret != 0) @@ -1177,8 +1173,7 @@ static int qemudClientRead(struct qemud_ return -1; } } else { - ret = gnutls_record_recv (client->session, data, len); - client->direction = gnutls_record_get_direction (client->session); + ret = gnutls_record_recv (client->tlssession, data, len); if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); else if (ret <= 0) { @@ -1193,9 +1188,79 @@ static int qemudClientRead(struct qemud_ } } + return ret; +} + +static int qemudClientReadPlain(struct qemud_server *server, + struct qemud_client *client) { + int ret; + ret = qemudClientReadBuf(server, client, + client->buffer + client->bufferOffset, + client->bufferLength - client->bufferOffset); + if (ret < 0) + return ret; client->bufferOffset += ret; return 0; } + +#if HAVE_SASL +static int qemudClientReadSASL(struct qemud_server *server, + struct qemud_client *client) { + int got, want; + + /* We're doing a SSF data read, so now its times to ensure + * future writes are under SSF too. + * + * cf remoteSASLCheckSSF in remote.c + */ + client->saslSSF |= QEMUD_SASL_SSF_WRITE; + + /* Need to read some more data off the wire */ + if (client->saslDecoded == NULL) { + char encoded[8192]; + int encodedLen = sizeof(encoded); + encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen); + + if (encodedLen < 0) + return -1; + + sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + + client->saslDecodedOffset = 0; + } + + /* Some buffered decoded data to return now */ + got = client->saslDecodedLength - client->saslDecodedOffset; + want = client->bufferLength - client->bufferOffset; + + if (want > got) + want = got; + + memcpy(client->buffer + client->bufferOffset, + client->saslDecoded + client->saslDecodedOffset, want); + client->saslDecodedOffset += want; + client->bufferOffset += want; + + if (client->saslDecodedOffset == client->saslDecodedLength) { + client->saslDecoded = NULL; + client->saslDecodedOffset = client->saslDecodedLength = 0; + } + + return 0; +} +#endif + +static int qemudClientRead(struct qemud_server *server, + struct qemud_client *client) { +#if HAVE_SASL + if (client->saslSSF & QEMUD_SASL_SSF_READ) + return qemudClientReadSASL(server, client); + else +#endif + return qemudClientReadPlain(server, client); +} + static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { @@ -1239,7 +1304,6 @@ static void qemudDispatchClientRead(stru client->mode = QEMUD_MODE_RX_PAYLOAD; client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ; if (qemudRegisterClientEvent(server, client, 1) < 0) { qemudDispatchClientFailure(server, client); @@ -1267,7 +1331,7 @@ static void qemudDispatchClientRead(stru int ret; /* Continue the handshake. */ - ret = gnutls_handshake (client->session); + ret = gnutls_handshake (client->tlssession); if (ret == 0) { /* Finished. Next step is to check the certificate. */ if (remoteCheckAccess (client) == -1) @@ -1279,7 +1343,6 @@ static void qemudDispatchClientRead(stru gnutls_strerror (ret)); qemudDispatchClientFailure (server, client); } else { - client->direction = gnutls_record_get_direction (client->session); if (qemudRegisterClientEvent (server ,client, 1) < 0) qemudDispatchClientFailure (server, client); } @@ -1294,15 +1357,11 @@ static void qemudDispatchClientRead(stru } -static int qemudClientWrite(struct qemud_server *server, - struct qemud_client *client) { - int ret, len; - char *data; - - data = client->buffer + client->bufferOffset; - len = client->bufferLength - client->bufferOffset; - - if (!client->tls) { +static int qemudClientWriteBuf(struct qemud_server *server, + struct qemud_client *client, + const char *data, int len) { + int ret; + if (!client->tlssession) { if ((ret = write(client->fd, data, len)) == -1) { if (errno != EAGAIN) { qemudLog (QEMUD_ERR, "write: %s", strerror (errno)); @@ -1311,8 +1370,7 @@ static int qemudClientWrite(struct qemud return -1; } } else { - ret = gnutls_record_send (client->session, data, len); - client->direction = gnutls_record_get_direction (client->session); + ret = gnutls_record_send (client->tlssession, data, len); if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); else if (ret < 0) { @@ -1324,9 +1382,69 @@ static int qemudClientWrite(struct qemud return -1; } } - + return ret; +} + + +static int qemudClientWritePlain(struct qemud_server *server, + struct qemud_client *client) { + int ret = qemudClientWriteBuf(server, client, + client->buffer + client->bufferOffset, + client->bufferLength - client->bufferOffset); + if (ret < 0) + return -1; client->bufferOffset += ret; return 0; +} + + +#if HAVE_SASL +static int qemudClientWriteSASL(struct qemud_server *server, + struct qemud_client *client) { + int ret; + + /* Not got any pending encoded data, so we need to encode raw stuff */ + if (client->saslEncoded == NULL) { + int err; + err = sasl_encode(client->saslconn, + client->buffer + client->bufferOffset, + client->bufferLength - client->bufferOffset, + &client->saslEncoded, + &client->saslEncodedLength); + + client->saslEncodedOffset = 0; + } + + /* Send some of the encoded stuff out on the wire */ + ret = qemudClientWriteBuf(server, client, + client->saslEncoded + client->saslEncodedOffset, + client->saslEncodedLength - client->saslEncodedOffset); + + if (ret < 0) + return -1; + + /* Note how much we sent */ + client->saslEncodedOffset += ret; + + /* Sent all encoded, so update raw buffer to indicate completion */ + if (client->saslEncodedOffset == client->saslEncodedLength) { + client->saslEncoded = NULL; + client->saslEncodedOffset = client->saslEncodedLength = 0; + client->bufferOffset = client->bufferLength; + } + + return 0; +} +#endif + +static int qemudClientWrite(struct qemud_server *server, + struct qemud_client *client) { +#if HAVE_SASL + if (client->saslSSF & QEMUD_SASL_SSF_WRITE) + return qemudClientWriteSASL(server, client); + else +#endif + return qemudClientWritePlain(server, client); } @@ -1341,7 +1459,6 @@ static void qemudDispatchClientWrite(str client->mode = QEMUD_MODE_RX_HEADER; client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ; if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); @@ -1354,7 +1471,7 @@ static void qemudDispatchClientWrite(str int ret; /* Continue the handshake. */ - ret = gnutls_handshake (client->session); + ret = gnutls_handshake (client->tlssession); if (ret == 0) { /* Finished. Next step is to check the certificate. */ if (remoteCheckAccess (client) == -1) @@ -1366,7 +1483,6 @@ static void qemudDispatchClientWrite(str gnutls_strerror (ret)); qemudDispatchClientFailure (server, client); } else { - client->direction = gnutls_record_get_direction (client->session); if (qemudRegisterClientEvent (server, client, 1)) qemudDispatchClientFailure (server, client); } @@ -1406,25 +1522,37 @@ static int qemudRegisterClientEvent(stru static int qemudRegisterClientEvent(struct qemud_server *server, struct qemud_client *client, int removeFirst) { + int mode; + switch (client->mode) { + case QEMUD_MODE_TLS_HANDSHAKE: + if (gnutls_record_get_direction (client->tlssession) == 0) + mode = POLLIN; + else + mode = POLLOUT; + break; + + case QEMUD_MODE_RX_HEADER: + case QEMUD_MODE_RX_PAYLOAD: + mode = POLLIN; + break; + + case QEMUD_MODE_TX_PACKET: + mode = POLLOUT; + break; + + default: + return -1; + } + if (removeFirst) if (virEventRemoveHandleImpl(client->fd) < 0) return -1; - if (client->tls) { - if (virEventAddHandleImpl(client->fd, - (client->direction ? - POLLOUT : POLLIN) | POLLERR | POLLHUP, - qemudDispatchClientEvent, - server) < 0) - return -1; - } else { - if (virEventAddHandleImpl(client->fd, - (client->mode == QEMUD_MODE_TX_PACKET ? - POLLOUT : POLLIN) | POLLERR | POLLHUP, - qemudDispatchClientEvent, - server) < 0) - return -1; - } + if (virEventAddHandleImpl(client->fd, + mode | POLLERR | POLLHUP, + qemudDispatchClientEvent, + server) < 0) + return -1; return 0; } diff -r 5a37498017ac qemud/remote.c --- a/qemud/remote.c Wed Nov 28 23:00:04 2007 -0500 +++ b/qemud/remote.c Wed Nov 28 23:00:47 2007 -0500 @@ -284,7 +284,6 @@ remoteDispatchClientRequest (struct qemu client->mode = QEMUD_MODE_TX_PACKET; client->bufferLength = len; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; } /* An error occurred during the dispatching process itself (ie. not @@ -369,7 +368,6 @@ remoteDispatchSendError (struct qemud_cl client->mode = QEMUD_MODE_TX_PACKET; client->bufferLength = len; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; } static void @@ -2042,6 +2040,7 @@ remoteDispatchAuthSaslInit (struct qemud remote_auth_sasl_init_ret *ret) { const char *mechlist = NULL; + sasl_security_properties_t secprops; int err; struct sockaddr_storage sa; socklen_t salen; @@ -2097,6 +2096,60 @@ remoteDispatchAuthSaslInit (struct qemud return -2; } + /* Inform SASL that we've got an external SSF layer from TLS */ + if (client->type == QEMUD_SOCK_TYPE_TLS) { + gnutls_cipher_algorithm_t cipher; + sasl_ssf_t ssf; + + cipher = gnutls_cipher_get(client->tlssession); + if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { + qemudLog(QEMUD_ERR, "cannot TLS get cipher size"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + ssf *= 8; /* tls key size is bytes, sasl wants bits */ + + err = sasl_setprop(client->saslconn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot set SASL external SSF %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + } + + memset (&secprops, 0, sizeof secprops); + if (client->type == QEMUD_SOCK_TYPE_TLS || + client->type == QEMUD_SOCK_TYPE_UNIX) { + /* If we've got TLS or UNIX domain sock, we don't care about SSF */ + secprops.min_ssf = 0; + secprops.max_ssf = 0; + secprops.maxbufsize = 8192; + secprops.security_flags = 0; + } else { + /* Plain TCP, better get an SSF layer */ + secprops.min_ssf = 56; /* Good enough to require kerberos */ + secprops.max_ssf = 100000; /* Arbitrary big number */ + secprops.maxbufsize = 8192; + /* Forbid any anonymous or trivially crackable auth */ + secprops.security_flags = + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + } + + err = sasl_setprop(client->saslconn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot set SASL security props %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + err = sasl_listmech(client->saslconn, NULL, /* Don't need to set user */ "", /* Prefix */ @@ -2126,6 +2179,49 @@ remoteDispatchAuthSaslInit (struct qemud return 0; } + +/* We asked for an SSF layer, so sanity check that we actaully + * got what we asked for */ +static int +remoteSASLCheckSSF (struct qemud_client *client, + remote_message_header *req) { + const void *val; + int err, ssf; + + if (client->type == QEMUD_SOCK_TYPE_TLS || + client->type == QEMUD_SOCK_TYPE_UNIX) + return 0; /* TLS or UNIX domain sockets trivially OK */ + + err = sasl_getprop(client->saslconn, SASL_SSF, &val); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot query SASL ssf on connection %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + ssf = *(const int *)val; + REMOTE_DEBUG("negotiated an SSF of %d", ssf); + if (ssf < 56) { /* 56 is good for Kerberos */ + qemudLog(QEMUD_ERR, "negotiated SSF %d was not strong enough", ssf); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + + /* Only setup for read initially, because we're about to send an RPC + * reply which must be in plain text. When the next incoming RPC + * arrives, we'll switch on writes too + * + * cf qemudClientReadSASL in qemud.c + */ + client->saslSSF = QEMUD_SASL_SSF_READ; + + /* We have a SSF !*/ + return 0; +} /* * This starts the SASL authentication negotiation. @@ -2192,6 +2288,9 @@ remoteDispatchAuthSaslStart (struct qemu if (err == SASL_CONTINUE) { ret->complete = 0; } else { + if (remoteSASLCheckSSF(client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; @@ -2263,6 +2362,9 @@ remoteDispatchAuthSaslStep (struct qemud if (err == SASL_CONTINUE) { ret->complete = 0; } else { + if (remoteSASLCheckSSF(client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; diff -r 5a37498017ac src/remote_internal.c --- a/src/remote_internal.c Wed Nov 28 23:00:04 2007 -0500 +++ b/src/remote_internal.c Wed Nov 28 23:00:47 2007 -0500 @@ -79,6 +79,9 @@ struct private_data { FILE *debugLog; /* Debug remote protocol */ #if HAVE_SASL sasl_conn_t *saslconn; /* SASL context */ + const char *saslDecoded; + unsigned int saslDecodedLength; + unsigned int saslDecodedOffset; #endif }; @@ -2907,15 +2910,14 @@ static char *addrToString(struct sockadd /* Perform the SASL authentication process * - * XXX negotiate a session encryption layer for non-TLS sockets * XXX fetch credentials from a libvirt client app callback - * XXX max packet size spec * XXX better mechanism negotiation ? Ask client app ? */ static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open) { sasl_conn_t *saslconn = NULL; + sasl_security_properties_t secprops; remote_auth_sasl_init_ret iret; remote_auth_sasl_start_args sargs; remote_auth_sasl_start_ret sret; @@ -2929,6 +2931,8 @@ remoteAuthSASL (virConnectPtr conn, stru struct sockaddr_storage sa; socklen_t salen; char *localAddr, *remoteAddr; + const void *val; + sasl_ssf_t ssf; remoteDebug(priv, "Client initialize SASL authentication"); /* Sets up the SASL library as a whole */ @@ -2984,6 +2988,51 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "Failed to create SASL client context: %d (%s)", err, sasl_errstring(err, NULL, NULL)); + return -1; + } + + /* Initialize some connection props we care about */ + if (priv->uses_tls) { + gnutls_cipher_algorithm_t cipher; + + cipher = gnutls_cipher_get(priv->session); + if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "invalid cipher size for TLS session"); + sasl_dispose(&saslconn); + return -1; + } + ssf *= 8; /* key size is bytes, sasl wants bits */ + + remoteDebug(priv, "Setting external SSF %d", ssf); + err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "cannot set external SSF %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&saslconn); + return -1; + } + } + + memset (&secprops, 0, sizeof secprops); + /* If we've got TLS, we don't care about SSF */ + secprops.min_ssf = priv->uses_tls ? 0 : 56; /* Equiv to DES supported by all Kerberos */ + secprops.max_ssf = priv->uses_tls ? 0 : 100000; /* Very strong ! AES == 256 */ + secprops.maxbufsize = 100000; + /* If we're not TLS, then forbid any anonymous or trivially crackable auth */ + secprops.security_flags = priv->uses_tls ? 0 : + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + + err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "cannot set security props %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&saslconn); return -1; } @@ -3103,9 +3152,30 @@ remoteAuthSASL (virConnectPtr conn, stru } } + /* Check for suitable SSF if non-TLS */ + if (!priv->uses_tls) { + err = sasl_getprop(saslconn, SASL_SSF, &val); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "cannot query SASL ssf on connection %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&saslconn); + return -1; + } + ssf = *(const int *)val; + remoteDebug(priv, "SASL SSF value %d", ssf); + if (ssf < 56) { /* 56 == DES level, good for Kerberos */ + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "negotiation SSF %d was not strong enough", ssf); + sasl_dispose(&saslconn); + return -1; + } + } + remoteDebug(priv, "SASL authentication complete"); - /* XXX keep this around for wire encoding */ - sasl_dispose(&saslconn); + priv->saslconn = saslconn; return 0; } #endif /* HAVE_SASL */ @@ -3306,11 +3376,11 @@ call (virConnectPtr conn, struct private } static int -really_write (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - char *p; +really_write_buf (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + const char *bytes, int len) +{ + const char *p; int err; p = bytes; @@ -3348,55 +3418,156 @@ really_write (virConnectPtr conn, struct } static int +really_write_plain (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + return really_write_buf(conn, priv, in_open, bytes, len); +} + +#if HAVE_SASL +static int +really_write_sasl (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + const char *output; + unsigned int outputlen; + int err; + + err = sasl_encode(priv->saslconn, bytes, len, &output, &outputlen); + if (err != SASL_OK) { + return -1; + } + + return really_write_buf(conn, priv, in_open, output, outputlen); +} +#endif + +static int +really_write (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ +#if HAVE_SASL + if (priv->saslconn) + return really_write_sasl(conn, priv, in_open, bytes, len); + else +#endif + return really_write_plain(conn, priv, in_open, bytes, len); +} + +static int +really_read_buf (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + int err; + + if (priv->uses_tls) { + tlsreread: + err = gnutls_record_recv (priv->session, bytes, len); + if (err < 0) { + if (err == GNUTLS_E_INTERRUPTED) + goto tlsreread; + error (in_open ? NULL : conn, + VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); + return -1; + } + if (err == 0) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, "socket closed unexpectedly"); + return -1; + } + return err; + } else { + reread: + err = read (priv->sock, bytes, len); + if (err == -1) { + if (errno == EINTR) + goto reread; + error (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, strerror (errno)); + return -1; + } + if (err == 0) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, "socket closed unexpectedly"); + return -1; + } + return err; + } + + return 0; +} + +static int +really_read_plain (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + do { + int ret = really_read_buf (conn, priv, in_open, bytes, len); + if (ret < 0) + return -1; + + len -= ret; + bytes += ret; + } while (len > 0); + + return 0; +} + +#if HAVE_SASL +static int +really_read_sasl (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + do { + int want, got; + if (priv->saslDecoded == NULL) { + char encoded[8192]; + int encodedLen = sizeof(encoded); + int err, ret; + ret = really_read_buf (conn, priv, in_open, encoded, encodedLen); + if (ret < 0) + return -1; + + err = sasl_decode(priv->saslconn, encoded, ret, + &priv->saslDecoded, &priv->saslDecodedLength); + } + + got = priv->saslDecodedLength - priv->saslDecodedOffset; + want = len; + if (want > got) + want = got; + + memcpy(bytes, priv->saslDecoded + priv->saslDecodedOffset, want); + priv->saslDecodedOffset += want; + if (priv->saslDecodedOffset == priv->saslDecodedLength) { + priv->saslDecoded = NULL; + priv->saslDecodedOffset = priv->saslDecodedLength = 0; + } + bytes += want; + len -= want; + } while (len > 0); + + return 0; +} +#endif + +static int really_read (virConnectPtr conn, struct private_data *priv, int in_open /* if we are in virConnectOpen */, char *bytes, int len) { - char *p; - int err; - - p = bytes; - if (priv->uses_tls) { - do { - err = gnutls_record_recv (priv->session, p, len); - if (err < 0) { - if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, "socket closed unexpectedly"); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } else { - do { - err = read (priv->sock, p, len); - if (err == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, "socket closed unexpectedly"); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } - - return 0; +#if HAVE_SASL + if (priv->saslconn) + return really_read_sasl (conn, priv, in_open, bytes, len); + else +#endif + return really_read_plain (conn, priv, in_open, bytes, len); } /* For errors internal to this library. */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:17:07PM +0000, Daniel P. Berrange wrote:
With the TLS socket, all data is encrypted on the wire. The TCP socket though is still clear text. Fortunately some SASL authentication mechanism can also supply encryption capabilities. This is called SSF in SASL terminology.
This patch mandates the use of an SSF capable SASL authentiction mechanism on the TCP socket. This in effect restricts you to a choise between GSSAPI and DIGEST-MD5 as your SASL auth methods (the latter is a user/password based scheme). We also disallow anonymous & plaintext auth methods. If you really want to run the TCP socket in clear text & with anonymous auth, simply turn off SASL altogether. Since TLS already provides encryptiuon, if you run SASL over the TLS socket, we don't place any restrictions on the choice of SASL auth mechanism.
On the server side I have removed the 'direction' field of the client object. This was only used on the TLS socket & was intended to track whether the handshake process was waiting to receive or send. Rather than try to set this in various places throughout the daemon code, we simply query the neccessary direction at the one point where we register a FD event handle with poll(). This makes the code clearer to follow & reduces the chance of accidentally messing up the state.
The send & receive functions previously would call read vs gnutls_record_recv and write vs gnutls_record_send depending on the type of socket. If there is a SASL SSF layer enabled, we have to first pass the outgoing data through the sasl_encode() API, and pass incoming data through sasl_decode(). So the send/recive APIs have changed a little, to deal with this codec need and thus there is also some more state being tracked per connection - we may have to cache the output for sasl_decode for future calls depending on how much data we need in short term.
NB, the SSF layer lets you choose a strength factor from 0 -> a large number and the docs all talk about
* 0 = no protection * 1 = integrity protection only * 40 = 40-bit DES or 40-bit RC2/RC4 * 56 = DES * 112 = triple-DES * 128 = 128-bit RC2/RC4/BLOWFISH * 256 = baseline AES
This is incredibly misleading. The GSSAPI mechanism in SASL will never report a strength of anything other than 56. Even if it is using triple-DES. The true strength of the GSSAPI/Kerberos impl is impossible to figure out from the SASL apis. To ensure that Kerberos uses strong encryption, you need to make sure that the Kerberos principles issued only have the 3-DES cipher/keys present. If you are truely paranoid though, you always have the option of using TLS (which gives at least 128 bit ciphers, often 256 bit), and then just using Kerberos for auth and ignore the SASL SSF layer. A subsequent patch will make it possible to configure this stuff.
Rebased to latest CVS. diff -r 5a37498017ac qemud/internal.h --- a/qemud/internal.h Wed Nov 28 23:00:04 2007 -0500 +++ b/qemud/internal.h Wed Nov 28 23:00:47 2007 -0500 @@ -73,10 +73,17 @@ enum qemud_mode { QEMUD_MODE_TLS_HANDSHAKE, }; -/* These have to remain compatible with gnutls_record_get_direction. */ -enum qemud_tls_direction { - QEMUD_TLS_DIRECTION_READ = 0, - QEMUD_TLS_DIRECTION_WRITE = 1, +/* Whether we're passing reads & writes through a sasl SSF */ +enum qemud_sasl_ssf { + QEMUD_SASL_SSF_NONE = 0, + QEMUD_SASL_SSF_READ = 1, + QEMUD_SASL_SSF_WRITE = 2, +}; + +enum qemud_sock_type { + QEMUD_SOCK_TYPE_UNIX = 0, + QEMUD_SOCK_TYPE_TCP = 1, + QEMUD_SOCK_TYPE_TLS = 2, }; /* Stores the per-client connection state */ @@ -90,13 +97,18 @@ struct qemud_client { struct sockaddr_storage addr; socklen_t addrlen; - /* If set, TLS is required on this socket. */ - int tls; - gnutls_session_t session; - enum qemud_tls_direction direction; + int type; /* qemud_sock_type */ + gnutls_session_t tlssession; int auth; #if HAVE_SASL sasl_conn_t *saslconn; + int saslSSF; + const char *saslDecoded; + unsigned int saslDecodedLength; + unsigned int saslDecodedOffset; + const char *saslEncoded; + unsigned int saslEncodedLength; + unsigned int saslEncodedOffset; #endif unsigned int incomingSerial; @@ -121,8 +133,7 @@ struct qemud_socket { struct qemud_socket { int fd; int readonly; - /* If set, TLS is required on this socket. */ - int tls; + int type; /* qemud_sock_type */ int auth; int port; struct qemud_socket *next; diff -r 5a37498017ac qemud/qemud.c --- a/qemud/qemud.c Wed Nov 28 23:00:04 2007 -0500 +++ b/qemud/qemud.c Wed Nov 28 23:00:47 2007 -0500 @@ -463,6 +463,7 @@ static int qemudListenUnix(struct qemud_ sock->readonly = readonly; sock->port = -1; + sock->type = QEMUD_SOCK_TYPE_UNIX; if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { qemudLog(QEMUD_ERR, "Failed to create socket: %s", @@ -577,7 +578,7 @@ static int static int remoteListenTCP (struct qemud_server *server, const char *port, - int tls, + int type, int auth) { int fds[2]; @@ -606,7 +607,7 @@ remoteListenTCP (struct qemud_server *se server->nsockets++; sock->fd = fds[i]; - sock->tls = tls; + sock->type = type; sock->auth = auth; if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0) @@ -745,10 +746,10 @@ static struct qemud_server *qemudInitial if (ipsock) { #if HAVE_SASL - if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_SASL) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_SASL) < 0) goto cleanup; #else - if (listen_tcp && remoteListenTCP (server, tcp_port, 0, REMOTE_AUTH_NONE) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_NONE) < 0) goto cleanup; #endif @@ -756,7 +757,7 @@ static struct qemud_server *qemudInitial if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, 1, REMOTE_AUTH_NONE) < 0) + if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, REMOTE_AUTH_NONE) < 0) goto cleanup; } } @@ -789,7 +790,7 @@ static struct qemud_server *qemudInitial */ sock = server->sockets; while (sock) { - if (sock->port != -1 && sock->tls) { + if (sock->port != -1 && sock->type == QEMUD_SOCK_TYPE_TLS) { port = sock->port; break; } @@ -981,7 +982,7 @@ remoteCheckAccess (struct qemud_client * int found, err; /* Verify client certificate. */ - if (remoteCheckCertificate (client->session) == -1) { + if (remoteCheckCertificate (client->tlssession) == -1) { qemudLog (QEMUD_ERR, "remoteCheckCertificate: failed to verify client's certificate"); if (!tls_no_verify_certificate) return -1; else qemudLog (QEMUD_INFO, "remoteCheckCertificate: tls_no_verify_certificate is set so the bad certificate is ignored"); @@ -1033,7 +1034,6 @@ remoteCheckAccess (struct qemud_client * client->bufferOffset = 0; client->buffer[0] = '\1'; client->mode = QEMUD_MODE_TX_PACKET; - client->direction = QEMUD_TLS_DIRECTION_WRITE; return 0; } @@ -1065,12 +1065,12 @@ static int qemudDispatchServer(struct qe client->magic = QEMUD_CLIENT_MAGIC; client->fd = fd; client->readonly = sock->readonly; - client->tls = sock->tls; + client->type = sock->type; client->auth = sock->auth; memcpy (&client->addr, &addr, sizeof addr); client->addrlen = addrlen; - if (!client->tls) { + if (client->type != QEMUD_SOCK_TYPE_TLS) { client->mode = QEMUD_MODE_RX_HEADER; client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; @@ -1079,15 +1079,15 @@ static int qemudDispatchServer(struct qe } else { int ret; - client->session = remoteInitializeTLSSession (); - if (client->session == NULL) + client->tlssession = remoteInitializeTLSSession (); + if (client->tlssession == NULL) goto cleanup; - gnutls_transport_set_ptr (client->session, + gnutls_transport_set_ptr (client->tlssession, (gnutls_transport_ptr_t) (long) fd); /* Begin the TLS handshake. */ - ret = gnutls_handshake (client->session); + ret = gnutls_handshake (client->tlssession); if (ret == 0) { /* Unlikely, but ... Next step is to check the certificate. */ if (remoteCheckAccess (client) == -1) @@ -1099,7 +1099,6 @@ static int qemudDispatchServer(struct qe /* Most likely. */ client->mode = QEMUD_MODE_TLS_HANDSHAKE; client->bufferLength = -1; - client->direction = gnutls_record_get_direction (client->session); if (qemudRegisterClientEvent (server, client, 0) < 0) goto cleanup; @@ -1117,7 +1116,7 @@ static int qemudDispatchServer(struct qe return 0; cleanup: - if (client->session) gnutls_deinit (client->session); + if (client->tlssession) gnutls_deinit (client->tlssession); close (fd); free (client); return -1; @@ -1150,24 +1149,21 @@ static void qemudDispatchClientFailure(s #if HAVE_SASL if (client->saslconn) sasl_dispose(&client->saslconn); #endif - if (client->tls && client->session) gnutls_deinit (client->session); + if (client->tlssession) gnutls_deinit (client->tlssession); close(client->fd); free(client); } -static int qemudClientRead(struct qemud_server *server, - struct qemud_client *client) { - int ret, len; - char *data; - - data = client->buffer + client->bufferOffset; - len = client->bufferLength - client->bufferOffset; +static int qemudClientReadBuf(struct qemud_server *server, + struct qemud_client *client, + char *data, unsigned len) { + int ret; /*qemudDebug ("qemudClientRead: len = %d", len);*/ - if (!client->tls) { + if (!client->tlssession) { if ((ret = read (client->fd, data, len)) <= 0) { if (ret == 0 || errno != EAGAIN) { if (ret != 0) @@ -1177,8 +1173,7 @@ static int qemudClientRead(struct qemud_ return -1; } } else { - ret = gnutls_record_recv (client->session, data, len); - client->direction = gnutls_record_get_direction (client->session); + ret = gnutls_record_recv (client->tlssession, data, len); if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); else if (ret <= 0) { @@ -1193,9 +1188,79 @@ static int qemudClientRead(struct qemud_ } } + return ret; +} + +static int qemudClientReadPlain(struct qemud_server *server, + struct qemud_client *client) { + int ret; + ret = qemudClientReadBuf(server, client, + client->buffer + client->bufferOffset, + client->bufferLength - client->bufferOffset); + if (ret < 0) + return ret; client->bufferOffset += ret; return 0; } + +#if HAVE_SASL +static int qemudClientReadSASL(struct qemud_server *server, + struct qemud_client *client) { + int got, want; + + /* We're doing a SSF data read, so now its times to ensure + * future writes are under SSF too. + * + * cf remoteSASLCheckSSF in remote.c + */ + client->saslSSF |= QEMUD_SASL_SSF_WRITE; + + /* Need to read some more data off the wire */ + if (client->saslDecoded == NULL) { + char encoded[8192]; + int encodedLen = sizeof(encoded); + encodedLen = qemudClientReadBuf(server, client, encoded, encodedLen); + + if (encodedLen < 0) + return -1; + + sasl_decode(client->saslconn, encoded, encodedLen, + &client->saslDecoded, &client->saslDecodedLength); + + client->saslDecodedOffset = 0; + } + + /* Some buffered decoded data to return now */ + got = client->saslDecodedLength - client->saslDecodedOffset; + want = client->bufferLength - client->bufferOffset; + + if (want > got) + want = got; + + memcpy(client->buffer + client->bufferOffset, + client->saslDecoded + client->saslDecodedOffset, want); + client->saslDecodedOffset += want; + client->bufferOffset += want; + + if (client->saslDecodedOffset == client->saslDecodedLength) { + client->saslDecoded = NULL; + client->saslDecodedOffset = client->saslDecodedLength = 0; + } + + return 0; +} +#endif + +static int qemudClientRead(struct qemud_server *server, + struct qemud_client *client) { +#if HAVE_SASL + if (client->saslSSF & QEMUD_SASL_SSF_READ) + return qemudClientReadSASL(server, client); + else +#endif + return qemudClientReadPlain(server, client); +} + static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) { @@ -1239,7 +1304,6 @@ static void qemudDispatchClientRead(stru client->mode = QEMUD_MODE_RX_PAYLOAD; client->bufferLength = len - REMOTE_MESSAGE_HEADER_XDR_LEN; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ; if (qemudRegisterClientEvent(server, client, 1) < 0) { qemudDispatchClientFailure(server, client); @@ -1267,7 +1331,7 @@ static void qemudDispatchClientRead(stru int ret; /* Continue the handshake. */ - ret = gnutls_handshake (client->session); + ret = gnutls_handshake (client->tlssession); if (ret == 0) { /* Finished. Next step is to check the certificate. */ if (remoteCheckAccess (client) == -1) @@ -1279,7 +1343,6 @@ static void qemudDispatchClientRead(stru gnutls_strerror (ret)); qemudDispatchClientFailure (server, client); } else { - client->direction = gnutls_record_get_direction (client->session); if (qemudRegisterClientEvent (server ,client, 1) < 0) qemudDispatchClientFailure (server, client); } @@ -1294,15 +1357,11 @@ static void qemudDispatchClientRead(stru } -static int qemudClientWrite(struct qemud_server *server, - struct qemud_client *client) { - int ret, len; - char *data; - - data = client->buffer + client->bufferOffset; - len = client->bufferLength - client->bufferOffset; - - if (!client->tls) { +static int qemudClientWriteBuf(struct qemud_server *server, + struct qemud_client *client, + const char *data, int len) { + int ret; + if (!client->tlssession) { if ((ret = write(client->fd, data, len)) == -1) { if (errno != EAGAIN) { qemudLog (QEMUD_ERR, "write: %s", strerror (errno)); @@ -1311,8 +1370,7 @@ static int qemudClientWrite(struct qemud return -1; } } else { - ret = gnutls_record_send (client->session, data, len); - client->direction = gnutls_record_get_direction (client->session); + ret = gnutls_record_send (client->tlssession, data, len); if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); else if (ret < 0) { @@ -1324,9 +1382,69 @@ static int qemudClientWrite(struct qemud return -1; } } - + return ret; +} + + +static int qemudClientWritePlain(struct qemud_server *server, + struct qemud_client *client) { + int ret = qemudClientWriteBuf(server, client, + client->buffer + client->bufferOffset, + client->bufferLength - client->bufferOffset); + if (ret < 0) + return -1; client->bufferOffset += ret; return 0; +} + + +#if HAVE_SASL +static int qemudClientWriteSASL(struct qemud_server *server, + struct qemud_client *client) { + int ret; + + /* Not got any pending encoded data, so we need to encode raw stuff */ + if (client->saslEncoded == NULL) { + int err; + err = sasl_encode(client->saslconn, + client->buffer + client->bufferOffset, + client->bufferLength - client->bufferOffset, + &client->saslEncoded, + &client->saslEncodedLength); + + client->saslEncodedOffset = 0; + } + + /* Send some of the encoded stuff out on the wire */ + ret = qemudClientWriteBuf(server, client, + client->saslEncoded + client->saslEncodedOffset, + client->saslEncodedLength - client->saslEncodedOffset); + + if (ret < 0) + return -1; + + /* Note how much we sent */ + client->saslEncodedOffset += ret; + + /* Sent all encoded, so update raw buffer to indicate completion */ + if (client->saslEncodedOffset == client->saslEncodedLength) { + client->saslEncoded = NULL; + client->saslEncodedOffset = client->saslEncodedLength = 0; + client->bufferOffset = client->bufferLength; + } + + return 0; +} +#endif + +static int qemudClientWrite(struct qemud_server *server, + struct qemud_client *client) { +#if HAVE_SASL + if (client->saslSSF & QEMUD_SASL_SSF_WRITE) + return qemudClientWriteSASL(server, client); + else +#endif + return qemudClientWritePlain(server, client); } @@ -1341,7 +1459,6 @@ static void qemudDispatchClientWrite(str client->mode = QEMUD_MODE_RX_HEADER; client->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_READ; if (qemudRegisterClientEvent (server, client, 1) < 0) qemudDispatchClientFailure (server, client); @@ -1354,7 +1471,7 @@ static void qemudDispatchClientWrite(str int ret; /* Continue the handshake. */ - ret = gnutls_handshake (client->session); + ret = gnutls_handshake (client->tlssession); if (ret == 0) { /* Finished. Next step is to check the certificate. */ if (remoteCheckAccess (client) == -1) @@ -1366,7 +1483,6 @@ static void qemudDispatchClientWrite(str gnutls_strerror (ret)); qemudDispatchClientFailure (server, client); } else { - client->direction = gnutls_record_get_direction (client->session); if (qemudRegisterClientEvent (server, client, 1)) qemudDispatchClientFailure (server, client); } @@ -1406,25 +1522,37 @@ static int qemudRegisterClientEvent(stru static int qemudRegisterClientEvent(struct qemud_server *server, struct qemud_client *client, int removeFirst) { + int mode; + switch (client->mode) { + case QEMUD_MODE_TLS_HANDSHAKE: + if (gnutls_record_get_direction (client->tlssession) == 0) + mode = POLLIN; + else + mode = POLLOUT; + break; + + case QEMUD_MODE_RX_HEADER: + case QEMUD_MODE_RX_PAYLOAD: + mode = POLLIN; + break; + + case QEMUD_MODE_TX_PACKET: + mode = POLLOUT; + break; + + default: + return -1; + } + if (removeFirst) if (virEventRemoveHandleImpl(client->fd) < 0) return -1; - if (client->tls) { - if (virEventAddHandleImpl(client->fd, - (client->direction ? - POLLOUT : POLLIN) | POLLERR | POLLHUP, - qemudDispatchClientEvent, - server) < 0) - return -1; - } else { - if (virEventAddHandleImpl(client->fd, - (client->mode == QEMUD_MODE_TX_PACKET ? - POLLOUT : POLLIN) | POLLERR | POLLHUP, - qemudDispatchClientEvent, - server) < 0) - return -1; - } + if (virEventAddHandleImpl(client->fd, + mode | POLLERR | POLLHUP, + qemudDispatchClientEvent, + server) < 0) + return -1; return 0; } diff -r 5a37498017ac qemud/remote.c --- a/qemud/remote.c Wed Nov 28 23:00:04 2007 -0500 +++ b/qemud/remote.c Wed Nov 28 23:00:47 2007 -0500 @@ -284,7 +284,6 @@ remoteDispatchClientRequest (struct qemu client->mode = QEMUD_MODE_TX_PACKET; client->bufferLength = len; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; } /* An error occurred during the dispatching process itself (ie. not @@ -369,7 +368,6 @@ remoteDispatchSendError (struct qemud_cl client->mode = QEMUD_MODE_TX_PACKET; client->bufferLength = len; client->bufferOffset = 0; - if (client->tls) client->direction = QEMUD_TLS_DIRECTION_WRITE; } static void @@ -2042,6 +2040,7 @@ remoteDispatchAuthSaslInit (struct qemud remote_auth_sasl_init_ret *ret) { const char *mechlist = NULL; + sasl_security_properties_t secprops; int err; struct sockaddr_storage sa; socklen_t salen; @@ -2097,6 +2096,60 @@ remoteDispatchAuthSaslInit (struct qemud return -2; } + /* Inform SASL that we've got an external SSF layer from TLS */ + if (client->type == QEMUD_SOCK_TYPE_TLS) { + gnutls_cipher_algorithm_t cipher; + sasl_ssf_t ssf; + + cipher = gnutls_cipher_get(client->tlssession); + if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { + qemudLog(QEMUD_ERR, "cannot TLS get cipher size"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + ssf *= 8; /* tls key size is bytes, sasl wants bits */ + + err = sasl_setprop(client->saslconn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot set SASL external SSF %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + } + + memset (&secprops, 0, sizeof secprops); + if (client->type == QEMUD_SOCK_TYPE_TLS || + client->type == QEMUD_SOCK_TYPE_UNIX) { + /* If we've got TLS or UNIX domain sock, we don't care about SSF */ + secprops.min_ssf = 0; + secprops.max_ssf = 0; + secprops.maxbufsize = 8192; + secprops.security_flags = 0; + } else { + /* Plain TCP, better get an SSF layer */ + secprops.min_ssf = 56; /* Good enough to require kerberos */ + secprops.max_ssf = 100000; /* Arbitrary big number */ + secprops.maxbufsize = 8192; + /* Forbid any anonymous or trivially crackable auth */ + secprops.security_flags = + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + } + + err = sasl_setprop(client->saslconn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot set SASL security props %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -2; + } + err = sasl_listmech(client->saslconn, NULL, /* Don't need to set user */ "", /* Prefix */ @@ -2126,6 +2179,49 @@ remoteDispatchAuthSaslInit (struct qemud return 0; } + +/* We asked for an SSF layer, so sanity check that we actaully + * got what we asked for */ +static int +remoteSASLCheckSSF (struct qemud_client *client, + remote_message_header *req) { + const void *val; + int err, ssf; + + if (client->type == QEMUD_SOCK_TYPE_TLS || + client->type == QEMUD_SOCK_TYPE_UNIX) + return 0; /* TLS or UNIX domain sockets trivially OK */ + + err = sasl_getprop(client->saslconn, SASL_SSF, &val); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot query SASL ssf on connection %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + ssf = *(const int *)val; + REMOTE_DEBUG("negotiated an SSF of %d", ssf); + if (ssf < 56) { /* 56 is good for Kerberos */ + qemudLog(QEMUD_ERR, "negotiated SSF %d was not strong enough", ssf); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + + /* Only setup for read initially, because we're about to send an RPC + * reply which must be in plain text. When the next incoming RPC + * arrives, we'll switch on writes too + * + * cf qemudClientReadSASL in qemud.c + */ + client->saslSSF = QEMUD_SASL_SSF_READ; + + /* We have a SSF !*/ + return 0; +} /* * This starts the SASL authentication negotiation. @@ -2192,6 +2288,9 @@ remoteDispatchAuthSaslStart (struct qemu if (err == SASL_CONTINUE) { ret->complete = 0; } else { + if (remoteSASLCheckSSF(client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; @@ -2263,6 +2362,9 @@ remoteDispatchAuthSaslStep (struct qemud if (err == SASL_CONTINUE) { ret->complete = 0; } else { + if (remoteSASLCheckSSF(client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; diff -r 5a37498017ac src/remote_internal.c --- a/src/remote_internal.c Wed Nov 28 23:00:04 2007 -0500 +++ b/src/remote_internal.c Wed Nov 28 23:00:47 2007 -0500 @@ -79,6 +79,9 @@ struct private_data { FILE *debugLog; /* Debug remote protocol */ #if HAVE_SASL sasl_conn_t *saslconn; /* SASL context */ + const char *saslDecoded; + unsigned int saslDecodedLength; + unsigned int saslDecodedOffset; #endif }; @@ -2907,15 +2910,14 @@ static char *addrToString(struct sockadd /* Perform the SASL authentication process * - * XXX negotiate a session encryption layer for non-TLS sockets * XXX fetch credentials from a libvirt client app callback - * XXX max packet size spec * XXX better mechanism negotiation ? Ask client app ? */ static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open) { sasl_conn_t *saslconn = NULL; + sasl_security_properties_t secprops; remote_auth_sasl_init_ret iret; remote_auth_sasl_start_args sargs; remote_auth_sasl_start_ret sret; @@ -2929,6 +2931,8 @@ remoteAuthSASL (virConnectPtr conn, stru struct sockaddr_storage sa; socklen_t salen; char *localAddr, *remoteAddr; + const void *val; + sasl_ssf_t ssf; remoteDebug(priv, "Client initialize SASL authentication"); /* Sets up the SASL library as a whole */ @@ -2984,6 +2988,51 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "Failed to create SASL client context: %d (%s)", err, sasl_errstring(err, NULL, NULL)); + return -1; + } + + /* Initialize some connection props we care about */ + if (priv->uses_tls) { + gnutls_cipher_algorithm_t cipher; + + cipher = gnutls_cipher_get(priv->session); + if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "invalid cipher size for TLS session"); + sasl_dispose(&saslconn); + return -1; + } + ssf *= 8; /* key size is bytes, sasl wants bits */ + + remoteDebug(priv, "Setting external SSF %d", ssf); + err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "cannot set external SSF %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&saslconn); + return -1; + } + } + + memset (&secprops, 0, sizeof secprops); + /* If we've got TLS, we don't care about SSF */ + secprops.min_ssf = priv->uses_tls ? 0 : 56; /* Equiv to DES supported by all Kerberos */ + secprops.max_ssf = priv->uses_tls ? 0 : 100000; /* Very strong ! AES == 256 */ + secprops.maxbufsize = 100000; + /* If we're not TLS, then forbid any anonymous or trivially crackable auth */ + secprops.security_flags = priv->uses_tls ? 0 : + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + + err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "cannot set security props %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&saslconn); return -1; } @@ -3103,9 +3152,30 @@ remoteAuthSASL (virConnectPtr conn, stru } } + /* Check for suitable SSF if non-TLS */ + if (!priv->uses_tls) { + err = sasl_getprop(saslconn, SASL_SSF, &val); + if (err != SASL_OK) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "cannot query SASL ssf on connection %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&saslconn); + return -1; + } + ssf = *(const int *)val; + remoteDebug(priv, "SASL SSF value %d", ssf); + if (ssf < 56) { /* 56 == DES level, good for Kerberos */ + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "negotiation SSF %d was not strong enough", ssf); + sasl_dispose(&saslconn); + return -1; + } + } + remoteDebug(priv, "SASL authentication complete"); - /* XXX keep this around for wire encoding */ - sasl_dispose(&saslconn); + priv->saslconn = saslconn; return 0; } #endif /* HAVE_SASL */ @@ -3306,11 +3376,11 @@ call (virConnectPtr conn, struct private } static int -really_write (virConnectPtr conn, struct private_data *priv, - int in_open /* if we are in virConnectOpen */, - char *bytes, int len) -{ - char *p; +really_write_buf (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + const char *bytes, int len) +{ + const char *p; int err; p = bytes; @@ -3348,55 +3418,156 @@ really_write (virConnectPtr conn, struct } static int +really_write_plain (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + return really_write_buf(conn, priv, in_open, bytes, len); +} + +#if HAVE_SASL +static int +really_write_sasl (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + const char *output; + unsigned int outputlen; + int err; + + err = sasl_encode(priv->saslconn, bytes, len, &output, &outputlen); + if (err != SASL_OK) { + return -1; + } + + return really_write_buf(conn, priv, in_open, output, outputlen); +} +#endif + +static int +really_write (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ +#if HAVE_SASL + if (priv->saslconn) + return really_write_sasl(conn, priv, in_open, bytes, len); + else +#endif + return really_write_plain(conn, priv, in_open, bytes, len); +} + +static int +really_read_buf (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + int err; + + if (priv->uses_tls) { + tlsreread: + err = gnutls_record_recv (priv->session, bytes, len); + if (err < 0) { + if (err == GNUTLS_E_INTERRUPTED) + goto tlsreread; + error (in_open ? NULL : conn, + VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); + return -1; + } + if (err == 0) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, "socket closed unexpectedly"); + return -1; + } + return err; + } else { + reread: + err = read (priv->sock, bytes, len); + if (err == -1) { + if (errno == EINTR) + goto reread; + error (in_open ? NULL : conn, + VIR_ERR_SYSTEM_ERROR, strerror (errno)); + return -1; + } + if (err == 0) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, "socket closed unexpectedly"); + return -1; + } + return err; + } + + return 0; +} + +static int +really_read_plain (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + do { + int ret = really_read_buf (conn, priv, in_open, bytes, len); + if (ret < 0) + return -1; + + len -= ret; + bytes += ret; + } while (len > 0); + + return 0; +} + +#if HAVE_SASL +static int +really_read_sasl (virConnectPtr conn, struct private_data *priv, + int in_open /* if we are in virConnectOpen */, + char *bytes, int len) +{ + do { + int want, got; + if (priv->saslDecoded == NULL) { + char encoded[8192]; + int encodedLen = sizeof(encoded); + int err, ret; + ret = really_read_buf (conn, priv, in_open, encoded, encodedLen); + if (ret < 0) + return -1; + + err = sasl_decode(priv->saslconn, encoded, ret, + &priv->saslDecoded, &priv->saslDecodedLength); + } + + got = priv->saslDecodedLength - priv->saslDecodedOffset; + want = len; + if (want > got) + want = got; + + memcpy(bytes, priv->saslDecoded + priv->saslDecodedOffset, want); + priv->saslDecodedOffset += want; + if (priv->saslDecodedOffset == priv->saslDecodedLength) { + priv->saslDecoded = NULL; + priv->saslDecodedOffset = priv->saslDecodedLength = 0; + } + bytes += want; + len -= want; + } while (len > 0); + + return 0; +} +#endif + +static int really_read (virConnectPtr conn, struct private_data *priv, int in_open /* if we are in virConnectOpen */, char *bytes, int len) { - char *p; - int err; - - p = bytes; - if (priv->uses_tls) { - do { - err = gnutls_record_recv (priv->session, p, len); - if (err < 0) { - if (err == GNUTLS_E_INTERRUPTED || err == GNUTLS_E_AGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_GNUTLS_ERROR, gnutls_strerror (err)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, "socket closed unexpectedly"); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } else { - do { - err = read (priv->sock, p, len); - if (err == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - error (in_open ? NULL : conn, - VIR_ERR_SYSTEM_ERROR, strerror (errno)); - return -1; - } - if (err == 0) { - error (in_open ? NULL : conn, - VIR_ERR_RPC, "socket closed unexpectedly"); - return -1; - } - len -= err; - p += err; - } - while (len > 0); - } - - return 0; +#if HAVE_SASL + if (priv->saslconn) + return really_read_sasl (conn, priv, in_open, bytes, len); + else +#endif + return really_read_plain (conn, priv, in_open, bytes, len); } /* For errors internal to this library. */ Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:17:07PM +0000, Daniel P. Berrange wrote:
With the TLS socket, all data is encrypted on the wire. The TCP socket though is still clear text. Fortunately some SASL authentication mechanism can also supply encryption capabilities. This is called SSF in SASL terminology.
This patch mandates the use of an SSF capable SASL authentiction mechanism on the TCP socket. This in effect restricts you to a choise between GSSAPI and DIGEST-MD5 as your SASL auth methods (the latter is a user/password based scheme). We also disallow anonymous & plaintext auth methods. If you really want to run the TCP socket in clear text & with anonymous auth, simply turn off SASL altogether. Since TLS already provides encryptiuon, if you run SASL over the TLS socket, we don't place any restrictions on the choice of SASL auth mechanism.
On the server side I have removed the 'direction' field of the client object. This was only used on the TLS socket & was intended to track whether the handshake process was waiting to receive or send. Rather than try to set this in various places throughout the daemon code, we simply query the neccessary direction at the one point where we register a FD event handle with poll(). This makes the code clearer to follow & reduces the chance of accidentally messing up the state.
The send & receive functions previously would call read vs gnutls_record_recv and write vs gnutls_record_send depending on the type of socket. If there is a SASL SSF layer enabled, we have to first pass the outgoing data through the sasl_encode() API, and pass incoming data through sasl_decode(). So the send/recive APIs have changed a little, to deal with this codec need and thus there is also some more state being tracked per connection - we may have to cache the output for sasl_decode for future calls depending on how much data we need in short term.
NB, the SSF layer lets you choose a strength factor from 0 -> a large number and the docs all talk about
* 0 = no protection * 1 = integrity protection only * 40 = 40-bit DES or 40-bit RC2/RC4 * 56 = DES * 112 = triple-DES * 128 = 128-bit RC2/RC4/BLOWFISH * 256 = baseline AES
This is incredibly misleading. The GSSAPI mechanism in SASL will never report a strength of anything other than 56. Even if it is using triple-DES. The true strength of the GSSAPI/Kerberos impl is impossible to figure out from the SASL apis. To ensure that Kerberos uses strong encryption, you need to make sure that the Kerberos principles issued only have the 3-DES cipher/keys present. If you are truely paranoid though, you always have the option of using TLS (which gives at least 128 bit ciphers, often 256 bit), and then just using Kerberos for auth and ignore the SASL SSF layer. A subsequent patch will make it possible to configure this stuff.
qemud/internal.h | 31 +++-- qemud/qemud.c | 248 +++++++++++++++++++++++++++++++++----------- qemud/remote.c | 106 ++++++++++++++++++ src/remote_internal.c | 279 ++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 538 insertions(+), 126 deletions(-)
This patch is now committed. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch provides the ability to configure what authentication mechanism is used on each socket - UNIX RW, UNIX RO, TCP, and TLS sockets - all can have independant settings. By default the UNIX & TLS sockets have no auth, and the TCP socket has SASL auth enabled. The /etc/libvirt/libvirtd.conf file lets you override these options. There is also a new sasl_allowed_username_list = ["admin"] config param to let you whitelist the users you want to allow. This supports use of wildcards. The username is dependnat on the SASL auth mechanism. For DIGEST-MD5 it will be plain usernames, for Kerberos it will be a username + realm, eg admin EXAMPLE COM After discussion with Rich, I also remove the tls_allowed_ip_list for whitelisting source IP addresses. This was a) not protecting us because it was only checked after the TLS handshake - thus allowing trivial DOS attack b) much easier to handle via tcp wrappers, or IPtables. c) only ever checked for the TLS socket d) IP addresses are easily spoofed. If summary, if you're using a real authentication mechanism, this is only useful for protecting against DOS attacks & that's better done by iptables. Makefile.am | 12 - internal.h | 4 libvirtd.conf | 143 ++++++++++++---- qemud.c | 379 ++++++++++++++++++++++--------------------- remote.c | 290 ++++++++++++++++++++++++-------- remote_dispatch_prototypes.h | 138 +++++++-------- remote_generate_stubs.pl | 5 7 files changed, 602 insertions(+), 369 deletions(-) diff -r f28fe18bd7f5 qemud/Makefile.am --- a/qemud/Makefile.am Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/Makefile.am Thu Nov 29 09:51:32 2007 -0500 @@ -101,14 +101,14 @@ remote.c: remote_dispatch_prototypes.h \ remote_dispatch_localvars.h \ remote_dispatch_proc_switch.h -remote_dispatch_prototypes.h: remote_generate_stubs.pl remote_protocol.x - perl -w remote_generate_stubs.pl -i remote_protocol.x > $@ +remote_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl remote_protocol.x + perl -w $(srcdir)/remote_generate_stubs.pl -i $(srcdir)/remote_protocol.x > $@ -remote_dispatch_localvars.h: remote_generate_stubs.pl remote_protocol.x - perl -w remote_generate_stubs.pl -v remote_protocol.x > $@ +remote_dispatch_localvars.h: $(srcdir)/remote_generate_stubs.pl remote_protocol.x + perl -w $(srcdir)/remote_generate_stubs.pl -v $(srcdir)/remote_protocol.x > $@ -remote_dispatch_proc_switch.h: remote_generate_stubs.pl remote_protocol.x - perl -w remote_generate_stubs.pl -w remote_protocol.x > $@ +remote_dispatch_proc_switch.h: $(srcdir)/remote_generate_stubs.pl remote_protocol.x + perl -w $(srcdir)/remote_generate_stubs.pl -w $(srcdir)/remote_protocol.x > $@ if LIBVIRT_INIT_SCRIPTS_RED_HAT install-init: libvirtd.init diff -r f28fe18bd7f5 qemud/internal.h --- a/qemud/internal.h Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/internal.h Thu Nov 29 09:51:32 2007 -0500 @@ -109,6 +109,7 @@ struct qemud_client { const char *saslEncoded; unsigned int saslEncodedLength; unsigned int saslEncodedOffset; + char *saslUsername; #endif unsigned int incomingSerial; @@ -151,6 +152,9 @@ struct qemud_server { #ifdef HAVE_AVAHI struct libvirtd_mdns *mdns; #endif +#if HAVE_SASL + char **saslUsernameWhitelist; +#endif }; void qemudLog(int priority, const char *fmt, ...) diff -r f28fe18bd7f5 qemud/libvirtd.conf --- a/qemud/libvirtd.conf Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/libvirtd.conf Thu Nov 29 09:51:32 2007 -0500 @@ -2,6 +2,11 @@ # # For further information consult http://libvirt.org/format.html + +################################################################# +# +# Network connectivitiy controls +# # Flag listening for secure TLS connections on the public TCP/IP port. # NB, must pass the --listen flag to the libvirtd process for this to @@ -17,7 +22,9 @@ # NB, must pass the --listen flag to the libvirtd process for this to # have any effect. # -# NB, this is insecure. Do not use except for development. +# Using the TCP socket requires SASL authentication by default. Only +# SASL mechanisms which support data encryption are allowed. This is +# DIGEST_MD5 and GSSAPI (Kerberos5) # # This is disabled by default, uncomment this to enable it. # listen_tcp = 1 @@ -53,6 +60,10 @@ # mdns_name "Virtualization Host Joe Demo" +################################################################# +# +# UNIX socket access controls +# # Set the UNIX domain socket group ownership. This can be used to # allow a 'trusted' set of users access to management capabilities @@ -73,8 +84,88 @@ # # Default allows only root. If setting group ownership may want to # relax this to: -# unix_sock_rw_perms "octal-perms" "0770" - +# unix_sock_rw_perms "0770" + + + +################################################################# +# +# Authentication. +# +# - none: do not perform auth checks. If you can connect to the +# socket you are allowed. This is suitable if there are +# restrictions on connecting to the socket (eg, UNIX +# socket permissions), or if there is a lower layer in +# the network providing auth (eg, TLS/x509 certificates) +# +# - sasl: use SASL infrastructure. The actual auth scheme is then +# controlled from /etc/sasl2/libvirt.conf. For the TCP +# socket only GSSAPI & DIGEST-MD5 mechanisms will be used. +# For non-TCP or TLS sockets, any scheme is allowed. +# + +# Set an authentication scheme for UNIX read-only sockets +# By default socket permissions allow anyone to connect +# +# To restrict monitoring of domains you may wish to enable +# an authentication mechanism here +# auth_unix_ro = "none" + +# Set an authentication scheme for UNIX read-write sockets +# By default socket permissions only allow root. +# +# If the unix_sock_rw_perms are changed you may wish to enable +# an authentication mechanism here +# auth_unix_rw = "none" + +# Change the authentication scheme for TCP sockets. +# +# If you don't enable SASL, then all TCP traffic is cleartext. +# Don't do this outside of a dev/test scenario. For real world +# use, always enable SASL and use the GSSAPI or DIGEST-MD5 +# mechanism in /etc/sasl2/libvirt.conf +# auth_tcp = "sasl" + +# Change the authentication scheme for TLS sockets. +# +# TLS sockets already have encryption provided by the TLS +# layer, and limited authentication is done by certificates +# +# It is possible to make use of any SASL authentication +# mechanism as well, by using 'sasl' for this option +# auth_tls = "none" + + + +################################################################# +# +# TLS x509 certificate configuration +# + + +# Override the default server key file path +# +# key_file "/etc/pki/libvirt/private/serverkey.pem" + +# Override the default server certificate file path +# +# cert_file "/etc/pki/libvirt/servercert.pem" + +# Override the default CA certificate path +# +# ca_file "/etc/pki/CA/cacert.pem" + +# Specify a certificate revocation list. +# +# Defaults to not using a CRL, uncomment to enable it +# crl_file "/etc/pki/CA/crl.pem" + + + +################################################################# +# +# Authorization controls +# # Flag to disable verification of client certificates @@ -87,31 +178,6 @@ # verification - make sure an IP whitelist is set # tls_no_verify_certificate 1 -# Flag to disable verification of client IP address -# -# Client IP address will be verified against the CommonName field -# of the x509 certificate. This has minimal security benefit since -# it is easy to spoof source IP. -# -# Uncommenting this will disable verification -# tls_no_verify_address 1 - -# Override the default server key file path -# -# key_file "/etc/pki/libvirt/private/serverkey.pem" - -# Override the default server certificate file path -# -# cert_file "/etc/pki/libvirt/servercert.pem" - -# Override the default CA certificate path -# -# ca_file "/etc/pki/CA/cacert.pem" - -# Specify a certificate revocation list. -# -# Defaults to not using a CRL, uncomment to enable it -# crl_file "/etc/pki/CA/crl.pem" # A whitelist of allowed x509 Distinguished Names # This list may contain wildcards such as @@ -127,15 +193,20 @@ # tls_allowed_dn_list ["DN1", "DN2"] -# A whitelist of allowed client IP addresses -# -# This list may contain wildcards such as 192.168.* See the POSIX fnmatch -# function for the format of the wildcards. +# A whitelist of allowed SASL usernames. The format for usernames +# depends on the SASL authentication mechanism. Kerberos usernames +# look like username@REALM +# +# This list may contain wildcards such as +# +# "*@EXAMPLE.COM" +# +# See the POSIX fnmatch function for the format of the wildcards. # # NB If this is an empty list, no client can connect, so comment out # entirely rather than using empty list to disable these checks # -# By default, no IP's are checked. This can be IPv4 or IPv6 addresses -# tls_allowed_ip_list ["ip1", "ip2", "ip3"] - - +# By default, no Username's are checked +# sasl_allowed_username_list ["joe@EXAMPLE.COM", "fred@EXAMPLE.COM" ] + + diff -r f28fe18bd7f5 qemud/qemud.c --- a/qemud/qemud.c Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/qemud.c Thu Nov 29 09:51:32 2007 -0500 @@ -77,15 +77,23 @@ static int unix_sock_rw_perms = 0700; /* static int unix_sock_rw_perms = 0700; /* Allow user only */ static int unix_sock_ro_perms = 0777; /* Allow world */ + +static int auth_unix_rw = REMOTE_AUTH_NONE; +static int auth_unix_ro = REMOTE_AUTH_NONE; +#if HAVE_SASL +static int auth_tcp = REMOTE_AUTH_SASL; +#else +static int auth_tcp = REMOTE_AUTH_NONE; +#endif +static int auth_tls = REMOTE_AUTH_NONE; + #ifdef HAVE_AVAHI static int mdns_adv = 1; static const char *mdns_name = NULL; #endif static int tls_no_verify_certificate = 0; -static int tls_no_verify_address = 0; -static const char **tls_allowed_ip_list = 0; -static const char **tls_allowed_dn_list = 0; +static char **tls_allowed_dn_list = 0; static const char *key_file = LIBVIRT_SERVERKEY; static const char *cert_file = LIBVIRT_SERVERCERT; @@ -450,7 +458,7 @@ static int qemudWritePidFile(const char } static int qemudListenUnix(struct qemud_server *server, - const char *path, int readonly) { + const char *path, int readonly, int auth) { struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket)); struct sockaddr_un addr; mode_t oldmask; @@ -464,6 +472,7 @@ static int qemudListenUnix(struct qemud_ sock->readonly = readonly; sock->port = -1; sock->type = QEMUD_SOCK_TYPE_UNIX; + sock->auth = auth; if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { qemudLog(QEMUD_ERR, "Failed to create socket: %s", @@ -701,6 +710,27 @@ static int qemudInitPaths(struct qemud_s static struct qemud_server *qemudInitialize(int sigread) { struct qemud_server *server; + + if (!(server = calloc(1, sizeof(struct qemud_server)))) { + qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); + return NULL; + } + + server->sigread = sigread; + + __virEventRegisterImpl(virEventAddHandleImpl, + virEventUpdateHandleImpl, + virEventRemoveHandleImpl, + virEventAddTimeoutImpl, + virEventUpdateTimeoutImpl, + virEventRemoveTimeoutImpl); + + virStateInitialize(); + + return server; +} + +static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { struct qemud_socket *sock; char sockname[PATH_MAX]; char roSockname[PATH_MAX]; @@ -708,56 +738,39 @@ static struct qemud_server *qemudInitial int err; #endif /* HAVE_SASL */ - if (!(server = calloc(1, sizeof(struct qemud_server)))) { - qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); - return NULL; - } - - /* We don't have a dom-0, so start from 1 */ - server->sigread = sigread; - roSockname[0] = '\0'; if (qemudInitPaths(server, sockname, roSockname, PATH_MAX) < 0) goto cleanup; - if (qemudListenUnix(server, sockname, 0) < 0) + if (qemudListenUnix(server, sockname, 0, auth_unix_rw) < 0) goto cleanup; - if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1) < 0) + if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1, auth_unix_ro) < 0) goto cleanup; - __virEventRegisterImpl(virEventAddHandleImpl, - virEventUpdateHandleImpl, - virEventRemoveHandleImpl, - virEventAddTimeoutImpl, - virEventUpdateTimeoutImpl, - virEventRemoveTimeoutImpl); - - virStateInitialize(); - #if HAVE_SASL - if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { - qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", - sasl_errstring(err, NULL, NULL)); - goto cleanup; + if (auth_unix_rw == REMOTE_AUTH_SASL || + auth_unix_ro == REMOTE_AUTH_SASL || + auth_tcp == REMOTE_AUTH_SASL || + auth_tls == REMOTE_AUTH_SASL) { + if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { + qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", + sasl_errstring(err, NULL, NULL)); + goto cleanup; + } } #endif if (ipsock) { -#if HAVE_SASL - if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_SASL) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0) goto cleanup; -#else - if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_NONE) < 0) - goto cleanup; -#endif if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, REMOTE_AUTH_NONE) < 0) + if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, auth_tls) < 0) goto cleanup; } } @@ -859,7 +872,7 @@ remoteCheckDN (gnutls_x509_crt_t cert) { char name[256]; size_t namesize = sizeof name; - const char **wildcards; + char **wildcards; int err; err = gnutls_x509_crt_get_dn (cert, name, &namesize); @@ -977,53 +990,11 @@ static int static int remoteCheckAccess (struct qemud_client *client) { - char addr[NI_MAXHOST]; - const char **wildcards; - int found, err; - /* Verify client certificate. */ if (remoteCheckCertificate (client->tlssession) == -1) { qemudLog (QEMUD_ERR, "remoteCheckCertificate: failed to verify client's certificate"); if (!tls_no_verify_certificate) return -1; else qemudLog (QEMUD_INFO, "remoteCheckCertificate: tls_no_verify_certificate is set so the bad certificate is ignored"); - } - - /*----- IP address check, similar to tcp wrappers -----*/ - - /* Convert IP address to printable string (eg. "127.0.0.1" or "::1"). */ - err = getnameinfo ((struct sockaddr *) &client->addr, client->addrlen, - addr, sizeof addr, NULL, 0, - NI_NUMERICHOST); - if (err != 0) { - qemudLog (QEMUD_ERR, "getnameinfo: %s", gai_strerror (err)); - return -1; - } - - /* Verify the client is on the list of allowed clients. - * - * NB: No tls_allowed_ip_list in config file means anyone can access. - * If tls_allowed_ip_list is in the config file but empty, means no - * one can access (not particularly useful, but it's what the sysadmin - * would expect). - */ - wildcards = tls_allowed_ip_list; - if (wildcards) { - found = 0; - - while (*wildcards) { - if (fnmatch (*wildcards, addr, 0) == 0) { - found = 1; - break; - } - wildcards++; - } - } else - found = 1; - - if (!found) { - qemudLog (QEMUD_ERR, "remoteCheckAccess: client's IP address (%s) is not on the list of allowed clients (tls_allowed_ip_list)", addr); - if (!tls_no_verify_address) return -1; - else qemudLog (QEMUD_INFO, "remoteCheckAccess: tls_no_verify_address is set so the client's IP address is ignored"); } /* Checks have succeeded. Write a '\1' byte back to the client to @@ -1148,6 +1119,7 @@ static void qemudDispatchClientFailure(s #if HAVE_SASL if (client->saslconn) sasl_dispose(&client->saslconn); + if (client->saslUsername) free(client->saslUsername); #endif if (client->tlssession) gnutls_deinit (client->tlssession); close(client->fd); @@ -1649,17 +1621,114 @@ static void qemudCleanup(struct qemud_se sock = next; } - + if (server->saslUsernameWhitelist) { + char **list = server->saslUsernameWhitelist; + while (*list) { + if (*list) + free(*list); + list++; + } + } virStateCleanup(); free(server); +} + + +static int remoteConfigGetStringList(virConfPtr conf, const char *key, char ***list, const char *filename) { + virConfValuePtr p; + + p = virConfGetValue (conf, key); + if (p) { + switch (p->type) { + case VIR_CONF_STRING: + *list = malloc (2 * sizeof (char *)); + if (*list == NULL) { + qemudLog (QEMUD_ERR, "failed to allocate memory for %s config list", key); + return -1; + } + (*list)[0] = strdup (p->str); + (*list)[1] = 0; + if ((*list)[0] == NULL) { + qemudLog (QEMUD_ERR, "failed to allocate memory for %s config list value", key); + free(*list); + return -1; + } + break; + + case VIR_CONF_LIST: { + int i, len = 0; + virConfValuePtr pp; + for (pp = p->list; pp; pp = pp->next) + len++; + *list = + calloc (1+len, sizeof (char *)); + if (*list == NULL) { + qemudLog (QEMUD_ERR, "failed to allocate memory for %s config list", key); + return -1; + } + for (i = 0, pp = p->list; pp; ++i, pp = pp->next) { + if (pp->type != VIR_CONF_STRING) { + qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: should be a string or list of strings\n", filename, key); + return -1; + } + (*list)[i] = strdup (pp->str); + if ((*list)[i] == NULL) { + for (i = 0 ; i < len ; i++) + if ((*list)[i]) free((*list)[i]); + free(*list); + qemudLog (QEMUD_ERR, "failed to allocate memory for %s config list value", key); + return -1; + } + + } + (*list)[i] = 0; + break; + } + + default: + qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: should be a string or list of strings\n", filename, key); + return -1; + } + } + + return 0; +} + +static int remoteConfigGetAuth(virConfPtr conf, const char *key, int *auth, const char *filename) { + virConfValuePtr p; + + p = virConfGetValue (conf, key); + if (!p) + return 0; + + if (p->type != VIR_CONF_STRING) { + qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: should be a string\n", filename, key); + return -1; + } + + if (!p->str) + return 0; + + if (STREQ(p->str, "none")) { + *auth = REMOTE_AUTH_NONE; +#if HAVE_SASL + } else if (STREQ(p->str, "sasl")) { + *auth = REMOTE_AUTH_SASL; +#endif + } else { + qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: unsupported auth %s\n", filename, key, p->str); + return -1; + } + + return 0; } /* Read the config file if it exists. * Only used in the remote case, hence the name. */ static int -remoteReadConfigFile (const char *filename) +remoteReadConfigFile (struct qemud_server *server, const char *filename) { virConfPtr conf; @@ -1695,6 +1764,15 @@ remoteReadConfigFile (const char *filena p = virConfGetValue (conf, "tcp_port"); CHECK_TYPE ("tcp_port", VIR_CONF_STRING); tcp_port = p ? strdup (p->str) : tcp_port; + + if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0) + return -1; + if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0) + return -1; + if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0) + return -1; + if (remoteConfigGetAuth(conf, "auth_tls", &auth_tls, filename) < 0) + return -1; p = virConfGetValue (conf, "unix_sock_group"); CHECK_TYPE ("unix_sock_group", VIR_CONF_STRING); @@ -1743,10 +1821,6 @@ remoteReadConfigFile (const char *filena CHECK_TYPE ("tls_no_verify_certificate", VIR_CONF_LONG); tls_no_verify_certificate = p ? p->l : tls_no_verify_certificate; - p = virConfGetValue (conf, "tls_no_verify_address"); - CHECK_TYPE ("tls_no_verify_address", VIR_CONF_LONG); - tls_no_verify_address = p ? p->l : tls_no_verify_address; - p = virConfGetValue (conf, "key_file"); CHECK_TYPE ("key_file", VIR_CONF_STRING); key_file = p ? strdup (p->str) : key_file; @@ -1763,71 +1837,11 @@ remoteReadConfigFile (const char *filena CHECK_TYPE ("crl_file", VIR_CONF_STRING); crl_file = p ? strdup (p->str) : crl_file; - p = virConfGetValue (conf, "tls_allowed_dn_list"); - if (p) { - switch (p->type) { - case VIR_CONF_STRING: - tls_allowed_dn_list = malloc (2 * sizeof (char *)); - tls_allowed_dn_list[0] = strdup (p->str); - tls_allowed_dn_list[1] = 0; - break; - - case VIR_CONF_LIST: { - int i, len = 0; - virConfValuePtr pp; - for (pp = p->list; pp; pp = p->next) - len++; - tls_allowed_dn_list = - malloc ((1+len) * sizeof (char *)); - for (i = 0, pp = p->list; pp; ++i, pp = p->next) { - if (pp->type != VIR_CONF_STRING) { - qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: tls_allowed_dn_list: should be a string or list of strings\n", filename); - return -1; - } - tls_allowed_dn_list[i] = strdup (pp->str); - } - tls_allowed_dn_list[i] = 0; - break; - } - - default: - qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: tls_allowed_dn_list: should be a string or list of strings\n", filename); - return -1; - } - } - - p = virConfGetValue (conf, "tls_allowed_ip_list"); - if (p) { - switch (p->type) { - case VIR_CONF_STRING: - tls_allowed_ip_list = malloc (2 * sizeof (char *)); - tls_allowed_ip_list[0] = strdup (p->str); - tls_allowed_ip_list[1] = 0; - break; - - case VIR_CONF_LIST: { - int i, len = 0; - virConfValuePtr pp; - for (pp = p->list; pp; pp = p->next) - len++; - tls_allowed_ip_list = - malloc ((1+len) * sizeof (char *)); - for (i = 0, pp = p->list; pp; ++i, pp = p->next) { - if (pp->type != VIR_CONF_STRING) { - qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: tls_allowed_ip_list: should be a string or list of strings\n", filename); - return -1; - } - tls_allowed_ip_list[i] = strdup (pp->str); - } - tls_allowed_ip_list[i] = 0; - break; - } - - default: - qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: tls_allowed_ip_list: should be a string or list of strings\n", filename); - return -1; - } - } + if (remoteConfigGetStringList(conf, "tls_allowed_dn_list", &tls_allowed_dn_list, filename) < 0) + return -1; + + if (remoteConfigGetStringList(conf, "sasl_allowed_username_list", &server->saslUsernameWhitelist, filename) < 0) + return -1; virConfFree (conf); return 0; @@ -1950,13 +1964,6 @@ int main(int argc, char **argv) { } } - /* Read the config file (if it exists). */ - if (remoteReadConfigFile (remote_config_file) < 0) - goto error1; - - if (godaemon) - openlog("libvirtd", 0, 0); - if (pipe(sigpipe) < 0 || qemudSetNonBlock(sigpipe[0]) < 0 || qemudSetNonBlock(sigpipe[1]) < 0) { @@ -1964,8 +1971,38 @@ int main(int argc, char **argv) { strerror(errno)); goto error1; } - sigwrite = sigpipe[1]; + + if (!(server = qemudInitialize(sigpipe[0]))) { + ret = 2; + goto error1; + } + + /* Read the config file (if it exists). */ + if (remoteReadConfigFile (server, remote_config_file) < 0) + goto error1; + + if (godaemon) { + int pid; + openlog("libvirtd", 0, 0); + pid = qemudGoDaemon(); + if (pid < 0) { + qemudLog(QEMUD_ERR, "Failed to fork as daemon: %s", + strerror(errno)); + goto error1; + } + if (pid > 0) + goto out; + + /* Choose the name of the PID file. */ + if (!pid_file) { + if (REMOTE_PID_FILE[0] != '\0') + pid_file = REMOTE_PID_FILE; + } + + if (pid_file && qemudWritePidFile (pid_file) < 0) + goto error1; + } sig_action.sa_handler = sig_handler; sig_action.sa_flags = 0; @@ -1979,31 +2016,6 @@ int main(int argc, char **argv) { sig_action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig_action, NULL); - - if (godaemon) { - int pid = qemudGoDaemon(); - if (pid < 0) { - qemudLog(QEMUD_ERR, "Failed to fork as daemon: %s", - strerror(errno)); - goto error1; - } - if (pid > 0) - goto out; - - /* Choose the name of the PID file. */ - if (!pid_file) { - if (REMOTE_PID_FILE[0] != '\0') - pid_file = REMOTE_PID_FILE; - } - - if (pid_file && qemudWritePidFile (pid_file) < 0) - goto error1; - } - - if (!(server = qemudInitialize(sigpipe[0]))) { - ret = 2; - goto error2; - } if (virEventAddHandleImpl(sigpipe[0], POLLIN, @@ -2011,6 +2023,11 @@ int main(int argc, char **argv) { server) < 0) { qemudLog(QEMUD_ERR, "Failed to register callback for signal pipe"); ret = 3; + goto error2; + } + + if (!(server = qemudNetworkInit(server))) { + ret = 2; goto error2; } diff -r f28fe18bd7f5 qemud/remote.c --- a/qemud/remote.c Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/remote.c Thu Nov 29 09:51:32 2007 -0500 @@ -44,6 +44,7 @@ #include <getopt.h> #include <ctype.h> #include <assert.h> +#include <fnmatch.h> #include <libvirt/virterror.h> @@ -65,14 +66,18 @@ static void make_nonnull_network (remote #include "remote_dispatch_prototypes.h" -typedef int (*dispatch_fn) (struct qemud_client *client, remote_message_header *req, char *args, char *ret); +typedef int (*dispatch_fn) (struct qemud_server *server, + struct qemud_client *client, + remote_message_header *req, + char *args, + char *ret); /* This function gets called from qemud when it detects an incoming * remote protocol message. At this point, client->buffer contains * the full call message (including length word which we skip). */ void -remoteDispatchClientRequest (struct qemud_server *server ATTRIBUTE_UNUSED, +remoteDispatchClientRequest (struct qemud_server *server, struct qemud_client *client) { XDR xdr; @@ -158,7 +163,7 @@ remoteDispatchClientRequest (struct qemu xdr_destroy (&xdr); /* Call function. */ - rv = fn (client, &req, args, ret); + rv = fn (server, client, &req, args, ret); xdr_free (args_filter, args); /* Dispatch function must return -2, -1 or 0. Anything else is @@ -398,7 +403,8 @@ remoteDispatchError (struct qemud_client /*----- Functions. -----*/ static int -remoteDispatchOpen (struct qemud_client *client, remote_message_header *req, +remoteDispatchOpen (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, struct remote_open_args *args, void *ret ATTRIBUTE_UNUSED) { const char *name; @@ -437,7 +443,8 @@ remoteDispatchOpen (struct qemud_client } static int -remoteDispatchClose (struct qemud_client *client, remote_message_header *req, +remoteDispatchClose (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED) { int rv; @@ -450,7 +457,8 @@ remoteDispatchClose (struct qemud_client } static int -remoteDispatchSupportsFeature (struct qemud_client *client, remote_message_header *req, +remoteDispatchSupportsFeature (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret) { CHECK_CONN(client); @@ -462,7 +470,8 @@ remoteDispatchSupportsFeature (struct qe } static int -remoteDispatchGetType (struct qemud_client *client, remote_message_header *req, +remoteDispatchGetType (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_type_ret *ret) { const char *type; @@ -484,7 +493,8 @@ remoteDispatchGetType (struct qemud_clie } static int -remoteDispatchGetVersion (struct qemud_client *client, +remoteDispatchGetVersion (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_version_ret *ret) @@ -500,7 +510,8 @@ remoteDispatchGetVersion (struct qemud_c } static int -remoteDispatchGetHostname (struct qemud_client *client, +remoteDispatchGetHostname (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_hostname_ret *ret) @@ -516,7 +527,8 @@ remoteDispatchGetHostname (struct qemud_ } static int -remoteDispatchGetMaxVcpus (struct qemud_client *client, +remoteDispatchGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret) @@ -532,7 +544,8 @@ remoteDispatchGetMaxVcpus (struct qemud_ } static int -remoteDispatchNodeGetInfo (struct qemud_client *client, +remoteDispatchNodeGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_node_get_info_ret *ret) @@ -556,7 +569,8 @@ remoteDispatchNodeGetInfo (struct qemud_ } static int -remoteDispatchGetCapabilities (struct qemud_client *client, +remoteDispatchGetCapabilities (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_capabilities_ret *ret) @@ -572,7 +586,8 @@ remoteDispatchGetCapabilities (struct qe } static int -remoteDispatchDomainGetSchedulerType (struct qemud_client *client, +remoteDispatchDomainGetSchedulerType (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret) @@ -601,7 +616,8 @@ remoteDispatchDomainGetSchedulerType (st } static int -remoteDispatchDomainGetSchedulerParameters (struct qemud_client *client, +remoteDispatchDomainGetSchedulerParameters (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret) @@ -687,7 +703,8 @@ remoteDispatchDomainGetSchedulerParamete } static int -remoteDispatchDomainSetSchedulerParameters (struct qemud_client *client, +remoteDispatchDomainSetSchedulerParameters (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_scheduler_parameters_args *args, void *ret ATTRIBUTE_UNUSED) @@ -747,7 +764,8 @@ remoteDispatchDomainSetSchedulerParamete } static int -remoteDispatchDomainBlockStats (struct qemud_client *client, +remoteDispatchDomainBlockStats (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret) @@ -777,7 +795,8 @@ remoteDispatchDomainBlockStats (struct q } static int -remoteDispatchDomainInterfaceStats (struct qemud_client *client, +remoteDispatchDomainInterfaceStats (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret) @@ -810,7 +829,8 @@ remoteDispatchDomainInterfaceStats (stru } static int -remoteDispatchDomainAttachDevice (struct qemud_client *client, +remoteDispatchDomainAttachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret ATTRIBUTE_UNUSED) @@ -833,7 +853,8 @@ remoteDispatchDomainAttachDevice (struct } static int -remoteDispatchDomainCreate (struct qemud_client *client, +remoteDispatchDomainCreate (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -856,7 +877,8 @@ remoteDispatchDomainCreate (struct qemud } static int -remoteDispatchDomainCreateLinux (struct qemud_client *client, +remoteDispatchDomainCreateLinux (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_create_linux_args *args, remote_domain_create_linux_ret *ret) @@ -874,7 +896,8 @@ remoteDispatchDomainCreateLinux (struct } static int -remoteDispatchDomainDefineXml (struct qemud_client *client, +remoteDispatchDomainDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret) @@ -892,7 +915,8 @@ remoteDispatchDomainDefineXml (struct qe } static int -remoteDispatchDomainDestroy (struct qemud_client *client, +remoteDispatchDomainDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -913,7 +937,8 @@ remoteDispatchDomainDestroy (struct qemu } static int -remoteDispatchDomainDetachDevice (struct qemud_client *client, +remoteDispatchDomainDetachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_detach_device_args *args, void *ret ATTRIBUTE_UNUSED) @@ -937,7 +962,8 @@ remoteDispatchDomainDetachDevice (struct } static int -remoteDispatchDomainDumpXml (struct qemud_client *client, +remoteDispatchDomainDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret) @@ -962,7 +988,8 @@ remoteDispatchDomainDumpXml (struct qemu } static int -remoteDispatchDomainGetAutostart (struct qemud_client *client, +remoteDispatchDomainGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret) @@ -985,7 +1012,8 @@ remoteDispatchDomainGetAutostart (struct } static int -remoteDispatchDomainGetInfo (struct qemud_client *client, +remoteDispatchDomainGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret) @@ -1017,7 +1045,8 @@ remoteDispatchDomainGetInfo (struct qemu } static int -remoteDispatchDomainGetMaxMemory (struct qemud_client *client, +remoteDispatchDomainGetMaxMemory (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret) @@ -1041,7 +1070,8 @@ remoteDispatchDomainGetMaxMemory (struct } static int -remoteDispatchDomainGetMaxVcpus (struct qemud_client *client, +remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret) @@ -1065,7 +1095,8 @@ remoteDispatchDomainGetMaxVcpus (struct } static int -remoteDispatchDomainGetOsType (struct qemud_client *client, +remoteDispatchDomainGetOsType (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret) @@ -1090,7 +1121,8 @@ remoteDispatchDomainGetOsType (struct qe } static int -remoteDispatchDomainGetVcpus (struct qemud_client *client, +remoteDispatchDomainGetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret) @@ -1154,7 +1186,8 @@ remoteDispatchDomainGetVcpus (struct qem } static int -remoteDispatchDomainMigratePrepare (struct qemud_client *client, +remoteDispatchDomainMigratePrepare (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret) @@ -1189,7 +1222,8 @@ remoteDispatchDomainMigratePrepare (stru } static int -remoteDispatchDomainMigratePerform (struct qemud_client *client, +remoteDispatchDomainMigratePerform (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1218,7 +1252,8 @@ remoteDispatchDomainMigratePerform (stru } static int -remoteDispatchDomainMigrateFinish (struct qemud_client *client, +remoteDispatchDomainMigrateFinish (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret) @@ -1239,7 +1274,8 @@ remoteDispatchDomainMigrateFinish (struc } static int -remoteDispatchListDefinedDomains (struct qemud_client *client, +remoteDispatchListDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret) @@ -1264,7 +1300,8 @@ remoteDispatchListDefinedDomains (struct } static int -remoteDispatchDomainLookupById (struct qemud_client *client, +remoteDispatchDomainLookupById (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret) @@ -1281,7 +1318,8 @@ remoteDispatchDomainLookupById (struct q } static int -remoteDispatchDomainLookupByName (struct qemud_client *client, +remoteDispatchDomainLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret) @@ -1298,7 +1336,8 @@ remoteDispatchDomainLookupByName (struct } static int -remoteDispatchDomainLookupByUuid (struct qemud_client *client, +remoteDispatchDomainLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret) @@ -1315,7 +1354,8 @@ remoteDispatchDomainLookupByUuid (struct } static int -remoteDispatchNumOfDefinedDomains (struct qemud_client *client, +remoteDispatchNumOfDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_domains_ret *ret) @@ -1329,7 +1369,8 @@ remoteDispatchNumOfDefinedDomains (struc } static int -remoteDispatchDomainPinVcpu (struct qemud_client *client, +remoteDispatchDomainPinVcpu (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1362,7 +1403,8 @@ remoteDispatchDomainPinVcpu (struct qemu } static int -remoteDispatchDomainReboot (struct qemud_client *client, +remoteDispatchDomainReboot (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1385,7 +1427,8 @@ remoteDispatchDomainReboot (struct qemud } static int -remoteDispatchDomainRestore (struct qemud_client *client, +remoteDispatchDomainRestore (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1399,7 +1442,8 @@ remoteDispatchDomainRestore (struct qemu } static int -remoteDispatchDomainResume (struct qemud_client *client, +remoteDispatchDomainResume (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_resume_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1422,7 +1466,8 @@ remoteDispatchDomainResume (struct qemud } static int -remoteDispatchDomainSave (struct qemud_client *client, +remoteDispatchDomainSave (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_save_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1445,7 +1490,8 @@ remoteDispatchDomainSave (struct qemud_c } static int -remoteDispatchDomainCoreDump (struct qemud_client *client, +remoteDispatchDomainCoreDump (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1468,7 +1514,8 @@ remoteDispatchDomainCoreDump (struct qem } static int -remoteDispatchDomainSetAutostart (struct qemud_client *client, +remoteDispatchDomainSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1491,7 +1538,8 @@ remoteDispatchDomainSetAutostart (struct } static int -remoteDispatchDomainSetMaxMemory (struct qemud_client *client, +remoteDispatchDomainSetMaxMemory (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_max_memory_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1514,7 +1562,8 @@ remoteDispatchDomainSetMaxMemory (struct } static int -remoteDispatchDomainSetMemory (struct qemud_client *client, +remoteDispatchDomainSetMemory (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_memory_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1537,7 +1586,8 @@ remoteDispatchDomainSetMemory (struct qe } static int -remoteDispatchDomainSetVcpus (struct qemud_client *client, +remoteDispatchDomainSetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_vcpus_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1560,7 +1610,8 @@ remoteDispatchDomainSetVcpus (struct qem } static int -remoteDispatchDomainShutdown (struct qemud_client *client, +remoteDispatchDomainShutdown (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1583,7 +1634,8 @@ remoteDispatchDomainShutdown (struct qem } static int -remoteDispatchDomainSuspend (struct qemud_client *client, +remoteDispatchDomainSuspend (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1606,7 +1658,8 @@ remoteDispatchDomainSuspend (struct qemu } static int -remoteDispatchDomainUndefine (struct qemud_client *client, +remoteDispatchDomainUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1629,7 +1682,8 @@ remoteDispatchDomainUndefine (struct qem } static int -remoteDispatchListDefinedNetworks (struct qemud_client *client, +remoteDispatchListDefinedNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret) @@ -1654,7 +1708,8 @@ remoteDispatchListDefinedNetworks (struc } static int -remoteDispatchListDomains (struct qemud_client *client, +remoteDispatchListDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret) @@ -1678,7 +1733,8 @@ remoteDispatchListDomains (struct qemud_ } static int -remoteDispatchListNetworks (struct qemud_client *client, +remoteDispatchListNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret) @@ -1703,7 +1759,8 @@ remoteDispatchListNetworks (struct qemud } static int -remoteDispatchNetworkCreate (struct qemud_client *client, +remoteDispatchNetworkCreate (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1726,7 +1783,8 @@ remoteDispatchNetworkCreate (struct qemu } static int -remoteDispatchNetworkCreateXml (struct qemud_client *client, +remoteDispatchNetworkCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret) @@ -1743,7 +1801,8 @@ remoteDispatchNetworkCreateXml (struct q } static int -remoteDispatchNetworkDefineXml (struct qemud_client *client, +remoteDispatchNetworkDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret) @@ -1760,7 +1819,8 @@ remoteDispatchNetworkDefineXml (struct q } static int -remoteDispatchNetworkDestroy (struct qemud_client *client, +remoteDispatchNetworkDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1783,7 +1843,8 @@ remoteDispatchNetworkDestroy (struct qem } static int -remoteDispatchNetworkDumpXml (struct qemud_client *client, +remoteDispatchNetworkDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret) @@ -1808,7 +1869,8 @@ remoteDispatchNetworkDumpXml (struct qem } static int -remoteDispatchNetworkGetAutostart (struct qemud_client *client, +remoteDispatchNetworkGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret) @@ -1831,7 +1893,8 @@ remoteDispatchNetworkGetAutostart (struc } static int -remoteDispatchNetworkGetBridgeName (struct qemud_client *client, +remoteDispatchNetworkGetBridgeName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret) @@ -1856,7 +1919,8 @@ remoteDispatchNetworkGetBridgeName (stru } static int -remoteDispatchNetworkLookupByName (struct qemud_client *client, +remoteDispatchNetworkLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret) @@ -1873,7 +1937,8 @@ remoteDispatchNetworkLookupByName (struc } static int -remoteDispatchNetworkLookupByUuid (struct qemud_client *client, +remoteDispatchNetworkLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret) @@ -1890,7 +1955,8 @@ remoteDispatchNetworkLookupByUuid (struc } static int -remoteDispatchNetworkSetAutostart (struct qemud_client *client, +remoteDispatchNetworkSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1913,7 +1979,8 @@ remoteDispatchNetworkSetAutostart (struc } static int -remoteDispatchNetworkUndefine (struct qemud_client *client, +remoteDispatchNetworkUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1936,7 +2003,8 @@ remoteDispatchNetworkUndefine (struct qe } static int -remoteDispatchNumOfDefinedNetworks (struct qemud_client *client, +remoteDispatchNumOfDefinedNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_networks_ret *ret) @@ -1950,7 +2018,8 @@ remoteDispatchNumOfDefinedNetworks (stru } static int -remoteDispatchNumOfDomains (struct qemud_client *client, +remoteDispatchNumOfDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_domains_ret *ret) @@ -1964,7 +2033,8 @@ remoteDispatchNumOfDomains (struct qemud } static int -remoteDispatchNumOfNetworks (struct qemud_client *client, +remoteDispatchNumOfNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_networks_ret *ret) @@ -1979,7 +2049,8 @@ remoteDispatchNumOfNetworks (struct qemu static int -remoteDispatchAuthList (struct qemud_client *client, +remoteDispatchAuthList (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, remote_auth_list_ret *ret) @@ -2034,7 +2105,8 @@ static char *addrToString(struct qemud_c * XXX callbacks for stuff like password verification ? */ static int -remoteDispatchAuthSaslInit (struct qemud_client *client, +remoteDispatchAuthSaslInit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_auth_sasl_init_ret *ret) @@ -2223,11 +2295,67 @@ remoteSASLCheckSSF (struct qemud_client return 0; } +static int +remoteSASLCheckAccess (struct qemud_server *server, + struct qemud_client *client, + remote_message_header *req) { + const void *val; + int err; + char **wildcards; + + err = sasl_getprop(client->saslconn, SASL_USERNAME, &val); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot query SASL username on connection %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + if (val == NULL) { + qemudLog(QEMUD_ERR, "no client username was found"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + REMOTE_DEBUG("SASL client username %s", (const char *)val); + + client->saslUsername = strdup((const char*)val); + if (client->saslUsername == NULL) { + qemudLog(QEMUD_ERR, "out of memory copying username"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + + /* If the list is not set, allow any DN. */ + wildcards = server->saslUsernameWhitelist; + if (!wildcards) + return 0; /* No ACL, allow all */ + + while (*wildcards) { + if (fnmatch (*wildcards, client->saslUsername, 0) == 0) + return 0; /* Allowed */ + wildcards++; + } + + /* Denied */ + qemudLog(QEMUD_ERR, "SASL client %s not allowed in whitelist", client->saslUsername); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; +} + + /* * This starts the SASL authentication negotiation. */ static int -remoteDispatchAuthSaslStart (struct qemud_client *client, +remoteDispatchAuthSaslStart (struct qemud_server *server, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret) @@ -2291,6 +2419,10 @@ remoteDispatchAuthSaslStart (struct qemu if (remoteSASLCheckSSF(client, req) < 0) return -2; + /* Check username whitelist ACL */ + if (remoteSASLCheckAccess(server, client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; @@ -2301,7 +2433,8 @@ remoteDispatchAuthSaslStart (struct qemu static int -remoteDispatchAuthSaslStep (struct qemud_client *client, +remoteDispatchAuthSaslStep (struct qemud_server *server, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret) @@ -2365,6 +2498,10 @@ remoteDispatchAuthSaslStep (struct qemud if (remoteSASLCheckSSF(client, req) < 0) return -2; + /* Check username whitelist ACL */ + if (remoteSASLCheckAccess(server, client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; @@ -2376,7 +2513,8 @@ remoteDispatchAuthSaslStep (struct qemud #else /* HAVE_SASL */ static int -remoteDispatchAuthSaslInit (struct qemud_client *client, +remoteDispatchAuthSaslInit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) @@ -2387,7 +2525,8 @@ remoteDispatchAuthSaslInit (struct qemud } static int -remoteDispatchAuthSaslStart (struct qemud_client *client, +remoteDispatchAuthSaslStart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) @@ -2398,7 +2537,8 @@ remoteDispatchAuthSaslStart (struct qemu } static int -remoteDispatchAuthSaslStep (struct qemud_client *client, +remoteDispatchAuthSaslStep (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) diff -r f28fe18bd7f5 qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/remote_dispatch_prototypes.h Thu Nov 29 09:51:32 2007 -0500 @@ -2,72 +2,72 @@ * Do not edit this file. Any changes you make will be lost. */ -static int remoteDispatchAuthList (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); -static int remoteDispatchAuthSaslInit (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); -static int remoteDispatchAuthSaslStart (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); -static int remoteDispatchAuthSaslStep (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); -static int remoteDispatchClose (struct qemud_client *client, remote_message_header *req, void *args, void *ret); -static int remoteDispatchDomainAttachDevice (struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); -static int remoteDispatchDomainBlockStats (struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); -static int remoteDispatchDomainCoreDump (struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret); -static int remoteDispatchDomainCreate (struct qemud_client *client, remote_message_header *req, remote_domain_create_args *args, void *ret); -static int remoteDispatchDomainCreateLinux (struct qemud_client *client, remote_message_header *req, remote_domain_create_linux_args *args, remote_domain_create_linux_ret *ret); -static int remoteDispatchDomainDefineXml (struct qemud_client *client, remote_message_header *req, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret); -static int remoteDispatchDomainDestroy (struct qemud_client *client, remote_message_header *req, remote_domain_destroy_args *args, void *ret); -static int remoteDispatchDomainDetachDevice (struct qemud_client *client, remote_message_header *req, remote_domain_detach_device_args *args, void *ret); -static int remoteDispatchDomainDumpXml (struct qemud_client *client, remote_message_header *req, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret); -static int remoteDispatchDomainGetAutostart (struct qemud_client *client, remote_message_header *req, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret); -static int remoteDispatchDomainGetInfo (struct qemud_client *client, remote_message_header *req, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret); -static int remoteDispatchDomainGetMaxMemory (struct qemud_client *client, remote_message_header *req, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret); -static int remoteDispatchDomainGetMaxVcpus (struct qemud_client *client, remote_message_header *req, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret); -static int remoteDispatchDomainGetOsType (struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret); -static int remoteDispatchDomainGetSchedulerParameters (struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret); -static int remoteDispatchDomainGetSchedulerType (struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); -static int remoteDispatchDomainGetVcpus (struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret); -static int remoteDispatchDomainInterfaceStats (struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret); -static int remoteDispatchDomainLookupById (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); -static int remoteDispatchDomainLookupByName (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); -static int remoteDispatchDomainLookupByUuid (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); -static int remoteDispatchDomainMigrateFinish (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); -static int remoteDispatchDomainMigratePerform (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret); -static int remoteDispatchDomainMigratePrepare (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); -static int remoteDispatchDomainPinVcpu (struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret); -static int remoteDispatchDomainReboot (struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret); -static int remoteDispatchDomainRestore (struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret); -static int remoteDispatchDomainResume (struct qemud_client *client, remote_message_header *req, remote_domain_resume_args *args, void *ret); -static int remoteDispatchDomainSave (struct qemud_client *client, remote_message_header *req, remote_domain_save_args *args, void *ret); -static int remoteDispatchDomainSetAutostart (struct qemud_client *client, remote_message_header *req, remote_domain_set_autostart_args *args, void *ret); -static int remoteDispatchDomainSetMaxMemory (struct qemud_client *client, remote_message_header *req, remote_domain_set_max_memory_args *args, void *ret); -static int remoteDispatchDomainSetMemory (struct qemud_client *client, remote_message_header *req, remote_domain_set_memory_args *args, void *ret); -static int remoteDispatchDomainSetSchedulerParameters (struct qemud_client *client, remote_message_header *req, remote_domain_set_scheduler_parameters_args *args, void *ret); -static int remoteDispatchDomainSetVcpus (struct qemud_client *client, remote_message_header *req, remote_domain_set_vcpus_args *args, void *ret); -static int remoteDispatchDomainShutdown (struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret); -static int remoteDispatchDomainSuspend (struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret); -static int remoteDispatchDomainUndefine (struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret); -static int remoteDispatchGetCapabilities (struct qemud_client *client, remote_message_header *req, void *args, remote_get_capabilities_ret *ret); -static int remoteDispatchGetHostname (struct qemud_client *client, remote_message_header *req, void *args, remote_get_hostname_ret *ret); -static int remoteDispatchGetMaxVcpus (struct qemud_client *client, remote_message_header *req, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret); -static int remoteDispatchGetType (struct qemud_client *client, remote_message_header *req, void *args, remote_get_type_ret *ret); -static int remoteDispatchGetVersion (struct qemud_client *client, remote_message_header *req, void *args, remote_get_version_ret *ret); -static int remoteDispatchListDefinedDomains (struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret); -static int remoteDispatchListDefinedNetworks (struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret); -static int remoteDispatchListDomains (struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret); -static int remoteDispatchListNetworks (struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret); -static int remoteDispatchNetworkCreate (struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret); -static int remoteDispatchNetworkCreateXml (struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret); -static int remoteDispatchNetworkDefineXml (struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret); -static int remoteDispatchNetworkDestroy (struct qemud_client *client, remote_message_header *req, remote_network_destroy_args *args, void *ret); -static int remoteDispatchNetworkDumpXml (struct qemud_client *client, remote_message_header *req, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret); -static int remoteDispatchNetworkGetAutostart (struct qemud_client *client, remote_message_header *req, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret); -static int remoteDispatchNetworkGetBridgeName (struct qemud_client *client, remote_message_header *req, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret); -static int remoteDispatchNetworkLookupByName (struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret); -static int remoteDispatchNetworkLookupByUuid (struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret); -static int remoteDispatchNetworkSetAutostart (struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret); -static int remoteDispatchNetworkUndefine (struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret); -static int remoteDispatchNodeGetInfo (struct qemud_client *client, remote_message_header *req, void *args, remote_node_get_info_ret *ret); -static int remoteDispatchNumOfDefinedDomains (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_domains_ret *ret); -static int remoteDispatchNumOfDefinedNetworks (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_networks_ret *ret); -static int remoteDispatchNumOfDomains (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_domains_ret *ret); -static int remoteDispatchNumOfNetworks (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_networks_ret *ret); -static int remoteDispatchOpen (struct qemud_client *client, remote_message_header *req, remote_open_args *args, void *ret); -static int remoteDispatchSupportsFeature (struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret); +static int remoteDispatchAuthList (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); +static int remoteDispatchAuthSaslInit (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); +static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); +static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); +static int remoteDispatchClose (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, void *ret); +static int remoteDispatchDomainAttachDevice (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); +static int remoteDispatchDomainBlockStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); +static int remoteDispatchDomainCoreDump (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret); +static int remoteDispatchDomainCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_create_args *args, void *ret); +static int remoteDispatchDomainCreateLinux (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_create_linux_args *args, remote_domain_create_linux_ret *ret); +static int remoteDispatchDomainDefineXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret); +static int remoteDispatchDomainDestroy (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_destroy_args *args, void *ret); +static int remoteDispatchDomainDetachDevice (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_detach_device_args *args, void *ret); +static int remoteDispatchDomainDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret); +static int remoteDispatchDomainGetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret); +static int remoteDispatchDomainGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret); +static int remoteDispatchDomainGetMaxMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret); +static int remoteDispatchDomainGetMaxVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret); +static int remoteDispatchDomainGetOsType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret); +static int remoteDispatchDomainGetSchedulerParameters (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret); +static int remoteDispatchDomainGetSchedulerType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); +static int remoteDispatchDomainGetVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret); +static int remoteDispatchDomainInterfaceStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret); +static int remoteDispatchDomainLookupById (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); +static int remoteDispatchDomainLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); +static int remoteDispatchDomainLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); +static int remoteDispatchDomainMigrateFinish (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); +static int remoteDispatchDomainMigratePerform (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret); +static int remoteDispatchDomainMigratePrepare (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); +static int remoteDispatchDomainPinVcpu (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret); +static int remoteDispatchDomainReboot (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret); +static int remoteDispatchDomainRestore (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret); +static int remoteDispatchDomainResume (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_resume_args *args, void *ret); +static int remoteDispatchDomainSave (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_save_args *args, void *ret); +static int remoteDispatchDomainSetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_autostart_args *args, void *ret); +static int remoteDispatchDomainSetMaxMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_max_memory_args *args, void *ret); +static int remoteDispatchDomainSetMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_memory_args *args, void *ret); +static int remoteDispatchDomainSetSchedulerParameters (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_scheduler_parameters_args *args, void *ret); +static int remoteDispatchDomainSetVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_vcpus_args *args, void *ret); +static int remoteDispatchDomainShutdown (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret); +static int remoteDispatchDomainSuspend (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret); +static int remoteDispatchDomainUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret); +static int remoteDispatchGetCapabilities (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_capabilities_ret *ret); +static int remoteDispatchGetHostname (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_hostname_ret *ret); +static int remoteDispatchGetMaxVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret); +static int remoteDispatchGetType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_type_ret *ret); +static int remoteDispatchGetVersion (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_version_ret *ret); +static int remoteDispatchListDefinedDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret); +static int remoteDispatchListDefinedNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret); +static int remoteDispatchListDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret); +static int remoteDispatchListNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret); +static int remoteDispatchNetworkCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret); +static int remoteDispatchNetworkCreateXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret); +static int remoteDispatchNetworkDefineXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret); +static int remoteDispatchNetworkDestroy (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_destroy_args *args, void *ret); +static int remoteDispatchNetworkDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret); +static int remoteDispatchNetworkGetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret); +static int remoteDispatchNetworkGetBridgeName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret); +static int remoteDispatchNetworkLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret); +static int remoteDispatchNetworkLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret); +static int remoteDispatchNetworkSetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret); +static int remoteDispatchNetworkUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret); +static int remoteDispatchNodeGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_node_get_info_ret *ret); +static int remoteDispatchNumOfDefinedDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_domains_ret *ret); +static int remoteDispatchNumOfDefinedNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_networks_ret *ret); +static int remoteDispatchNumOfDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_domains_ret *ret); +static int remoteDispatchNumOfNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_networks_ret *ret); +static int remoteDispatchOpen (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_open_args *args, void *ret); +static int remoteDispatchSupportsFeature (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret); diff -r f28fe18bd7f5 qemud/remote_generate_stubs.pl --- a/qemud/remote_generate_stubs.pl Thu Nov 29 09:47:39 2007 -0500 +++ b/qemud/remote_generate_stubs.pl Thu Nov 29 09:51:32 2007 -0500 @@ -93,7 +93,7 @@ elsif ($opt_i) { elsif ($opt_i) { my @keys = sort (keys %calls); foreach (@keys) { - print "static int remoteDispatch$calls{$_}->{ProcName} (struct qemud_client *client, remote_message_header *req, $calls{$_}->{args} *args, $calls{$_}->{ret} *ret);\n"; + print "static int remoteDispatch$calls{$_}->{ProcName} (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, $calls{$_}->{args} *args, $calls{$_}->{ret} *ret);\n"; } } @@ -196,7 +196,8 @@ elsif ($opt_s) { my $retvoid = $ret eq "void"; print "static int\n"; - print "remoteDispatch$calls{$_}->{ProcName} (struct qemud_client *client,\n"; + print "remoteDispatch$calls{$_}->{ProcName} (struct qemud_server *server,\n"; + print " struct qemud_client *client,\n"; print " remote_message_header *req,\n"; print " remote_get_max_vcpus_args *args,\n"; print " remote_get_max_vcpus_ret *ret)\n"; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:18:06PM +0000, Daniel P. Berrange wrote:
This patch provides the ability to configure what authentication mechanism is used on each socket - UNIX RW, UNIX RO, TCP, and TLS sockets - all can have independant settings. By default the UNIX & TLS sockets have no auth, and the TCP socket has SASL auth enabled. The /etc/libvirt/libvirtd.conf file lets you override these options.
There is also a new sasl_allowed_username_list = ["admin"] config param to let you whitelist the users you want to allow. This supports use of wildcards. The username is dependnat on the SASL auth mechanism. For DIGEST-MD5 it will be plain usernames, for Kerberos it will be a username + realm, eg admin EXAMPLE COM
After discussion with Rich, I also remove the tls_allowed_ip_list for whitelisting source IP addresses. This was a) not protecting us because it was only checked after the TLS handshake - thus allowing trivial DOS attack b) much easier to handle via tcp wrappers, or IPtables. c) only ever checked for the TLS socket d) IP addresses are easily spoofed.
If summary, if you're using a real authentication mechanism, this is only useful for protecting against DOS attacks & that's better done by iptables.
Rebased to take account of Jim's changes, and incorporated fixes to the config file diff -r b68f6ec36358 qemud/Makefile.am --- a/qemud/Makefile.am Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/Makefile.am Fri Nov 30 15:15:26 2007 -0500 @@ -101,14 +101,14 @@ remote.c: remote_dispatch_prototypes.h \ remote_dispatch_localvars.h \ remote_dispatch_proc_switch.h -remote_dispatch_prototypes.h: remote_generate_stubs.pl remote_protocol.x - perl -w remote_generate_stubs.pl -i remote_protocol.x > $@ +remote_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl remote_protocol.x + perl -w $(srcdir)/remote_generate_stubs.pl -i $(srcdir)/remote_protocol.x > $@ -remote_dispatch_localvars.h: remote_generate_stubs.pl remote_protocol.x - perl -w remote_generate_stubs.pl -v remote_protocol.x > $@ +remote_dispatch_localvars.h: $(srcdir)/remote_generate_stubs.pl remote_protocol.x + perl -w $(srcdir)/remote_generate_stubs.pl -v $(srcdir)/remote_protocol.x > $@ -remote_dispatch_proc_switch.h: remote_generate_stubs.pl remote_protocol.x - perl -w remote_generate_stubs.pl -w remote_protocol.x > $@ +remote_dispatch_proc_switch.h: $(srcdir)/remote_generate_stubs.pl remote_protocol.x + perl -w $(srcdir)/remote_generate_stubs.pl -w $(srcdir)/remote_protocol.x > $@ if LIBVIRT_INIT_SCRIPTS_RED_HAT install-init: libvirtd.init diff -r b68f6ec36358 qemud/internal.h --- a/qemud/internal.h Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/internal.h Fri Nov 30 15:15:26 2007 -0500 @@ -109,6 +109,7 @@ struct qemud_client { const char *saslEncoded; unsigned int saslEncodedLength; unsigned int saslEncodedOffset; + char *saslUsername; #endif unsigned int incomingSerial; @@ -151,6 +152,9 @@ struct qemud_server { #ifdef HAVE_AVAHI struct libvirtd_mdns *mdns; #endif +#if HAVE_SASL + char **saslUsernameWhitelist; +#endif }; void qemudLog(int priority, const char *fmt, ...) diff -r b68f6ec36358 qemud/libvirtd.conf --- a/qemud/libvirtd.conf Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/libvirtd.conf Fri Nov 30 15:15:26 2007 -0500 @@ -2,6 +2,11 @@ # # For further information consult http://libvirt.org/format.html + +################################################################# +# +# Network connectivitiy controls +# # Flag listening for secure TLS connections on the public TCP/IP port. # NB, must pass the --listen flag to the libvirtd process for this to @@ -11,28 +16,30 @@ # using this capability. # # This is enabled by default, uncomment this to disable it -# listen_tls = 0 +#listen_tls = 0 # Listen for unencrypted TCP connections on the public TCP/IP port. # NB, must pass the --listen flag to the libvirtd process for this to # have any effect. # -# NB, this is insecure. Do not use except for development. +# Using the TCP socket requires SASL authentication by default. Only +# SASL mechanisms which support data encryption are allowed. This is +# DIGEST_MD5 and GSSAPI (Kerberos5) # # This is disabled by default, uncomment this to enable it. -# listen_tcp = 1 +#listen_tcp = 1 # Override the port for accepting secure TLS connections # This can be a port number, or service name # -# tls_port = "16514" +#tls_port = "16514" # Override the port for accepting insecure TCP connections # This can be a port number, or service name -# -# tcp_port = "16509" +# +#tcp_port = "16509" @@ -42,39 +49,123 @@ # stopping the Avahi daemon # # This is enabled by default, uncomment this to disable it -# mdns_adv = 0 +#mdns_adv = 0 # Override the default mDNS advertizement name. This must be # unique on the immediate broadcast network. -# +# # The default is "Virtualization Host HOSTNAME", where HOSTNAME # is subsituted for the short hostname of the machine (without domain) # -# mdns_name "Virtualization Host Joe Demo" - - +#mdns_name = "Virtualization Host Joe Demo" + + +################################################################# +# +# UNIX socket access controls +# # Set the UNIX domain socket group ownership. This can be used to # allow a 'trusted' set of users access to management capabilities # without becoming root. -# -# This is restricted to 'root' by default. -# unix_sock_group "libvirt" +# +# This is restricted to 'root' by default. +#unix_sock_group = "libvirt" # Set the UNIX socket permissions for the R/O socket. This is used # for monitoring VM status only # # Default allows any user. If setting group ownership may want to # restrict this to: -# unix_sock_ro_perms "0777" +#unix_sock_ro_perms = "0777" # Set the UNIX socket permissions for the R/W socket. This is used # for full management of VMs # # Default allows only root. If setting group ownership may want to # relax this to: -# unix_sock_rw_perms "octal-perms" "0770" - +#unix_sock_rw_perms = "0770" + + + +################################################################# +# +# Authentication. +# +# - none: do not perform auth checks. If you can connect to the +# socket you are allowed. This is suitable if there are +# restrictions on connecting to the socket (eg, UNIX +# socket permissions), or if there is a lower layer in +# the network providing auth (eg, TLS/x509 certificates) +# +# - sasl: use SASL infrastructure. The actual auth scheme is then +# controlled from /etc/sasl2/libvirt.conf. For the TCP +# socket only GSSAPI & DIGEST-MD5 mechanisms will be used. +# For non-TCP or TLS sockets, any scheme is allowed. +# + +# Set an authentication scheme for UNIX read-only sockets +# By default socket permissions allow anyone to connect +# +# To restrict monitoring of domains you may wish to enable +# an authentication mechanism here +#auth_unix_ro = "none" + +# Set an authentication scheme for UNIX read-write sockets +# By default socket permissions only allow root. +# +# If the unix_sock_rw_perms are changed you may wish to enable +# an authentication mechanism here +#auth_unix_rw = "none" + +# Change the authentication scheme for TCP sockets. +# +# If you don't enable SASL, then all TCP traffic is cleartext. +# Don't do this outside of a dev/test scenario. For real world +# use, always enable SASL and use the GSSAPI or DIGEST-MD5 +# mechanism in /etc/sasl2/libvirt.conf +#auth_tcp = "sasl" + +# Change the authentication scheme for TLS sockets. +# +# TLS sockets already have encryption provided by the TLS +# layer, and limited authentication is done by certificates +# +# It is possible to make use of any SASL authentication +# mechanism as well, by using 'sasl' for this option +#auth_tls = "none" + + + +################################################################# +# +# TLS x509 certificate configuration +# + + +# Override the default server key file path +# +#key_file = "/etc/pki/libvirt/private/serverkey.pem" + +# Override the default server certificate file path +# +#cert_file = "/etc/pki/libvirt/servercert.pem" + +# Override the default CA certificate path +# +#ca_file = "/etc/pki/CA/cacert.pem" + +# Specify a certificate revocation list. +# +# Defaults to not using a CRL, uncomment to enable it +#crl_file = "/etc/pki/CA/crl.pem" + + + +################################################################# +# +# Authorization controls +# # Flag to disable verification of client certificates @@ -85,36 +176,11 @@ # # Default is to always verify. Uncommenting this will disable # verification - make sure an IP whitelist is set -# tls_no_verify_certificate 1 - -# Flag to disable verification of client IP address -# -# Client IP address will be verified against the CommonName field -# of the x509 certificate. This has minimal security benefit since -# it is easy to spoof source IP. -# -# Uncommenting this will disable verification -# tls_no_verify_address 1 - -# Override the default server key file path -# -# key_file "/etc/pki/libvirt/private/serverkey.pem" - -# Override the default server certificate file path -# -# cert_file "/etc/pki/libvirt/servercert.pem" - -# Override the default CA certificate path -# -# ca_file "/etc/pki/CA/cacert.pem" - -# Specify a certificate revocation list. -# -# Defaults to not using a CRL, uncomment to enable it -# crl_file "/etc/pki/CA/crl.pem" +#tls_no_verify_certificate = 1 + # A whitelist of allowed x509 Distinguished Names -# This list may contain wildcards such as +# This list may contain wildcards such as # # "C=GB,ST=London,L=London,O=Red Hat,CN=*" # @@ -124,18 +190,23 @@ # entirely rather than using empty list to disable these checks # # By default, no DN's are checked -# tls_allowed_dn_list ["DN1", "DN2"] - - -# A whitelist of allowed client IP addresses -# -# This list may contain wildcards such as 192.168.* See the POSIX fnmatch -# function for the format of the wildcards. +#tls_allowed_dn_list = ["DN1", "DN2"] + + +# A whitelist of allowed SASL usernames. The format for usernames +# depends on the SASL authentication mechanism. Kerberos usernames +# look like username@REALM +# +# This list may contain wildcards such as +# +# "*@EXAMPLE.COM" +# +# See the POSIX fnmatch function for the format of the wildcards. # # NB If this is an empty list, no client can connect, so comment out # entirely rather than using empty list to disable these checks # -# By default, no IP's are checked. This can be IPv4 or IPv6 addresses -# tls_allowed_ip_list ["ip1", "ip2", "ip3"] - - +# By default, no Username's are checked +#sasl_allowed_username_list = ["joe@EXAMPLE.COM", "fred@EXAMPLE.COM" ] + + diff -r b68f6ec36358 qemud/qemud.c --- a/qemud/qemud.c Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/qemud.c Fri Nov 30 15:15:26 2007 -0500 @@ -77,14 +77,21 @@ static int unix_sock_rw_mask = 0700; /* static int unix_sock_rw_mask = 0700; /* Allow user only */ static int unix_sock_ro_mask = 0777; /* Allow world */ +static int auth_unix_rw = REMOTE_AUTH_NONE; +static int auth_unix_ro = REMOTE_AUTH_NONE; +#if HAVE_SASL +static int auth_tcp = REMOTE_AUTH_SASL; +#else +static int auth_tcp = REMOTE_AUTH_NONE; +#endif +static int auth_tls = REMOTE_AUTH_NONE; + #ifdef HAVE_AVAHI static int mdns_adv = 1; static char *mdns_name = NULL; #endif static int tls_no_verify_certificate = 0; -static int tls_no_verify_address = 0; -static char **tls_allowed_ip_list = NULL; static char **tls_allowed_dn_list = NULL; static char *key_file = (char *) LIBVIRT_SERVERKEY; @@ -450,7 +457,7 @@ static int qemudWritePidFile(const char } static int qemudListenUnix(struct qemud_server *server, - const char *path, int readonly) { + const char *path, int readonly, int auth) { struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket)); struct sockaddr_un addr; mode_t oldmask; @@ -464,6 +471,7 @@ static int qemudListenUnix(struct qemud_ sock->readonly = readonly; sock->port = -1; sock->type = QEMUD_SOCK_TYPE_UNIX; + sock->auth = auth; if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { qemudLog(QEMUD_ERR, "Failed to create socket: %s", @@ -701,6 +709,27 @@ static int qemudInitPaths(struct qemud_s static struct qemud_server *qemudInitialize(int sigread) { struct qemud_server *server; + + if (!(server = calloc(1, sizeof(struct qemud_server)))) { + qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); + return NULL; + } + + server->sigread = sigread; + + __virEventRegisterImpl(virEventAddHandleImpl, + virEventUpdateHandleImpl, + virEventRemoveHandleImpl, + virEventAddTimeoutImpl, + virEventUpdateTimeoutImpl, + virEventRemoveTimeoutImpl); + + virStateInitialize(); + + return server; +} + +static struct qemud_server *qemudNetworkInit(struct qemud_server *server) { struct qemud_socket *sock; char sockname[PATH_MAX]; char roSockname[PATH_MAX]; @@ -708,56 +737,39 @@ static struct qemud_server *qemudInitial int err; #endif /* HAVE_SASL */ - if (!(server = calloc(1, sizeof(struct qemud_server)))) { - qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); - return NULL; - } - - /* We don't have a dom-0, so start from 1 */ - server->sigread = sigread; - roSockname[0] = '\0'; if (qemudInitPaths(server, sockname, roSockname, PATH_MAX) < 0) goto cleanup; - if (qemudListenUnix(server, sockname, 0) < 0) + if (qemudListenUnix(server, sockname, 0, auth_unix_rw) < 0) goto cleanup; - if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1) < 0) + if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1, auth_unix_ro) < 0) goto cleanup; - __virEventRegisterImpl(virEventAddHandleImpl, - virEventUpdateHandleImpl, - virEventRemoveHandleImpl, - virEventAddTimeoutImpl, - virEventUpdateTimeoutImpl, - virEventRemoveTimeoutImpl); - - virStateInitialize(); - #if HAVE_SASL - if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { - qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", - sasl_errstring(err, NULL, NULL)); - goto cleanup; + if (auth_unix_rw == REMOTE_AUTH_SASL || + auth_unix_ro == REMOTE_AUTH_SASL || + auth_tcp == REMOTE_AUTH_SASL || + auth_tls == REMOTE_AUTH_SASL) { + if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) { + qemudLog(QEMUD_ERR, "Failed to initialize SASL authentication %s", + sasl_errstring(err, NULL, NULL)); + goto cleanup; + } } #endif if (ipsock) { -#if HAVE_SASL - if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_SASL) < 0) + if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0) goto cleanup; -#else - if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, REMOTE_AUTH_NONE) < 0) - goto cleanup; -#endif if (listen_tls) { if (remoteInitializeGnuTLS () < 0) goto cleanup; - if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, REMOTE_AUTH_NONE) < 0) + if (remoteListenTCP (server, tls_port, QEMUD_SOCK_TYPE_TLS, auth_tls) < 0) goto cleanup; } } @@ -977,53 +989,11 @@ static int static int remoteCheckAccess (struct qemud_client *client) { - char addr[NI_MAXHOST]; - char **wildcards; - int found, err; - /* Verify client certificate. */ if (remoteCheckCertificate (client->tlssession) == -1) { qemudLog (QEMUD_ERR, "remoteCheckCertificate: failed to verify client's certificate"); if (!tls_no_verify_certificate) return -1; else qemudLog (QEMUD_INFO, "remoteCheckCertificate: tls_no_verify_certificate is set so the bad certificate is ignored"); - } - - /*----- IP address check, similar to tcp wrappers -----*/ - - /* Convert IP address to printable string (eg. "127.0.0.1" or "::1"). */ - err = getnameinfo ((struct sockaddr *) &client->addr, client->addrlen, - addr, sizeof addr, NULL, 0, - NI_NUMERICHOST); - if (err != 0) { - qemudLog (QEMUD_ERR, "getnameinfo: %s", gai_strerror (err)); - return -1; - } - - /* Verify the client is on the list of allowed clients. - * - * NB: No tls_allowed_ip_list in config file means anyone can access. - * If tls_allowed_ip_list is in the config file but empty, means no - * one can access (not particularly useful, but it's what the sysadmin - * would expect). - */ - wildcards = tls_allowed_ip_list; - if (wildcards) { - found = 0; - - while (*wildcards) { - if (fnmatch (*wildcards, addr, 0) == 0) { - found = 1; - break; - } - wildcards++; - } - } else - found = 1; - - if (!found) { - qemudLog (QEMUD_ERR, "remoteCheckAccess: client's IP address (%s) is not on the list of allowed clients (tls_allowed_ip_list)", addr); - if (!tls_no_verify_address) return -1; - else qemudLog (QEMUD_INFO, "remoteCheckAccess: tls_no_verify_address is set so the client's IP address is ignored"); } /* Checks have succeeded. Write a '\1' byte back to the client to @@ -1150,6 +1120,7 @@ static void qemudDispatchClientFailure(s #if HAVE_SASL if (client->saslconn) sasl_dispose(&client->saslconn); + if (client->saslUsername) free(client->saslUsername); #endif if (client->tlssession) gnutls_deinit (client->tlssession); close(client->fd); @@ -1651,6 +1622,14 @@ static void qemudCleanup(struct qemud_se sock = next; } + if (server->saslUsernameWhitelist) { + char **list = server->saslUsernameWhitelist; + while (*list) { + if (*list) + free(*list); + list++; + } + } virStateCleanup(); @@ -1782,11 +1761,41 @@ checkType (virConfValuePtr p, const char } \ } while (0) + +static int remoteConfigGetAuth(virConfPtr conf, const char *key, int *auth, const char *filename) { + virConfValuePtr p; + + p = virConfGetValue (conf, key); + if (!p) + return 0; + + if (p->type != VIR_CONF_STRING) { + qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: should be a string\n", filename, key); + return -1; + } + + if (!p->str) + return 0; + + if (STREQ(p->str, "none")) { + *auth = REMOTE_AUTH_NONE; +#if HAVE_SASL + } else if (STREQ(p->str, "sasl")) { + *auth = REMOTE_AUTH_SASL; +#endif + } else { + qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: unsupported auth %s\n", filename, key, p->str); + return -1; + } + + return 0; +} + /* Read the config file if it exists. * Only used in the remote case, hence the name. */ static int -remoteReadConfigFile (const char *filename) +remoteReadConfigFile (struct qemud_server *server, const char *filename) { virConfPtr conf; @@ -1806,6 +1815,15 @@ remoteReadConfigFile (const char *filena GET_CONF_STR (conf, filename, tls_port); GET_CONF_STR (conf, filename, tcp_port); + + if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0) + return -1; + if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0) + return -1; + if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0) + return -1; + if (remoteConfigGetAuth(conf, "auth_tls", &auth_tls, filename) < 0) + return -1; GET_CONF_STR (conf, filename, unix_sock_group); if (unix_sock_group) { @@ -1852,7 +1870,6 @@ remoteReadConfigFile (const char *filena #endif GET_CONF_INT (conf, filename, tls_no_verify_certificate); - GET_CONF_INT (conf, filename, tls_no_verify_address); GET_CONF_STR (conf, filename, key_file); GET_CONF_STR (conf, filename, cert_file); @@ -1863,8 +1880,8 @@ remoteReadConfigFile (const char *filena &tls_allowed_dn_list, filename) < 0) goto free_and_fail; - if (remoteConfigGetStringList (conf, "tls_allowed_ip_list", - &tls_allowed_ip_list, filename) < 0) + if (remoteConfigGetStringList (conf, "sasl_allowed_username_list", + &server->saslUsernameWhitelist, filename) < 0) goto free_and_fail; virConfFree (conf); @@ -1890,14 +1907,6 @@ remoteReadConfigFile (const char *filena free (tls_allowed_dn_list[i]); free (tls_allowed_dn_list); tls_allowed_dn_list = NULL; - } - - if (tls_allowed_ip_list) { - int i; - for (i = 0; tls_allowed_ip_list[i]; i++) - free (tls_allowed_ip_list[i]); - free (tls_allowed_ip_list); - tls_allowed_ip_list = NULL; } return -1; @@ -2020,13 +2029,6 @@ int main(int argc, char **argv) { } } - /* Read the config file (if it exists). */ - if (remoteReadConfigFile (remote_config_file) < 0) - goto error1; - - if (godaemon) - openlog("libvirtd", 0, 0); - if (pipe(sigpipe) < 0 || qemudSetNonBlock(sigpipe[0]) < 0 || qemudSetNonBlock(sigpipe[1]) < 0) { @@ -2034,8 +2036,38 @@ int main(int argc, char **argv) { strerror(errno)); goto error1; } - sigwrite = sigpipe[1]; + + if (!(server = qemudInitialize(sigpipe[0]))) { + ret = 2; + goto error1; + } + + /* Read the config file (if it exists). */ + if (remoteReadConfigFile (server, remote_config_file) < 0) + goto error1; + + if (godaemon) { + int pid; + openlog("libvirtd", 0, 0); + pid = qemudGoDaemon(); + if (pid < 0) { + qemudLog(QEMUD_ERR, "Failed to fork as daemon: %s", + strerror(errno)); + goto error1; + } + if (pid > 0) + goto out; + + /* Choose the name of the PID file. */ + if (!pid_file) { + if (REMOTE_PID_FILE[0] != '\0') + pid_file = REMOTE_PID_FILE; + } + + if (pid_file && qemudWritePidFile (pid_file) < 0) + goto error1; + } sig_action.sa_handler = sig_handler; sig_action.sa_flags = 0; @@ -2049,31 +2081,6 @@ int main(int argc, char **argv) { sig_action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sig_action, NULL); - - if (godaemon) { - int pid = qemudGoDaemon(); - if (pid < 0) { - qemudLog(QEMUD_ERR, "Failed to fork as daemon: %s", - strerror(errno)); - goto error1; - } - if (pid > 0) - goto out; - - /* Choose the name of the PID file. */ - if (!pid_file) { - if (REMOTE_PID_FILE[0] != '\0') - pid_file = REMOTE_PID_FILE; - } - - if (pid_file && qemudWritePidFile (pid_file) < 0) - goto error1; - } - - if (!(server = qemudInitialize(sigpipe[0]))) { - ret = 2; - goto error2; - } if (virEventAddHandleImpl(sigpipe[0], POLLIN, @@ -2081,6 +2088,11 @@ int main(int argc, char **argv) { server) < 0) { qemudLog(QEMUD_ERR, "Failed to register callback for signal pipe"); ret = 3; + goto error2; + } + + if (!(server = qemudNetworkInit(server))) { + ret = 2; goto error2; } diff -r b68f6ec36358 qemud/remote.c --- a/qemud/remote.c Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/remote.c Fri Nov 30 15:15:26 2007 -0500 @@ -44,6 +44,7 @@ #include <getopt.h> #include <ctype.h> #include <assert.h> +#include <fnmatch.h> #include <libvirt/virterror.h> @@ -65,14 +66,18 @@ static void make_nonnull_network (remote #include "remote_dispatch_prototypes.h" -typedef int (*dispatch_fn) (struct qemud_client *client, remote_message_header *req, char *args, char *ret); +typedef int (*dispatch_fn) (struct qemud_server *server, + struct qemud_client *client, + remote_message_header *req, + char *args, + char *ret); /* This function gets called from qemud when it detects an incoming * remote protocol message. At this point, client->buffer contains * the full call message (including length word which we skip). */ void -remoteDispatchClientRequest (struct qemud_server *server ATTRIBUTE_UNUSED, +remoteDispatchClientRequest (struct qemud_server *server, struct qemud_client *client) { XDR xdr; @@ -158,7 +163,7 @@ remoteDispatchClientRequest (struct qemu xdr_destroy (&xdr); /* Call function. */ - rv = fn (client, &req, args, ret); + rv = fn (server, client, &req, args, ret); xdr_free (args_filter, args); /* Dispatch function must return -2, -1 or 0. Anything else is @@ -398,7 +403,8 @@ remoteDispatchError (struct qemud_client /*----- Functions. -----*/ static int -remoteDispatchOpen (struct qemud_client *client, remote_message_header *req, +remoteDispatchOpen (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, struct remote_open_args *args, void *ret ATTRIBUTE_UNUSED) { const char *name; @@ -437,7 +443,8 @@ remoteDispatchOpen (struct qemud_client } static int -remoteDispatchClose (struct qemud_client *client, remote_message_header *req, +remoteDispatchClose (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED) { int rv; @@ -450,7 +457,8 @@ remoteDispatchClose (struct qemud_client } static int -remoteDispatchSupportsFeature (struct qemud_client *client, remote_message_header *req, +remoteDispatchSupportsFeature (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret) { CHECK_CONN(client); @@ -462,7 +470,8 @@ remoteDispatchSupportsFeature (struct qe } static int -remoteDispatchGetType (struct qemud_client *client, remote_message_header *req, +remoteDispatchGetType (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_type_ret *ret) { const char *type; @@ -484,7 +493,8 @@ remoteDispatchGetType (struct qemud_clie } static int -remoteDispatchGetVersion (struct qemud_client *client, +remoteDispatchGetVersion (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_version_ret *ret) @@ -500,7 +510,8 @@ remoteDispatchGetVersion (struct qemud_c } static int -remoteDispatchGetHostname (struct qemud_client *client, +remoteDispatchGetHostname (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_hostname_ret *ret) @@ -516,7 +527,8 @@ remoteDispatchGetHostname (struct qemud_ } static int -remoteDispatchGetMaxVcpus (struct qemud_client *client, +remoteDispatchGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret) @@ -532,7 +544,8 @@ remoteDispatchGetMaxVcpus (struct qemud_ } static int -remoteDispatchNodeGetInfo (struct qemud_client *client, +remoteDispatchNodeGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_node_get_info_ret *ret) @@ -556,7 +569,8 @@ remoteDispatchNodeGetInfo (struct qemud_ } static int -remoteDispatchGetCapabilities (struct qemud_client *client, +remoteDispatchGetCapabilities (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_get_capabilities_ret *ret) @@ -572,7 +586,8 @@ remoteDispatchGetCapabilities (struct qe } static int -remoteDispatchDomainGetSchedulerType (struct qemud_client *client, +remoteDispatchDomainGetSchedulerType (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret) @@ -601,7 +616,8 @@ remoteDispatchDomainGetSchedulerType (st } static int -remoteDispatchDomainGetSchedulerParameters (struct qemud_client *client, +remoteDispatchDomainGetSchedulerParameters (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret) @@ -687,7 +703,8 @@ remoteDispatchDomainGetSchedulerParamete } static int -remoteDispatchDomainSetSchedulerParameters (struct qemud_client *client, +remoteDispatchDomainSetSchedulerParameters (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_scheduler_parameters_args *args, void *ret ATTRIBUTE_UNUSED) @@ -747,7 +764,8 @@ remoteDispatchDomainSetSchedulerParamete } static int -remoteDispatchDomainBlockStats (struct qemud_client *client, +remoteDispatchDomainBlockStats (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret) @@ -777,7 +795,8 @@ remoteDispatchDomainBlockStats (struct q } static int -remoteDispatchDomainInterfaceStats (struct qemud_client *client, +remoteDispatchDomainInterfaceStats (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret) @@ -810,7 +829,8 @@ remoteDispatchDomainInterfaceStats (stru } static int -remoteDispatchDomainAttachDevice (struct qemud_client *client, +remoteDispatchDomainAttachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret ATTRIBUTE_UNUSED) @@ -833,7 +853,8 @@ remoteDispatchDomainAttachDevice (struct } static int -remoteDispatchDomainCreate (struct qemud_client *client, +remoteDispatchDomainCreate (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -856,7 +877,8 @@ remoteDispatchDomainCreate (struct qemud } static int -remoteDispatchDomainCreateLinux (struct qemud_client *client, +remoteDispatchDomainCreateLinux (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_create_linux_args *args, remote_domain_create_linux_ret *ret) @@ -874,7 +896,8 @@ remoteDispatchDomainCreateLinux (struct } static int -remoteDispatchDomainDefineXml (struct qemud_client *client, +remoteDispatchDomainDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret) @@ -892,7 +915,8 @@ remoteDispatchDomainDefineXml (struct qe } static int -remoteDispatchDomainDestroy (struct qemud_client *client, +remoteDispatchDomainDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -913,7 +937,8 @@ remoteDispatchDomainDestroy (struct qemu } static int -remoteDispatchDomainDetachDevice (struct qemud_client *client, +remoteDispatchDomainDetachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_detach_device_args *args, void *ret ATTRIBUTE_UNUSED) @@ -937,7 +962,8 @@ remoteDispatchDomainDetachDevice (struct } static int -remoteDispatchDomainDumpXml (struct qemud_client *client, +remoteDispatchDomainDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret) @@ -962,7 +988,8 @@ remoteDispatchDomainDumpXml (struct qemu } static int -remoteDispatchDomainGetAutostart (struct qemud_client *client, +remoteDispatchDomainGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret) @@ -985,7 +1012,8 @@ remoteDispatchDomainGetAutostart (struct } static int -remoteDispatchDomainGetInfo (struct qemud_client *client, +remoteDispatchDomainGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret) @@ -1017,7 +1045,8 @@ remoteDispatchDomainGetInfo (struct qemu } static int -remoteDispatchDomainGetMaxMemory (struct qemud_client *client, +remoteDispatchDomainGetMaxMemory (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret) @@ -1041,7 +1070,8 @@ remoteDispatchDomainGetMaxMemory (struct } static int -remoteDispatchDomainGetMaxVcpus (struct qemud_client *client, +remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret) @@ -1065,7 +1095,8 @@ remoteDispatchDomainGetMaxVcpus (struct } static int -remoteDispatchDomainGetOsType (struct qemud_client *client, +remoteDispatchDomainGetOsType (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret) @@ -1090,7 +1121,8 @@ remoteDispatchDomainGetOsType (struct qe } static int -remoteDispatchDomainGetVcpus (struct qemud_client *client, +remoteDispatchDomainGetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret) @@ -1154,7 +1186,8 @@ remoteDispatchDomainGetVcpus (struct qem } static int -remoteDispatchDomainMigratePrepare (struct qemud_client *client, +remoteDispatchDomainMigratePrepare (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret) @@ -1189,7 +1222,8 @@ remoteDispatchDomainMigratePrepare (stru } static int -remoteDispatchDomainMigratePerform (struct qemud_client *client, +remoteDispatchDomainMigratePerform (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1218,7 +1252,8 @@ remoteDispatchDomainMigratePerform (stru } static int -remoteDispatchDomainMigrateFinish (struct qemud_client *client, +remoteDispatchDomainMigrateFinish (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret) @@ -1239,7 +1274,8 @@ remoteDispatchDomainMigrateFinish (struc } static int -remoteDispatchListDefinedDomains (struct qemud_client *client, +remoteDispatchListDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret) @@ -1264,7 +1300,8 @@ remoteDispatchListDefinedDomains (struct } static int -remoteDispatchDomainLookupById (struct qemud_client *client, +remoteDispatchDomainLookupById (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret) @@ -1281,7 +1318,8 @@ remoteDispatchDomainLookupById (struct q } static int -remoteDispatchDomainLookupByName (struct qemud_client *client, +remoteDispatchDomainLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret) @@ -1298,7 +1336,8 @@ remoteDispatchDomainLookupByName (struct } static int -remoteDispatchDomainLookupByUuid (struct qemud_client *client, +remoteDispatchDomainLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret) @@ -1315,7 +1354,8 @@ remoteDispatchDomainLookupByUuid (struct } static int -remoteDispatchNumOfDefinedDomains (struct qemud_client *client, +remoteDispatchNumOfDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_domains_ret *ret) @@ -1329,7 +1369,8 @@ remoteDispatchNumOfDefinedDomains (struc } static int -remoteDispatchDomainPinVcpu (struct qemud_client *client, +remoteDispatchDomainPinVcpu (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1362,7 +1403,8 @@ remoteDispatchDomainPinVcpu (struct qemu } static int -remoteDispatchDomainReboot (struct qemud_client *client, +remoteDispatchDomainReboot (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1385,7 +1427,8 @@ remoteDispatchDomainReboot (struct qemud } static int -remoteDispatchDomainRestore (struct qemud_client *client, +remoteDispatchDomainRestore (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1399,7 +1442,8 @@ remoteDispatchDomainRestore (struct qemu } static int -remoteDispatchDomainResume (struct qemud_client *client, +remoteDispatchDomainResume (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_resume_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1422,7 +1466,8 @@ remoteDispatchDomainResume (struct qemud } static int -remoteDispatchDomainSave (struct qemud_client *client, +remoteDispatchDomainSave (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_save_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1445,7 +1490,8 @@ remoteDispatchDomainSave (struct qemud_c } static int -remoteDispatchDomainCoreDump (struct qemud_client *client, +remoteDispatchDomainCoreDump (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1468,7 +1514,8 @@ remoteDispatchDomainCoreDump (struct qem } static int -remoteDispatchDomainSetAutostart (struct qemud_client *client, +remoteDispatchDomainSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1491,7 +1538,8 @@ remoteDispatchDomainSetAutostart (struct } static int -remoteDispatchDomainSetMaxMemory (struct qemud_client *client, +remoteDispatchDomainSetMaxMemory (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_max_memory_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1514,7 +1562,8 @@ remoteDispatchDomainSetMaxMemory (struct } static int -remoteDispatchDomainSetMemory (struct qemud_client *client, +remoteDispatchDomainSetMemory (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_memory_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1537,7 +1586,8 @@ remoteDispatchDomainSetMemory (struct qe } static int -remoteDispatchDomainSetVcpus (struct qemud_client *client, +remoteDispatchDomainSetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_set_vcpus_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1560,7 +1610,8 @@ remoteDispatchDomainSetVcpus (struct qem } static int -remoteDispatchDomainShutdown (struct qemud_client *client, +remoteDispatchDomainShutdown (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1583,7 +1634,8 @@ remoteDispatchDomainShutdown (struct qem } static int -remoteDispatchDomainSuspend (struct qemud_client *client, +remoteDispatchDomainSuspend (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1606,7 +1658,8 @@ remoteDispatchDomainSuspend (struct qemu } static int -remoteDispatchDomainUndefine (struct qemud_client *client, +remoteDispatchDomainUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1629,7 +1682,8 @@ remoteDispatchDomainUndefine (struct qem } static int -remoteDispatchListDefinedNetworks (struct qemud_client *client, +remoteDispatchListDefinedNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret) @@ -1654,7 +1708,8 @@ remoteDispatchListDefinedNetworks (struc } static int -remoteDispatchListDomains (struct qemud_client *client, +remoteDispatchListDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret) @@ -1678,7 +1733,8 @@ remoteDispatchListDomains (struct qemud_ } static int -remoteDispatchListNetworks (struct qemud_client *client, +remoteDispatchListNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret) @@ -1703,7 +1759,8 @@ remoteDispatchListNetworks (struct qemud } static int -remoteDispatchNetworkCreate (struct qemud_client *client, +remoteDispatchNetworkCreate (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1726,7 +1783,8 @@ remoteDispatchNetworkCreate (struct qemu } static int -remoteDispatchNetworkCreateXml (struct qemud_client *client, +remoteDispatchNetworkCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret) @@ -1743,7 +1801,8 @@ remoteDispatchNetworkCreateXml (struct q } static int -remoteDispatchNetworkDefineXml (struct qemud_client *client, +remoteDispatchNetworkDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret) @@ -1760,7 +1819,8 @@ remoteDispatchNetworkDefineXml (struct q } static int -remoteDispatchNetworkDestroy (struct qemud_client *client, +remoteDispatchNetworkDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1783,7 +1843,8 @@ remoteDispatchNetworkDestroy (struct qem } static int -remoteDispatchNetworkDumpXml (struct qemud_client *client, +remoteDispatchNetworkDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret) @@ -1808,7 +1869,8 @@ remoteDispatchNetworkDumpXml (struct qem } static int -remoteDispatchNetworkGetAutostart (struct qemud_client *client, +remoteDispatchNetworkGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret) @@ -1831,7 +1893,8 @@ remoteDispatchNetworkGetAutostart (struc } static int -remoteDispatchNetworkGetBridgeName (struct qemud_client *client, +remoteDispatchNetworkGetBridgeName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret) @@ -1856,7 +1919,8 @@ remoteDispatchNetworkGetBridgeName (stru } static int -remoteDispatchNetworkLookupByName (struct qemud_client *client, +remoteDispatchNetworkLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret) @@ -1873,7 +1937,8 @@ remoteDispatchNetworkLookupByName (struc } static int -remoteDispatchNetworkLookupByUuid (struct qemud_client *client, +remoteDispatchNetworkLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret) @@ -1890,7 +1955,8 @@ remoteDispatchNetworkLookupByUuid (struc } static int -remoteDispatchNetworkSetAutostart (struct qemud_client *client, +remoteDispatchNetworkSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1913,7 +1979,8 @@ remoteDispatchNetworkSetAutostart (struc } static int -remoteDispatchNetworkUndefine (struct qemud_client *client, +remoteDispatchNetworkUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1936,7 +2003,8 @@ remoteDispatchNetworkUndefine (struct qe } static int -remoteDispatchNumOfDefinedNetworks (struct qemud_client *client, +remoteDispatchNumOfDefinedNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_networks_ret *ret) @@ -1950,7 +2018,8 @@ remoteDispatchNumOfDefinedNetworks (stru } static int -remoteDispatchNumOfDomains (struct qemud_client *client, +remoteDispatchNumOfDomains (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_domains_ret *ret) @@ -1964,7 +2033,8 @@ remoteDispatchNumOfDomains (struct qemud } static int -remoteDispatchNumOfNetworks (struct qemud_client *client, +remoteDispatchNumOfNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_num_of_networks_ret *ret) @@ -1979,7 +2049,8 @@ remoteDispatchNumOfNetworks (struct qemu static int -remoteDispatchAuthList (struct qemud_client *client, +remoteDispatchAuthList (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, remote_auth_list_ret *ret) @@ -2034,7 +2105,8 @@ static char *addrToString(struct qemud_c * XXX callbacks for stuff like password verification ? */ static int -remoteDispatchAuthSaslInit (struct qemud_client *client, +remoteDispatchAuthSaslInit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_auth_sasl_init_ret *ret) @@ -2223,11 +2295,67 @@ remoteSASLCheckSSF (struct qemud_client return 0; } +static int +remoteSASLCheckAccess (struct qemud_server *server, + struct qemud_client *client, + remote_message_header *req) { + const void *val; + int err; + char **wildcards; + + err = sasl_getprop(client->saslconn, SASL_USERNAME, &val); + if (err != SASL_OK) { + qemudLog(QEMUD_ERR, "cannot query SASL username on connection %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + if (val == NULL) { + qemudLog(QEMUD_ERR, "no client username was found"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + REMOTE_DEBUG("SASL client username %s", (const char *)val); + + client->saslUsername = strdup((const char*)val); + if (client->saslUsername == NULL) { + qemudLog(QEMUD_ERR, "out of memory copying username"); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; + } + + /* If the list is not set, allow any DN. */ + wildcards = server->saslUsernameWhitelist; + if (!wildcards) + return 0; /* No ACL, allow all */ + + while (*wildcards) { + if (fnmatch (*wildcards, client->saslUsername, 0) == 0) + return 0; /* Allowed */ + wildcards++; + } + + /* Denied */ + qemudLog(QEMUD_ERR, "SASL client %s not allowed in whitelist", client->saslUsername); + remoteDispatchFailAuth(client, req); + sasl_dispose(&client->saslconn); + client->saslconn = NULL; + return -1; +} + + /* * This starts the SASL authentication negotiation. */ static int -remoteDispatchAuthSaslStart (struct qemud_client *client, +remoteDispatchAuthSaslStart (struct qemud_server *server, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret) @@ -2291,6 +2419,10 @@ remoteDispatchAuthSaslStart (struct qemu if (remoteSASLCheckSSF(client, req) < 0) return -2; + /* Check username whitelist ACL */ + if (remoteSASLCheckAccess(server, client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; @@ -2301,7 +2433,8 @@ remoteDispatchAuthSaslStart (struct qemu static int -remoteDispatchAuthSaslStep (struct qemud_client *client, +remoteDispatchAuthSaslStep (struct qemud_server *server, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret) @@ -2365,6 +2498,10 @@ remoteDispatchAuthSaslStep (struct qemud if (remoteSASLCheckSSF(client, req) < 0) return -2; + /* Check username whitelist ACL */ + if (remoteSASLCheckAccess(server, client, req) < 0) + return -2; + REMOTE_DEBUG("Authentication successful %d", client->fd); ret->complete = 1; client->auth = REMOTE_AUTH_NONE; @@ -2376,7 +2513,8 @@ remoteDispatchAuthSaslStep (struct qemud #else /* HAVE_SASL */ static int -remoteDispatchAuthSaslInit (struct qemud_client *client, +remoteDispatchAuthSaslInit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, void *args ATTRIBUTE_UNUSED, remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) @@ -2387,7 +2525,8 @@ remoteDispatchAuthSaslInit (struct qemud } static int -remoteDispatchAuthSaslStart (struct qemud_client *client, +remoteDispatchAuthSaslStart (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) @@ -2398,7 +2537,8 @@ remoteDispatchAuthSaslStart (struct qemu } static int -remoteDispatchAuthSaslStep (struct qemud_client *client, +remoteDispatchAuthSaslStep (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) diff -r b68f6ec36358 qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/remote_dispatch_prototypes.h Fri Nov 30 15:15:26 2007 -0500 @@ -2,72 +2,72 @@ * Do not edit this file. Any changes you make will be lost. */ -static int remoteDispatchAuthList (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); -static int remoteDispatchAuthSaslInit (struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); -static int remoteDispatchAuthSaslStart (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); -static int remoteDispatchAuthSaslStep (struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); -static int remoteDispatchClose (struct qemud_client *client, remote_message_header *req, void *args, void *ret); -static int remoteDispatchDomainAttachDevice (struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); -static int remoteDispatchDomainBlockStats (struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); -static int remoteDispatchDomainCoreDump (struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret); -static int remoteDispatchDomainCreate (struct qemud_client *client, remote_message_header *req, remote_domain_create_args *args, void *ret); -static int remoteDispatchDomainCreateLinux (struct qemud_client *client, remote_message_header *req, remote_domain_create_linux_args *args, remote_domain_create_linux_ret *ret); -static int remoteDispatchDomainDefineXml (struct qemud_client *client, remote_message_header *req, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret); -static int remoteDispatchDomainDestroy (struct qemud_client *client, remote_message_header *req, remote_domain_destroy_args *args, void *ret); -static int remoteDispatchDomainDetachDevice (struct qemud_client *client, remote_message_header *req, remote_domain_detach_device_args *args, void *ret); -static int remoteDispatchDomainDumpXml (struct qemud_client *client, remote_message_header *req, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret); -static int remoteDispatchDomainGetAutostart (struct qemud_client *client, remote_message_header *req, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret); -static int remoteDispatchDomainGetInfo (struct qemud_client *client, remote_message_header *req, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret); -static int remoteDispatchDomainGetMaxMemory (struct qemud_client *client, remote_message_header *req, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret); -static int remoteDispatchDomainGetMaxVcpus (struct qemud_client *client, remote_message_header *req, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret); -static int remoteDispatchDomainGetOsType (struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret); -static int remoteDispatchDomainGetSchedulerParameters (struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret); -static int remoteDispatchDomainGetSchedulerType (struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); -static int remoteDispatchDomainGetVcpus (struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret); -static int remoteDispatchDomainInterfaceStats (struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret); -static int remoteDispatchDomainLookupById (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); -static int remoteDispatchDomainLookupByName (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); -static int remoteDispatchDomainLookupByUuid (struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); -static int remoteDispatchDomainMigrateFinish (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); -static int remoteDispatchDomainMigratePerform (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret); -static int remoteDispatchDomainMigratePrepare (struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); -static int remoteDispatchDomainPinVcpu (struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret); -static int remoteDispatchDomainReboot (struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret); -static int remoteDispatchDomainRestore (struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret); -static int remoteDispatchDomainResume (struct qemud_client *client, remote_message_header *req, remote_domain_resume_args *args, void *ret); -static int remoteDispatchDomainSave (struct qemud_client *client, remote_message_header *req, remote_domain_save_args *args, void *ret); -static int remoteDispatchDomainSetAutostart (struct qemud_client *client, remote_message_header *req, remote_domain_set_autostart_args *args, void *ret); -static int remoteDispatchDomainSetMaxMemory (struct qemud_client *client, remote_message_header *req, remote_domain_set_max_memory_args *args, void *ret); -static int remoteDispatchDomainSetMemory (struct qemud_client *client, remote_message_header *req, remote_domain_set_memory_args *args, void *ret); -static int remoteDispatchDomainSetSchedulerParameters (struct qemud_client *client, remote_message_header *req, remote_domain_set_scheduler_parameters_args *args, void *ret); -static int remoteDispatchDomainSetVcpus (struct qemud_client *client, remote_message_header *req, remote_domain_set_vcpus_args *args, void *ret); -static int remoteDispatchDomainShutdown (struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret); -static int remoteDispatchDomainSuspend (struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret); -static int remoteDispatchDomainUndefine (struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret); -static int remoteDispatchGetCapabilities (struct qemud_client *client, remote_message_header *req, void *args, remote_get_capabilities_ret *ret); -static int remoteDispatchGetHostname (struct qemud_client *client, remote_message_header *req, void *args, remote_get_hostname_ret *ret); -static int remoteDispatchGetMaxVcpus (struct qemud_client *client, remote_message_header *req, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret); -static int remoteDispatchGetType (struct qemud_client *client, remote_message_header *req, void *args, remote_get_type_ret *ret); -static int remoteDispatchGetVersion (struct qemud_client *client, remote_message_header *req, void *args, remote_get_version_ret *ret); -static int remoteDispatchListDefinedDomains (struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret); -static int remoteDispatchListDefinedNetworks (struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret); -static int remoteDispatchListDomains (struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret); -static int remoteDispatchListNetworks (struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret); -static int remoteDispatchNetworkCreate (struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret); -static int remoteDispatchNetworkCreateXml (struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret); -static int remoteDispatchNetworkDefineXml (struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret); -static int remoteDispatchNetworkDestroy (struct qemud_client *client, remote_message_header *req, remote_network_destroy_args *args, void *ret); -static int remoteDispatchNetworkDumpXml (struct qemud_client *client, remote_message_header *req, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret); -static int remoteDispatchNetworkGetAutostart (struct qemud_client *client, remote_message_header *req, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret); -static int remoteDispatchNetworkGetBridgeName (struct qemud_client *client, remote_message_header *req, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret); -static int remoteDispatchNetworkLookupByName (struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret); -static int remoteDispatchNetworkLookupByUuid (struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret); -static int remoteDispatchNetworkSetAutostart (struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret); -static int remoteDispatchNetworkUndefine (struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret); -static int remoteDispatchNodeGetInfo (struct qemud_client *client, remote_message_header *req, void *args, remote_node_get_info_ret *ret); -static int remoteDispatchNumOfDefinedDomains (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_domains_ret *ret); -static int remoteDispatchNumOfDefinedNetworks (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_networks_ret *ret); -static int remoteDispatchNumOfDomains (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_domains_ret *ret); -static int remoteDispatchNumOfNetworks (struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_networks_ret *ret); -static int remoteDispatchOpen (struct qemud_client *client, remote_message_header *req, remote_open_args *args, void *ret); -static int remoteDispatchSupportsFeature (struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret); +static int remoteDispatchAuthList (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); +static int remoteDispatchAuthSaslInit (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); +static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); +static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); +static int remoteDispatchClose (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, void *ret); +static int remoteDispatchDomainAttachDevice (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); +static int remoteDispatchDomainBlockStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); +static int remoteDispatchDomainCoreDump (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_core_dump_args *args, void *ret); +static int remoteDispatchDomainCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_create_args *args, void *ret); +static int remoteDispatchDomainCreateLinux (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_create_linux_args *args, remote_domain_create_linux_ret *ret); +static int remoteDispatchDomainDefineXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret); +static int remoteDispatchDomainDestroy (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_destroy_args *args, void *ret); +static int remoteDispatchDomainDetachDevice (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_detach_device_args *args, void *ret); +static int remoteDispatchDomainDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret); +static int remoteDispatchDomainGetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret); +static int remoteDispatchDomainGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret); +static int remoteDispatchDomainGetMaxMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret); +static int remoteDispatchDomainGetMaxVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret); +static int remoteDispatchDomainGetOsType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret); +static int remoteDispatchDomainGetSchedulerParameters (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret); +static int remoteDispatchDomainGetSchedulerType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); +static int remoteDispatchDomainGetVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret); +static int remoteDispatchDomainInterfaceStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret); +static int remoteDispatchDomainLookupById (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); +static int remoteDispatchDomainLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); +static int remoteDispatchDomainLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); +static int remoteDispatchDomainMigrateFinish (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); +static int remoteDispatchDomainMigratePerform (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret); +static int remoteDispatchDomainMigratePrepare (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); +static int remoteDispatchDomainPinVcpu (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_pin_vcpu_args *args, void *ret); +static int remoteDispatchDomainReboot (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_reboot_args *args, void *ret); +static int remoteDispatchDomainRestore (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_restore_args *args, void *ret); +static int remoteDispatchDomainResume (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_resume_args *args, void *ret); +static int remoteDispatchDomainSave (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_save_args *args, void *ret); +static int remoteDispatchDomainSetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_autostart_args *args, void *ret); +static int remoteDispatchDomainSetMaxMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_max_memory_args *args, void *ret); +static int remoteDispatchDomainSetMemory (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_memory_args *args, void *ret); +static int remoteDispatchDomainSetSchedulerParameters (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_scheduler_parameters_args *args, void *ret); +static int remoteDispatchDomainSetVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_set_vcpus_args *args, void *ret); +static int remoteDispatchDomainShutdown (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret); +static int remoteDispatchDomainSuspend (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret); +static int remoteDispatchDomainUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret); +static int remoteDispatchGetCapabilities (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_capabilities_ret *ret); +static int remoteDispatchGetHostname (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_hostname_ret *ret); +static int remoteDispatchGetMaxVcpus (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret); +static int remoteDispatchGetType (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_type_ret *ret); +static int remoteDispatchGetVersion (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_version_ret *ret); +static int remoteDispatchListDefinedDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret); +static int remoteDispatchListDefinedNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret); +static int remoteDispatchListDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_domains_args *args, remote_list_domains_ret *ret); +static int remoteDispatchListNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_list_networks_args *args, remote_list_networks_ret *ret); +static int remoteDispatchNetworkCreate (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_create_args *args, void *ret); +static int remoteDispatchNetworkCreateXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret); +static int remoteDispatchNetworkDefineXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret); +static int remoteDispatchNetworkDestroy (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_destroy_args *args, void *ret); +static int remoteDispatchNetworkDumpXml (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret); +static int remoteDispatchNetworkGetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret); +static int remoteDispatchNetworkGetBridgeName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret); +static int remoteDispatchNetworkLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret); +static int remoteDispatchNetworkLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret); +static int remoteDispatchNetworkSetAutostart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_set_autostart_args *args, void *ret); +static int remoteDispatchNetworkUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_network_undefine_args *args, void *ret); +static int remoteDispatchNodeGetInfo (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_node_get_info_ret *ret); +static int remoteDispatchNumOfDefinedDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_domains_ret *ret); +static int remoteDispatchNumOfDefinedNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_defined_networks_ret *ret); +static int remoteDispatchNumOfDomains (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_domains_ret *ret); +static int remoteDispatchNumOfNetworks (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_num_of_networks_ret *ret); +static int remoteDispatchOpen (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_open_args *args, void *ret); +static int remoteDispatchSupportsFeature (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_supports_feature_args *args, remote_supports_feature_ret *ret); diff -r b68f6ec36358 qemud/remote_generate_stubs.pl --- a/qemud/remote_generate_stubs.pl Fri Nov 30 14:31:14 2007 -0500 +++ b/qemud/remote_generate_stubs.pl Fri Nov 30 15:15:26 2007 -0500 @@ -93,7 +93,7 @@ elsif ($opt_i) { elsif ($opt_i) { my @keys = sort (keys %calls); foreach (@keys) { - print "static int remoteDispatch$calls{$_}->{ProcName} (struct qemud_client *client, remote_message_header *req, $calls{$_}->{args} *args, $calls{$_}->{ret} *ret);\n"; + print "static int remoteDispatch$calls{$_}->{ProcName} (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, $calls{$_}->{args} *args, $calls{$_}->{ret} *ret);\n"; } } @@ -196,7 +196,8 @@ elsif ($opt_s) { my $retvoid = $ret eq "void"; print "static int\n"; - print "remoteDispatch$calls{$_}->{ProcName} (struct qemud_client *client,\n"; + print "remoteDispatch$calls{$_}->{ProcName} (struct qemud_server *server,\n"; + print " struct qemud_client *client,\n"; print " remote_message_header *req,\n"; print " remote_get_max_vcpus_args *args,\n"; print " remote_get_max_vcpus_ret *ret)\n"; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 05:18:06PM +0000, Daniel P. Berrange wrote:
This patch provides the ability to configure what authentication mechanism is used on each socket - UNIX RW, UNIX RO, TCP, and TLS sockets - all can have independant settings. By default the UNIX & TLS sockets have no auth, and the TCP socket has SASL auth enabled. The /etc/libvirt/libvirtd.conf file lets you override these options.
There is also a new sasl_allowed_username_list = ["admin"] config param to let you whitelist the users you want to allow. This supports use of wildcards. The username is dependnat on the SASL auth mechanism. For DIGEST-MD5 it will be plain usernames, for Kerberos it will be a username + realm, eg admin EXAMPLE COM
After discussion with Rich, I also remove the tls_allowed_ip_list for whitelisting source IP addresses. This was a) not protecting us because it was only checked after the TLS handshake - thus allowing trivial DOS attack b) much easier to handle via tcp wrappers, or IPtables. c) only ever checked for the TLS socket d) IP addresses are easily spoofed.
If summary, if you're using a real authentication mechanism, this is only useful for protecting against DOS attacks & that's better done by iptables.
Rebased to take account of Jim's changes, and incorporated fixes to the config file
This looks fine. Thanks for preserving my convention of "#var = ..." (no space after '#') in the config file. I have a test that depends on that -- will post it after you commit this change. I find code/diffs easier to read when the lines themselves fit in 80 columns. There are lots of 100+-byte lines here. I know some are generated, but I'll be happy to normalize the others once this is checked in.

On Mon, Dec 03, 2007 at 01:43:01PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 05:18:06PM +0000, Daniel P. Berrange wrote:
This patch provides the ability to configure what authentication mechanism is used on each socket - UNIX RW, UNIX RO, TCP, and TLS sockets - all can have independant settings. By default the UNIX & TLS sockets have no auth, and the TCP socket has SASL auth enabled. The /etc/libvirt/libvirtd.conf file lets you override these options.
There is also a new sasl_allowed_username_list = ["admin"] config param to let you whitelist the users you want to allow. This supports use of wildcards. The username is dependnat on the SASL auth mechanism. For DIGEST-MD5 it will be plain usernames, for Kerberos it will be a username + realm, eg admin EXAMPLE COM
After discussion with Rich, I also remove the tls_allowed_ip_list for whitelisting source IP addresses. This was a) not protecting us because it was only checked after the TLS handshake - thus allowing trivial DOS attack b) much easier to handle via tcp wrappers, or IPtables. c) only ever checked for the TLS socket d) IP addresses are easily spoofed.
If summary, if you're using a real authentication mechanism, this is only useful for protecting against DOS attacks & that's better done by iptables.
Rebased to take account of Jim's changes, and incorporated fixes to the config file
This looks fine. Thanks for preserving my convention of "#var = ..." (no space after '#') in the config file. I have a test that depends on that -- will post it after you commit this change.
I find code/diffs easier to read when the lines themselves fit in 80 columns. There are lots of 100+-byte lines here. I know some are generated, but I'll be happy to normalize the others once this is checked in.
This is now comited. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch adds support for an PolicyKit authentication mechanism. This was previously described here: http://www.redhat.com/archives/libvir-list/2007-September/msg00168.html If PolicyKit is compiled in, then the UNIX domain sockets have their default settings changed to make sure of PolicyKit. Thus, when PolicyKit is enabled, both the RO & RW sockets are mode 0777. PolicyKit is then called upon client connect to decide whether to allow the client to gain access. The policyfile is shipped in /usr/share/PolicyKit/policy and has default settings to mimic current non-PolicyKit access. If making a read-only connection, any application will be granted access by default. If making a read-write connection, applications will need to authenticate against policykit by providing the user's own password. This is akin to 'sudo' style auth. The credentials persist until the user logs out. The file in /etc/PolicyKit/PolicyKit.conf can be used by the local sysadmin to override the default policy on a per-host basis. eg, they could restrict access to the read-only connections, or open up the read-write connections to more apps. See 'man PolicyKit.conf' for more info. The configure script will check for PolicyKit using pkg-config and only enable it if actually present. So any OS without PolicyKit will not be impacted by this patch. b/qemud/libvirtd.policy | 42 +++++++++++ configure.in | 25 ++++++ libvirt.spec.in | 3 qemud/Makefile.am | 11 ++ qemud/internal.h | 7 + qemud/libvirtd.conf | 18 +++- qemud/qemud.c | 37 +++++++++ qemud/remote.c | 135 +++++++++++++++++++++++++++++++++++- qemud/remote_dispatch_localvars.h | 1 qemud/remote_dispatch_proc_switch.h | 6 + qemud/remote_dispatch_prototypes.h | 1 qemud/remote_protocol.c | 9 ++ qemud/remote_protocol.h | 9 ++ qemud/remote_protocol.x | 10 ++ src/remote_internal.c | 35 +++++++++ 15 files changed, 340 insertions(+), 9 deletions(-) diff -r 40e7c976f974 configure.in --- a/configure.in Thu Nov 29 09:51:32 2007 -0500 +++ b/configure.in Thu Nov 29 10:02:52 2007 -0500 @@ -25,6 +25,7 @@ LIBXML_REQUIRED="2.5.0" LIBXML_REQUIRED="2.5.0" GNUTLS_REQUIRED="1.0.25" AVAHI_REQUIRED="0.6.0" +POLKIT_REQUIRED="0.6" dnl Checks for programs. AC_PROG_CC @@ -388,6 +389,25 @@ AC_SUBST(SASL_LIBS) AC_SUBST(SASL_LIBS) +dnl PolicyKit library +POLKIT_CFLAGS= +POLKIT_LIBS= +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-dbus >= $POLKIT_REQUIRED, [with_polkit=yes], [with_polkit=no]) +fi + +if test "$with_polkit" = "yes"; then + PKG_CHECK_MODULES(POLKIT, polkit-dbus >= $POLKIT_REQUIRED) + AC_DEFINE_UNQUOTED(HAVE_POLKIT, 1, [use PolicyKit for UNIX socket access checks]) +fi +AM_CONDITIONAL(HAVE_POLKIT, [test "$with_polkit" = "yes"]) +AC_SUBST(POLKIT_CFLAGS) +AC_SUBST(POLKIT_LIBS) dnl Avahi library AC_ARG_WITH(avahi, @@ -608,6 +628,11 @@ else else AC_MSG_NOTICE([ avahi: no]) fi +if test "$with_polkit" = "yes" ; then +AC_MSG_NOTICE([ polkit: $POLKIT_CFLAGS $POLKIT_LIBS]) +else +AC_MSG_NOTICE([ polkit: no]) +fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Miscellaneous]) AC_MSG_NOTICE([]) diff -r 40e7c976f974 libvirt.spec.in --- a/libvirt.spec.in Thu Nov 29 09:51:32 2007 -0500 +++ b/libvirt.spec.in Thu Nov 29 10:02:52 2007 -0500 @@ -18,6 +18,7 @@ Requires: iptables Requires: iptables Requires: cyrus-sasl Requires: cyrus-sasl-gssapi +Requires: PolicyKit BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -29,6 +30,7 @@ BuildRequires: bridge-utils BuildRequires: bridge-utils BuildRequires: qemu BuildRequires: cyrus-sasl-devel +BuildRequires: PolicyKit-devel Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -141,6 +143,7 @@ fi %{_datadir}/libvirt/networks/default.xml %dir %{_localstatedir}/run/libvirt/ %dir %{_localstatedir}/lib/libvirt/ +%{_datadir}/PolicyKit/policy/libvirtd.policy %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/qemu/ %attr(4755, root, root) %{_libexecdir}/libvirt_proxy %attr(0755, root, root) %{_sbindir}/libvirtd diff -r 40e7c976f974 qemud/Makefile.am --- a/qemud/Makefile.am Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/Makefile.am Thu Nov 29 10:02:52 2007 -0500 @@ -30,15 +30,24 @@ libvirtd_CFLAGS = \ libvirtd_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include \ $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \ + $(POLKIT_CFLAGS) \ $(WARN_CFLAGS) -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ -DQEMUD_PID_FILE="\"$(QEMUD_PID_FILE)\"" \ -DREMOTE_PID_FILE="\"$(REMOTE_PID_FILE)\"" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" -libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) +libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) \ + $(POLKIT_LIBS) libvirtd_DEPENDENCIES = ../src/libvirt.la libvirtd_LDADD = ../src/libvirt.la + + +if HAVE_POLKIT +policydir = $(datadir)/PolicyKit/policy +policy_DATA = libvirtd.policy +endif +EXTRA_DIST += libvirtd.policy if HAVE_AVAHI libvirtd_SOURCES += mdns.c mdns.h diff -r 40e7c976f974 qemud/internal.h --- a/qemud/internal.h Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/internal.h Thu Nov 29 10:02:52 2007 -0500 @@ -30,6 +30,10 @@ #include "../src/gnutls_1_0_compat.h" #if HAVE_SASL #include <sasl/sasl.h> +#endif + +#ifdef HAVE_POLKIT +#include <dbus/dbus.h> #endif #ifdef HAVE_SYS_SYSLIMITS_H @@ -155,6 +159,9 @@ struct qemud_server { #if HAVE_SASL char **saslUsernameWhitelist; #endif +#if HAVE_POLKIT + DBusConnection *sysbus; +#endif }; void qemudLog(int priority, const char *fmt, ...) diff -r 40e7c976f974 qemud/libvirtd.conf --- a/qemud/libvirtd.conf Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/libvirtd.conf Thu Nov 29 10:02:52 2007 -0500 @@ -82,8 +82,11 @@ # Set the UNIX socket permissions for the R/W socket. This is used # for full management of VMs # -# Default allows only root. If setting group ownership may want to -# relax this to: +# Default allows only root. If PolicyKit is enabled on the socket, +# the default will change to allow everyone (eg, 0777) +# +# If not using PolicyKit and setting group ownership for access +# control then you may want to relax this to: # unix_sock_rw_perms "0770" @@ -103,7 +106,12 @@ # socket only GSSAPI & DIGEST-MD5 mechanisms will be used. # For non-TCP or TLS sockets, any scheme is allowed. # - +# - polkit: use PolicyKit to authenticate. This is only suitable +# for use on the UNIX sockets. The default policy will +# require a user to supply their own password to gain +# full read/write access (aka sudo like), while anyone +# is allowed read/only access. +# # Set an authentication scheme for UNIX read-only sockets # By default socket permissions allow anyone to connect # @@ -112,7 +120,9 @@ # auth_unix_ro = "none" # Set an authentication scheme for UNIX read-write sockets -# By default socket permissions only allow root. +# By default socket permissions only allow root. If PolicyKit +# support was compiled into libvirt, the default will be to +# use 'polkit' auth. # # If the unix_sock_rw_perms are changed you may wish to enable # an authentication mechanism here diff -r 40e7c976f974 qemud/libvirtd.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qemud/libvirtd.policy Thu Nov 29 10:02:52 2007 -0500 @@ -0,0 +1,42 @@ +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> + +<!-- +Policy definitions for libvirt daemon + +Copyright (c) 2007 Daniel P. Berrange <berrange redhat com> + +libvirt is licensed to you under the GNU Lesser General Public License +version 2. See COPYING for details. + +NOTE: If you make changes to this file, make sure to validate the file +using the polkit-policy-file-validate(1) tool. Changes made to this +file are instantly applied. +--> + +<policyconfig> + <action id="org.libvirt.unix.monitor"> + <description>Monitor local virtualized systems</description> + <message>System policy prevents monitoring of local virtualized systems</message> + <defaults> + <!-- Any program can use libvirt in read-only mode for monitoring, + even if not part of a session --> + <allow_any>yes</allow_any> + <allow_inactive>yes</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> + + <action id="org.libvirt.unix.manage"> + <description>Manage local virtualized systems</description> + <message>System policy prevents management of local virtualized systems</message> + <defaults> + <!-- Only a program in the active host session can use libvirt in + read-write mode for management, and we require user password --> + <allow_any>no</allow_any> + <allow_inactive>no</allow_inactive> + <allow_active>auth_self_keep_session</allow_active> + </defaults> + </action> +</policyconfig> \ No newline at end of file diff -r 40e7c976f974 qemud/qemud.c --- a/qemud/qemud.c Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/qemud.c Thu Nov 29 10:02:52 2007 -0500 @@ -77,9 +77,14 @@ static int unix_sock_rw_perms = 0700; /* static int unix_sock_rw_perms = 0700; /* Allow user only */ static int unix_sock_ro_perms = 0777; /* Allow world */ - +#if HAVE_POLKIT +static int auth_unix_rw = REMOTE_AUTH_POLKIT; +static int auth_unix_ro = REMOTE_AUTH_POLKIT; +#else static int auth_unix_rw = REMOTE_AUTH_NONE; static int auth_unix_ro = REMOTE_AUTH_NONE; +#endif + #if HAVE_SASL static int auth_tcp = REMOTE_AUTH_SASL; #else @@ -762,6 +767,21 @@ static struct qemud_server *qemudNetwork } #endif +#ifdef HAVE_POLKIT + if (auth_unix_rw == REMOTE_AUTH_POLKIT || + auth_unix_ro == REMOTE_AUTH_POLKIT) { + DBusError derr; + dbus_error_init(&derr); + server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr); + if (!(server->sysbus)) { + qemudLog(QEMUD_ERR, "Failed to connect to system bus for PolicyKit auth: %s", + derr.message); + dbus_error_free(&derr); + goto cleanup; + } + } +#endif + if (ipsock) { if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0) goto cleanup; @@ -829,6 +849,10 @@ static struct qemud_server *qemudNetwork sock = sock->next; } +#ifdef HAVE_POLKIT + if (server->sysbus) + dbus_connection_unref(server->sysbus); +#endif free(server); } return NULL; @@ -1716,6 +1740,10 @@ static int remoteConfigGetAuth(virConfPt } else if (STREQ(p->str, "sasl")) { *auth = REMOTE_AUTH_SASL; #endif +#if HAVE_POLKIT + } else if (STREQ(p->str, "polkit")) { + *auth = REMOTE_AUTH_POLKIT; +#endif } else { qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: unsupported auth %s\n", filename, key, p->str); return -1; @@ -1767,6 +1795,13 @@ remoteReadConfigFile (struct qemud_serve if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0) return -1; +#if HAVE_POLKIT + /* Change default perms to be wide-open if PolicyKit is enabled. + * Admin can always override in config file + */ + if (auth_unix_rw == REMOTE_AUTH_POLKIT) + unix_sock_rw_perms = 0777; +#endif if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0) return -1; if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0) diff -r 40e7c976f974 qemud/remote.c --- a/qemud/remote.c Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote.c Thu Nov 29 10:02:52 2007 -0500 @@ -46,6 +46,11 @@ #include <assert.h> #include <fnmatch.h> +#ifdef HAVE_POLKIT +#include <polkit/polkit.h> +#include <polkit-dbus/polkit-dbus.h> +#endif + #include <libvirt/virterror.h> #include "internal.h" @@ -132,7 +137,8 @@ remoteDispatchClientRequest (struct qemu if (req.proc != REMOTE_PROC_AUTH_LIST && req.proc != REMOTE_PROC_AUTH_SASL_INIT && req.proc != REMOTE_PROC_AUTH_SASL_START && - req.proc != REMOTE_PROC_AUTH_SASL_STEP + req.proc != REMOTE_PROC_AUTH_SASL_STEP && + req.proc != REMOTE_PROC_AUTH_POLKIT ) { remoteDispatchError (client, &req, "authentication required"); xdr_destroy (&xdr); @@ -2550,6 +2556,133 @@ remoteDispatchAuthSaslStep (struct qemud #endif /* HAVE_SASL */ +#if HAVE_POLKIT +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 +remoteDispatchAuthPolkit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_polkit_ret *ret) +{ + pid_t callerPid; + uid_t callerUid; + + REMOTE_DEBUG("Start PolicyKit auth %d", client->fd); + if (client->auth != REMOTE_AUTH_POLKIT) { + qemudLog(QEMUD_ERR, "client tried invalid PolicyKit init request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + if (qemudGetSocketIdentity(client->fd, &callerUid, &callerPid) < 0) { + qemudLog(QEMUD_ERR, "cannot get peer socket identity"); + remoteDispatchFailAuth(client, req); + return -2; + } + + /* Only do policy checks for non-root - allow root user + through with no checks, as a fail-safe - root can easily + change policykit policy anyway, so its pointless trying + to restrict root */ + if (callerUid == 0) { + qemudLog(QEMUD_INFO, "Allowing PID %d running as root", callerPid); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } else { + PolKitCaller *pkcaller = NULL; + PolKitAction *pkaction = NULL; + PolKitContext *pkcontext = NULL; + PolKitError *pkerr; + PolKitResult pkresult; + DBusError err; + const char *action = client->readonly ? + "org.libvirt.unix.monitor" : + "org.libvirt.unix.manage"; + + 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); + remoteDispatchFailAuth(client, req); + return -2; + } + + if (!(pkaction = polkit_action_new())) { + qemudLog(QEMUD_ERR, "Failed to create polkit action %s\n", strerror(errno)); + polkit_caller_unref(pkcaller); + remoteDispatchFailAuth(client, req); + return -2; + } + 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); + remoteDispatchFailAuth(client, req); + return -2; + } + + 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)); + remoteDispatchFailAuth(client, req); + return -2; + } + qemudLog(QEMUD_INFO, "Policy allowed action %s from pid %d, uid %d, result %s", + action, callerPid, callerUid, polkit_result_to_string_representation(pkresult)); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } + + return 0; +} + +#else /* HAVE_POLKIT */ + +static int +remoteDispatchAuthPolkitInit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported PolicyKit init request"); + remoteDispatchFailAuth(client, req); + return -1; +} +#endif /* HAVE_POLKIT */ + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -r 40e7c976f974 qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote_dispatch_localvars.h Thu Nov 29 10:02:52 2007 -0500 @@ -37,6 +37,7 @@ remote_domain_create_linux_args lv_remot remote_domain_create_linux_args lv_remote_domain_create_linux_args; remote_domain_create_linux_ret lv_remote_domain_create_linux_ret; remote_domain_set_scheduler_parameters_args lv_remote_domain_set_scheduler_parameters_args; +remote_auth_polkit_ret lv_remote_auth_polkit_ret; remote_auth_sasl_start_args lv_remote_auth_sasl_start_args; remote_auth_sasl_start_ret lv_remote_auth_sasl_start_ret; remote_domain_interface_stats_args lv_remote_domain_interface_stats_args; diff -r 40e7c976f974 qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote_dispatch_proc_switch.h Thu Nov 29 10:02:52 2007 -0500 @@ -8,6 +8,12 @@ case REMOTE_PROC_AUTH_LIST: ret = (char *) &lv_remote_auth_list_ret; memset (&lv_remote_auth_list_ret, 0, sizeof lv_remote_auth_list_ret); break; +case REMOTE_PROC_AUTH_POLKIT: + fn = (dispatch_fn) remoteDispatchAuthPolkit; + ret_filter = (xdrproc_t) xdr_remote_auth_polkit_ret; + ret = (char *) &lv_remote_auth_polkit_ret; + memset (&lv_remote_auth_polkit_ret, 0, sizeof lv_remote_auth_polkit_ret); + break; case REMOTE_PROC_AUTH_SASL_INIT: fn = (dispatch_fn) remoteDispatchAuthSaslInit; ret_filter = (xdrproc_t) xdr_remote_auth_sasl_init_ret; diff -r 40e7c976f974 qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote_dispatch_prototypes.h Thu Nov 29 10:02:52 2007 -0500 @@ -3,6 +3,7 @@ */ static int remoteDispatchAuthList (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); +static int remoteDispatchAuthPolkit (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_polkit_ret *ret); static int remoteDispatchAuthSaslInit (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); diff -r 40e7c976f974 qemud/remote_protocol.c --- a/qemud/remote_protocol.c Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote_protocol.c Thu Nov 29 10:02:52 2007 -0500 @@ -1309,6 +1309,15 @@ xdr_remote_auth_sasl_step_ret (XDR *xdrs } bool_t +xdr_remote_auth_polkit_ret (XDR *xdrs, remote_auth_polkit_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->complete)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff -r 40e7c976f974 qemud/remote_protocol.h --- a/qemud/remote_protocol.h Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote_protocol.h Thu Nov 29 10:02:52 2007 -0500 @@ -68,6 +68,7 @@ enum remote_auth_type { enum remote_auth_type { REMOTE_AUTH_NONE = 0, REMOTE_AUTH_SASL = 1, + REMOTE_AUTH_POLKIT = 2, }; typedef enum remote_auth_type remote_auth_type; @@ -719,6 +720,11 @@ struct remote_auth_sasl_step_ret { } data; }; typedef struct remote_auth_sasl_step_ret remote_auth_sasl_step_ret; + +struct remote_auth_polkit_ret { + int complete; +}; +typedef struct remote_auth_polkit_ret remote_auth_polkit_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -792,6 +798,7 @@ enum remote_procedure { REMOTE_PROC_AUTH_SASL_INIT = 67, REMOTE_PROC_AUTH_SASL_START = 68, REMOTE_PROC_AUTH_SASL_STEP = 69, + REMOTE_PROC_AUTH_POLKIT = 70, }; typedef enum remote_procedure remote_procedure; @@ -935,6 +942,7 @@ extern bool_t xdr_remote_auth_sasl_star extern bool_t xdr_remote_auth_sasl_start_ret (XDR *, remote_auth_sasl_start_ret*); extern bool_t xdr_remote_auth_sasl_step_args (XDR *, remote_auth_sasl_step_args*); extern bool_t xdr_remote_auth_sasl_step_ret (XDR *, remote_auth_sasl_step_ret*); +extern bool_t xdr_remote_auth_polkit_ret (XDR *, remote_auth_polkit_ret*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_direction (XDR *, remote_message_direction*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -1054,6 +1062,7 @@ extern bool_t xdr_remote_auth_sasl_start extern bool_t xdr_remote_auth_sasl_start_ret (); extern bool_t xdr_remote_auth_sasl_step_args (); extern bool_t xdr_remote_auth_sasl_step_ret (); +extern bool_t xdr_remote_auth_polkit_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_direction (); extern bool_t xdr_remote_message_status (); diff -r 40e7c976f974 qemud/remote_protocol.x --- a/qemud/remote_protocol.x Thu Nov 29 09:51:32 2007 -0500 +++ b/qemud/remote_protocol.x Thu Nov 29 10:02:52 2007 -0500 @@ -132,7 +132,8 @@ struct remote_error { /* Authentication types available thus far.... */ enum remote_auth_type { REMOTE_AUTH_NONE = 0, - REMOTE_AUTH_SASL = 1 + REMOTE_AUTH_SASL = 1, + REMOTE_AUTH_POLKIT = 2 }; @@ -654,6 +655,10 @@ struct remote_auth_sasl_step_ret { int complete; int nil; char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_polkit_ret { + int complete; }; /*----- Protocol. -----*/ @@ -731,7 +736,8 @@ enum remote_procedure { REMOTE_PROC_AUTH_LIST = 66, REMOTE_PROC_AUTH_SASL_INIT = 67, REMOTE_PROC_AUTH_SASL_START = 68, - REMOTE_PROC_AUTH_SASL_STEP = 69 + REMOTE_PROC_AUTH_SASL_STEP = 69, + REMOTE_PROC_AUTH_POLKIT = 70 }; /* Custom RPC structure. */ diff -r 40e7c976f974 src/remote_internal.c --- a/src/remote_internal.c Thu Nov 29 09:51:32 2007 -0500 +++ b/src/remote_internal.c Thu Nov 29 10:02:52 2007 -0500 @@ -115,6 +115,9 @@ static int remoteAuthenticate (virConnec #if HAVE_SASL static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open); #endif +#if HAVE_POLKIT +static int remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open); +#endif /* HAVE_POLKIT */ static void error (virConnectPtr conn, virErrorNumber code, const char *info); static void server_error (virConnectPtr conn, remote_error *err); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); @@ -2854,6 +2857,15 @@ remoteAuthenticate (virConnectPtr conn, break; #endif +#if HAVE_POLKIT + case REMOTE_AUTH_POLKIT: + if (remoteAuthPolkit(conn, priv, in_open) < 0) { + free(ret.types.types_val); + return -1; + } + break; +#endif + case REMOTE_AUTH_NONE: /* Nothing todo, hurrah ! */ break; @@ -3443,6 +3455,29 @@ really_write_sasl (virConnectPtr conn, s return really_write_buf(conn, priv, in_open, output, outputlen); } #endif + + +#if HAVE_POLKIT +/* Perform the PolicyKit authentication process + */ +static int +remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open) +{ + remote_auth_polkit_ret ret; + + remoteDebug(priv, "Client initialize PolicyKit authentication"); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_POLKIT, + (xdrproc_t) xdr_void, (char *)NULL, + (xdrproc_t) xdr_remote_auth_polkit_ret, (char *) &ret) != 0) { + return -1; /* virError already set by call */ + } + + remoteDebug(priv, "PolicyKit authentication complete"); + return 0; +} +#endif /* HAVE_POLKIT */ static int really_write (virConnectPtr conn, struct private_data *priv, -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:18:41PM +0000, Daniel P. Berrange wrote:
This patch adds support for an PolicyKit authentication mechanism. This was previously described here:
http://www.redhat.com/archives/libvir-list/2007-September/msg00168.html
If PolicyKit is compiled in, then the UNIX domain sockets have their default settings changed to make sure of PolicyKit. Thus, when PolicyKit is enabled, both the RO & RW sockets are mode 0777. PolicyKit is then called upon client connect to decide whether to allow the client to gain access.
The policyfile is shipped in /usr/share/PolicyKit/policy and has default settings to mimic current non-PolicyKit access. If making a read-only connection, any application will be granted access by default. If making a read-write connection, applications will need to authenticate against policykit by providing the user's own password. This is akin to 'sudo' style auth. The credentials persist until the user logs out.
The file in /etc/PolicyKit/PolicyKit.conf can be used by the local sysadmin to override the default policy on a per-host basis. eg, they could restrict access to the read-only connections, or open up the read-write connections to more apps. See 'man PolicyKit.conf' for more info.
The configure script will check for PolicyKit using pkg-config and only enable it if actually present. So any OS without PolicyKit will not be impacted by this patch.
Rebased to latest CVS. Added RPM dep on PolicyKit stuff, dependant on being on Fedora 8 or later. diff -r ca9413d4d890 configure.in --- a/configure.in Fri Nov 30 15:15:26 2007 -0500 +++ b/configure.in Fri Nov 30 15:15:51 2007 -0500 @@ -25,6 +25,7 @@ LIBXML_REQUIRED="2.5.0" LIBXML_REQUIRED="2.5.0" GNUTLS_REQUIRED="1.0.25" AVAHI_REQUIRED="0.6.0" +POLKIT_REQUIRED="0.6" dnl Checks for C compiler. AC_PROG_CC @@ -395,6 +396,25 @@ AC_SUBST(SASL_LIBS) AC_SUBST(SASL_LIBS) +dnl PolicyKit library +POLKIT_CFLAGS= +POLKIT_LIBS= +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-dbus >= $POLKIT_REQUIRED, [with_polkit=yes], [with_polkit=no]) +fi + +if test "$with_polkit" = "yes"; then + PKG_CHECK_MODULES(POLKIT, polkit-dbus >= $POLKIT_REQUIRED) + AC_DEFINE_UNQUOTED(HAVE_POLKIT, 1, [use PolicyKit for UNIX socket access checks]) +fi +AM_CONDITIONAL(HAVE_POLKIT, [test "$with_polkit" = "yes"]) +AC_SUBST(POLKIT_CFLAGS) +AC_SUBST(POLKIT_LIBS) dnl Avahi library AC_ARG_WITH(avahi, @@ -634,6 +654,11 @@ else else AC_MSG_NOTICE([ avahi: no]) fi +if test "$with_polkit" = "yes" ; then +AC_MSG_NOTICE([ polkit: $POLKIT_CFLAGS $POLKIT_LIBS]) +else +AC_MSG_NOTICE([ polkit: no]) +fi AC_MSG_NOTICE([]) AC_MSG_NOTICE([Miscellaneous]) AC_MSG_NOTICE([]) diff -r ca9413d4d890 libvirt.spec.in --- a/libvirt.spec.in Fri Nov 30 15:15:26 2007 -0500 +++ b/libvirt.spec.in Fri Nov 30 15:15:51 2007 -0500 @@ -1,4 +1,10 @@ # -*- rpm-spec -*- + +%if %{fedora} >= 8 +%define with_polkit 1 +%else +%define with_polkit 0 +%endif Summary: Library providing a simple API virtualization Name: libvirt @@ -20,6 +26,9 @@ Requires: cyrus-sasl # Not technically required, but makes 'out-of-box' config # work correctly & doesn't have onerous dependancies Requires: cyrus-sasl-md5 +%if %{with_polkit} +Requires: PolicyKit >= 0.6 +%endif BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -31,6 +40,9 @@ BuildRequires: bridge-utils BuildRequires: bridge-utils BuildRequires: qemu BuildRequires: cyrus-sasl-devel +%if %{with_polkit} +BuildRequires: PolicyKit-devel >= 0.6 +%endif Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 @@ -143,6 +155,7 @@ fi %{_datadir}/libvirt/networks/default.xml %dir %{_localstatedir}/run/libvirt/ %dir %{_localstatedir}/lib/libvirt/ +%{_datadir}/PolicyKit/policy/libvirtd.policy %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/qemu/ %attr(4755, root, root) %{_libexecdir}/libvirt_proxy %attr(0755, root, root) %{_sbindir}/libvirtd diff -r ca9413d4d890 qemud/Makefile.am --- a/qemud/Makefile.am Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/Makefile.am Fri Nov 30 15:15:51 2007 -0500 @@ -30,15 +30,24 @@ libvirtd_CFLAGS = \ libvirtd_CFLAGS = \ -I$(top_srcdir)/include -I$(top_builddir)/include \ $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) $(SASL_CFLAGS) \ + $(POLKIT_CFLAGS) \ $(WARN_CFLAGS) -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \ -DSYSCONF_DIR="\"$(sysconfdir)\"" \ -DQEMUD_PID_FILE="\"$(QEMUD_PID_FILE)\"" \ -DREMOTE_PID_FILE="\"$(REMOTE_PID_FILE)\"" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" -libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) +libvirtd_LDFLAGS = $(WARN_CFLAGS) $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) \ + $(POLKIT_LIBS) libvirtd_DEPENDENCIES = ../src/libvirt.la libvirtd_LDADD = ../src/libvirt.la + + +if HAVE_POLKIT +policydir = $(datadir)/PolicyKit/policy +policy_DATA = libvirtd.policy +endif +EXTRA_DIST += libvirtd.policy if HAVE_AVAHI libvirtd_SOURCES += mdns.c mdns.h diff -r ca9413d4d890 qemud/internal.h --- a/qemud/internal.h Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/internal.h Fri Nov 30 15:15:51 2007 -0500 @@ -30,6 +30,10 @@ #include "../src/gnutls_1_0_compat.h" #if HAVE_SASL #include <sasl/sasl.h> +#endif + +#ifdef HAVE_POLKIT +#include <dbus/dbus.h> #endif #ifdef HAVE_SYS_SYSLIMITS_H @@ -155,6 +159,9 @@ struct qemud_server { #if HAVE_SASL char **saslUsernameWhitelist; #endif +#if HAVE_POLKIT + DBusConnection *sysbus; +#endif }; void qemudLog(int priority, const char *fmt, ...) diff -r ca9413d4d890 qemud/libvirtd.conf --- a/qemud/libvirtd.conf Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/libvirtd.conf Fri Nov 30 15:15:51 2007 -0500 @@ -82,8 +82,11 @@ # Set the UNIX socket permissions for the R/W socket. This is used # for full management of VMs # -# Default allows only root. If setting group ownership may want to -# relax this to: +# Default allows only root. If PolicyKit is enabled on the socket, +# the default will change to allow everyone (eg, 0777) +# +# If not using PolicyKit and setting group ownership for access +# control then you may want to relax this to: #unix_sock_rw_perms = "0770" @@ -103,7 +106,12 @@ # socket only GSSAPI & DIGEST-MD5 mechanisms will be used. # For non-TCP or TLS sockets, any scheme is allowed. # - +# - polkit: use PolicyKit to authenticate. This is only suitable +# for use on the UNIX sockets. The default policy will +# require a user to supply their own password to gain +# full read/write access (aka sudo like), while anyone +# is allowed read/only access. +# # Set an authentication scheme for UNIX read-only sockets # By default socket permissions allow anyone to connect # @@ -112,7 +120,9 @@ #auth_unix_ro = "none" # Set an authentication scheme for UNIX read-write sockets -# By default socket permissions only allow root. +# By default socket permissions only allow root. If PolicyKit +# support was compiled into libvirt, the default will be to +# use 'polkit' auth. # # If the unix_sock_rw_perms are changed you may wish to enable # an authentication mechanism here diff -r ca9413d4d890 qemud/libvirtd.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qemud/libvirtd.policy Fri Nov 30 15:15:51 2007 -0500 @@ -0,0 +1,42 @@ +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> + +<!-- +Policy definitions for libvirt daemon + +Copyright (c) 2007 Daniel P. Berrange <berrange redhat com> + +libvirt is licensed to you under the GNU Lesser General Public License +version 2. See COPYING for details. + +NOTE: If you make changes to this file, make sure to validate the file +using the polkit-policy-file-validate(1) tool. Changes made to this +file are instantly applied. +--> + +<policyconfig> + <action id="org.libvirt.unix.monitor"> + <description>Monitor local virtualized systems</description> + <message>System policy prevents monitoring of local virtualized systems</message> + <defaults> + <!-- Any program can use libvirt in read-only mode for monitoring, + even if not part of a session --> + <allow_any>yes</allow_any> + <allow_inactive>yes</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> + + <action id="org.libvirt.unix.manage"> + <description>Manage local virtualized systems</description> + <message>System policy prevents management of local virtualized systems</message> + <defaults> + <!-- Only a program in the active host session can use libvirt in + read-write mode for management, and we require user password --> + <allow_any>no</allow_any> + <allow_inactive>no</allow_inactive> + <allow_active>auth_self_keep_session</allow_active> + </defaults> + </action> +</policyconfig> \ No newline at end of file diff -r ca9413d4d890 qemud/qemud.c --- a/qemud/qemud.c Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/qemud.c Fri Nov 30 15:15:51 2007 -0500 @@ -77,8 +77,13 @@ static int unix_sock_rw_mask = 0700; /* 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; +#else static int auth_unix_rw = REMOTE_AUTH_NONE; static int auth_unix_ro = REMOTE_AUTH_NONE; +#endif /* HAVE_POLKIT */ #if HAVE_SASL static int auth_tcp = REMOTE_AUTH_SASL; #else @@ -761,6 +766,21 @@ static struct qemud_server *qemudNetwork } #endif +#ifdef HAVE_POLKIT + if (auth_unix_rw == REMOTE_AUTH_POLKIT || + auth_unix_ro == REMOTE_AUTH_POLKIT) { + DBusError derr; + dbus_error_init(&derr); + server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr); + if (!(server->sysbus)) { + qemudLog(QEMUD_ERR, "Failed to connect to system bus for PolicyKit auth: %s", + derr.message); + dbus_error_free(&derr); + goto cleanup; + } + } +#endif + if (ipsock) { if (listen_tcp && remoteListenTCP (server, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0) goto cleanup; @@ -828,6 +848,10 @@ static struct qemud_server *qemudNetwork sock = sock->next; } +#ifdef HAVE_POLKIT + if (server->sysbus) + dbus_connection_unref(server->sysbus); +#endif free(server); } return NULL; @@ -1783,6 +1807,10 @@ static int remoteConfigGetAuth(virConfPt } else if (STREQ(p->str, "sasl")) { *auth = REMOTE_AUTH_SASL; #endif +#if HAVE_POLKIT + } else if (STREQ(p->str, "polkit")) { + *auth = REMOTE_AUTH_POLKIT; +#endif } else { qemudLog (QEMUD_ERR, "remoteReadConfigFile: %s: %s: unsupported auth %s\n", filename, key, p->str); return -1; @@ -1818,6 +1846,13 @@ remoteReadConfigFile (struct qemud_serve if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0) return -1; +#if HAVE_POLKIT + /* Change default perms to be wide-open if PolicyKit is enabled. + * Admin can always override in config file + */ + if (auth_unix_rw == REMOTE_AUTH_POLKIT) + unix_sock_rw_mask = 0777; +#endif if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0) return -1; if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0) diff -r ca9413d4d890 qemud/remote.c --- a/qemud/remote.c Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote.c Fri Nov 30 15:15:51 2007 -0500 @@ -46,6 +46,11 @@ #include <assert.h> #include <fnmatch.h> +#ifdef HAVE_POLKIT +#include <polkit/polkit.h> +#include <polkit-dbus/polkit-dbus.h> +#endif + #include <libvirt/virterror.h> #include "internal.h" @@ -132,7 +137,8 @@ remoteDispatchClientRequest (struct qemu if (req.proc != REMOTE_PROC_AUTH_LIST && req.proc != REMOTE_PROC_AUTH_SASL_INIT && req.proc != REMOTE_PROC_AUTH_SASL_START && - req.proc != REMOTE_PROC_AUTH_SASL_STEP + req.proc != REMOTE_PROC_AUTH_SASL_STEP && + req.proc != REMOTE_PROC_AUTH_POLKIT ) { remoteDispatchError (client, &req, "authentication required"); xdr_destroy (&xdr); @@ -2550,6 +2556,133 @@ remoteDispatchAuthSaslStep (struct qemud #endif /* HAVE_SASL */ +#if HAVE_POLKIT +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 +remoteDispatchAuthPolkit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_polkit_ret *ret) +{ + pid_t callerPid; + uid_t callerUid; + + REMOTE_DEBUG("Start PolicyKit auth %d", client->fd); + if (client->auth != REMOTE_AUTH_POLKIT) { + qemudLog(QEMUD_ERR, "client tried invalid PolicyKit init request"); + remoteDispatchFailAuth(client, req); + return -2; + } + + if (qemudGetSocketIdentity(client->fd, &callerUid, &callerPid) < 0) { + qemudLog(QEMUD_ERR, "cannot get peer socket identity"); + remoteDispatchFailAuth(client, req); + return -2; + } + + /* Only do policy checks for non-root - allow root user + through with no checks, as a fail-safe - root can easily + change policykit policy anyway, so its pointless trying + to restrict root */ + if (callerUid == 0) { + qemudLog(QEMUD_INFO, "Allowing PID %d running as root", callerPid); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } else { + PolKitCaller *pkcaller = NULL; + PolKitAction *pkaction = NULL; + PolKitContext *pkcontext = NULL; + PolKitError *pkerr; + PolKitResult pkresult; + DBusError err; + const char *action = client->readonly ? + "org.libvirt.unix.monitor" : + "org.libvirt.unix.manage"; + + 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); + remoteDispatchFailAuth(client, req); + return -2; + } + + if (!(pkaction = polkit_action_new())) { + qemudLog(QEMUD_ERR, "Failed to create polkit action %s\n", strerror(errno)); + polkit_caller_unref(pkcaller); + remoteDispatchFailAuth(client, req); + return -2; + } + 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); + remoteDispatchFailAuth(client, req); + return -2; + } + + 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)); + remoteDispatchFailAuth(client, req); + return -2; + } + qemudLog(QEMUD_INFO, "Policy allowed action %s from pid %d, uid %d, result %s", + action, callerPid, callerUid, polkit_result_to_string_representation(pkresult)); + ret->complete = 1; + client->auth = REMOTE_AUTH_NONE; + } + + return 0; +} + +#else /* HAVE_POLKIT */ + +static int +remoteDispatchAuthPolkitInit (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + void *args ATTRIBUTE_UNUSED, + remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED) +{ + qemudLog(QEMUD_ERR, "client tried unsupported PolicyKit init request"); + remoteDispatchFailAuth(client, req); + return -1; +} +#endif /* HAVE_POLKIT */ + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -r ca9413d4d890 qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote_dispatch_localvars.h Fri Nov 30 15:15:51 2007 -0500 @@ -37,6 +37,7 @@ remote_domain_create_linux_args lv_remot remote_domain_create_linux_args lv_remote_domain_create_linux_args; remote_domain_create_linux_ret lv_remote_domain_create_linux_ret; remote_domain_set_scheduler_parameters_args lv_remote_domain_set_scheduler_parameters_args; +remote_auth_polkit_ret lv_remote_auth_polkit_ret; remote_auth_sasl_start_args lv_remote_auth_sasl_start_args; remote_auth_sasl_start_ret lv_remote_auth_sasl_start_ret; remote_domain_interface_stats_args lv_remote_domain_interface_stats_args; diff -r ca9413d4d890 qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote_dispatch_proc_switch.h Fri Nov 30 15:15:51 2007 -0500 @@ -8,6 +8,12 @@ case REMOTE_PROC_AUTH_LIST: ret = (char *) &lv_remote_auth_list_ret; memset (&lv_remote_auth_list_ret, 0, sizeof lv_remote_auth_list_ret); break; +case REMOTE_PROC_AUTH_POLKIT: + fn = (dispatch_fn) remoteDispatchAuthPolkit; + ret_filter = (xdrproc_t) xdr_remote_auth_polkit_ret; + ret = (char *) &lv_remote_auth_polkit_ret; + memset (&lv_remote_auth_polkit_ret, 0, sizeof lv_remote_auth_polkit_ret); + break; case REMOTE_PROC_AUTH_SASL_INIT: fn = (dispatch_fn) remoteDispatchAuthSaslInit; ret_filter = (xdrproc_t) xdr_remote_auth_sasl_init_ret; diff -r ca9413d4d890 qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote_dispatch_prototypes.h Fri Nov 30 15:15:51 2007 -0500 @@ -3,6 +3,7 @@ */ static int remoteDispatchAuthList (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_list_ret *ret); +static int remoteDispatchAuthPolkit (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_polkit_ret *ret); static int remoteDispatchAuthSaslInit (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_auth_sasl_init_ret *ret); static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); diff -r ca9413d4d890 qemud/remote_protocol.c --- a/qemud/remote_protocol.c Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote_protocol.c Fri Nov 30 15:15:51 2007 -0500 @@ -1309,6 +1309,15 @@ xdr_remote_auth_sasl_step_ret (XDR *xdrs } bool_t +xdr_remote_auth_polkit_ret (XDR *xdrs, remote_auth_polkit_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->complete)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff -r ca9413d4d890 qemud/remote_protocol.h --- a/qemud/remote_protocol.h Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote_protocol.h Fri Nov 30 15:15:51 2007 -0500 @@ -68,6 +68,7 @@ enum remote_auth_type { enum remote_auth_type { REMOTE_AUTH_NONE = 0, REMOTE_AUTH_SASL = 1, + REMOTE_AUTH_POLKIT = 2, }; typedef enum remote_auth_type remote_auth_type; @@ -719,6 +720,11 @@ struct remote_auth_sasl_step_ret { } data; }; typedef struct remote_auth_sasl_step_ret remote_auth_sasl_step_ret; + +struct remote_auth_polkit_ret { + int complete; +}; +typedef struct remote_auth_polkit_ret remote_auth_polkit_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -792,6 +798,7 @@ enum remote_procedure { REMOTE_PROC_AUTH_SASL_INIT = 67, REMOTE_PROC_AUTH_SASL_START = 68, REMOTE_PROC_AUTH_SASL_STEP = 69, + REMOTE_PROC_AUTH_POLKIT = 70, }; typedef enum remote_procedure remote_procedure; @@ -935,6 +942,7 @@ extern bool_t xdr_remote_auth_sasl_star extern bool_t xdr_remote_auth_sasl_start_ret (XDR *, remote_auth_sasl_start_ret*); extern bool_t xdr_remote_auth_sasl_step_args (XDR *, remote_auth_sasl_step_args*); extern bool_t xdr_remote_auth_sasl_step_ret (XDR *, remote_auth_sasl_step_ret*); +extern bool_t xdr_remote_auth_polkit_ret (XDR *, remote_auth_polkit_ret*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_direction (XDR *, remote_message_direction*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -1054,6 +1062,7 @@ extern bool_t xdr_remote_auth_sasl_start extern bool_t xdr_remote_auth_sasl_start_ret (); extern bool_t xdr_remote_auth_sasl_step_args (); extern bool_t xdr_remote_auth_sasl_step_ret (); +extern bool_t xdr_remote_auth_polkit_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_direction (); extern bool_t xdr_remote_message_status (); diff -r ca9413d4d890 qemud/remote_protocol.x --- a/qemud/remote_protocol.x Fri Nov 30 15:15:26 2007 -0500 +++ b/qemud/remote_protocol.x Fri Nov 30 15:15:51 2007 -0500 @@ -132,7 +132,8 @@ struct remote_error { /* Authentication types available thus far.... */ enum remote_auth_type { REMOTE_AUTH_NONE = 0, - REMOTE_AUTH_SASL = 1 + REMOTE_AUTH_SASL = 1, + REMOTE_AUTH_POLKIT = 2 }; @@ -654,6 +655,10 @@ struct remote_auth_sasl_step_ret { int complete; int nil; char data<REMOTE_AUTH_SASL_DATA_MAX>; +}; + +struct remote_auth_polkit_ret { + int complete; }; /*----- Protocol. -----*/ @@ -731,7 +736,8 @@ enum remote_procedure { REMOTE_PROC_AUTH_LIST = 66, REMOTE_PROC_AUTH_SASL_INIT = 67, REMOTE_PROC_AUTH_SASL_START = 68, - REMOTE_PROC_AUTH_SASL_STEP = 69 + REMOTE_PROC_AUTH_SASL_STEP = 69, + REMOTE_PROC_AUTH_POLKIT = 70 }; /* Custom RPC structure. */ diff -r ca9413d4d890 src/remote_internal.c --- a/src/remote_internal.c Fri Nov 30 15:15:26 2007 -0500 +++ b/src/remote_internal.c Fri Nov 30 15:15:51 2007 -0500 @@ -115,6 +115,9 @@ static int remoteAuthenticate (virConnec #if HAVE_SASL static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open); #endif +#if HAVE_POLKIT +static int remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open); +#endif /* HAVE_POLKIT */ static void error (virConnectPtr conn, virErrorNumber code, const char *info); static void server_error (virConnectPtr conn, remote_error *err); static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); @@ -2854,6 +2857,15 @@ remoteAuthenticate (virConnectPtr conn, break; #endif +#if HAVE_POLKIT + case REMOTE_AUTH_POLKIT: + if (remoteAuthPolkit(conn, priv, in_open) < 0) { + free(ret.types.types_val); + return -1; + } + break; +#endif + case REMOTE_AUTH_NONE: /* Nothing todo, hurrah ! */ break; @@ -3443,6 +3455,29 @@ really_write_sasl (virConnectPtr conn, s return really_write_buf(conn, priv, in_open, output, outputlen); } #endif + + +#if HAVE_POLKIT +/* Perform the PolicyKit authentication process + */ +static int +remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open) +{ + remote_auth_polkit_ret ret; + + remoteDebug(priv, "Client initialize PolicyKit authentication"); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_POLKIT, + (xdrproc_t) xdr_void, (char *)NULL, + (xdrproc_t) xdr_remote_auth_polkit_ret, (char *) &ret) != 0) { + return -1; /* virError already set by call */ + } + + remoteDebug(priv, "PolicyKit authentication complete"); + return 0; +} +#endif /* HAVE_POLKIT */ static int really_write (virConnectPtr conn, struct private_data *priv, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:18:41PM +0000, Daniel P. Berrange wrote:
This patch adds support for an PolicyKit authentication mechanism. This was previously described here:
http://www.redhat.com/archives/libvir-list/2007-September/msg00168.html
If PolicyKit is compiled in, then the UNIX domain sockets have their default settings changed to make sure of PolicyKit. Thus, when PolicyKit is enabled, both the RO & RW sockets are mode 0777. PolicyKit is then called upon client connect to decide whether to allow the client to gain access.
The policyfile is shipped in /usr/share/PolicyKit/policy and has default settings to mimic current non-PolicyKit access. If making a read-only connection, any application will be granted access by default. If making a read-write connection, applications will need to authenticate against policykit by providing the user's own password. This is akin to 'sudo' style auth. The credentials persist until the user logs out.
The file in /etc/PolicyKit/PolicyKit.conf can be used by the local sysadmin to override the default policy on a per-host basis. eg, they could restrict access to the read-only connections, or open up the read-write connections to more apps. See 'man PolicyKit.conf' for more info.
The configure script will check for PolicyKit using pkg-config and only enable it if actually present. So any OS without PolicyKit will not be impacted by this patch.
b/qemud/libvirtd.policy | 42 +++++++++++ configure.in | 25 ++++++ libvirt.spec.in | 3 qemud/Makefile.am | 11 ++ qemud/internal.h | 7 + qemud/libvirtd.conf | 18 +++- qemud/qemud.c | 37 +++++++++ qemud/remote.c | 135 +++++++++++++++++++++++++++++++++++- qemud/remote_dispatch_localvars.h | 1 qemud/remote_dispatch_proc_switch.h | 6 + qemud/remote_dispatch_prototypes.h | 1 qemud/remote_protocol.c | 9 ++ qemud/remote_protocol.h | 9 ++ qemud/remote_protocol.x | 10 ++ src/remote_internal.c | 35 +++++++++ 15 files changed, 340 insertions(+), 9 deletions(-)
If anyone has objections / comments wrt to this patch please say so now otherwise I'll commit it in an hour or so. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch introduces the public & driver APIs for collecting auth credentials via a callback. This allows username/password based auth schemes to be used. This basically introduces a 3rd variant of the virConnectOpen call which takes a set of authentication parameters, and some flags. virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags); The flags parameter allows VIR_CONNECT_RO, so there is no need for a separate ReadOnly API. The 'auth' parameter is a small struct containing a list of credentials that the calling application knows how to collect, a function pointer for the callback, and an opaque data blob. struct _virConnectAuth { int *credtype; /* List of supported virConnectCredentialType values */ unsigned int ncredtype; virConnectAuthCallbackPtr cb; /* Callback used to collect credentials */ void *cbdata; }; At the very least apps should support the VIR_CRED_AUTHNAME and the VIR_CRED_PASSPHRASE credential types for collecting username+password respectively. There are a bunch of other credential type, but they're for fairly niche use cases. When the callback is invoked, it will be passed a list of virConnectCredentialPtr structs which contain details on all the credentials that the authentication mechanism needs to collect. They are passed all at once to make it easy to construct a big form in UI: typedef int (*virConnectAuthCallbackPtr)(virConnectCredentialPtr cred, unsigned int ncred, void *cbdata); The virConnectCredentialPtr struct contains a prompt which can be displayed in the UI. There may optionally be a challenge if doing a challenge/response type authentication. There may also be a default result. The application should collect a credential from the user & fill it into the 'result' field, or use the default result. If the callback returns 0, authentication will continue. If it returns -1, it will assume the user wants to cancel the auth process. Makefile.am | 2 docs/devhelp/libvirt-libvirt.html | 80 ++++++++++++- docs/devhelp/libvirt-virterror.html | 3 docs/libvirt-api.xml | 114 +++++++++++++++---- docs/libvirt-refs.xml | 210 ++++++++++++++++++++++++++++++++---- include/libvirt/libvirt.h.in | 72 ++++++++++++ proxy/libvirt_proxy.c | 4 qemud/remote.c | 2 src/driver.h | 10 - src/internal.h | 5 src/libvirt.c | 40 +++++- src/openvz_driver.c | 23 +++ src/proxy_internal.c | 9 + src/qemu_driver.c | 2 src/remote_internal.c | 22 ++- src/test.c | 2 src/xen_internal.c | 1 src/xen_internal.h | 1 src/xen_unified.c | 4 src/xend_internal.c | 6 - src/xend_internal.h | 2 src/xm_internal.c | 4 src/xm_internal.h | 2 src/xs_internal.c | 3 src/xs_internal.h | 1 25 files changed, 526 insertions(+), 98 deletions(-) diff -r ec2d8c632fd9 Makefile.am --- a/Makefile.am Wed Nov 28 15:00:45 2007 -0500 +++ b/Makefile.am Wed Nov 28 20:35:23 2007 -0500 @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = src qemud proxy include docs @PYTHON_SUBDIR@ tests po m4 scripts +SUBDIRS = include src qemud proxy docs @PYTHON_SUBDIR@ tests po m4 scripts ACLOCAL_AMFLAGS = -I m4 diff -r ec2d8c632fd9 docs/devhelp/libvirt-libvirt.html --- a/docs/devhelp/libvirt-libvirt.html Wed Nov 28 15:00:45 2007 -0500 +++ b/docs/devhelp/libvirt-libvirt.html Wed Nov 28 20:35:23 2007 -0500 @@ -61,19 +61,26 @@ typedef enum <a href="#virDomainState">v typedef enum <a href="#virDomainState">virDomainState</a>; typedef struct _virDomain <a href="#virDomain">virDomain</a>; typedef <a href="libvirt-libvirt.html#virDomainInterfaceStatsStruct">virDomainInterfaceStatsStruct</a> * <a href="#virDomainInterfaceStatsPtr">virDomainInterfaceStatsPtr</a>; +typedef <a href="libvirt-libvirt.html#virConnectAuth">virConnectAuth</a> * <a href="#virConnectAuthPtr">virConnectAuthPtr</a>; typedef enum <a href="#virSchedParameterType">virSchedParameterType</a>; -typedef <a href="libvirt-libvirt.html#virSchedParameter">virSchedParameter</a> * <a href="#virSchedParameterPtr">virSchedParameterPtr</a>; +typedef <a href="libvirt-libvirt.html#virConnectCredential">virConnectCredential</a> * <a href="#virConnectCredentialPtr">virConnectCredentialPtr</a>; typedef <a href="libvirt-libvirt.html#virNodeInfo">virNodeInfo</a> * <a href="#virNodeInfoPtr">virNodeInfoPtr</a>; typedef <a href="libvirt-libvirt.html#virNetwork">virNetwork</a> * <a href="#virNetworkPtr">virNetworkPtr</a>; typedef <a href="libvirt-libvirt.html#virDomainInfo">virDomainInfo</a> * <a href="#virDomainInfoPtr">virDomainInfoPtr</a>; typedef <a href="libvirt-libvirt.html#virConnect">virConnect</a> * <a href="#virConnectPtr">virConnectPtr</a>; typedef struct _virSchedParameter <a href="#virSchedParameter">virSchedParameter</a>; +typedef enum <a href="#virConnectFlags">virConnectFlags</a>; +typedef <a href="libvirt-libvirt.html#virSchedParameter">virSchedParameter</a> * <a href="#virSchedParameterPtr">virSchedParameterPtr</a>; typedef enum <a href="#virVcpuState">virVcpuState</a>; +typedef struct _virConnectAuth <a href="#virConnectAuth">virConnectAuth</a>; +typedef struct _virConnectCredential <a href="#virConnectCredential">virConnectCredential</a>; typedef <a href="libvirt-libvirt.html#virVcpuInfo">virVcpuInfo</a> * <a href="#virVcpuInfoPtr">virVcpuInfoPtr</a>; typedef enum <a href="#virDomainXMLFlags">virDomainXMLFlags</a>; typedef <a href="libvirt-libvirt.html#virDomain">virDomain</a> * <a href="#virDomainPtr">virDomainPtr</a>; +typedef enum <a href="#virConnectCredentialType">virConnectCredentialType</a>; typedef enum <a href="#virDomainCreateFlags">virDomainCreateFlags</a>; int <a href="#virDomainGetInfo">virDomainGetInfo</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> <a href="libvirt-libvirt.html#virDomainInfoPtr">virDomainInfoPtr</a> info); +int <a href="#virNetworkCreate">virNetworkCreate</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); int <a href="#virNetworkFree">virNetworkFree</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); const char * <a href="#virNetworkGetName">virNetworkGetName</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); const char * <a href="#virDomainGetName">virDomainGetName</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain); @@ -88,6 +95,7 @@ int <a href="#virInitialize">virInitiali <a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> <a href="#virDomainLookupByName">virDomainLookupByName</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * name); <a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> <a href="#virNetworkLookupByUUID">virNetworkLookupByUUID</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const unsigned char * uuid); char * <a href="#virNetworkGetBridgeName">virNetworkGetBridgeName</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); +char * <a href="#virNetworkGetXMLDesc">virNetworkGetXMLDesc</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network, <br/> int flags); int <a href="#virDomainPinVcpu">virDomainPinVcpu</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> unsigned int vcpu, <br/> unsigned char * cpumap, <br/> int maplen); int <a href="#virDomainRestore">virDomainRestore</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * from); const char * <a href="#virConnectGetType">virConnectGetType</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); @@ -119,21 +127,20 @@ int <a href="#virDomainSetAutostart">vir int <a href="#virDomainSetAutostart">virDomainSetAutostart</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> int autostart); int <a href="#virDomainSuspend">virDomainSuspend</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain); int <a href="#virConnectGetMaxVcpus">virConnectGetMaxVcpus</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * type); +int <a href="#virConnectNumOfDefinedDomains">virConnectNumOfDefinedDomains</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); <a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> <a href="#virNetworkGetConnect">virNetworkGetConnect</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> net); -int <a href="#virConnectNumOfDefinedDomains">virConnectNumOfDefinedDomains</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); -<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> <a href="#virNetworkCreateXML">virNetworkCreateXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc); <a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> <a href="#virNetworkDefineXML">virNetworkDefineXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xml); -int <a href="#virNetworkCreate">virNetworkCreate</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network); +<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> <a href="#virDomainLookupByID">virDomainLookupByID</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> int id); int <a href="#virDomainSetSchedulerParameters">virDomainSetSchedulerParameters</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> <a href="libvirt-libvirt.html#virSchedParameterPtr">virSchedParameterPtr</a> params, <br/> int nparams); int <a href="#virNodeGetCellsFreeMemory">virNodeGetCellsFreeMemory</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> unsigned long long * freeMems, <br/> int startCell, <br/> int maxCells); int <a href="#virConnectClose">virConnectClose</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); int <a href="#virDomainReboot">virDomainReboot</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> unsigned int flags); int <a href="#virDomainBlockStats">virDomainBlockStats</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> dom, <br/> const char * path, <br/> <a href="libvirt-libvirt.html#virDomainBlockStatsPtr">virDomainBlockStatsPtr</a> stats, <br/> size_t size); int <a href="#virDomainSetVcpus">virDomainSetVcpus</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> unsigned int nvcpus); -<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> <a href="#virDomainLookupByID">virDomainLookupByID</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> int id); +<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> <a href="#virConnectOpenAuth">virConnectOpenAuth</a> (const char * name, <br/> <a href="libvirt-libvirt.html#virConnectAuthPtr">virConnectAuthPtr</a> auth, <br/> int flags); int <a href="#virNetworkGetUUIDString">virNetworkGetUUIDString</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network, <br/> char * buf); <a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> <a href="#virDomainMigrate">virDomainMigrate</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> <a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> dconn, <br/> unsigned long flags, <br/> const char * dname, <br/> const char * uri, <br/> unsigned long bandwidth); -char * <a href="#virNetworkGetXMLDesc">virNetworkGetXMLDesc</a> (<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> network, <br/> int flags); +<a href="libvirt-libvirt.html#virNetworkPtr">virNetworkPtr</a> <a href="#virNetworkCreateXML">virNetworkCreateXML</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc); unsigned int <a href="#virDomainGetID">virDomainGetID</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain); int <a href="#virDomainResume">virDomainResume</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain); <a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> <a href="#virDomainCreateLinux">virDomainCreateLinux</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> const char * xmlDesc, <br/> unsigned int flags); @@ -149,6 +156,7 @@ char * <a href="#virDomainGetXMLDesc">vi char * <a href="#virDomainGetXMLDesc">virDomainGetXMLDesc</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> int flags); int <a href="#virDomainSetMaxMemory">virDomainSetMaxMemory</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/> unsigned long memory); int <a href="#virDomainInterfaceStats">virDomainInterfaceStats</a> (<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> dom, <br/> const char * path, <br/> <a href="libvirt-libvirt.html#virDomainInterfaceStatsPtr">virDomainInterfaceStatsPtr</a> stats, <br/> size_t size); +typedef int <a href="#virConnectAuthCallbackPtr">virConnectAuthCallbackPtr</a> (<a href="libvirt-libvirt.html#virConnectCredentialPtr">virConnectCredentialPtr</a> cred, <br/> unsigned int ncred, <br/> void * cbdata); int <a href="#virConnectGetVersion">virConnectGetVersion</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> unsigned long * hvVer); int <a href="#virConnectListNetworks">virConnectListNetworks</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/> char ** const names, <br/> int maxnames); char * <a href="#virConnectGetURI">virConnectGetURI</a> (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn); @@ -212,6 +220,54 @@ The content of this structure is not mad </pre><p/> </div> <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectAuth">Structure </a>virConnectAuth</h3><pre class="programlisting">struct _virConnectAuth { + int * credtype : List of supported <a href="libvirt-libvirt.html#virConnectCredentialType">virConnectCredentialType</a> values + unsigned int ncredtype + <a href="libvirt-libvirt.html#virConnectAuthCallbackPtr">virConnectAuthCallbackPtr</a> cb : Callback used to collect credentials + void * cbdata +} virConnectAuth; +</pre><p/> +</div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectAuthPtr">Typedef </a>virConnectAuthPtr</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectAuth">virConnectAuth</a> * virConnectAuthPtr; +</pre><p/> +</div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectCredential">Structure </a>virConnectCredential</h3><pre class="programlisting">struct _virConnectCredential { + int type : One of <a href="libvirt-libvirt.html#virConnectCredentialType">virConnectCredentialType</a> constants + const char * prompt : Prompt to show to user + const char * challenge : Additional challenge to show + const char * defresult : Optional default result + char * result : Result to be filled with user response (or defresult) + unsigned int resultlen : Length of the result +} virConnectCredential; +</pre><p/> +</div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectCredentialPtr">Typedef </a>virConnectCredentialPtr</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectCredential">virConnectCredential</a> * virConnectCredentialPtr; +</pre><p/> +</div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectCredentialType">Enum </a>virConnectCredentialType</h3><pre class="programlisting">enum <a href="#virConnectCredentialType">virConnectCredentialType</a> { + <a name="VIR_CRED_USERNAME">VIR_CRED_USERNAME</a> = 1 /* Identity to act as */ + <a name="VIR_CRED_AUTHNAME">VIR_CRED_AUTHNAME</a> = 2 /* Identify to authorize as */ + <a name="VIR_CRED_LANGUAGE">VIR_CRED_LANGUAGE</a> = 3 /* RFC 1766 languages, comma separated */ + <a name="VIR_CRED_CNONCE">VIR_CRED_CNONCE</a> = 4 /* client supplies a nonce */ + <a name="VIR_CRED_PASSPHRASE">VIR_CRED_PASSPHRASE</a> = 5 /* Passphrase secret */ + <a name="VIR_CRED_ECHOPROMPT">VIR_CRED_ECHOPROMPT</a> = 6 /* Challenge response */ + <a name="VIR_CRED_NOECHOPROMPT">VIR_CRED_NOECHOPROMPT</a> = 7 /* Challenge response */ + <a name="VIR_CRED_REALM">VIR_CRED_REALM</a> = 8 /* Authentication realm */ + <a name="VIR_CRED_EXTERNAL">VIR_CRED_EXTERNAL</a> = 9 /* Externally managed credential More may be added - expect the unexpected */ +}; +</pre><p/> +</div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectFlags">Enum </a>virConnectFlags</h3><pre class="programlisting">enum <a href="#virConnectFlags">virConnectFlags</a> { + <a name="VIR_CONNECT_RO">VIR_CONNECT_RO</a> = 1 /* A readonly connection */ +}; +</pre><p/> +</div> + <hr/> <div class="refsect2" lang="en"><h3><a name="virConnectPtr">Typedef </a>virConnectPtr</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnect">virConnect</a> * virConnectPtr; </pre><p>a <a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> is pointer to a <a href="libvirt-libvirt.html#virConnect">virConnect</a> private structure, this is the type used to reference a connection to the Xen Hypervisor in the API.</p> </div> @@ -372,6 +428,14 @@ The content of this structure is not mad </pre><p/> </div> <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectAuthCallbackPtr"/>Function type virConnectAuthCallbackPtr</h3><pre class="programlisting">int virConnectAuthCallbackPtr (<a href="libvirt-libvirt.html#virConnectCredentialPtr">virConnectCredentialPtr</a> cred, <br/> unsigned int ncred, <br/> void * cbdata)<br/> +</pre><p/> +<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>cred</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>ncred</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>cbdata</tt></i>:</span></td><td/></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td/></tr></tbody></table></div></div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectAuthPtrDefault">Variable </a>virConnectAuthPtrDefault</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectAuthPtr">virConnectAuthPtr</a> virConnectAuthPtrDefault; +</pre><p/> +</div> + <hr/> <div class="refsect2" lang="en"><h3><a name="virConnectClose"/>virConnectClose ()</h3><pre class="programlisting">int virConnectClose (<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn)<br/> </pre><p>This function closes the connection to the Hypervisor. This should not be called if further interaction with the Hypervisor are needed especially if there is running domain which need further monitoring by the application.</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>conn</tt></i>:</span></td><td>pointer to the hypervisor connection</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 in case of success or -1 in case of error.</td></tr></tbody></table></div></div> @@ -435,6 +499,10 @@ The content of this structure is not mad <div class="refsect2" lang="en"><h3><a name="virConnectOpen"/>virConnectOpen ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> virConnectOpen (const char * name)<br/> </pre><p>This function should be called first to get a connection to the Hypervisor and xen store</p> <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>name</tt></i>:</span></td><td>URI of the hypervisor</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a pointer to the hypervisor connection or NULL in case of error URIs are documented at http://libvirt.org/uri.html</td></tr></tbody></table></div></div> + <hr/> + <div class="refsect2" lang="en"><h3><a name="virConnectOpenAuth"/>virConnectOpenAuth ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> virConnectOpenAuth (const char * name, <br/> <a href="libvirt-libvirt.html#virConnectAuthPtr">virConnectAuthPtr</a> auth, <br/> int flags)<br/> +</pre><p>This function should be called first to get a connection to the Hypervisor. If neccessary, authentication will be performed fetching credentials via the callback</p> +<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>name</tt></i>:</span></td><td>URI of the hypervisor</td></tr><tr><td><span class="term"><i><tt>auth</tt></i>:</span></td><td>Authenticate callback parameters</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>Open flags</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a pointer to the hypervisor connection or NULL in case of error URIs are documented at http://libvirt.org/uri.html</td></tr></tbody></table></div></div> <hr/> <div class="refsect2" lang="en"><h3><a name="virConnectOpenReadOnly"/>virConnectOpenReadOnly ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> virConnectOpenReadOnly (const char * name)<br/> </pre><p>This function should be called first to get a restricted connection to the libbrary functionalities. The set of APIs usable are then restricted on the available methods to control the domains.</p> diff -r ec2d8c632fd9 docs/devhelp/libvirt-virterror.html --- a/docs/devhelp/libvirt-virterror.html Wed Nov 28 15:00:45 2007 -0500 +++ b/docs/devhelp/libvirt-virterror.html Wed Nov 28 20:35:23 2007 -0500 @@ -154,7 +154,8 @@ void <a href="#virConnResetLastError">vi <a name="VIR_WAR_NO_NETWORK">VIR_WAR_NO_NETWORK</a> = 41 /* failed to start network */ <a name="VIR_ERR_NO_DOMAIN">VIR_ERR_NO_DOMAIN</a> = 42 /* domain not found or unexpectedly disappeared */ <a name="VIR_ERR_NO_NETWORK">VIR_ERR_NO_NETWORK</a> = 43 /* network not found */ - <a name="VIR_ERR_INVALID_MAC">VIR_ERR_INVALID_MAC</a> = 44 /* invalid MAC adress */ + <a name="VIR_ERR_INVALID_MAC">VIR_ERR_INVALID_MAC</a> = 44 /* invalid MAC adress */ + <a name="VIR_ERR_AUTH_FAILED">VIR_ERR_AUTH_FAILED</a> = 45 /* authentication failed */ }; </pre><p/> </div> diff -r ec2d8c632fd9 docs/libvirt-api.xml --- a/docs/libvirt-api.xml Wed Nov 28 15:00:45 2007 -0500 +++ b/docs/libvirt-api.xml Wed Nov 28 20:35:23 2007 -0500 @@ -16,26 +16,36 @@ <exports symbol='VIR_UUID_STRING_BUFLEN' type='macro'/> <exports symbol='VIR_GET_CPUMAP' type='macro'/> <exports symbol='VIR_UNUSE_CPU' type='macro'/> + <exports symbol='VIR_CRED_EXTERNAL' type='enum'/> + <exports symbol='VIR_DOMAIN_SHUTDOWN' type='enum'/> + <exports symbol='VIR_DOMAIN_SCHED_FIELD_UINT' type='enum'/> + <exports symbol='VIR_CRED_CNONCE' type='enum'/> + <exports symbol='VIR_CRED_ECHOPROMPT' type='enum'/> + <exports symbol='VIR_DOMAIN_SCHED_FIELD_DOUBLE' type='enum'/> + <exports symbol='VIR_DOMAIN_SCHED_FIELD_LLONG' type='enum'/> + <exports symbol='VIR_CONNECT_RO' type='enum'/> + <exports symbol='VIR_CRED_AUTHNAME' type='enum'/> + <exports symbol='VIR_CRED_LANGUAGE' type='enum'/> + <exports symbol='VIR_CRED_NOECHOPROMPT' type='enum'/> + <exports symbol='VIR_DOMAIN_SCHED_FIELD_BOOLEAN' type='enum'/> + <exports symbol='VIR_MIGRATE_LIVE' type='enum'/> + <exports symbol='VIR_CRED_USERNAME' type='enum'/> + <exports symbol='VIR_VCPU_OFFLINE' type='enum'/> + <exports symbol='VIR_DOMAIN_RUNNING' type='enum'/> + <exports symbol='VIR_DOMAIN_BLOCKED' type='enum'/> <exports symbol='VIR_DOMAIN_SHUTOFF' type='enum'/> - <exports symbol='VIR_DOMAIN_SCHED_FIELD_DOUBLE' type='enum'/> + <exports symbol='VIR_VCPU_BLOCKED' type='enum'/> + <exports symbol='VIR_DOMAIN_SCHED_FIELD_INT' type='enum'/> <exports symbol='VIR_DOMAIN_SCHED_FIELD_ULLONG' type='enum'/> - <exports symbol='VIR_VCPU_OFFLINE' type='enum'/> + <exports symbol='VIR_CRED_REALM' type='enum'/> + <exports symbol='VIR_VCPU_RUNNING' type='enum'/> + <exports symbol='VIR_DOMAIN_PAUSED' type='enum'/> + <exports symbol='VIR_DOMAIN_CRASHED' type='enum'/> <exports symbol='VIR_DOMAIN_XML_INACTIVE' type='enum'/> - <exports symbol='VIR_DOMAIN_SCHED_FIELD_BOOLEAN' type='enum'/> - <exports symbol='VIR_DOMAIN_SCHED_FIELD_UINT' type='enum'/> - <exports symbol='VIR_MIGRATE_LIVE' type='enum'/> - <exports symbol='VIR_DOMAIN_SCHED_FIELD_INT' type='enum'/> - <exports symbol='VIR_VCPU_RUNNING' type='enum'/> - <exports symbol='VIR_DOMAIN_CRASHED' type='enum'/> <exports symbol='VIR_DOMAIN_NONE' type='enum'/> - <exports symbol='VIR_DOMAIN_SHUTDOWN' type='enum'/> - <exports symbol='VIR_VCPU_BLOCKED' type='enum'/> <exports symbol='VIR_DOMAIN_NOSTATE' type='enum'/> - <exports symbol='VIR_DOMAIN_PAUSED' type='enum'/> - <exports symbol='VIR_DOMAIN_SCHED_FIELD_LLONG' type='enum'/> - <exports symbol='VIR_DOMAIN_RUNNING' type='enum'/> + <exports symbol='VIR_CRED_PASSPHRASE' type='enum'/> <exports symbol='VIR_DOMAIN_XML_SECURE' type='enum'/> - <exports symbol='VIR_DOMAIN_BLOCKED' type='enum'/> <exports symbol='virDomainBlockStatsStruct' type='typedef'/> <exports symbol='virDomainMigrateFlags' type='typedef'/> <exports symbol='virNodeInfo' type='typedef'/> @@ -48,25 +58,35 @@ <exports symbol='virDomainState' type='typedef'/> <exports symbol='virDomain' type='typedef'/> <exports symbol='virDomainInterfaceStatsPtr' type='typedef'/> + <exports symbol='virConnectAuthPtr' type='typedef'/> <exports symbol='virSchedParameterType' type='typedef'/> - <exports symbol='virSchedParameterPtr' type='typedef'/> + <exports symbol='virConnectCredentialPtr' type='typedef'/> <exports symbol='virNodeInfoPtr' type='typedef'/> <exports symbol='virNetworkPtr' type='typedef'/> <exports symbol='virDomainInfoPtr' type='typedef'/> <exports symbol='virConnectPtr' type='typedef'/> <exports symbol='virSchedParameter' type='typedef'/> + <exports symbol='virConnectFlags' type='typedef'/> + <exports symbol='virSchedParameterPtr' type='typedef'/> <exports symbol='virVcpuState' type='typedef'/> + <exports symbol='virConnectAuth' type='typedef'/> + <exports symbol='virConnectCredential' type='typedef'/> <exports symbol='virVcpuInfoPtr' type='typedef'/> <exports symbol='virDomainXMLFlags' type='typedef'/> <exports symbol='virDomainPtr' type='typedef'/> + <exports symbol='virConnectCredentialType' type='typedef'/> <exports symbol='virDomainCreateFlags' type='typedef'/> <exports symbol='_virDomainInfo' type='struct'/> + <exports symbol='_virConnectAuth' type='struct'/> + <exports symbol='_virVcpuInfo' type='struct'/> <exports symbol='_virDomainInterfaceStats' type='struct'/> - <exports symbol='_virVcpuInfo' type='struct'/> - <exports symbol='_virSchedParameter' type='struct'/> + <exports symbol='_virConnectCredential' type='struct'/> <exports symbol='_virDomainBlockStats' type='struct'/> <exports symbol='_virNodeInfo' type='struct'/> + <exports symbol='_virSchedParameter' type='struct'/> + <exports symbol='virConnectAuthPtrDefault' type='variable'/> <exports symbol='virDomainGetInfo' type='function'/> + <exports symbol='virNetworkCreate' type='function'/> <exports symbol='virNetworkFree' type='function'/> <exports symbol='virNetworkGetName' type='function'/> <exports symbol='virDomainGetName' type='function'/> @@ -81,6 +101,7 @@ <exports symbol='virDomainLookupByName' type='function'/> <exports symbol='virNetworkLookupByUUID' type='function'/> <exports symbol='virNetworkGetBridgeName' type='function'/> + <exports symbol='virNetworkGetXMLDesc' type='function'/> <exports symbol='virDomainPinVcpu' type='function'/> <exports symbol='virDomainRestore' type='function'/> <exports symbol='virConnectGetType' type='function'/> @@ -112,21 +133,20 @@ <exports symbol='virDomainSetAutostart' type='function'/> <exports symbol='virDomainSuspend' type='function'/> <exports symbol='virConnectGetMaxVcpus' type='function'/> + <exports symbol='virConnectNumOfDefinedDomains' type='function'/> <exports symbol='virNetworkGetConnect' type='function'/> - <exports symbol='virConnectNumOfDefinedDomains' type='function'/> - <exports symbol='virNetworkCreateXML' type='function'/> <exports symbol='virNetworkDefineXML' type='function'/> - <exports symbol='virNetworkCreate' type='function'/> + <exports symbol='virDomainLookupByID' type='function'/> <exports symbol='virDomainSetSchedulerParameters' type='function'/> <exports symbol='virNodeGetCellsFreeMemory' type='function'/> <exports symbol='virConnectClose' type='function'/> <exports symbol='virDomainReboot' type='function'/> <exports symbol='virDomainBlockStats' type='function'/> <exports symbol='virDomainSetVcpus' type='function'/> - <exports symbol='virDomainLookupByID' type='function'/> + <exports symbol='virConnectOpenAuth' type='function'/> <exports symbol='virNetworkGetUUIDString' type='function'/> <exports symbol='virDomainMigrate' type='function'/> - <exports symbol='virNetworkGetXMLDesc' type='function'/> + <exports symbol='virNetworkCreateXML' type='function'/> <exports symbol='virDomainGetID' type='function'/> <exports symbol='virDomainResume' type='function'/> <exports symbol='virDomainCreateLinux' type='function'/> @@ -142,6 +162,7 @@ <exports symbol='virDomainGetXMLDesc' type='function'/> <exports symbol='virDomainSetMaxMemory' type='function'/> <exports symbol='virDomainInterfaceStats' type='function'/> + <exports symbol='virConnectAuthCallbackPtr' type='function'/> <exports symbol='virConnectGetVersion' type='function'/> <exports symbol='virConnectListNetworks' type='function'/> <exports symbol='virConnectGetURI' type='function'/> @@ -159,6 +180,7 @@ <exports symbol='VIR_ERR_WARNING' type='enum'/> <exports symbol='VIR_FROM_OPENVZ' type='enum'/> <exports symbol='VIR_ERR_NO_XEN' type='enum'/> + <exports symbol='VIR_FROM_STATS_LINUX' type='enum'/> <exports symbol='VIR_ERR_INVALID_ARG' type='enum'/> <exports symbol='VIR_ERR_ERROR' type='enum'/> <exports symbol='VIR_ERR_NO_OS' type='enum'/> @@ -176,7 +198,7 @@ <exports symbol='VIR_FROM_QEMU' type='enum'/> <exports symbol='VIR_FROM_CONF' type='enum'/> <exports symbol='VIR_ERR_INVALID_NETWORK' type='enum'/> - <exports symbol='VIR_FROM_STATS_LINUX' type='enum'/> + <exports symbol='VIR_ERR_AUTH_FAILED' type='enum'/> <exports symbol='VIR_ERR_OPERATION_DENIED' type='enum'/> <exports symbol='VIR_ERR_NO_KERNEL' type='enum'/> <exports symbol='VIR_ERR_GNUTLS_ERROR' type='enum'/> @@ -287,6 +309,16 @@ <macro name='VIR_UUID_STRING_BUFLEN' file='libvirt'> <info>This macro provides the length of the buffer required for virDomainGetUUIDString()</info> </macro> + <enum name='VIR_CONNECT_RO' file='libvirt' value='1' type='virConnectFlags' info=' A readonly connection'/> + <enum name='VIR_CRED_AUTHNAME' file='libvirt' value='2' type='virConnectCredentialType' info='Identify to authorize as'/> + <enum name='VIR_CRED_CNONCE' file='libvirt' value='4' type='virConnectCredentialType' info='client supplies a nonce'/> + <enum name='VIR_CRED_ECHOPROMPT' file='libvirt' value='6' type='virConnectCredentialType' info='Challenge response'/> + <enum name='VIR_CRED_EXTERNAL' file='libvirt' value='9' type='virConnectCredentialType' info=' Externally managed credential More may be added - expect the unexpected'/> + <enum name='VIR_CRED_LANGUAGE' file='libvirt' value='3' type='virConnectCredentialType' info='RFC 1766 languages, comma separated'/> + <enum name='VIR_CRED_NOECHOPROMPT' file='libvirt' value='7' type='virConnectCredentialType' info='Challenge response'/> + <enum name='VIR_CRED_PASSPHRASE' file='libvirt' value='5' type='virConnectCredentialType' info='Passphrase secret'/> + <enum name='VIR_CRED_REALM' file='libvirt' value='8' type='virConnectCredentialType' info='Authentication realm'/> + <enum name='VIR_CRED_USERNAME' file='libvirt' value='1' type='virConnectCredentialType' info='Identity to act as'/> <enum name='VIR_DOMAIN_BLOCKED' file='libvirt' value='2' type='virDomainState' info='the domain is blocked on resource'/> <enum name='VIR_DOMAIN_CRASHED' file='libvirt' value='6' type='virDomainState' info=' the domain is crashed'/> <enum name='VIR_DOMAIN_NONE' file='libvirt' value='0' type='virDomainCreateFlags'/> @@ -303,6 +335,7 @@ <enum name='VIR_DOMAIN_SHUTOFF' file='libvirt' value='5' type='virDomainState' info='the domain is shut off'/> <enum name='VIR_DOMAIN_XML_INACTIVE' file='libvirt' value='2' type='virDomainXMLFlags' info=' dump inactive domain informations'/> <enum name='VIR_DOMAIN_XML_SECURE' file='libvirt' value='1' type='virDomainXMLFlags' info='dump security sensitive informations too'/> + <enum name='VIR_ERR_AUTH_FAILED' file='virterror' value='45' type='virErrorNumber' info=' authentication failed'/> <enum name='VIR_ERR_CALL_FAILED' file='virterror' value='26' type='virErrorNumber' info='not supported by the drivers (DEPRECATED)'/> <enum name='VIR_ERR_CONF_SYNTAX' file='virterror' value='33' type='virErrorNumber' info='failed to parse the syntax of a conf file'/> <enum name='VIR_ERR_DOM_EXIST' file='virterror' value='28' type='virErrorNumber' info='the domain already exist'/> @@ -315,7 +348,7 @@ <enum name='VIR_ERR_INVALID_ARG' file='virterror' value='8' type='virErrorNumber' info='invalid function argument'/> <enum name='VIR_ERR_INVALID_CONN' file='virterror' value='6' type='virErrorNumber' info='invalid connection object'/> <enum name='VIR_ERR_INVALID_DOMAIN' file='virterror' value='7' type='virErrorNumber' info='invalid domain object'/> - <enum name='VIR_ERR_INVALID_MAC' file='virterror' value='44' type='virErrorNumber' info=' invalid MAC adress'/> + <enum name='VIR_ERR_INVALID_MAC' file='virterror' value='44' type='virErrorNumber' info='invalid MAC adress'/> <enum name='VIR_ERR_INVALID_NETWORK' file='virterror' value='36' type='virErrorNumber' info='invalid network object'/> <enum name='VIR_ERR_NETWORK_EXIST' file='virterror' value='37' type='virErrorNumber' info='the network already exist'/> <enum name='VIR_ERR_NONE' file='virterror' value='0' type='virErrorLevel'/> @@ -373,6 +406,24 @@ <enum name='VIR_VCPU_RUNNING' file='libvirt' value='1' type='virVcpuState' info='the virtual CPU is running'/> <enum name='VIR_WAR_NO_NETWORK' file='virterror' value='41' type='virErrorNumber' info='failed to start network'/> <struct name='virConnect' file='libvirt' type='struct _virConnect'/> + <struct name='virConnectAuth' file='libvirt' type='struct _virConnectAuth'> + <field name='credtype' type='int *' info=' List of supported virConnectCredentialType values'/> + <field name='ncredtype' type='unsigned int' info=''/> + <field name='cb' type='virConnectAuthCallbackPtr' info=' Callback used to collect credentials'/> + <field name='cbdata' type='void *' info=''/> + </struct> + <typedef name='virConnectAuthPtr' file='libvirt' type='virConnectAuth *'/> + <struct name='virConnectCredential' file='libvirt' type='struct _virConnectCredential'> + <field name='type' type='int' info=' One of virConnectCredentialType constants'/> + <field name='prompt' type='const char *' info=' Prompt to show to user'/> + <field name='challenge' type='const char *' info=' Additional challenge to show'/> + <field name='defresult' type='const char *' info=' Optional default result'/> + <field name='result' type='char *' info=' Result to be filled with user response (or defresult)'/> + <field name='resultlen' type='unsigned int' info=' Length of the result'/> + </struct> + <typedef name='virConnectCredentialPtr' file='libvirt' type='virConnectCredential *'/> + <typedef name='virConnectCredentialType' file='libvirt' type='enum'/> + <typedef name='virConnectFlags' file='libvirt' type='enum'/> <typedef name='virConnectPtr' file='libvirt' type='virConnect *'> <info>a virConnectPtr is pointer to a virConnect private structure, this is the type used to reference a connection to the Xen Hypervisor in the API.</info> </typedef> @@ -468,6 +519,7 @@ </struct> <typedef name='virVcpuInfoPtr' file='libvirt' type='virVcpuInfo *'/> <typedef name='virVcpuState' file='libvirt' type='enum'/> + <variable name='virConnectAuthPtrDefault' file='libvirt' type='virConnectAuthPtr'/> <function name='virConnCopyLastError' file='virterror' module='virterror'> <info>Copy the content of the last error caught on that connection One will need to free the result with virResetError()</info> <return type='int' info='0 if no error was found and the error code otherwise and -1 in case of parameter error.'/> @@ -491,6 +543,13 @@ <arg name='userData' type='void *' info='pointer to the user data provided in the handler callback'/> <arg name='handler' type='virErrorFunc' info='the function to get called in case of error or NULL'/> </function> + <functype name='virConnectAuthCallbackPtr' file='libvirt' module='libvirt'> + <info></info> + <return type='int' info=''/> + <arg name='cred' type='virConnectCredentialPtr' info=''/> + <arg name='ncred' type='unsigned int' info=''/> + <arg name='cbdata' type='void *' info=''/> + </functype> <function name='virConnectClose' file='libvirt' module='libvirt'> <info>This function closes the connection to the Hypervisor. This should not be called if further interaction with the Hypervisor are needed especially if there is running domain which need further monitoring by the application.</info> <return type='int' info='0 in case of success or -1 in case of error.'/> @@ -580,6 +639,13 @@ <info>This function should be called first to get a connection to the Hypervisor and xen store</info> <return type='virConnectPtr' info='a pointer to the hypervisor connection or NULL in case of error URIs are documented at http://libvirt.org/uri.html'/> <arg name='name' type='const char *' info='URI of the hypervisor'/> + </function> + <function name='virConnectOpenAuth' file='libvirt' module='libvirt'> + <info>This function should be called first to get a connection to the Hypervisor. If neccessary, authentication will be performed fetching credentials via the callback</info> + <return type='virConnectPtr' info='a pointer to the hypervisor connection or NULL in case of error URIs are documented at http://libvirt.org/uri.html'/> + <arg name='name' type='const char *' info='URI of the hypervisor'/> + <arg name='auth' type='virConnectAuthPtr' info='Authenticate callback parameters'/> + <arg name='flags' type='int' info='Open flags'/> </function> <function name='virConnectOpenReadOnly' file='libvirt' module='libvirt'> <info>This function should be called first to get a restricted connection to the libbrary functionalities. The set of APIs usable are then restricted on the available methods to control the domains.</info> diff -r ec2d8c632fd9 docs/libvirt-refs.xml --- a/docs/libvirt-refs.xml Wed Nov 28 15:00:45 2007 -0500 +++ b/docs/libvirt-refs.xml Wed Nov 28 20:35:23 2007 -0500 @@ -2,9 +2,19 @@ <apirefs name='libvirt'> <references> <reference name='LIBVIR_VERSION_NUMBER' href='html/libvirt-libvirt.html#LIBVIR_VERSION_NUMBER'/> + <reference name='VIR_CONNECT_RO' href='html/libvirt-libvirt.html#VIR_CONNECT_RO'/> <reference name='VIR_COPY_CPUMAP' href='html/libvirt-libvirt.html#VIR_COPY_CPUMAP'/> <reference name='VIR_CPU_MAPLEN' href='html/libvirt-libvirt.html#VIR_CPU_MAPLEN'/> <reference name='VIR_CPU_USABLE' href='html/libvirt-libvirt.html#VIR_CPU_USABLE'/> + <reference name='VIR_CRED_AUTHNAME' href='html/libvirt-libvirt.html#VIR_CRED_AUTHNAME'/> + <reference name='VIR_CRED_CNONCE' href='html/libvirt-libvirt.html#VIR_CRED_CNONCE'/> + <reference name='VIR_CRED_ECHOPROMPT' href='html/libvirt-libvirt.html#VIR_CRED_ECHOPROMPT'/> + <reference name='VIR_CRED_EXTERNAL' href='html/libvirt-libvirt.html#VIR_CRED_EXTERNAL'/> + <reference name='VIR_CRED_LANGUAGE' href='html/libvirt-libvirt.html#VIR_CRED_LANGUAGE'/> + <reference name='VIR_CRED_NOECHOPROMPT' href='html/libvirt-libvirt.html#VIR_CRED_NOECHOPROMPT'/> + <reference name='VIR_CRED_PASSPHRASE' href='html/libvirt-libvirt.html#VIR_CRED_PASSPHRASE'/> + <reference name='VIR_CRED_REALM' href='html/libvirt-libvirt.html#VIR_CRED_REALM'/> + <reference name='VIR_CRED_USERNAME' href='html/libvirt-libvirt.html#VIR_CRED_USERNAME'/> <reference name='VIR_DOMAIN_BLOCKED' href='html/libvirt-libvirt.html#VIR_DOMAIN_BLOCKED'/> <reference name='VIR_DOMAIN_CRASHED' href='html/libvirt-libvirt.html#VIR_DOMAIN_CRASHED'/> <reference name='VIR_DOMAIN_NONE' href='html/libvirt-libvirt.html#VIR_DOMAIN_NONE'/> @@ -22,6 +32,7 @@ <reference name='VIR_DOMAIN_SHUTOFF' href='html/libvirt-libvirt.html#VIR_DOMAIN_SHUTOFF'/> <reference name='VIR_DOMAIN_XML_INACTIVE' href='html/libvirt-libvirt.html#VIR_DOMAIN_XML_INACTIVE'/> <reference name='VIR_DOMAIN_XML_SECURE' href='html/libvirt-libvirt.html#VIR_DOMAIN_XML_SECURE'/> + <reference name='VIR_ERR_AUTH_FAILED' href='html/libvirt-virterror.html#VIR_ERR_AUTH_FAILED'/> <reference name='VIR_ERR_CALL_FAILED' href='html/libvirt-virterror.html#VIR_ERR_CALL_FAILED'/> <reference name='VIR_ERR_CONF_SYNTAX' href='html/libvirt-virterror.html#VIR_ERR_CONF_SYNTAX'/> <reference name='VIR_ERR_DOM_EXIST' href='html/libvirt-virterror.html#VIR_ERR_DOM_EXIST'/> @@ -97,6 +108,8 @@ <reference name='VIR_VCPU_OFFLINE' href='html/libvirt-libvirt.html#VIR_VCPU_OFFLINE'/> <reference name='VIR_VCPU_RUNNING' href='html/libvirt-libvirt.html#VIR_VCPU_RUNNING'/> <reference name='VIR_WAR_NO_NETWORK' href='html/libvirt-virterror.html#VIR_WAR_NO_NETWORK'/> + <reference name='_virConnectAuth' href='html/libvirt-libvirt.html#_virConnectAuth'/> + <reference name='_virConnectCredential' href='html/libvirt-libvirt.html#_virConnectCredential'/> <reference name='_virDomainBlockStats' href='html/libvirt-libvirt.html#_virDomainBlockStats'/> <reference name='_virDomainInfo' href='html/libvirt-libvirt.html#_virDomainInfo'/> <reference name='_virDomainInterfaceStats' href='html/libvirt-libvirt.html#_virDomainInterfaceStats'/> @@ -109,7 +122,15 @@ <reference name='virConnResetLastError' href='html/libvirt-virterror.html#virConnResetLastError'/> <reference name='virConnSetErrorFunc' href='html/libvirt-virterror.html#virConnSetErrorFunc'/> <reference name='virConnect' href='html/libvirt-libvirt.html#virConnect'/> + <reference name='virConnectAuth' href='html/libvirt-libvirt.html#virConnectAuth'/> + <reference name='virConnectAuthCallbackPtr' href='html/libvirt-libvirt.html#virConnectAuthCallbackPtr'/> + <reference name='virConnectAuthPtr' href='html/libvirt-libvirt.html#virConnectAuthPtr'/> + <reference name='virConnectAuthPtrDefault' href='html/libvirt-libvirt.html#virConnectAuthPtrDefault'/> <reference name='virConnectClose' href='html/libvirt-libvirt.html#virConnectClose'/> + <reference name='virConnectCredential' href='html/libvirt-libvirt.html#virConnectCredential'/> + <reference name='virConnectCredentialPtr' href='html/libvirt-libvirt.html#virConnectCredentialPtr'/> + <reference name='virConnectCredentialType' href='html/libvirt-libvirt.html#virConnectCredentialType'/> + <reference name='virConnectFlags' href='html/libvirt-libvirt.html#virConnectFlags'/> <reference name='virConnectGetCapabilities' href='html/libvirt-libvirt.html#virConnectGetCapabilities'/> <reference name='virConnectGetHostname' href='html/libvirt-libvirt.html#virConnectGetHostname'/> <reference name='virConnectGetMaxVcpus' href='html/libvirt-libvirt.html#virConnectGetMaxVcpus'/> @@ -125,6 +146,7 @@ <reference name='virConnectNumOfDomains' href='html/libvirt-libvirt.html#virConnectNumOfDomains'/> <reference name='virConnectNumOfNetworks' href='html/libvirt-libvirt.html#virConnectNumOfNetworks'/> <reference name='virConnectOpen' href='html/libvirt-libvirt.html#virConnectOpen'/> + <reference name='virConnectOpenAuth' href='html/libvirt-libvirt.html#virConnectOpenAuth'/> <reference name='virConnectOpenReadOnly' href='html/libvirt-libvirt.html#virConnectOpenReadOnly'/> <reference name='virConnectPtr' href='html/libvirt-libvirt.html#virConnectPtr'/> <reference name='virCopyLastError' href='html/libvirt-virterror.html#virCopyLastError'/> @@ -231,9 +253,19 @@ <ref name='LIBVIR_VERSION_NUMBER'/> </letter> <letter name='V'> + <ref name='VIR_CONNECT_RO'/> <ref name='VIR_COPY_CPUMAP'/> <ref name='VIR_CPU_MAPLEN'/> <ref name='VIR_CPU_USABLE'/> + <ref name='VIR_CRED_AUTHNAME'/> + <ref name='VIR_CRED_CNONCE'/> + <ref name='VIR_CRED_ECHOPROMPT'/> + <ref name='VIR_CRED_EXTERNAL'/> + <ref name='VIR_CRED_LANGUAGE'/> + <ref name='VIR_CRED_NOECHOPROMPT'/> + <ref name='VIR_CRED_PASSPHRASE'/> + <ref name='VIR_CRED_REALM'/> + <ref name='VIR_CRED_USERNAME'/> <ref name='VIR_DOMAIN_BLOCKED'/> <ref name='VIR_DOMAIN_CRASHED'/> <ref name='VIR_DOMAIN_NONE'/> @@ -251,6 +283,7 @@ <ref name='VIR_DOMAIN_SHUTOFF'/> <ref name='VIR_DOMAIN_XML_INACTIVE'/> <ref name='VIR_DOMAIN_XML_SECURE'/> + <ref name='VIR_ERR_AUTH_FAILED'/> <ref name='VIR_ERR_CALL_FAILED'/> <ref name='VIR_ERR_CONF_SYNTAX'/> <ref name='VIR_ERR_DOM_EXIST'/> @@ -328,6 +361,8 @@ <ref name='VIR_WAR_NO_NETWORK'/> </letter> <letter name='_'> + <ref name='_virConnectAuth'/> + <ref name='_virConnectCredential'/> <ref name='_virDomainBlockStats'/> <ref name='_virDomainInfo'/> <ref name='_virDomainInterfaceStats'/> @@ -342,7 +377,15 @@ <ref name='virConnResetLastError'/> <ref name='virConnSetErrorFunc'/> <ref name='virConnect'/> + <ref name='virConnectAuth'/> + <ref name='virConnectAuthCallbackPtr'/> + <ref name='virConnectAuthPtr'/> + <ref name='virConnectAuthPtrDefault'/> <ref name='virConnectClose'/> + <ref name='virConnectCredential'/> + <ref name='virConnectCredentialPtr'/> + <ref name='virConnectCredentialType'/> + <ref name='virConnectFlags'/> <ref name='virConnectGetCapabilities'/> <ref name='virConnectGetHostname'/> <ref name='virConnectGetMaxVcpus'/> @@ -358,6 +401,7 @@ <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virConnectPtr'/> <ref name='virCopyLastError'/> @@ -472,6 +516,7 @@ </type> <type name='virConnectPtr'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainGetConnect'/> <ref name='virNetworkGetConnect'/> @@ -525,6 +570,7 @@ <ref name='virNetworkGetUUID'/> </type> <type name='unsigned int'> + <ref name='virConnectAuthCallbackPtr'/> <ref name='virDomainCreateLinux'/> <ref name='virDomainPinVcpu'/> <ref name='virDomainReboot'/> @@ -541,6 +587,12 @@ </type> <type name='unsigned long long *'> <ref name='virNodeGetCellsFreeMemory'/> + </type> + <type name='virConnectAuthPtr'> + <ref name='virConnectOpenAuth'/> + </type> + <type name='virConnectCredentialPtr'> + <ref name='virConnectAuthCallbackPtr'/> </type> <type name='virConnectPtr'> <ref name='virConnCopyLastError'/> @@ -662,6 +714,7 @@ </type> <type name='void *'> <ref name='virConnSetErrorFunc'/> + <ref name='virConnectAuthCallbackPtr'/> <ref name='virErrorFunc'/> <ref name='virSetErrorFunc'/> </type> @@ -669,9 +722,19 @@ <files> <file name='libvirt'> <ref name='LIBVIR_VERSION_NUMBER'/> + <ref name='VIR_CONNECT_RO'/> <ref name='VIR_COPY_CPUMAP'/> <ref name='VIR_CPU_MAPLEN'/> <ref name='VIR_CPU_USABLE'/> + <ref name='VIR_CRED_AUTHNAME'/> + <ref name='VIR_CRED_CNONCE'/> + <ref name='VIR_CRED_ECHOPROMPT'/> + <ref name='VIR_CRED_EXTERNAL'/> + <ref name='VIR_CRED_LANGUAGE'/> + <ref name='VIR_CRED_NOECHOPROMPT'/> + <ref name='VIR_CRED_PASSPHRASE'/> + <ref name='VIR_CRED_REALM'/> + <ref name='VIR_CRED_USERNAME'/> <ref name='VIR_DOMAIN_BLOCKED'/> <ref name='VIR_DOMAIN_CRASHED'/> <ref name='VIR_DOMAIN_NONE'/> @@ -699,6 +762,8 @@ <ref name='VIR_VCPU_BLOCKED'/> <ref name='VIR_VCPU_OFFLINE'/> <ref name='VIR_VCPU_RUNNING'/> + <ref name='_virConnectAuth'/> + <ref name='_virConnectCredential'/> <ref name='_virDomainBlockStats'/> <ref name='_virDomainInfo'/> <ref name='_virDomainInterfaceStats'/> @@ -706,7 +771,15 @@ <ref name='_virSchedParameter'/> <ref name='_virVcpuInfo'/> <ref name='virConnect'/> + <ref name='virConnectAuth'/> + <ref name='virConnectAuthCallbackPtr'/> + <ref name='virConnectAuthPtr'/> + <ref name='virConnectAuthPtrDefault'/> <ref name='virConnectClose'/> + <ref name='virConnectCredential'/> + <ref name='virConnectCredentialPtr'/> + <ref name='virConnectCredentialType'/> + <ref name='virConnectFlags'/> <ref name='virConnectGetCapabilities'/> <ref name='virConnectGetHostname'/> <ref name='virConnectGetMaxVcpus'/> @@ -722,6 +795,7 @@ <ref name='virConnectNumOfDomains'/> <ref name='virConnectNumOfNetworks'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virConnectPtr'/> <ref name='virDomain'/> @@ -812,6 +886,7 @@ <ref name='virVcpuState'/> </file> <file name='virterror'> + <ref name='VIR_ERR_AUTH_FAILED'/> <ref name='VIR_ERR_CALL_FAILED'/> <ref name='VIR_ERR_CONF_SYNTAX'/> <ref name='VIR_ERR_DOM_EXIST'/> @@ -912,11 +987,17 @@ <ref name='VIR_GET_CPUMAP'/> <ref name='virConnectOpenReadOnly'/> </word> + <word name='Additional'> + <ref name='_virConnectCredential'/> + </word> <word name='After'> <ref name='virDomainSave'/> </word> <word name='Attempt'> <ref name='virDomainMigrate'/> + </word> + <word name='Authenticate'> + <ref name='virConnectOpenAuth'/> </word> </letter> <letter name='B'> @@ -956,6 +1037,9 @@ <ref name='virDomainPinVcpu'/> <ref name='virDomainSetVcpus'/> </word> + <word name='Callback'> + <ref name='_virConnectAuth'/> + </word> <word name='Change'> <ref name='virDomainSetSchedulerParameters'/> </word> @@ -1058,6 +1142,7 @@ <ref name='virConnectGetType'/> <ref name='virConnectGetVersion'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> </word> </letter> <letter name='I'> @@ -1085,8 +1170,14 @@ <word name='Launch'> <ref name='virDomainCreateLinux'/> </word> + <word name='Length'> + <ref name='_virConnectCredential'/> + </word> <word name='Linux'> <ref name='virDomainCreateLinux'/> + </word> + <word name='List'> + <ref name='_virConnectAuth'/> </word> </letter> <letter name='M'> @@ -1132,12 +1223,22 @@ <ref name='virGetVersion'/> </word> <word name='One'> + <ref name='_virConnectCredential'/> <ref name='virConnCopyLastError'/> <ref name='virCopyLastError'/> <ref name='virDomainGetVcpus'/> </word> + <word name='Open'> + <ref name='virConnectOpenAuth'/> + </word> + <word name='Optional'> + <ref name='_virConnectCredential'/> + </word> </letter> <letter name='P'> + <word name='Prompt'> + <ref name='_virConnectCredential'/> + </word> <word name='Provide'> <ref name='virConnGetLastError'/> <ref name='virDomainGetXMLDesc'/> @@ -1179,6 +1280,9 @@ <ref name='virResetError'/> <ref name='virResetLastError'/> </word> + <word name='Result'> + <ref name='_virConnectCredential'/> + </word> <word name='Resume'> <ref name='virDomainResume'/> </word> @@ -1235,11 +1339,13 @@ <word name='URI'> <ref name='virConnectGetURI'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainMigrate'/> </word> <word name='URIs'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainMigrate'/> </word> @@ -1274,6 +1380,8 @@ <ref name='virDomainSuspend'/> </word> </letter> + </chunk> + <chunk name='chunk1'> <letter name='V'> <word name='VIR_COPY_CPUMAP'> <ref name='VIR_COPY_CPUMAP'/> @@ -1307,8 +1415,6 @@ <ref name='virNetworkGetUUIDString'/> </word> </letter> - </chunk> - <chunk name='chunk1'> <letter name='W'> <word name='WARNING:'> <ref name='virDomainGetConnect'/> @@ -1464,6 +1570,7 @@ <ref name='virConnectClose'/> <ref name='virConnectGetHostname'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainDestroy'/> <ref name='virDomainMigrate'/> @@ -1506,6 +1613,9 @@ <word name='attribute'> <ref name='virConnectGetMaxVcpus'/> <ref name='virDomainBlockStats'/> + </word> + <word name='authentication'> + <ref name='virConnectOpenAuth'/> </word> <word name='automatically'> <ref name='virDomainGetAutostart'/> @@ -1633,6 +1743,8 @@ <ref name='virNetworkGetUUIDString'/> </word> </letter> + </chunk> + <chunk name='chunk2'> <letter name='c'> <word name='calculate'> <ref name='VIR_NODEINFO_MAXCPUS'/> @@ -1654,6 +1766,7 @@ </word> <word name='callback'> <ref name='virConnSetErrorFunc'/> + <ref name='virConnectOpenAuth'/> <ref name='virErrorFunc'/> <ref name='virSetErrorFunc'/> </word> @@ -1661,6 +1774,7 @@ <ref name='virConnSetErrorFunc'/> <ref name='virConnectClose'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virSetErrorFunc'/> </word> @@ -1729,6 +1843,9 @@ <word name='cells'> <ref name='virNodeGetCellsFreeMemory'/> </word> + <word name='challenge'> + <ref name='_virConnectCredential'/> + </word> <word name='change'> <ref name='virDomainPinVcpu'/> <ref name='virDomainSetMaxMemory'/> @@ -1755,6 +1872,7 @@ <ref name='virGetVersion'/> </word> <word name='collect'> + <ref name='_virConnectAuth'/> <ref name='virConnectListDomains'/> <ref name='virConnectListNetworks'/> </word> @@ -1789,6 +1907,9 @@ </word> <word name='consequent'> <ref name='_virError'/> + </word> + <word name='constants'> + <ref name='_virConnectCredential'/> </word> <word name='content'> <ref name='virConnCopyLastError'/> @@ -1846,6 +1967,10 @@ <word name='create'> <ref name='virNetworkDefineXML'/> </word> + <word name='credentials'> + <ref name='_virConnectAuth'/> + <ref name='virConnectOpenAuth'/> + </word> <word name='cupmap'> <ref name='virDomainGetVcpus'/> </word> @@ -1856,8 +1981,6 @@ <ref name='virDomainCoreDump'/> </word> </letter> - </chunk> - <chunk name='chunk2'> <letter name='d'> <word name='data'> <ref name='virConnSetErrorFunc'/> @@ -1876,6 +1999,7 @@ <ref name='virNetworkGetName'/> </word> <word name='default'> + <ref name='_virConnectCredential'/> <ref name='virConnSetErrorFunc'/> <ref name='virDomainMigrate'/> <ref name='virSetErrorFunc'/> @@ -1892,6 +2016,9 @@ </word> <word name='defining'> <ref name='virConnectGetCapabilities'/> + </word> + <word name='defresult'> + <ref name='_virConnectCredential'/> </word> <word name='described'> <ref name='virDomainGetVcpus'/> @@ -1945,6 +2072,7 @@ </word> <word name='documented'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> </word> <word name='does'> @@ -1984,6 +2112,8 @@ <ref name='virDomainMigrate'/> </word> </letter> + </chunk> + <chunk name='chunk3'> <letter name='e'> <word name='each'> <ref name='virDomainBlockStats'/> @@ -2053,6 +2183,9 @@ <word name='features'> <ref name='virDomainMigrate'/> </word> + <word name='fetching'> + <ref name='virConnectOpenAuth'/> + </word> <word name='field'> <ref name='VIR_DOMAIN_SCHED_FIELD_LENGTH'/> </word> @@ -2065,6 +2198,7 @@ <ref name='virDomainSave'/> </word> <word name='filled'> + <ref name='_virConnectCredential'/> <ref name='virDomainGetSchedulerParameters'/> <ref name='virDomainGetVcpus'/> <ref name='virNodeGetCellsFreeMemory'/> @@ -2078,10 +2212,12 @@ </word> <word name='first'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virNodeGetCellsFreeMemory'/> </word> <word name='flags'> + <ref name='virConnectOpenAuth'/> <ref name='virDomainCoreDump'/> <ref name='virDomainMigrate'/> <ref name='virDomainReboot'/> @@ -2159,6 +2295,7 @@ <ref name='virConnSetErrorFunc'/> <ref name='virConnectClose'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainBlockStats'/> <ref name='virDomainCreateLinux'/> @@ -2191,6 +2328,7 @@ <word name='get'> <ref name='virConnSetErrorFunc'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainBlockStats'/> <ref name='virDomainGetInfo'/> @@ -2232,8 +2370,6 @@ <ref name='virDomainShutdown'/> </word> </letter> - </chunk> - <chunk name='chunk3'> <letter name='h'> <word name='handler'> <ref name='virConnSetErrorFunc'/> @@ -2276,11 +2412,13 @@ <word name='html'> <ref name='virConnectGetType'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> </word> <word name='http:'> <ref name='virConnectGetType'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> </word> <word name='human-readable'> @@ -2388,6 +2526,8 @@ <ref name='virNetworkLookupByUUIDString'/> </word> </letter> + </chunk> + <chunk name='chunk4'> <letter name='j'> <word name='join'> <ref name='virNetworkGetBridgeName'/> @@ -2479,6 +2619,7 @@ </word> <word name='libvirt'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainGetConnect'/> <ref name='virDomainMigrate'/> @@ -2709,8 +2850,6 @@ <ref name='_virDomainBlockStats'/> </word> </letter> - </chunk> - <chunk name='chunk4'> <letter name='n'> <word name='name'> <ref name='_virSchedParameter'/> @@ -2736,6 +2875,9 @@ </word> <word name='neccessarily'> <ref name='VIR_NODEINFO_MAXCPUS'/> + </word> + <word name='neccessary'> + <ref name='virConnectOpenAuth'/> </word> <word name='need'> <ref name='virConnCopyLastError'/> @@ -2789,6 +2931,8 @@ <ref name='virDomainSetSchedulerParameters'/> </word> </letter> + </chunk> + <chunk name='chunk5'> <letter name='o'> <word name='objects'> <ref name='virDomainSetSchedulerParameters'/> @@ -2839,6 +2983,7 @@ </word> <word name='org'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> </word> <word name='other'> @@ -2874,6 +3019,7 @@ <ref name='virDomainSetSchedulerParameters'/> </word> <word name='parameters'> + <ref name='virConnectOpenAuth'/> <ref name='virDomainGetSchedulerParameters'/> <ref name='virDomainGetSchedulerType'/> <ref name='virDomainSetSchedulerParameters'/> @@ -2908,6 +3054,9 @@ </word> <word name='perform'> <ref name='virDomainMigrate'/> + </word> + <word name='performed'> + <ref name='virConnectOpenAuth'/> </word> <word name='physical'> <ref name='VIR_CPU_MAPLEN'/> @@ -3102,6 +3251,9 @@ <ref name='virDomainSuspend'/> <ref name='virNetworkDestroy'/> </word> + <word name='response'> + <ref name='_virConnectCredential'/> + </word> <word name='restart'> <ref name='virDomainReboot'/> </word> @@ -3116,6 +3268,7 @@ <ref name='virConnectOpenReadOnly'/> </word> <word name='result'> + <ref name='_virConnectCredential'/> <ref name='virConnCopyLastError'/> <ref name='virConnectGetHostname'/> <ref name='virCopyLastError'/> @@ -3193,7 +3346,7 @@ </word> </letter> </chunk> - <chunk name='chunk5'> + <chunk name='chunk6'> <letter name='s'> <word name='same'> <ref name='virConnectGetURI'/> @@ -3242,6 +3395,7 @@ <word name='should'> <ref name='virConnectClose'/> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainBlockStats'/> <ref name='virDomainDestroy'/> @@ -3254,6 +3408,9 @@ <ref name='virNetworkDestroy'/> <ref name='virNetworkFree'/> <ref name='virNetworkSetAutostart'/> + </word> + <word name='show'> + <ref name='_virConnectCredential'/> </word> <word name='shutdown'> <ref name='virDomainDestroy'/> @@ -3428,6 +3585,7 @@ </word> <word name='supported'> <ref name='VIR_NODEINFO_MAXCPUS'/> + <ref name='_virConnectAuth'/> <ref name='virConnectGetMaxVcpus'/> <ref name='virDomainGetMaxVcpus'/> <ref name='virDomainMigrate'/> @@ -3589,7 +3747,7 @@ </word> </letter> </chunk> - <chunk name='chunk6'> + <chunk name='chunk7'> <letter name='u'> <word name='undefine'> <ref name='virDomainUndefine'/> @@ -3615,6 +3773,7 @@ </word> <word name='uri'> <ref name='virConnectOpen'/> + <ref name='virConnectOpenAuth'/> <ref name='virConnectOpenReadOnly'/> <ref name='virDomainMigrate'/> </word> @@ -3642,6 +3801,7 @@ <ref name='VIR_GET_CPUMAP'/> <ref name='VIR_UNUSE_CPU'/> <ref name='VIR_USE_CPU'/> + <ref name='_virConnectAuth'/> <ref name='_virDomainInfo'/> <ref name='_virVcpuInfo'/> <ref name='virConnectGetType'/> @@ -3658,6 +3818,7 @@ <ref name='virNetworkGetXMLDesc'/> </word> <word name='user'> + <ref name='_virConnectCredential'/> <ref name='virConnSetErrorFunc'/> <ref name='virDomainGetInfo'/> <ref name='virDomainMigrate'/> @@ -3686,6 +3847,7 @@ <ref name='virNetworkGetXMLDesc'/> </word> <word name='values'> + <ref name='_virConnectAuth'/> <ref name='virDomainGetSchedulerParameters'/> <ref name='virGetVersion'/> </word> @@ -3702,8 +3864,15 @@ <ref name='virConnectGetVersion'/> <ref name='virGetVersion'/> </word> + <word name='via'> + <ref name='virConnectOpenAuth'/> + </word> <word name='virConnCopyLastError'> <ref name='virConnGetLastError'/> + </word> + <word name='virConnectCredentialType'> + <ref name='_virConnectAuth'/> + <ref name='_virConnectCredential'/> </word> <word name='virConnectGetCapabilities'> <ref name='virDomainMigrate'/> @@ -3882,6 +4051,7 @@ <ref name='virConnCopyLastError'/> <ref name='virConnSetErrorFunc'/> <ref name='virConnectGetURI'/> + <ref name='virConnectOpenAuth'/> <ref name='virCopyLastError'/> <ref name='virDomainCoreDump'/> <ref name='virDomainGetMaxVcpus'/> @@ -3903,6 +4073,7 @@ <ref name='VIR_GET_CPUMAP'/> <ref name='VIR_UNUSE_CPU'/> <ref name='VIR_USE_CPU'/> + <ref name='_virConnectCredential'/> <ref name='virConnCopyLastError'/> <ref name='virConnectClose'/> <ref name='virConnectGetVersion'/> @@ -3935,7 +4106,7 @@ </word> </letter> </chunk> - <chunk name='chunk7'> + <chunk name='chunk8'> <letter name='x'> <word name='xen'> <ref name='virConnectOpen'/> @@ -3964,14 +4135,15 @@ </letter> </chunk> <chunks> - <chunk name='chunk0' start='A' end='V'/> - <chunk name='chunk1' start='W' end='c'/> - <chunk name='chunk2' start='d' end='g'/> - <chunk name='chunk3' start='h' end='m'/> - <chunk name='chunk4' start='n' end='r'/> - <chunk name='chunk5' start='s' end='t'/> - <chunk name='chunk6' start='u' end='w'/> - <chunk name='chunk7' start='x' end='z'/> + <chunk name='chunk0' start='A' end='U'/> + <chunk name='chunk1' start='V' end='b'/> + <chunk name='chunk2' start='c' end='d'/> + <chunk name='chunk3' start='e' end='i'/> + <chunk name='chunk4' start='j' end='n'/> + <chunk name='chunk5' start='o' end='r'/> + <chunk name='chunk6' start='s' end='t'/> + <chunk name='chunk7' start='u' end='w'/> + <chunk name='chunk8' start='x' end='z'/> </chunks> </index> </apirefs> diff -r ec2d8c632fd9 include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in Wed Nov 28 15:00:45 2007 -0500 +++ b/include/libvirt/libvirt.h.in Wed Nov 28 20:35:24 2007 -0500 @@ -1,3 +1,4 @@ + /* -*- c -*- * libvirt.h: * Summary: core interfaces for the libvirt library @@ -275,6 +276,74 @@ typedef virNodeInfo *virNodeInfoPtr; typedef virNodeInfo *virNodeInfoPtr; /** + * virConnectFlags + * + * Flags when openning a connection to a hypervisor + */ +typedef enum { + VIR_CONNECT_RO = 1, /* A readonly connection */ +} virConnectFlags; + + +typedef enum { + VIR_CRED_USERNAME = 1, /* Identity to act as */ + VIR_CRED_AUTHNAME = 2, /* Identify to authorize as */ + VIR_CRED_LANGUAGE = 3, /* RFC 1766 languages, comma separated */ + VIR_CRED_CNONCE = 4, /* client supplies a nonce */ + VIR_CRED_PASSPHRASE = 5, /* Passphrase secret */ + VIR_CRED_ECHOPROMPT = 6, /* Challenge response */ + VIR_CRED_NOECHOPROMPT = 7, /* Challenge response */ + VIR_CRED_REALM = 8, /* Authentication realm */ + VIR_CRED_EXTERNAL = 9, /* Externally managed credential */ + + /* More may be added - expect the unexpected */ +} virConnectCredentialType; + +struct _virConnectCredential { + int type; /* One of virConnectCredentialType constants */ + const char *prompt; /* Prompt to show to user */ + const char *challenge; /* Additional challenge to show */ + const char *defresult; /* Optional default result */ + char *result; /* Result to be filled with user response (or defresult) */ + unsigned int resultlen; /* Length of the result */ +}; + +typedef struct _virConnectCredential virConnectCredential; +typedef virConnectCredential *virConnectCredentialPtr; + + +/** + * virConnectCredCallbackPtr + * + * @param authtype type of authentication being performed + * @param cred array of credentials to fetch from user + * @param ncred size of cred array + * @param cbdata opaque data passed to virConnectOpenAuth + * + * When authentication requires one or more interactions, this callback + * is invoked. For each interaction supplied, data must be gathered + * from the user and filled in to the 'result' and 'resultlen' fields. + * If an interaction can not be filled, fill in NULL and 0. + * + * Return 0 if all interactions were filled, or -1 upon error + */ +typedef int (*virConnectAuthCallbackPtr)(virConnectCredentialPtr cred, + unsigned int ncred, + void *cbdata); + +struct _virConnectAuth { + int *credtype; /* List of supported virConnectCredentialType values */ + unsigned int ncredtype; + + virConnectAuthCallbackPtr cb; /* Callback used to collect credentials */ + void *cbdata; +}; + + +typedef struct _virConnectAuth virConnectAuth; +typedef virConnectAuth *virConnectAuthPtr; + +/** * VIR_UUID_BUFLEN: * * This macro provides the length of the buffer required @@ -314,6 +383,9 @@ int virInitialize (void); virConnectPtr virConnectOpen (const char *name); virConnectPtr virConnectOpenReadOnly (const char *name); +virConnectPtr virConnectOpenAuth (const char *name, + virConnectAuthPtr auth, + int flags); int virConnectClose (virConnectPtr conn); const char * virConnectGetType (virConnectPtr conn); int virConnectGetVersion (virConnectPtr conn, diff -r ec2d8c632fd9 proxy/libvirt_proxy.c --- a/proxy/libvirt_proxy.c Wed Nov 28 15:00:45 2007 -0500 +++ b/proxy/libvirt_proxy.c Wed Nov 28 20:35:24 2007 -0500 @@ -77,7 +77,7 @@ proxyInitXen(void) { priv->xshandle = NULL; priv->proxy = -1; - ret = xenHypervisorOpen(conn, NULL, 0); + ret = xenHypervisorOpen(conn, NULL, NULL, 0); if (ret < 0) { fprintf(stderr, "Failed to open Xen hypervisor\n"); return(-1); @@ -93,7 +93,7 @@ proxyInitXen(void) { fprintf(stderr, "Failed to connect to Xen daemon\n"); return(-1); } - ret = xenStoreOpen(conn, NULL, VIR_DRV_OPEN_RO); + ret = xenStoreOpen(conn, NULL, NULL, VIR_CONNECT_RO); if (ret < 0) { fprintf(stderr, "Failed to open XenStore connection"); return (-1); diff -r ec2d8c632fd9 qemud/remote.c --- a/qemud/remote.c Wed Nov 28 15:00:45 2007 -0500 +++ b/qemud/remote.c Wed Nov 28 20:35:24 2007 -0500 @@ -2062,7 +2062,7 @@ remoteDispatchAuthList (struct qemud_ser remote_auth_list_ret *ret) { ret->types.types_len = 1; - if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (remote_auth_type))) == NULL) { + if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (u_int))) == NULL) { remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, "auth types"); return -2; } diff -r ec2d8c632fd9 src/driver.h --- a/src/driver.h Wed Nov 28 15:00:45 2007 -0500 +++ b/src/driver.h Wed Nov 28 20:35:24 2007 -0500 @@ -27,11 +27,6 @@ typedef enum { } virDrvNo; -typedef enum { - /* VIR_DRV_OPEN_QUIET = 1 - removed by RWMJ */ - VIR_DRV_OPEN_RO = 2 -} virDrvOpenFlag; - /* Status codes returned from driver open call. */ typedef enum { /* Opened successfully. */ @@ -76,8 +71,9 @@ typedef enum { typedef virDrvOpenStatus (*virDrvOpen) (virConnectPtr conn, - xmlURIPtr uri, - int flags); + xmlURIPtr uri, + virConnectAuthPtr auth, + int flags); typedef int (*virDrvClose) (virConnectPtr conn); typedef int diff -r ec2d8c632fd9 src/internal.h --- a/src/internal.h Wed Nov 28 15:00:45 2007 -0500 +++ b/src/internal.h Wed Nov 28 20:35:24 2007 -0500 @@ -129,11 +129,6 @@ extern "C" { */ #define MAX_DRIVERS 10 #define MIN_XEN_GUEST_SIZE 64 /* 64 megabytes */ - -/* - * Flags for Xen connections - */ -#define VIR_CONNECT_RO 1 /** * _virConnect: diff -r ec2d8c632fd9 src/libvirt.c --- a/src/libvirt.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/libvirt.c Wed Nov 28 20:35:24 2007 -0500 @@ -395,7 +395,9 @@ virGetVersion(unsigned long *libVer, con } static virConnectPtr -do_open (const char *name, int flags) +do_open (const char *name, + virConnectAuthPtr auth, + int flags) { int i, res; virConnectPtr ret = NULL; @@ -458,7 +460,7 @@ do_open (const char *name, int flags) fprintf (stderr, "libvirt: do_open: trying driver %d (%s) ...\n", i, virDriverTab[i]->name); #endif - res = virDriverTab[i]->open (ret, uri, flags); + res = virDriverTab[i]->open (ret, uri, auth, flags); #ifdef ENABLE_DEBUG fprintf (stderr, "libvirt: do_open: driver %d %s returned %s\n", i, virDriverTab[i]->name, @@ -480,7 +482,7 @@ do_open (const char *name, int flags) } for (i = 0; i < virNetworkDriverTabCount; i++) { - res = virNetworkDriverTab[i]->open (ret, uri, flags); + res = virNetworkDriverTab[i]->open (ret, uri, auth, flags); #ifdef ENABLE_DEBUG fprintf (stderr, "libvirt: do_open: network driver %d %s returned %s\n", i, virNetworkDriverTab[i]->name, @@ -500,9 +502,8 @@ do_open (const char *name, int flags) } } - if (flags & VIR_DRV_OPEN_RO) { - ret->flags = VIR_CONNECT_RO; - } + /* Cleansing flags */ + ret->flags = flags & VIR_CONNECT_RO; xmlFreeURI (uri); @@ -531,7 +532,7 @@ virConnectOpen (const char *name) virConnectOpen (const char *name) { DEBUG("name=%s", name); - return do_open (name, 0); + return do_open (name, NULL, 0); } /** @@ -550,7 +551,30 @@ virConnectOpenReadOnly(const char *name) virConnectOpenReadOnly(const char *name) { DEBUG("name=%s", name); - return do_open (name, VIR_DRV_OPEN_RO); + return do_open (name, NULL, VIR_CONNECT_RO); +} + +/** + * virConnectOpenAuth: + * @name: URI of the hypervisor + * @auth: Authenticate callback parameters + * @flags: Open flags + * + * This function should be called first to get a connection to the + * Hypervisor. If neccessary, authentication will be performed fetching + * credentials via the callback + * + * Returns a pointer to the hypervisor connection or NULL in case of error + * + * URIs are documented at http://libvirt.org/uri.html + */ +virConnectPtr +virConnectOpenAuth(const char *name, + virConnectAuthPtr auth, + int flags) +{ + DEBUG("name=%s", name); + return do_open (name, auth, flags); } /** diff -r ec2d8c632fd9 src/openvz_driver.c --- a/src/openvz_driver.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/openvz_driver.c Wed Nov 28 20:35:24 2007 -0500 @@ -89,7 +89,12 @@ static int openvzActive(void); static int openvzActive(void); static int openvzCloseNetwork(virConnectPtr conn); static virDrvOpenStatus openvzOpenNetwork(virConnectPtr conn, - const char *name ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED); + const char *name ATTRIBUTE_UNUSED, + int *credtype ATTRIBUTE_UNUSED, + int ncredtype ATTRIBUTE_UNUSED, + virConnectAuthCallbackPtr cb ATTRIBUTE_UNUSED, + void *cbdata ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED); static virDomainPtr openvzDomainDefineXML(virConnectPtr conn, const char *xml); static virDomainPtr openvzDomainCreateLinux(virConnectPtr conn, const char *xml, @@ -551,8 +556,12 @@ bail_out5: } static virDrvOpenStatus openvzOpen(virConnectPtr conn, - const char *name, - int flags ATTRIBUTE_UNUSED) { + const char *name, + int *credtype ATTRIBUTE_UNUSED, + int ncredtype ATTRIBUTE_UNUSED, + virConnectAuthCallbackPtr cb ATTRIBUTE_UNUSED, + void *cbdata ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { struct openvz_vm *vms; /* Just check if the guy is root. Nothing really to open for OpenVZ */ @@ -693,8 +702,12 @@ static int openvzCloseNetwork(virConnect } static virDrvOpenStatus openvzOpenNetwork(virConnectPtr conn ATTRIBUTE_UNUSED, - const char *name ATTRIBUTE_UNUSED, - int flags ATTRIBUTE_UNUSED) { + const char *name ATTRIBUTE_UNUSED, + int *credtype ATTRIBUTE_UNUSED, + int ncredtype ATTRIBUTE_UNUSED, + virConnectAuthCallbackPtr cb ATTRIBUTE_UNUSED, + void *cbdata ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { return VIR_DRV_OPEN_SUCCESS; } diff -r ec2d8c632fd9 src/proxy_internal.c --- a/src/proxy_internal.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/proxy_internal.c Wed Nov 28 20:35:24 2007 -0500 @@ -29,7 +29,7 @@ static int debug = 0; static int debug = 0; static int xenProxyClose(virConnectPtr conn); -static int xenProxyOpen(virConnectPtr conn, xmlURIPtr uri, int flags); +static int xenProxyOpen(virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int flags); static int xenProxyGetVersion(virConnectPtr conn, unsigned long *hvVer); static int xenProxyNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); static char *xenProxyGetCapabilities(virConnectPtr conn); @@ -521,14 +521,17 @@ retry: * Returns 0 in case of success, and -1 in case of failure */ int -xenProxyOpen(virConnectPtr conn, xmlURIPtr uri ATTRIBUTE_UNUSED, int flags) +xenProxyOpen(virConnectPtr conn, + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags) { virProxyPacket req; int ret; int fd; xenUnifiedPrivatePtr priv; - if (!(flags & VIR_DRV_OPEN_RO)) + if (!(flags & VIR_CONNECT_RO)) return(-1); priv = (xenUnifiedPrivatePtr) conn->privateData; diff -r ec2d8c632fd9 src/qemu_driver.c --- a/src/qemu_driver.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/qemu_driver.c Wed Nov 28 20:35:24 2007 -0500 @@ -1387,6 +1387,7 @@ static int qemudMonitorCommand(struct qe static virDrvOpenStatus qemudOpen(virConnectPtr conn, xmlURIPtr uri, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { uid_t uid = getuid(); @@ -2572,6 +2573,7 @@ static virNetworkPtr qemudNetworkLookupB static virDrvOpenStatus qemudOpenNetwork(virConnectPtr conn, xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { if (!qemu_driver) return VIR_DRV_OPEN_DECLINED; diff -r ec2d8c632fd9 src/remote_internal.c --- a/src/remote_internal.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/remote_internal.c Wed Nov 28 20:35:24 2007 -0500 @@ -286,7 +286,6 @@ remoteForkDaemon(virConnectPtr conn) } -/* Must not overlap with virDrvOpenFlags */ enum virDrvOpenRemoteFlags { VIR_DRV_OPEN_REMOTE_RO = (1 << 0), VIR_DRV_OPEN_REMOTE_UNIX = (1 << 1), @@ -295,8 +294,11 @@ enum virDrvOpenRemoteFlags { }; static int -doRemoteOpen (virConnectPtr conn, struct private_data *priv, - xmlURIPtr uri, int flags) +doRemoteOpen (virConnectPtr conn, + struct private_data *priv, + xmlURIPtr uri, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags) { if (!uri || !uri->scheme) return VIR_DRV_OPEN_DECLINED; /* Decline - not a URL. */ @@ -779,7 +781,10 @@ doRemoteOpen (virConnectPtr conn, struct } static int -remoteOpen (virConnectPtr conn, xmlURIPtr uri, int flags) +remoteOpen (virConnectPtr conn, + xmlURIPtr uri, + virConnectAuthPtr auth, + int flags) { struct private_data *priv; int ret, rflags = 0; @@ -793,7 +798,7 @@ remoteOpen (virConnectPtr conn, xmlURIPt return VIR_DRV_OPEN_ERROR; } - if (flags & VIR_DRV_OPEN_RO) + if (flags & VIR_CONNECT_RO) rflags |= VIR_DRV_OPEN_REMOTE_RO; if (uri && @@ -814,7 +819,7 @@ remoteOpen (virConnectPtr conn, xmlURIPt memset(priv, 0, sizeof(struct private_data)); priv->magic = DEAD; priv->sock = -1; - ret = doRemoteOpen(conn, priv, uri, rflags); + ret = doRemoteOpen(conn, priv, uri, auth, rflags); if (ret != VIR_DRV_OPEN_SUCCESS) { conn->privateData = NULL; free(priv); @@ -2442,6 +2447,7 @@ static int static int remoteNetworkOpen (virConnectPtr conn, xmlURIPtr uri, + virConnectAuthPtr auth, int flags) { if (inside_daemon) @@ -2468,14 +2474,14 @@ remoteNetworkOpen (virConnectPtr conn, error (conn, VIR_ERR_NO_MEMORY, "struct private_data"); return VIR_DRV_OPEN_ERROR; } - if (flags & VIR_DRV_OPEN_RO) + if (flags & VIR_CONNECT_RO) rflags |= VIR_DRV_OPEN_REMOTE_RO; rflags |= VIR_DRV_OPEN_REMOTE_UNIX; memset(priv, 0, sizeof(struct private_data)); priv->magic = DEAD; priv->sock = -1; - ret = doRemoteOpen(conn, priv, uri, rflags); + ret = doRemoteOpen(conn, priv, uri, auth, rflags); if (ret != VIR_DRV_OPEN_SUCCESS) { conn->networkPrivateData = NULL; free(priv); diff -r ec2d8c632fd9 src/test.c --- a/src/test.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/test.c Wed Nov 28 20:35:24 2007 -0500 @@ -882,6 +882,7 @@ static int getNetworkIndex(virNetworkPtr static int testOpen(virConnectPtr conn, xmlURIPtr uri, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { int ret; @@ -1644,6 +1645,7 @@ static int testDomainSetSchedulerParams( static virDrvOpenStatus testOpenNetwork(virConnectPtr conn, xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { if (STRNEQ(conn->driver->name, "Test")) return VIR_DRV_OPEN_DECLINED; diff -r ec2d8c632fd9 src/xen_internal.c --- a/src/xen_internal.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xen_internal.c Wed Nov 28 20:35:24 2007 -0500 @@ -2060,6 +2060,7 @@ int int xenHypervisorOpen(virConnectPtr conn, xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { int ret; diff -r ec2d8c632fd9 src/xen_internal.h --- a/src/xen_internal.h Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xen_internal.h Wed Nov 28 20:35:24 2007 -0500 @@ -31,6 +31,7 @@ char * int xenHypervisorOpen (virConnectPtr conn, xmlURIPtr uri, + virConnectAuthPtr auth, int flags); int xenHypervisorClose (virConnectPtr conn); int xenHypervisorGetVersion (virConnectPtr conn, diff -r ec2d8c632fd9 src/xen_unified.c --- a/src/xen_unified.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xen_unified.c Wed Nov 28 20:35:24 2007 -0500 @@ -217,7 +217,7 @@ done: */ static int -xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, int flags) +xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int flags) { int i, j; xenUnifiedPrivatePtr priv; @@ -272,7 +272,7 @@ xenUnifiedOpen (virConnectPtr conn, xmlU #ifdef ENABLE_DEBUG fprintf (stderr, "libvirt: xenUnifiedOpen: trying Xen sub-driver %d\n", i); #endif - if (drivers[i]->open (conn, uri, flags) == VIR_DRV_OPEN_SUCCESS) + if (drivers[i]->open (conn, uri, auth, flags) == VIR_DRV_OPEN_SUCCESS) priv->opened[i] = 1; #ifdef ENABLE_DEBUG fprintf (stderr, "libvirt: xenUnifiedOpen: Xen sub-driver %d open %s\n", diff -r ec2d8c632fd9 src/xend_internal.c --- a/src/xend_internal.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xend_internal.c Wed Nov 28 20:35:24 2007 -0500 @@ -237,7 +237,7 @@ do_connect(virConnectPtr xend) * is rather normal, this should fallback to the proxy (or * remote) mechanism. */ - if ((getuid() == 0) || (xend->flags & VIR_DRV_OPEN_RO)) { + if ((getuid() == 0) || (xend->flags & VIR_CONNECT_RO)) { virXendError(xend, VIR_ERR_INTERNAL_ERROR, "failed to connect to xend"); } @@ -2044,7 +2044,9 @@ error: * Returns 0 in case of success, -1 in case of error. */ int -xenDaemonOpen(virConnectPtr conn, xmlURIPtr uri, +xenDaemonOpen(virConnectPtr conn, + xmlURIPtr uri, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { int ret; diff -r ec2d8c632fd9 src/xend_internal.h --- a/src/xend_internal.h Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xend_internal.h Wed Nov 28 20:35:24 2007 -0500 @@ -183,7 +183,7 @@ char *xenDaemonDomainDumpXMLByName(virCo char *xend_parse_domain_sexp(virConnectPtr conn, char *root, int xendConfigVersion); /* refactored ones */ -int xenDaemonOpen(virConnectPtr conn, xmlURIPtr uri, int flags); +int xenDaemonOpen(virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int flags); int xenDaemonClose(virConnectPtr conn); int xenDaemonGetVersion(virConnectPtr conn, unsigned long *hvVer); int xenDaemonNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); diff -r ec2d8c632fd9 src/xm_internal.c --- a/src/xm_internal.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xm_internal.c Wed Nov 28 20:35:24 2007 -0500 @@ -473,7 +473,9 @@ static int xenXMConfigCacheRefresh (virC */ int xenXMOpen (virConnectPtr conn ATTRIBUTE_UNUSED, - xmlURIPtr uri ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) { if (configCache == NULL) { configCache = virHashCreate(50); diff -r ec2d8c632fd9 src/xm_internal.h --- a/src/xm_internal.h Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xm_internal.h Wed Nov 28 20:35:24 2007 -0500 @@ -36,7 +36,7 @@ extern struct xenUnifiedDriver xenXMDriv extern struct xenUnifiedDriver xenXMDriver; int xenXMInit (void); -int xenXMOpen(virConnectPtr conn, xmlURIPtr uri, int flags); +int xenXMOpen(virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int flags); int xenXMClose(virConnectPtr conn); const char *xenXMGetType(virConnectPtr conn); int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); diff -r ec2d8c632fd9 src/xs_internal.c --- a/src/xs_internal.c Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xs_internal.c Wed Nov 28 20:35:24 2007 -0500 @@ -327,6 +327,7 @@ int int xenStoreOpen(virConnectPtr conn, xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; @@ -334,7 +335,7 @@ xenStoreOpen(virConnectPtr conn, #ifdef PROXY priv->xshandle = xs_daemon_open_readonly(); #else - if (flags & VIR_DRV_OPEN_RO) + if (flags & VIR_CONNECT_RO) priv->xshandle = xs_daemon_open_readonly(); else priv->xshandle = xs_daemon_open(); diff -r ec2d8c632fd9 src/xs_internal.h --- a/src/xs_internal.h Wed Nov 28 15:00:45 2007 -0500 +++ b/src/xs_internal.h Wed Nov 28 20:35:24 2007 -0500 @@ -22,6 +22,7 @@ int xenStoreInit (void); int xenStoreOpen (virConnectPtr conn, xmlURIPtr uri, + virConnectAuthPtr auth, int flags); int xenStoreClose (virConnectPtr conn); int xenStoreGetDomainInfo (virDomainPtr domain, -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch introduces the public & driver APIs for collecting auth credentials via a callback. This allows username/password based auth schemes to be used. ... diff -r ec2d8c632fd9 Makefile.am --- a/Makefile.am Wed Nov 28 15:00:45 2007 -0500 +++ b/Makefile.am Wed Nov 28 20:35:23 2007 -0500 @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in
-SUBDIRS = src qemud proxy include docs @PYTHON_SUBDIR@ tests po m4 scripts +SUBDIRS = include src qemud proxy docs @PYTHON_SUBDIR@ tests po m4 scripts
I guess you did the above because something in src/ or qemud/ now requires the generated libvirt.h? ...
diff -r ec2d8c632fd9 qemud/remote.c --- a/qemud/remote.c Wed Nov 28 15:00:45 2007 -0500 +++ b/qemud/remote.c Wed Nov 28 20:35:24 2007 -0500 @@ -2062,7 +2062,7 @@ remoteDispatchAuthList (struct qemud_ser remote_auth_list_ret *ret) { ret->types.types_len = 1; - if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (remote_auth_type))) == NULL) { + if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (u_int))) == NULL) {
This is a good demonstration of why using sizeof (type name) is fragile. Given code like T *var = calloc (n, sizeof (T)); I prefer this: T *var = calloc (n, sizeof (*var)); Then when type T changes, like it just did above, you don't have to be careful to update all sizeof arguments that are somehow related to "var". Not to say you should change your patch. If no one objects on principle, I'll do the global cleanup, eventually. BTW, changing the above code gives this: ret->types.types_val = calloc (ret->types.types_len, sizeof (*(ret->types.types_val))) == NULL) { which is a good argument for using a helper macro. e.g., #define CALLOC(Var,N) do { (Var) = calloc ((N), sizeof (*(Var))); } while (0) Of course, if you can have statement-expression macros (gcc-specific), you can do better, but I don't think we can depend on compiling with gcc.

On Mon, Dec 03, 2007 at 07:33:49PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch introduces the public & driver APIs for collecting auth credentials via a callback. This allows username/password based auth schemes to be used. ... diff -r ec2d8c632fd9 Makefile.am --- a/Makefile.am Wed Nov 28 15:00:45 2007 -0500 +++ b/Makefile.am Wed Nov 28 20:35:23 2007 -0500 @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in
-SUBDIRS = src qemud proxy include docs @PYTHON_SUBDIR@ tests po m4 scripts +SUBDIRS = include src qemud proxy docs @PYTHON_SUBDIR@ tests po m4 scripts
I guess you did the above because something in src/ or qemud/ now requires the generated libvirt.h?
Yes, everything in src & qemud dirs includes libvirt.h, and if you ever change libvirt.h.in, then we need to make sure libvirt.h is re-generated before compiling src & qemud otherwise we potentially mis-compile. Its a minor thing, but worth getting right to avoid suprising developers like myself in future :-)
...
diff -r ec2d8c632fd9 qemud/remote.c --- a/qemud/remote.c Wed Nov 28 15:00:45 2007 -0500 +++ b/qemud/remote.c Wed Nov 28 20:35:24 2007 -0500 @@ -2062,7 +2062,7 @@ remoteDispatchAuthList (struct qemud_ser remote_auth_list_ret *ret) { ret->types.types_len = 1; - if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (remote_auth_type))) == NULL) { + if ((ret->types.types_val = calloc (ret->types.types_len, sizeof (u_int))) == NULL) {
This is a good demonstration of why using sizeof (type name) is fragile.
Given code like T *var = calloc (n, sizeof (T)); I prefer this: T *var = calloc (n, sizeof (*var));
Then when type T changes, like it just did above, you don't have to be careful to update all sizeof arguments that are somehow related to "var".
Not to say you should change your patch. If no one objects on principle, I'll do the global cleanup, eventually.
Sounds reasonable to me. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:19:31PM +0000, Daniel P. Berrange wrote:
This patch introduces the public & driver APIs for collecting auth credentials via a callback. This allows username/password based auth schemes to be used.
This basically introduces a 3rd variant of the virConnectOpen call which takes a set of authentication parameters, and some flags.
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags);
The flags parameter allows VIR_CONNECT_RO, so there is no need for a separate ReadOnly API. The 'auth' parameter is a small struct containing a list of credentials that the calling application knows how to collect, a function pointer for the callback, and an opaque data blob.
struct _virConnectAuth { int *credtype; /* List of supported virConnectCredentialType values */ unsigned int ncredtype;
virConnectAuthCallbackPtr cb; /* Callback used to collect credentials */ void *cbdata; };
At the very least apps should support the VIR_CRED_AUTHNAME and the VIR_CRED_PASSPHRASE credential types for collecting username+password respectively. There are a bunch of other credential type, but they're for fairly niche use cases.
When the callback is invoked, it will be passed a list of virConnectCredentialPtr structs which contain details on all the credentials that the authentication mechanism needs to collect. They are passed all at once to make it easy to construct a big form in UI:
typedef int (*virConnectAuthCallbackPtr)(virConnectCredentialPtr cred, unsigned int ncred, void *cbdata);
okay, I have just two remarks about the API: that the callback seems to get passed an array of virConnectCredential as first argument, not a list of virConnectCredentialPtr. Also I hope we won't end up with too many virConnectOpenAuth flags, maybe using a long to be sure we can fit at least 64 options might be a good safe thing to do. We have not been very consistent so far in libvirt for the flags, sometime using int/unsigned int/unsigned long maybe unsigned long is safer and cleaner, i could see how various authentications options may end up growing that set over time.
The virConnectCredentialPtr struct contains a prompt which can be displayed in the UI. There may optionally be a challenge if doing a challenge/response type authentication. There may also be a default result. The application should collect a credential from the user & fill it into the 'result' field, or use the default result. If the callback returns 0, authentication will continue. If it returns -1, it will assume the user wants to cancel the auth process.
Yup looks fine to me :-) Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Dec 03, 2007 at 04:13:53PM -0500, Daniel Veillard wrote:
On Thu, Nov 29, 2007 at 05:19:31PM +0000, Daniel P. Berrange wrote:
When the callback is invoked, it will be passed a list of virConnectCredentialPtr structs which contain details on all the credentials that the authentication mechanism needs to collect. They are passed all at once to make it easy to construct a big form in UI:
typedef int (*virConnectAuthCallbackPtr)(virConnectCredentialPtr cred, unsigned int ncred, void *cbdata);
okay, I have just two remarks about the API: that the callback seems to get passed an array of virConnectCredential as first argument, not a list of virConnectCredentialPtr.
Yes, I'll fix the comment to reflect this.
Also I hope we won't end up with too many virConnectOpenAuth flags, maybe using a long to be sure we can fit at least 64 options might be a good safe thing to do. We have not been very consistent so far in libvirt for the flags, sometime using int/unsigned int/unsigned long maybe unsigned long is safer and cleaner, i could see how various authentications options may end up growing that set over time.
I don't anticipate the flags will be widely used in the future. Aside from the callbacks, and the read-only flag, all interesting params are part of the URI string. We already have a 32-bit 'int flags' param in the remote protocol wire format for the open call prior to this patch, so we can't make this new API use 64-bits without changing wire ABI. I'm not really worried though - I rather doubt we'll ever use this flags params for much. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags);
I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility. cf: http://www.libvirt.org/html/libvirt-libvirt.html#virDomainInterfaceStats Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags);
I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
cf: http://www.libvirt.org/html/libvirt-libvirt.html#virDomainInterfaceStats
Hum, honnestly, that's not my preferred way. If you really think there should be room for expansion, I would either: - add a version number to the structure and allocator/destructor functions as part of the API (prefered) - add padding at tyhe end of the structure which could allow a future growth adding the size of the structure as the argument moves the complexity away from the library implementor to the library user, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Daniel Veillard wrote:
On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags); I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
cf: http://www.libvirt.org/html/libvirt-libvirt.html#virDomainInterfaceStats
Hum, honnestly, that's not my preferred way. If you really think there should be room for expansion, I would either: - add a version number to the structure and allocator/destructor functions as part of the API (prefered)
Hmmm ... what if they forget to set it? Now we need an init function for the structure.
- add padding at tyhe end of the structure which could allow a future growth
This incurs an unnecessary memory penalty always, and limits us to a particular maximum size which we must choose now when we don't know what extra fields we might add.
adding the size of the structure as the argument moves the complexity away from the library implementor to the library user,
Well, it means they must append "sizeof (*auth)" to their list of arguments, which is hardly a lot of complexity. There _is_ much more complexity in the library however -- cf the implementation of virDomainBlockStats & virDomainInterfaceStats for details. Remember also the need for forwards compatibility: program which was compiled against an old version of the library, dynamically links to a new version of the library. If the structure has increased in size in the meantime, then having the caller pass the size means that the library can do the right thing and only fill in the first fields. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Tue, Dec 04, 2007 at 03:50:46PM +0000, Richard W.M. Jones wrote:
Daniel Veillard wrote:
On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags); I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
cf: http://www.libvirt.org/html/libvirt-libvirt.html#virDomainInterfaceStats
Hum, honnestly, that's not my preferred way. If you really think there should be room for expansion, I would either: - add a version number to the structure and allocator/destructor functions as part of the API (prefered)
Hmmm ... what if they forget to set it? Now we need an init function for the structure.
yes that's why I suggested an allocator, it would set the version.
- add padding at tyhe end of the structure which could allow a future growth
This incurs an unnecessary memory penalty always, and limits us to a particular maximum size which we must choose now when we don't know what extra fields we might add.
I don't like this much either, but I think the GNOME guys did this for libxml2 I usually provide allocator/deallocators.
adding the size of the structure as the argument moves the complexity away from the library implementor to the library user,
Well, it means they must append "sizeof (*auth)" to their list of arguments, which is hardly a lot of complexity. There _is_ much more
until they just add a numeric value ... and that will break just as well. trying to minimize the cases where the API user can do the wrong thing is an important goal :-) . There that's something which seems to work but will break later, that's why I prefer an allocator if we plan to change sizes.
Remember also the need for forwards compatibility: program which was compiled against an old version of the library, dynamically links to a new version of the library. If the structure has increased in size in the meantime, then having the caller pass the size means that the library can do the right thing and only fill in the first fields.
The version + allocator have that property too, except it's the library code which takes care of things. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Dec 04, 2007 at 10:46:50AM -0500, Daniel Veillard wrote:
On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags);
I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
cf: http://www.libvirt.org/html/libvirt-libvirt.html#virDomainInterfaceStats
Hum, honnestly, that's not my preferred way. If you really think there should be room for expansion, I would either: - add a version number to the structure and allocator/destructor functions as part of the API (prefered)
This adds more complexity and the single sizeof(*auth) param IMHO.
- add padding at tyhe end of the structure which could allow a future growth
adding the size of the structure as the argument moves the complexity away from the library implementor to the library user,
It doesn't really - the user just has to add sizeof(*auth) as an arg and all the rest of the complexity is in the library internals. In fact it doesn't need to add any complexity, because we could make it a compile time macro #define virConnectOpenAuth(name,auth,flags) virConnectOpenAuth(name,auth,sizeof(*auth),flags) Dan -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Dec 04, 2007 at 04:06:32PM +0000, Daniel P. Berrange wrote:
On Tue, Dec 04, 2007 at 10:46:50AM -0500, Daniel Veillard wrote:
On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags);
I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
cf: http://www.libvirt.org/html/libvirt-libvirt.html#virDomainInterfaceStats
Hum, honnestly, that's not my preferred way. If you really think there should be room for expansion, I would either: - add a version number to the structure and allocator/destructor functions as part of the API (prefered)
This adds more complexity and the single sizeof(*auth) param IMHO.
not really, I could argue that most users would prefer a function to allocate the data needed, instead of doing the right malloc/sizeof/return check themselve
- add padding at tyhe end of the structure which could allow a future growth
adding the size of the structure as the argument moves the complexity away from the library implementor to the library user,
It doesn't really - the user just has to add sizeof(*auth) as an arg and all the rest of the complexity is in the library internals.
and I'm afraid they won't naturally do sizeof(*auth) and could very well put a number in situ
In fact it doesn't need to add any complexity, because we could make it a compile time macro
#define virConnectOpenAuth(name,auth,flags) virConnectOpenAuth(name,auth,sizeof(*auth),flags)
Okay except I really prefer macros all uppercase, even if I see why you would do this. It's not a function anymore, you could hide or expose it, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags);
I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
So we never came to any conclusion on this... - Pass sizeof(*auth) in as a param - Have allocator/destroy functions for the auth param - Use the flags field to indicate use/presence of extra auth members IMHO, this whole discussion is rather over the top. The 'auth' param is merely a convenience to hold the data related to the auth callback, ie what credentials the auth callback support, the callback function pointer and the opaque data. There aren't going to be any params added to this struct, because the list of the params to the auth callback function itself is fixed. Adding an allocator / sizeof doesn't magically let us change the callback function signature. NB, there are lots of extra args & data that an application will want to pass in when opening a connection, and we may well be adding more of these in the future. We already have a place where we pass these in - the URI string. This is used to choose authentication mechansim for example. Any data required by authentication mechansims is fetched asynchronously via the calllback. So we already have 3 places to future proof ourselves - the URI string, and the set of credentials that can be fetched via the callback, and the 'flags' param of which we only use 1 bit. Adding sizeof or allocators to give a 4th way to future proof is frankly overkill and uneccessary complication. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
On Tue, Dec 04, 2007 at 12:57:39PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
virConnectPtr virConnectOpenAuth (const char *name, virConnectAuthPtr auth, int flags); I'm a fan of callers passing in the size of the structure (as they see it). Allows the structure to be expanded in future, and if done right can allow both forwards and backwards compatibility.
So we never came to any conclusion on this...
- Pass sizeof(*auth) in as a param - Have allocator/destroy functions for the auth param - Use the flags field to indicate use/presence of extra auth members
IMHO, this whole discussion is rather over the top. The 'auth' param is merely a convenience to hold the data related to the auth callback, ie what credentials the auth callback support, the callback function pointer and the opaque data. There aren't going to be any params added to this struct, because the list of the params to the auth callback function itself is fixed. Adding an allocator / sizeof doesn't magically let us change the callback function signature. [...]
OK, well in that case don't add anything to virConnectOpenAuth. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

This patch implements internal driver API for authentication callbacks in the remote driver. It is basically a bunch of code to bridge from the libvirt public API for auth/credentials and the SASL equivalent API. The libvirt API is very close in style to the SASL API so it is a fairly mechanical mapping. It also adds support for the VIR_CRED_EXTERNAL credential when doing PolicyKit auth. As with Kerberos where you have to kinit externally, credentials for policykit are also setup externally. The application will typically via to org.gnome.PolicyKit.Manager via DBus to get authentication credentials. The VIR_CRED_EXTERNAL credential in libvirt is basically a prompt for the application to do this external setup process. We also add a global 'virConnectAuthPtrDefault' variable to the public API. This is a simple implementation of the authentication callbacks which is able to be used by any command line based applications. It will prompt on STDOUT, and read from STDIN. It is used by virsh as the default authentication callback implementation. The URI syntax for the remote driver is also extended to support a new parameter 'auth'. This lets the client app specifiy a preferred auth scheme it wants to use. It is possible for the server to support many auth schemes on a single socket, so this lets an app prioritize. With the SASL auth scheme it also lets the client app choose one of many SASL mechanisms. If 'auth' is omitted in the URI, the remote driver will just choose the first auth mechanism reported by the server. eg mandate use of PolicyKit qemu:///system?auth=polkit eg mandate use of SASL, with Kerberos qemu+tcp://somehost/system?auth=sasl.GSSAPI eg mandate use of SASL with username/password qemu+tcp://somehost/system?auth=sasl.DIGEST-MD5 include/libvirt/libvirt.h.in | 2 src/internal.h | 2 src/libvirt.c | 72 ++++++ src/libvirt_sym.version | 3 src/remote_internal.c | 477 ++++++++++++++++++++++++++++++++++--------- src/virsh.c | 8 6 files changed, 465 insertions(+), 99 deletions(-) diff -r 98599cfde033 include/libvirt/libvirt.h.in --- a/include/libvirt/libvirt.h.in Wed Nov 28 23:01:08 2007 -0500 +++ b/include/libvirt/libvirt.h.in Wed Nov 28 23:29:58 2007 -0500 @@ -342,6 +342,8 @@ struct _virConnectAuth { typedef struct _virConnectAuth virConnectAuth; typedef virConnectAuth *virConnectAuthPtr; + +extern virConnectAuthPtr virConnectAuthPtrDefault; /** * VIR_UUID_BUFLEN: diff -r 98599cfde033 src/internal.h --- a/src/internal.h Wed Nov 28 23:01:08 2007 -0500 +++ b/src/internal.h Wed Nov 28 23:29:58 2007 -0500 @@ -50,7 +50,9 @@ extern "C" { #define STRNEQ(a,b) (strcmp((a),(b)) != 0) #define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) #define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) +#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) #define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) +#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) #ifndef __GNUC__ #define __FUNCTION__ __func__ diff -r 98599cfde033 src/libvirt.c --- a/src/libvirt.c Wed Nov 28 23:01:08 2007 -0500 +++ b/src/libvirt.c Wed Nov 28 23:29:58 2007 -0500 @@ -62,6 +62,78 @@ static int initialized = 0; #define DEBUG0 #define DEBUG(fs,...) #endif /* !ENABLE_DEBUG */ + +static int virConnectAuthCallbackDefault(virConnectCredentialPtr cred, + unsigned int ncred, + void *cbdata ATTRIBUTE_UNUSED) { + int i; + + for (i = 0 ; i < ncred ; i++) { + char buf[1024]; + char *bufptr = buf; + + printf("%s:", cred[i].prompt); + fflush(stdout); + switch (cred[i].type) { + case VIR_CRED_USERNAME: + case VIR_CRED_AUTHNAME: + case VIR_CRED_ECHOPROMPT: + case VIR_CRED_REALM: + if (!fgets(buf, sizeof(buf), stdin)) { + return -1; + } + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + break; + + case VIR_CRED_PASSPHRASE: + case VIR_CRED_NOECHOPROMPT: + bufptr = getpass(""); + break; + } + + if (STREQ(bufptr, "") && cred[i].defresult) + cred[i].result = strdup(cred[i].defresult); + else + cred[i].result = strdup(bufptr); + if (!cred[i].result) + return -1; + cred[i].resultlen = strlen(cred[i].result); + } + + return 0; +} + +/* Don't typically want VIR_CRED_USERNAME. It enables you to authenticate + * as one user, and act as another. It just results in annoying + * prompts for the username twice & is very rarely what you want + */ +static int virConnectCredTypeDefault[] = { + VIR_CRED_AUTHNAME, + VIR_CRED_ECHOPROMPT, + VIR_CRED_REALM, + VIR_CRED_PASSPHRASE, + VIR_CRED_NOECHOPROMPT, +}; + +static virConnectAuth virConnectAuthDefault = { + virConnectCredTypeDefault, + sizeof(virConnectCredTypeDefault)/sizeof(int), + virConnectAuthCallbackDefault, + NULL, +}; + +/* + * virConnectAuthPtrDefault + * + * A default implementation of the authentication callbacks. This + * implementation is suitable for command line based tools. It will + * prompt for username, passwords, realm and one time keys as needed. + * It will print on STDOUT, and read from STDIN. If this is not + * suitable for the application's needs an alternative implementation + * should be provided. + */ +virConnectAuthPtr virConnectAuthPtrDefault = &virConnectAuthDefault; /** * virInitialize: diff -r 98599cfde033 src/libvirt_sym.version --- a/src/libvirt_sym.version Wed Nov 28 23:01:08 2007 -0500 +++ b/src/libvirt_sym.version Wed Nov 28 23:29:58 2007 -0500 @@ -3,6 +3,9 @@ virInitialize; virConnectOpen; virConnectOpenReadOnly; + virConnectOpenAuth; + virConnectAuthPtrDefault; + virConnectClose; virConnectGetType; virConnectGetVersion; diff -r 98599cfde033 src/remote_internal.c --- a/src/remote_internal.c Wed Nov 28 23:01:08 2007 -0500 +++ b/src/remote_internal.c Wed Nov 28 23:29:58 2007 -0500 @@ -111,12 +111,15 @@ static int call (virConnectPtr conn, str int flags, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret); -static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open); +static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open, + virConnectAuthPtr auth, const char *authtype); #if HAVE_SASL -static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open); +static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, + virConnectAuthPtr auth, const char *mech); #endif #if HAVE_POLKIT -static int remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open); +static int remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, + virConnectAuthPtr auth); #endif /* HAVE_POLKIT */ static void error (virConnectPtr conn, virErrorNumber code, const char *info); static void server_error (virConnectPtr conn, remote_error *err); @@ -342,7 +345,7 @@ doRemoteOpen (virConnectPtr conn, * get freed in the failed: path. */ char *name = 0, *command = 0, *sockname = 0, *netcat = 0, *username = 0; - char *port = 0; + char *port = 0, *authtype = 0; int no_verify = 0, no_tty = 0; char **cmd_argv = 0; @@ -401,6 +404,10 @@ doRemoteOpen (virConnectPtr conn, } else if (strcasecmp (var->name, "socket") == 0) { sockname = strdup (var->value); if (!sockname) goto out_of_memory; + var->ignore = 1; + } else if (strcasecmp (var->name, "auth") == 0) { + authtype = strdup (var->value); + if (!authtype) goto out_of_memory; var->ignore = 1; } else if (strcasecmp (var->name, "netcat") == 0) { netcat = strdup (var->value); @@ -718,7 +725,7 @@ doRemoteOpen (virConnectPtr conn, /* Try and authenticate with server */ - if (remoteAuthenticate(conn, priv, 1) == -1) + if (remoteAuthenticate(conn, priv, 1, auth, authtype) == -1) goto failed; /* Finally we can call the remote side's open function. */ @@ -737,6 +744,7 @@ doRemoteOpen (virConnectPtr conn, if (name) free (name); if (command) free (command); if (sockname) free (sockname); + if (authtype) free (authtype); if (netcat) free (netcat); if (username) free (username); if (port) free (port); @@ -2833,10 +2841,11 @@ remoteNetworkSetAutostart (virNetworkPtr /*----------------------------------------------------------------------*/ static int -remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open) +remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open, + virConnectAuthPtr auth, const char *authtype) { struct remote_auth_list_ret ret; - int err; + int err, type = REMOTE_AUTH_NONE; memset(&ret, 0, sizeof ret); err = call (conn, priv, @@ -2853,19 +2862,52 @@ remoteAuthenticate (virConnectPtr conn, if (ret.types.types_len == 0) return 0; - switch (ret.types.types_val[0]) { + if (authtype) { + int want, i; + if (STRCASEEQ(authtype, "sasl") || + STRCASEEQLEN(authtype, "sasl.", 5)) { + want = REMOTE_AUTH_SASL; + } else if (STRCASEEQ(authtype, "polkit")) { + want = REMOTE_AUTH_POLKIT; + } else { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "unknown authentication type %s", authtype); + return -1; + } + for (i = 0 ; i < ret.types.types_len ; i++) { + if (ret.types.types_val[i] == want) + type = want; + } + if (type == REMOTE_AUTH_NONE) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "requested authentication type %s rejected", authtype); + return -1; + } + } else { + type = ret.types.types_val[0]; + } + + switch (type) { #if HAVE_SASL - case REMOTE_AUTH_SASL: - if (remoteAuthSASL(conn, priv, in_open) < 0) { + case REMOTE_AUTH_SASL: { + const char *mech = NULL; + if (authtype && + STRCASEEQLEN(authtype, "sasl.", 5)) + mech = authtype + 5; + + if (remoteAuthSASL(conn, priv, in_open, auth, mech) < 0) { free(ret.types.types_val); return -1; } break; + } #endif #if HAVE_POLKIT case REMOTE_AUTH_POLKIT: - if (remoteAuthPolkit(conn, priv, in_open) < 0) { + if (remoteAuthPolkit(conn, priv, in_open, auth) < 0) { free(ret.types.types_val); return -1; } @@ -2926,13 +2968,174 @@ static char *addrToString(struct sockadd } +static int remoteAuthCredVir2SASL(int vircred) +{ + switch (vircred) { + case VIR_CRED_USERNAME: + return SASL_CB_USER; + + case VIR_CRED_AUTHNAME: + return SASL_CB_AUTHNAME; + + case VIR_CRED_LANGUAGE: + return SASL_CB_LANGUAGE; + + case VIR_CRED_CNONCE: + return SASL_CB_CNONCE; + + case VIR_CRED_PASSPHRASE: + return SASL_CB_PASS; + + case VIR_CRED_ECHOPROMPT: + return SASL_CB_ECHOPROMPT; + + case VIR_CRED_NOECHOPROMPT: + return SASL_CB_NOECHOPROMPT; + + case VIR_CRED_REALM: + return SASL_CB_GETREALM; + } + + return 0; +} + +static int remoteAuthCredSASL2Vir(int vircred) +{ + switch (vircred) { + case SASL_CB_USER: + return VIR_CRED_USERNAME; + + case SASL_CB_AUTHNAME: + return VIR_CRED_AUTHNAME; + + case SASL_CB_LANGUAGE: + return VIR_CRED_LANGUAGE; + + case SASL_CB_CNONCE: + return VIR_CRED_CNONCE; + + case SASL_CB_PASS: + return VIR_CRED_PASSPHRASE; + + case SASL_CB_ECHOPROMPT: + return VIR_CRED_ECHOPROMPT; + + case SASL_CB_NOECHOPROMPT: + return VIR_CRED_NOECHOPROMPT; + + case SASL_CB_GETREALM: + return VIR_CRED_REALM; + } + + return 0; +} + +/* + * @param credtype array of credential types client supports + * @param ncredtype size of credtype array + * @return the SASL callback structure, or NULL on error + * + * Build up the SASL callback structure. We register one callback for + * each credential type that the libvirt client indicated they support. + * We explicitly leav the callback function pointer at NULL though, + * because we don't actually want to get SASL callbacks triggered. + * Instead, we want the start/step functions to return SASL_INTERACT. + * This lets us give the libvirt client a list of all required + * credentials in one go, rather than triggering the callback one + * credential at a time, + */ +static sasl_callback_t *remoteAuthMakeCallbacks(int *credtype, int ncredtype) +{ + sasl_callback_t *cbs = calloc(ncredtype+1, sizeof (sasl_callback_t)); + int i, n; + if (!cbs) { + return NULL; + } + + for (i = 0, n = 0 ; i < ncredtype ; i++) { + int id = remoteAuthCredVir2SASL(credtype[i]); + if (id != 0) + cbs[n++].id = id; + /* Don't fill proc or context fields of sasl_callback_t + * because we want to use interactions instead */ + } + cbs[n].id = 0; + return cbs; +} + + +/* + * @param interact SASL interactions required + * @param cred populated with libvirt credential metadata + * @return the size of the cred array returned + * + * Builds up an array of libvirt credential structs, populating + * with data from the SASL interaction struct. These two structs + * are basically a 1-to-1 copy of each other. + */ +static int remoteAuthMakeCredentials(sasl_interact_t *interact, + virConnectCredentialPtr *cred) +{ + int ninteract; + if (!cred) + return -1; + + for (ninteract = 0 ; interact[ninteract].id != 0 ; ninteract++) + ; /* empty */ + + *cred = calloc(ninteract, sizeof(virConnectCredential)); + if (!*cred) + return -1; + + for (ninteract = 0 ; interact[ninteract].id != 0 ; ninteract++) { + (*cred)[ninteract].type = remoteAuthCredSASL2Vir(interact[ninteract].id); + if (!(*cred)[ninteract].type) { + free(*cred); + return -1; + } + if (interact[ninteract].challenge) + (*cred)[ninteract].challenge = interact[ninteract].challenge; + (*cred)[ninteract].prompt = interact[ninteract].prompt; + if (interact[ninteract].defresult) + (*cred)[ninteract].defresult = interact[ninteract].defresult; + (*cred)[ninteract].result = NULL; + } + + return ninteract; +} + +static void remoteAuthFreeCredentials(virConnectCredentialPtr cred, + int ncred) +{ + int i; + for (i = 0 ; i < ncred ; i++) + free(cred[i].result); + free(cred); +} + + +/* + * @param cred the populated libvirt credentials + * @param interact the SASL interactions to fill in results for + * + * Fills the SASL interactions with the result from the libvirt + * callbacks + */ +static void remoteAuthFillInteract(virConnectCredentialPtr cred, + sasl_interact_t *interact) +{ + int ninteract; + for (ninteract = 0 ; interact[ninteract].id != 0 ; ninteract++) { + interact[ninteract].result = cred[ninteract].result; + interact[ninteract].len = cred[ninteract].resultlen; + } +} + /* Perform the SASL authentication process - * - * XXX fetch credentials from a libvirt client app callback - * XXX better mechanism negotiation ? Ask client app ? */ static int -remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open) +remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open, + virConnectAuthPtr auth, const char *wantmech) { sasl_conn_t *saslconn = NULL; sasl_security_properties_t secprops; @@ -2942,15 +3145,21 @@ remoteAuthSASL (virConnectPtr conn, stru remote_auth_sasl_step_args pargs; remote_auth_sasl_step_ret pret; const char *clientout; - char *serverin; + char *serverin = NULL; unsigned int clientoutlen, serverinlen; const char *mech; int err, complete; struct sockaddr_storage sa; socklen_t salen; - char *localAddr, *remoteAddr; + char *localAddr = NULL, *remoteAddr = NULL; const void *val; sasl_ssf_t ssf; + sasl_callback_t *saslcb = NULL; + sasl_interact_t *interact = NULL; + virConnectCredentialPtr cred = NULL; + int ncred = 0; + int ret = -1; + const char *mechlist; remoteDebug(priv, "Client initialize SASL authentication"); /* Sets up the SASL library as a whole */ @@ -2960,7 +3169,7 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "failed to initialize SASL library: %d (%s)", err, sasl_errstring(err, NULL, NULL)); - return -1; + goto cleanup; } /* Get local address in form IPADDR:PORT */ @@ -2970,11 +3179,10 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "failed to get sock address %d (%s)", errno, strerror(errno)); - return -1; - } - if ((localAddr = addrToString(&sa, salen)) == NULL) { - return -1; - } + goto cleanup; + } + if ((localAddr = addrToString(&sa, salen)) == NULL) + goto cleanup; /* Get remote address in form IPADDR:PORT */ salen = sizeof(sa); @@ -2983,30 +3191,29 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "failed to get peer address %d (%s)", errno, strerror(errno)); - free(localAddr); - return -1; - } - if ((remoteAddr = addrToString(&sa, salen)) == NULL) { - free(localAddr); - return -1; - } + goto cleanup; + } + if ((remoteAddr = addrToString(&sa, salen)) == NULL) + goto cleanup; + + if ((saslcb = remoteAuthMakeCallbacks(auth->credtype, auth->ncredtype)) == NULL) + goto cleanup; /* Setup a handle for being a client */ err = sasl_client_new("libvirt", priv->hostname, localAddr, remoteAddr, - NULL, /* XXX callbacks */ + saslcb, SASL_SUCCESS_DATA, &saslconn); - free(localAddr); - free(remoteAddr); + if (err != SASL_OK) { __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "Failed to create SASL client context: %d (%s)", err, sasl_errstring(err, NULL, NULL)); - return -1; + goto cleanup; } /* Initialize some connection props we care about */ @@ -3018,8 +3225,7 @@ remoteAuthSASL (virConnectPtr conn, stru __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "invalid cipher size for TLS session"); - sasl_dispose(&saslconn); - return -1; + goto cleanup; } ssf *= 8; /* key size is bytes, sasl wants bits */ @@ -3030,8 +3236,7 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "cannot set external SSF %d (%s)", err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&saslconn); - return -1; + goto cleanup; } } @@ -3050,36 +3255,70 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_INTERNAL_ERROR, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "cannot set security props %d (%s)", err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&saslconn); - return -1; + goto cleanup; } /* First call is to inquire about supported mechanisms in the server */ memset (&iret, 0, sizeof iret); if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_INIT, (xdrproc_t) xdr_void, (char *)NULL, - (xdrproc_t) xdr_remote_auth_sasl_init_ret, (char *) &iret) != 0) { - sasl_dispose(&saslconn); - return -1; /* virError already set by call */ - } - - + (xdrproc_t) xdr_remote_auth_sasl_init_ret, (char *) &iret) != 0) + goto cleanup; + + + mechlist = iret.mechlist; + if (wantmech) { + if (strstr(mechlist, wantmech) == NULL) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "SASL mechanism %s not supported by server", wantmech); + free(iret.mechlist); + goto cleanup; + } + mechlist = wantmech; + } + restart: /* Start the auth negotiation on the client end first */ - remoteDebug(priv, "Client start negotiation mechlist '%s'", iret.mechlist); + remoteDebug(priv, "Client start negotiation mechlist '%s'", mechlist); err = sasl_client_start(saslconn, - iret.mechlist, - NULL, /* XXX interactions */ + mechlist, + &interact, &clientout, &clientoutlen, &mech); - if (err != SASL_OK && err != SASL_CONTINUE) { + if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "Failed to start SASL negotiation: %d (%s)", err, sasl_errdetail(saslconn)); free(iret.mechlist); - sasl_dispose(&saslconn); - return -1; + goto cleanup; + } + + /* Need to gather some credentials from the client */ + if (err == SASL_INTERACT) { + if (cred) { + remoteAuthFreeCredentials(cred, ncred); + cred = NULL; + } + if ((ncred = + remoteAuthMakeCredentials(interact, &cred)) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to make auth credentials"); + free(iret.mechlist); + goto cleanup; + } + /* Run the authentication callback */ + if ((*(auth->cb))(cred, ncred, auth->cbdata) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to collect auth credentials"); + goto cleanup; + return -1; + } + remoteAuthFillInteract(cred, interact); + goto restart; } free(iret.mechlist); @@ -3087,8 +3326,7 @@ remoteAuthSASL (virConnectPtr conn, stru __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "SASL negotiation data too long: %d bytes", clientoutlen); - sasl_dispose(&saslconn); - return -1; + goto cleanup; } /* NB, distinction of NULL vs "" is *critical* in SASL */ memset(&sargs, 0, sizeof sargs); @@ -3102,10 +3340,8 @@ remoteAuthSASL (virConnectPtr conn, stru memset (&sret, 0, sizeof sret); if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_START, (xdrproc_t) xdr_remote_auth_sasl_start_args, (char *) &sargs, - (xdrproc_t) xdr_remote_auth_sasl_start_ret, (char *) &sret) != 0) { - sasl_dispose(&saslconn); - return -1; /* virError already set by call */ - } + (xdrproc_t) xdr_remote_auth_sasl_start_ret, (char *) &sret) != 0) + goto cleanup; complete = sret.complete; /* NB, distinction of NULL vs "" is *critical* in SASL */ @@ -3118,20 +3354,48 @@ remoteAuthSASL (virConnectPtr conn, stru * Even if the server has completed, the client must *always* do at least one step * in this loop to verify the server isn't lieing about something. Mutual auth */ for (;;) { + restep: err = sasl_client_step(saslconn, serverin, serverinlen, - NULL, /* XXX interactions */ + &interact, &clientout, &clientoutlen); - if (serverin) free(serverin); - if (err != SASL_OK && err != SASL_CONTINUE) { + if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "Failed SASL step: %d (%s)", err, sasl_errdetail(saslconn)); - sasl_dispose(&saslconn); - return -1; + goto cleanup; + } + /* Need to gather some credentials from the client */ + if (err == SASL_INTERACT) { + if (cred) { + remoteAuthFreeCredentials(cred, ncred); + cred = NULL; + } + if ((ncred = remoteAuthMakeCredentials(interact, &cred)) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to make auth credentials"); + goto cleanup; + return -1; + } + /* Run the authentication callback */ + if ((*(auth->cb))(cred, ncred, auth->cbdata) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to collect auth credentials"); + goto cleanup; + return -1; + } + remoteAuthFillInteract(cred, interact); + goto restep; + } + + if (serverin) { + free(serverin); + serverin = NULL; } remoteDebug(priv, "Client step result %d. Data %d bytes %p", err, clientoutlen, clientout); @@ -3150,10 +3414,8 @@ remoteAuthSASL (virConnectPtr conn, stru memset (&pret, 0, sizeof pret); if (call (conn, priv, in_open, REMOTE_PROC_AUTH_SASL_STEP, (xdrproc_t) xdr_remote_auth_sasl_step_args, (char *) &pargs, - (xdrproc_t) xdr_remote_auth_sasl_step_ret, (char *) &pret) != 0) { - sasl_dispose(&saslconn); - return -1; /* virError already set by call */ - } + (xdrproc_t) xdr_remote_auth_sasl_step_ret, (char *) &pret) != 0) + goto cleanup; complete = pret.complete; /* NB, distinction of NULL vs "" is *critical* in SASL */ @@ -3178,8 +3440,7 @@ remoteAuthSASL (virConnectPtr conn, stru VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "cannot query SASL ssf on connection %d (%s)", err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&saslconn); - return -1; + goto cleanup; } ssf = *(const int *)val; remoteDebug(priv, "SASL SSF value %d", ssf); @@ -3187,16 +3448,65 @@ remoteAuthSASL (virConnectPtr conn, stru __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, "negotiation SSF %d was not strong enough", ssf); - sasl_dispose(&saslconn); - return -1; + goto cleanup; } } remoteDebug(priv, "SASL authentication complete"); priv->saslconn = saslconn; - return 0; + ret = 0; + + cleanup: + if (localAddr) free(localAddr); + if (remoteAddr) free(remoteAddr); + if (serverin) free(serverin); + + free(saslcb); + remoteAuthFreeCredentials(cred, ncred); + if (ret != 0 && saslconn) + sasl_dispose(&saslconn); + return ret; } #endif /* HAVE_SASL */ + + +#if HAVE_POLKIT +/* Perform the PolicyKit authentication process + */ +static int +remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, + virConnectAuthPtr auth) +{ + remote_auth_polkit_ret ret; + virConnectCredential cred = { + VIR_CRED_EXTERNAL, + conn->flags & VIR_CONNECT_RO ? "org.libvirt.unix.monitor" : "org.libvirt.unix.manage", + "PolicyKit", + NULL, + NULL, + 0, + }; + remoteDebug(priv, "Client initialize PolicyKit authentication"); + + /* Run the authentication callback */ + if (auth && auth->cb && (*(auth->cb))(&cred, 1, auth->cbdata) < 0) { + __virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, + VIR_ERR_AUTH_FAILED, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, + "Failed to collect auth credentials"); + return -1; + } + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_POLKIT, + (xdrproc_t) xdr_void, (char *)NULL, + (xdrproc_t) xdr_remote_auth_polkit_ret, (char *) &ret) != 0) { + return -1; /* virError already set by call */ + } + + remoteDebug(priv, "PolicyKit authentication complete"); + return 0; +} +#endif /* HAVE_POLKIT */ /*----------------------------------------------------------------------*/ @@ -3461,29 +3771,6 @@ really_write_sasl (virConnectPtr conn, s return really_write_buf(conn, priv, in_open, output, outputlen); } #endif - - -#if HAVE_POLKIT -/* Perform the PolicyKit authentication process - */ -static int -remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open) -{ - remote_auth_polkit_ret ret; - - remoteDebug(priv, "Client initialize PolicyKit authentication"); - - memset (&ret, 0, sizeof ret); - if (call (conn, priv, in_open, REMOTE_PROC_AUTH_POLKIT, - (xdrproc_t) xdr_void, (char *)NULL, - (xdrproc_t) xdr_remote_auth_polkit_ret, (char *) &ret) != 0) { - return -1; /* virError already set by call */ - } - - remoteDebug(priv, "PolicyKit authentication complete"); - return 0; -} -#endif /* HAVE_POLKIT */ static int really_write (virConnectPtr conn, struct private_data *priv, diff -r 98599cfde033 src/virsh.c --- a/src/virsh.c Wed Nov 28 23:01:08 2007 -0500 +++ b/src/virsh.c Wed Nov 28 23:29:58 2007 -0500 @@ -4516,10 +4516,10 @@ vshInit(vshControl * ctl) !strcasecmp(ctl->name, "xen")) && ctl->uid != 0) ctl->readonly = 1; - if (!ctl->readonly) - ctl->conn = virConnectOpen(ctl->name); - else - ctl->conn = virConnectOpenReadOnly(ctl->name); + ctl->conn = virConnectOpenAuth(ctl->name, + virConnectAuthPtrDefault, + ctl->readonly ? VIR_CONNECT_RO : 0); + /* This is not necessarily fatal. All the individual commands check * vshConnectionUsability, except ones which don't need a connection -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch implements internal driver API for authentication callbacks in the remote driver. It is basically a bunch of code to bridge from the libvirt public API for auth/credentials and the SASL equivalent API. The libvirt API is very close in style to the SASL API so it is a fairly mechanical mapping.
Hi Dan, I have to start by admitting I've never used or even looked at policykit before.
diff -r 98599cfde033 src/libvirt.c --- a/src/libvirt.c Wed Nov 28 23:01:08 2007 -0500 +++ b/src/libvirt.c Wed Nov 28 23:29:58 2007 -0500 @@ -62,6 +62,78 @@ static int initialized = 0; #define DEBUG0 #define DEBUG(fs,...) #endif /* !ENABLE_DEBUG */ + +static int virConnectAuthCallbackDefault(virConnectCredentialPtr cred, + unsigned int ncred, + void *cbdata ATTRIBUTE_UNUSED) { + int i; + + for (i = 0 ; i < ncred ; i++) { + char buf[1024]; + char *bufptr = buf; + + printf("%s:", cred[i].prompt); + fflush(stdout);
If printf or fflush fails, this probably return -1.
+ switch (cred[i].type) { + case VIR_CRED_USERNAME: + case VIR_CRED_AUTHNAME: + case VIR_CRED_ECHOPROMPT: + case VIR_CRED_REALM: + if (!fgets(buf, sizeof(buf), stdin)) { + return -1; + }
A consistency nit: you might want to make EOF be treated the same as an empty name. Currently typing EOF to fgets (which then returns NULL) makes this code return -1, while entering an empty line doesn't. At least with passwords, I confirmed that cvs login treats ^D like the empty string. On the other hand, an empty name probably makes no sense in many applications.
+ if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + break; + + case VIR_CRED_PASSPHRASE: + case VIR_CRED_NOECHOPROMPT: + bufptr = getpass("");
If getpass fails (it'd return NULL), return -1. Otherwise, the following strdup would segfault.
+ break; + } + + if (STREQ(bufptr, "") && cred[i].defresult) + cred[i].result = strdup(cred[i].defresult); + else + cred[i].result = strdup(bufptr); + if (!cred[i].result) + return -1; + cred[i].resultlen = strlen(cred[i].result); + } + + return 0; +}

On Mon, Dec 03, 2007 at 11:53:04PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch implements internal driver API for authentication callbacks in the remote driver. It is basically a bunch of code to bridge from the libvirt public API for auth/credentials and the SASL equivalent API. The libvirt API is very close in style to the SASL API so it is a fairly mechanical mapping.
I have to start by admitting I've never used or even looked at policykit before.
diff -r 98599cfde033 src/libvirt.c --- a/src/libvirt.c Wed Nov 28 23:01:08 2007 -0500 +++ b/src/libvirt.c Wed Nov 28 23:29:58 2007 -0500 @@ -62,6 +62,78 @@ static int initialized = 0; #define DEBUG0 #define DEBUG(fs,...) #endif /* !ENABLE_DEBUG */ + +static int virConnectAuthCallbackDefault(virConnectCredentialPtr cred, + unsigned int ncred, + void *cbdata ATTRIBUTE_UNUSED) { + int i; + + for (i = 0 ; i < ncred ; i++) { + char buf[1024]; + char *bufptr = buf; + + printf("%s:", cred[i].prompt); + fflush(stdout);
If printf or fflush fails, this probably return -1.
Good point.
+ switch (cred[i].type) { + case VIR_CRED_USERNAME: + case VIR_CRED_AUTHNAME: + case VIR_CRED_ECHOPROMPT: + case VIR_CRED_REALM: + if (!fgets(buf, sizeof(buf), stdin)) { + return -1; + }
A consistency nit: you might want to make EOF be treated the same as an empty name. Currently typing EOF to fgets (which then returns NULL) makes this code return -1, while entering an empty line doesn't. At least with passwords, I confirmed that cvs login treats ^D like the empty string.
On the other hand, an empty name probably makes no sense in many applications.
Actually an empty name does make sense for things like REALM/AUTHNAME as it potentially means use the default value. Its upto underlying SASL impl whether is likes emptry names or not, so we should be treating EOF as same as "".
+ if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + break; + + case VIR_CRED_PASSPHRASE: + case VIR_CRED_NOECHOPROMPT: + bufptr = getpass("");
If getpass fails (it'd return NULL), return -1. Otherwise, the following strdup would segfault.
Yep, will do. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Mon, Dec 03, 2007 at 11:53:04PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This patch implements internal driver API for authentication callbacks in the remote driver. It is basically a bunch of code to bridge from the libvirt public API for auth/credentials and the SASL equivalent API. The libvirt API is very close in style to the SASL API so it is a fairly mechanical mapping.
Hi Dan,
I have to start by admitting I've never used or even looked at policykit before.
diff -r 98599cfde033 src/libvirt.c --- a/src/libvirt.c Wed Nov 28 23:01:08 2007 -0500 +++ b/src/libvirt.c Wed Nov 28 23:29:58 2007 -0500 @@ -62,6 +62,78 @@ static int initialized = 0; #define DEBUG0 #define DEBUG(fs,...) #endif /* !ENABLE_DEBUG */ + +static int virConnectAuthCallbackDefault(virConnectCredentialPtr cred, + unsigned int ncred, + void *cbdata ATTRIBUTE_UNUSED) { + int i; + + for (i = 0 ; i < ncred ; i++) { + char buf[1024]; + char *bufptr = buf; + + printf("%s:", cred[i].prompt); + fflush(stdout);
If printf or fflush fails, this probably return -1.
+ switch (cred[i].type) { + case VIR_CRED_USERNAME: + case VIR_CRED_AUTHNAME: + case VIR_CRED_ECHOPROMPT: + case VIR_CRED_REALM: + if (!fgets(buf, sizeof(buf), stdin)) { + return -1; + }
A consistency nit: you might want to make EOF be treated the same as an empty name. Currently typing EOF to fgets (which then returns NULL) makes this code return -1, while entering an empty line doesn't. At least with passwords, I confirmed that cvs login treats ^D like the empty string.
On the other hand, an empty name probably makes no sense in many applications.
+ if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + break; + + case VIR_CRED_PASSPHRASE: + case VIR_CRED_NOECHOPROMPT: + bufptr = getpass("");
If getpass fails (it'd return NULL), return -1. Otherwise, the following strdup would segfault.
Committed with this & the other suggested fixes all included. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback. This short example code illustrates the use of the API from a python app which wants to support username/password credentials only. from getpass import getpass mydata = "Hello" def getCred(creds, data): print "yes" print str(creds) for cred in creds: print cred[1] + ": ", if cred[0] == libvirt.VIR_CRED_AUTHNAME: data = sys.stdin.readline() data = data[0:len(data)-1] cred[4] = data elif cred[0] == libvirt.VIR_CRED_PASSPHRASE: cred[4] = getpass("") else: return -1 return 0 uri = "qemu+tcp://localhost/system" conn = libvirt.openAuth(uri, [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE], getCred, mydata], 0) print str(conn.listDefinedDomains()) python/libvir.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ python/libvir.py | 6 ++ src/qemu_conf.c | 8 +-- 3 files changed, 129 insertions(+), 4 deletions(-) diff -r 8a79678f789f python/libvir.c --- a/python/libvir.c Wed Nov 28 23:01:30 2007 -0500 +++ b/python/libvir.c Wed Nov 28 23:29:40 2007 -0500 @@ -241,6 +241,124 @@ libvirt_virRegisterErrorHandler(ATTRIBUT return (py_retval); } +static int virConnectCredCallbackWrapper(virConnectCredentialPtr cred, + unsigned int ncred, + void *cbdata) { + PyObject *list; + PyObject *pycred; + PyObject *pyauth = (PyObject *)cbdata; + PyObject *pycbdata; + PyObject *pycb; + PyObject *pyret; + int ret = -1, i; + + LIBVIRT_ENSURE_THREAD_STATE; + + pycb = PyList_GetItem(pyauth, 1); + pycbdata = PyList_GetItem(pyauth, 2); + + list = PyTuple_New(2); + pycred = PyTuple_New(ncred); + for (i = 0 ; i < ncred ; i++) { + PyObject *pycreditem; + pycreditem = PyList_New(5); + Py_INCREF(Py_None); + PyTuple_SetItem(pycred, i, pycreditem); + PyList_SetItem(pycreditem, 0, PyInt_FromLong((long) cred[i].type)); + PyList_SetItem(pycreditem, 1, PyString_FromString(cred[i].prompt)); + if (cred[i].challenge) { + PyList_SetItem(pycreditem, 2, PyString_FromString(cred[i].challenge)); + } else { + Py_INCREF(Py_None); + PyList_SetItem(pycreditem, 2, Py_None); + } + if (cred[i].defresult) { + PyList_SetItem(pycreditem, 3, PyString_FromString(cred[i].defresult)); + } else { + Py_INCREF(Py_None); + PyList_SetItem(pycreditem, 3, Py_None); + } + PyList_SetItem(pycreditem, 4, Py_None); + } + + PyTuple_SetItem(list, 0, pycred); + Py_XINCREF(pycbdata); + PyTuple_SetItem(list, 1, pycbdata); + + PyErr_Clear(); + pyret = PyEval_CallObject(pycb, list); + if (PyErr_Occurred()) + goto cleanup; + + ret = PyLong_AsLong(pyret); + if (ret == 0) { + for (i = 0 ; i < ncred ; i++) { + PyObject *pycreditem; + PyObject *pyresult; + char *result = NULL; + pycreditem = PyTuple_GetItem(pycred, i); + pyresult = PyList_GetItem(pycreditem, 4); + if (pyresult != Py_None) + result = PyString_AsString(pyresult); + if (result != NULL) { + cred[i].result = strdup(result); + cred[i].resultlen = strlen(result); + } else { + cred[i].result = NULL; + cred[i].resultlen = 0; + } + } + } + + cleanup: + Py_XDECREF(list); + Py_XDECREF(pyret); + + LIBVIRT_RELEASE_THREAD_STATE; + + return ret; +} + + +static PyObject * +libvirt_virConnectOpenAuth(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + virConnectPtr c_retval; + char * name; + int flags; + PyObject *pyauth; + PyObject *pycredcb; + PyObject *pycredtype; + virConnectAuth auth; + + if (!PyArg_ParseTuple(args, (char *)"zOi:virConnectOpenAuth", &name, &pyauth, &flags)) + return(NULL); + + pycredtype = PyList_GetItem(pyauth, 0); + pycredcb = PyList_GetItem(pyauth, 1); + + auth.ncredtype = PyList_Size(pycredtype); + if (auth.ncredtype) { + int i; + auth.credtype = malloc(sizeof(int) * auth.ncredtype); + for (i = 0 ; i < auth.ncredtype ; i++) { + PyObject *val; + val = PyList_GetItem(pycredtype, i); + auth.credtype[i] = (int)PyLong_AsLong(val); + } + } + auth.cb = pycredcb ? virConnectCredCallbackWrapper : NULL; + auth.cbdata = pyauth; + + LIBVIRT_BEGIN_ALLOW_THREADS; + + c_retval = virConnectOpenAuth(name, &auth, flags); + LIBVIRT_END_ALLOW_THREADS; + py_retval = libvirt_virConnectPtrWrap((virConnectPtr) c_retval); + return(py_retval); +} + + /************************************************************************ * * * Wrappers for functions where generator fails * @@ -729,6 +847,7 @@ static PyMethodDef libvirtMethods[] = { #include "libvirt-export.c" {(char *) "virGetVersion", libvirt_virGetVersion, METH_VARARGS, NULL}, {(char *) "virDomainFree", libvirt_virDomainFree, METH_VARARGS, NULL}, + {(char *) "virConnectOpenAuth", libvirt_virConnectOpenAuth, METH_VARARGS, NULL}, {(char *) "virConnectClose", libvirt_virConnectClose, METH_VARARGS, NULL}, {(char *) "virConnectListDomainsID", libvirt_virConnectListDomainsID, METH_VARARGS, NULL}, {(char *) "virConnectListDefinedDomains", libvirt_virConnectListDefinedDomains, METH_VARARGS, NULL}, diff -r 8a79678f789f python/libvir.py --- a/python/libvir.py Wed Nov 28 23:01:30 2007 -0500 +++ b/python/libvir.py Wed Nov 28 23:29:40 2007 -0500 @@ -83,6 +83,12 @@ def registerErrorHandler(f, ctx): Returns 1 in case of success.""" return libvirtmod.virRegisterErrorHandler(f,ctx) +def openAuth(uri, auth, flags): + ret = libvirtmod.virConnectOpenAuth(uri, auth, flags) + if ret is None:raise libvirtError('virConnectOpenAuth() failed') + return virConnect(_obj=ret) + + # # Return library version. # diff -r 8a79678f789f src/qemu_conf.c --- a/src/qemu_conf.c Wed Nov 28 23:01:30 2007 -0500 +++ b/src/qemu_conf.c Wed Nov 28 23:29:40 2007 -0500 @@ -2667,7 +2667,7 @@ checkLinkPointsTo(const char *checkLink, char *p; strncpy(dir, checkLink, PATH_MAX); - dir[PATH_MAX] = '\0'; + dir[PATH_MAX-1] = '\0'; if (!(p = strrchr(dir, '/'))) { qemudLog(QEMUD_WARN, "Symlink path '%s' is not absolute", checkLink); @@ -2685,7 +2685,7 @@ checkLinkPointsTo(const char *checkLink, } strncpy(dest, tmp, PATH_MAX); - dest[PATH_MAX] = '\0'; + dest[PATH_MAX-1] = '\0'; } /* canonicalize both paths */ @@ -2693,14 +2693,14 @@ checkLinkPointsTo(const char *checkLink, qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s", dest, strerror(errno)); strncpy(real, dest, PATH_MAX); - real[PATH_MAX] = '\0'; + real[PATH_MAX-1] = '\0'; } if (!realpath(checkDest, checkReal)) { qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s", checkDest, strerror(errno)); strncpy(checkReal, checkDest, PATH_MAX); - checkReal[PATH_MAX] = '\0'; + checkReal[PATH_MAX-1] = '\0'; } /* compare */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch against current virt-manager illustrates how the python API is used in a real world application to support Kerberos, Username/Password, and PolicyKit authentication Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:20:52PM +0000, Daniel P. Berrange wrote:
This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback.
src/qemu_conf.c | 8 +--
Opps, this wasn't mean to sneak in this patch. This fixes a wierd crash case which happens occassionally when libvirtd starts up. For me it only ever happens if i run configure with a --prefix pointing to a non-default value, eg /home/berrange/src/xen/libvirt Interestingly valgrind fails to catch this off-by-1 flaw
diff -r 8a79678f789f src/qemu_conf.c --- a/src/qemu_conf.c Wed Nov 28 23:01:30 2007 -0500 +++ b/src/qemu_conf.c Wed Nov 28 23:29:40 2007 -0500 @@ -2667,7 +2667,7 @@ checkLinkPointsTo(const char *checkLink, char *p;
strncpy(dir, checkLink, PATH_MAX); - dir[PATH_MAX] = '\0'; + dir[PATH_MAX-1] = '\0';
if (!(p = strrchr(dir, '/'))) { qemudLog(QEMUD_WARN, "Symlink path '%s' is not absolute", checkLink); @@ -2685,7 +2685,7 @@ checkLinkPointsTo(const char *checkLink, }
strncpy(dest, tmp, PATH_MAX); - dest[PATH_MAX] = '\0'; + dest[PATH_MAX-1] = '\0'; }
/* canonicalize both paths */ @@ -2693,14 +2693,14 @@ checkLinkPointsTo(const char *checkLink, qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s", dest, strerror(errno)); strncpy(real, dest, PATH_MAX); - real[PATH_MAX] = '\0'; + real[PATH_MAX-1] = '\0'; }
if (!realpath(checkDest, checkReal)) { qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s", checkDest, strerror(errno)); strncpy(checkReal, checkDest, PATH_MAX); - checkReal[PATH_MAX] = '\0'; + checkReal[PATH_MAX-1] = '\0'; }
/* compare */
-- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
-- Libvir-list mailing list Libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:20:52PM +0000, Daniel P. Berrange wrote:
This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback.
src/qemu_conf.c | 8 +--
Opps, this wasn't mean to sneak in this patch. This fixes a wierd crash case which happens occassionally when libvirtd starts up. For me it only ever happens if i run configure with a --prefix pointing to a non-default value, eg /home/berrange/src/xen/libvirt
Interestingly valgrind fails to catch this off-by-1 flaw [...]
+1 for this fix. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Thu, Nov 29, 2007 at 06:02:47PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:20:52PM +0000, Daniel P. Berrange wrote:
This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback.
src/qemu_conf.c | 8 +--
Opps, this wasn't mean to sneak in this patch. This fixes a wierd crash case which happens occassionally when libvirtd starts up. For me it only ever happens if i run configure with a --prefix pointing to a non-default value, eg /home/berrange/src/xen/libvirt
Interestingly valgrind fails to catch this off-by-1 flaw [...]
+1 for this fix.
Ok, this change is committed. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback.
This short example code illustrates the use of the API from a python app which wants to support username/password credentials only.
from getpass import getpass mydata = "Hello"
def getCred(creds, data): print "yes" print str(creds) for cred in creds: print cred[1] + ": ", if cred[0] == libvirt.VIR_CRED_AUTHNAME: data = sys.stdin.readline() data = data[0:len(data)-1] cred[4] = data elif cred[0] == libvirt.VIR_CRED_PASSPHRASE: cred[4] = getpass("") else: return -1 return 0
uri = "qemu+tcp://localhost/system" conn = libvirt.openAuth(uri, [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE], getCred, mydata], 0)
print str(conn.listDefinedDomains())
Nice. One nit:
diff -r 8a79678f789f python/libvir.c --- a/python/libvir.c Wed Nov 28 23:01:30 2007 -0500 +++ b/python/libvir.c Wed Nov 28 23:29:40 2007 -0500 ... +static PyObject * +libvirt_virConnectOpenAuth(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { ... + auth.ncredtype = PyList_Size(pycredtype); + if (auth.ncredtype) { + int i; + auth.credtype = malloc(sizeof(int) * auth.ncredtype);
Check for malloc failure: if (!auth.credtype) return NULL;
+ for (i = 0 ; i < auth.ncredtype ; i++) { + PyObject *val; + val = PyList_GetItem(pycredtype, i); + auth.credtype[i] = (int)PyLong_AsLong(val); + } + } + auth.cb = pycredcb ? virConnectCredCallbackWrapper : NULL; + auth.cbdata = pyauth;

On Tue, Dec 04, 2007 at 10:10:28PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback. diff -r 8a79678f789f python/libvir.c
--- a/python/libvir.c Wed Nov 28 23:01:30 2007 -0500 +++ b/python/libvir.c Wed Nov 28 23:29:40 2007 -0500 ... +static PyObject * +libvirt_virConnectOpenAuth(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { ... + auth.ncredtype = PyList_Size(pycredtype); + if (auth.ncredtype) { + int i; + auth.credtype = malloc(sizeof(int) * auth.ncredtype);
Check for malloc failure:
if (!auth.credtype) return NULL;
THanks for that. Will make the change.. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Tue, Dec 04, 2007 at 10:10:28PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
This adds a binding for the virConnectOpenAuth() api in the python API. This allows a python function to be used as the callback.
This short example code illustrates the use of the API from a python app which wants to support username/password credentials only.
from getpass import getpass mydata = "Hello"
def getCred(creds, data): print "yes" print str(creds) for cred in creds: print cred[1] + ": ", if cred[0] == libvirt.VIR_CRED_AUTHNAME: data = sys.stdin.readline() data = data[0:len(data)-1] cred[4] = data elif cred[0] == libvirt.VIR_CRED_PASSPHRASE: cred[4] = getpass("") else: return -1 return 0
uri = "qemu+tcp://localhost/system" conn = libvirt.openAuth(uri, [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE], getCred, mydata], 0)
print str(conn.listDefinedDomains())
Nice. One nit:
diff -r 8a79678f789f python/libvir.c --- a/python/libvir.c Wed Nov 28 23:01:30 2007 -0500 +++ b/python/libvir.c Wed Nov 28 23:29:40 2007 -0500 ... +static PyObject * +libvirt_virConnectOpenAuth(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { ... + auth.ncredtype = PyList_Size(pycredtype); + if (auth.ncredtype) { + int i; + auth.credtype = malloc(sizeof(int) * auth.ncredtype);
Check for malloc failure:
if (!auth.credtype) return NULL;
Committed with this fix included. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Testing with valgrind found a number of memory leaks in all sorts of places in the drivers and virsh. When freeing a virConnect object, we don't clear any set virError object, potentially leaking the message strings. When removing IPtables rules we inexplicably strdup() one of the params to strcmp. We fail to free the TLS x509 certificate directory path when shutting down the QEMU driver When the remote driver gets an error back from the server for some reason it strdup()s the strings passed into the __virRaiseError function, and then fails to free them. When virsh shuts down it doesn't clear the global error variable, nor free its URI string, and sometimes fails to release the virConnectPtr object hash.c | 1 + iptables.c | 2 +- qemu_driver.c | 3 +++ remote_internal.c | 15 ++++----------- virsh.c | 15 +++++++++++---- 5 files changed, 20 insertions(+), 16 deletions(-) diff -r efdf8bc4ae07 src/hash.c --- a/src/hash.c Mon Nov 26 09:58:42 2007 -0500 +++ b/src/hash.c Mon Nov 26 10:20:17 2007 -0500 @@ -726,6 +726,7 @@ virFreeConnect(virConnectPtr conn) { virHashFree(conn->networks, (virHashDeallocator) virNetworkFreeName); if (conn->hashes_mux != NULL) xmlFreeMutex(conn->hashes_mux); + virResetError(&conn->err); free(conn); return(0); } diff -r efdf8bc4ae07 src/iptables.c --- a/src/iptables.c Mon Nov 26 09:58:42 2007 -0500 +++ b/src/iptables.c Mon Nov 26 10:20:17 2007 -0500 @@ -246,7 +246,7 @@ iptRulesRemove(iptRules *rules, int i; for (i = 0; i < rules->nrules; i++) - if (!strcmp(rules->rules[i].rule, strdup(rule))) + if (!strcmp(rules->rules[i].rule, rule)) break; if (i >= rules->nrules) diff -r efdf8bc4ae07 src/qemu_driver.c --- a/src/qemu_driver.c Mon Nov 26 09:58:42 2007 -0500 +++ b/src/qemu_driver.c Mon Nov 26 10:20:17 2007 -0500 @@ -338,6 +338,9 @@ qemudShutdown(void) { free(qemu_driver->networkConfigDir); if (qemu_driver->networkAutostartDir) free(qemu_driver->networkAutostartDir); + + if (qemu_driver->vncTLSx509certdir) + free(qemu_driver->vncTLSx509certdir); if (qemu_driver->brctl) brShutdown(qemu_driver->brctl); diff -r efdf8bc4ae07 src/remote_internal.c --- a/src/remote_internal.c Mon Nov 26 09:58:42 2007 -0500 +++ b/src/remote_internal.c Mon Nov 26 10:20:17 2007 -0500 @@ -3808,20 +3808,13 @@ server_error (virConnectPtr conn, remote dom = err->dom ? get_nonnull_domain (conn, *err->dom) : NULL; net = err->net ? get_nonnull_network (conn, *err->net) : NULL; - /* These strings are nullable. OK to ignore the return value - * of strdup since these strings are informational. - */ - char *str1 = err->str1 ? strdup (*err->str1) : NULL; - char *str2 = err->str2 ? strdup (*err->str2) : NULL; - char *str3 = err->str3 ? strdup (*err->str3) : NULL; - - char *message = err->message ? strdup (*err->message) : NULL; - __virRaiseError (conn, dom, net, err->domain, err->code, err->level, - str1, str2, str3, + err->str1 ? *err->str1 : NULL, + err->str2 ? *err->str2 : NULL, + err->str3 ? *err->str3 : NULL, err->int1, err->int2, - "%s", message); + "%s", err->message ? *err->message : NULL); } /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -r efdf8bc4ae07 src/virsh.c --- a/src/virsh.c Mon Nov 26 09:58:42 2007 -0500 +++ b/src/virsh.c Mon Nov 26 10:20:17 2007 -0500 @@ -4779,7 +4779,8 @@ vshDeinit(vshControl * ctl) vshDeinit(vshControl * ctl) { vshCloseLogFile(ctl); - + if (ctl->name) + free(ctl->name); if (ctl->conn) { if (virConnectClose(ctl->conn) != 0) { ctl->conn = NULL; /* prevent recursive call from vshError() */ @@ -4787,6 +4788,8 @@ vshDeinit(vshControl * ctl) "failed to disconnect from the hypervisor"); } } + virResetLastError(); + return TRUE; } @@ -4984,11 +4987,15 @@ main(int argc, char **argv) ctl->name = strdup(defaultConn); } - if (!vshParseArgv(ctl, argc, argv)) + if (!vshParseArgv(ctl, argc, argv)) { + vshDeinit(ctl); exit(EXIT_FAILURE); - - if (!vshInit(ctl)) + } + + if (!vshInit(ctl)) { + vshDeinit(ctl); exit(EXIT_FAILURE); + } if (!ctl->imode) { ret = vshCommandRun(ctl, ctl->cmd); -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:21:30PM +0000, Daniel P. Berrange wrote:
Testing with valgrind found a number of memory leaks in all sorts of places in the drivers and virsh.
In case it is of use, attached is a valgrind suppressions file I used on Fedora 8 to hide the false positives it reports. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 05:21:30PM +0000, Daniel P. Berrange wrote:
Testing with valgrind found a number of memory leaks in all sorts of places in the drivers and virsh.
In case it is of use, attached is a valgrind suppressions file I used on Fedora 8 to hide the false positives it reports.
Great! I'd use it, too. Want to check it in?

On Thu, Nov 29, 2007 at 07:15:49PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 05:21:30PM +0000, Daniel P. Berrange wrote:
Testing with valgrind found a number of memory leaks in all sorts of places in the drivers and virsh.
In case it is of use, attached is a valgrind suppressions file I used on Fedora 8 to hide the false positives it reports.
Great! I'd use it, too. Want to check it in?
I was wondering about whether we should or not. I wasn't sure how portable the supression defintiions are. Different OS will have different calltraces for each of these. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 07:15:49PM +0100, Jim Meyering wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote:
On Thu, Nov 29, 2007 at 05:21:30PM +0000, Daniel P. Berrange wrote:
Testing with valgrind found a number of memory leaks in all sorts of places in the drivers and virsh.
In case it is of use, attached is a valgrind suppressions file I used on Fedora 8 to hide the false positives it reports.
Great! I'd use it, too. Want to check it in?
I was wondering about whether we should or not. I wasn't sure how portable the supression defintiions are. Different OS will have different calltraces for each of these.
Yes, they'll be different elsewhere. Some will even be different if you merely change libraries. If someone cares enough to run valgrind on a system for which they need new suppressions, they can append to the file.

Daniel P. Berrange wrote: All ACKed except:
@@ -3808,20 +3808,13 @@ server_error (virConnectPtr conn, remote dom = err->dom ? get_nonnull_domain (conn, *err->dom) : NULL; net = err->net ? get_nonnull_network (conn, *err->net) : NULL;
- /* These strings are nullable. OK to ignore the return value - * of strdup since these strings are informational. - */ - char *str1 = err->str1 ? strdup (*err->str1) : NULL; - char *str2 = err->str2 ? strdup (*err->str2) : NULL; - char *str3 = err->str3 ? strdup (*err->str3) : NULL; - - char *message = err->message ? strdup (*err->message) : NULL; - __virRaiseError (conn, dom, net, err->domain, err->code, err->level, - str1, str2, str3, + err->str1 ? *err->str1 : NULL, + err->str2 ? *err->str2 : NULL, + err->str3 ? *err->str3 : NULL, err->int1, err->int2, - "%s", message); + "%s", err->message ? *err->message : NULL); }
/* get_nonnull_domain and get_nonnull_network turn an on-wire
Don't we need to dup these strings because the following xdr_free (in the caller) will free them? Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Thu, Nov 29, 2007 at 05:28:07PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
All ACKed except:
@@ -3808,20 +3808,13 @@ server_error (virConnectPtr conn, remote dom = err->dom ? get_nonnull_domain (conn, *err->dom) : NULL; net = err->net ? get_nonnull_network (conn, *err->net) : NULL;
- /* These strings are nullable. OK to ignore the return value - * of strdup since these strings are informational. - */ - char *str1 = err->str1 ? strdup (*err->str1) : NULL; - char *str2 = err->str2 ? strdup (*err->str2) : NULL; - char *str3 = err->str3 ? strdup (*err->str3) : NULL; - - char *message = err->message ? strdup (*err->message) : NULL; - __virRaiseError (conn, dom, net, err->domain, err->code, err->level, - str1, str2, str3, + err->str1 ? *err->str1 : NULL, + err->str2 ? *err->str2 : NULL, + err->str3 ? *err->str3 : NULL, err->int1, err->int2, - "%s", message); + "%s", err->message ? *err->message : NULL); }
/* get_nonnull_domain and get_nonnull_network turn an on-wire
Don't we need to dup these strings because the following xdr_free (in the caller) will free them?
No, all the params to __virRaiseError, get strdup'd inside virRaiseError before being stored in the virError object. /* * Save the information about the error */ virResetError(to); to->conn = conn; to->dom = dom; to->net = net; to->domain = domain; to->code = code; to->message = str; to->level = level; if (str1 != NULL) to->str1 = strdup(str1); if (str2 != NULL) to->str2 = strdup(str2); if (str3 != NULL) to->str3 = strdup(str3); to->int1 = int1; to->int2 = int2; And the 'msg' arg gets re-allocated as the va-args are procssed with vsnprintf, so there's no need for the virRaiseError caller to strdup stuff itself Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:28:07PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
All ACKed except:
@@ -3808,20 +3808,13 @@ server_error (virConnectPtr conn, remote dom = err->dom ? get_nonnull_domain (conn, *err->dom) : NULL; net = err->net ? get_nonnull_network (conn, *err->net) : NULL;
- /* These strings are nullable. OK to ignore the return value - * of strdup since these strings are informational. - */ - char *str1 = err->str1 ? strdup (*err->str1) : NULL; - char *str2 = err->str2 ? strdup (*err->str2) : NULL; - char *str3 = err->str3 ? strdup (*err->str3) : NULL; - - char *message = err->message ? strdup (*err->message) : NULL; - __virRaiseError (conn, dom, net, err->domain, err->code, err->level, - str1, str2, str3, + err->str1 ? *err->str1 : NULL, + err->str2 ? *err->str2 : NULL, + err->str3 ? *err->str3 : NULL, err->int1, err->int2, - "%s", message); + "%s", err->message ? *err->message : NULL); }
/* get_nonnull_domain and get_nonnull_network turn an on-wire Don't we need to dup these strings because the following xdr_free (in the caller) will free them?
No, all the params to __virRaiseError, get strdup'd inside virRaiseError before being stored in the virError object.
/* * Save the information about the error */ virResetError(to); to->conn = conn; to->dom = dom; to->net = net; to->domain = domain; to->code = code; to->message = str; to->level = level; if (str1 != NULL) to->str1 = strdup(str1); if (str2 != NULL) to->str2 = strdup(str2); if (str3 != NULL) to->str3 = strdup(str3); to->int1 = int1; to->int2 = int2;
And the 'msg' arg gets re-allocated as the va-args are procssed with vsnprintf, so there's no need for the virRaiseError caller to strdup stuff itself
Right you are, so +1. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Thu, Nov 29, 2007 at 05:35:09PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:28:07PM +0000, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
All ACKed except:
Don't we need to dup these strings because the following xdr_free (in the caller) will free them?
No, all the params to __virRaiseError, get strdup'd inside virRaiseError before being stored in the virError object.
/* * Save the information about the error */ virResetError(to); to->conn = conn; to->dom = dom; to->net = net; to->domain = domain; to->code = code; to->message = str; to->level = level; if (str1 != NULL) to->str1 = strdup(str1); if (str2 != NULL) to->str2 = strdup(str2); if (str3 != NULL) to->str3 = strdup(str3); to->int1 = int1; to->int2 = int2;
And the 'msg' arg gets re-allocated as the va-args are procssed with vsnprintf, so there's no need for the virRaiseError caller to strdup stuff itself
Right you are, so +1.
Ok, this is applied. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

This patch will automatically disable the Xen setuid proxy if PolicyKit support is found to be enabled. It is possible to override this and force use of the proxy with --with-xen-proxy. Likewise it is possible to force disabling the proxy with the --without-xen-proxy arg. configure.in | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff -r 87b465795305 configure.in --- a/configure.in Thu Nov 29 10:02:55 2007 -0500 +++ b/configure.in Thu Nov 29 10:23:48 2007 -0500 @@ -571,7 +571,24 @@ fi fi dnl Enable building the proxy? -AM_CONDITIONAL(WITH_PROXY,[test "x$with_xen" = "xyes"]) + +AC_ARG_WITH(xen-proxy, +[ --with-xen-proxy add XEN setuid proxy support (on)],[],[with_xen_proxy=auto]) + +AC_MSG_CHECKING([if Xen setuid proxy is needed]) +if test "$with_xen_proxy" = "auto"; then + if test "$with_polkit" = "yes"; then + with_xen_proxy="no" + else + with_xen_proxy="yes" + fi +fi +if test "$with_xen" != "yes"; then + with_xen_proxy="no" +fi +AC_MSG_RESULT([$with_xen_proxy]) + +AM_CONDITIONAL(WITH_PROXY,[test "$with_xen_proxy" = "yes"]) dnl Check for gettext AM_GNU_GETTEXT_VERSION([0.14.1]) @@ -609,6 +626,7 @@ AC_MSG_NOTICE([Drivers]) AC_MSG_NOTICE([Drivers]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ Xen: $with_xen]) +AC_MSG_NOTICE([ Proxy: $with_xen_proxy]) AC_MSG_NOTICE([ QEMU: $with_qemu]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ Test: $with_test]) -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
This patch will automatically disable the Xen setuid proxy if PolicyKit support is found to be enabled. It is possible to override this and force use of the proxy with --with-xen-proxy. Likewise it is possible to force disabling the proxy with the --without-xen-proxy arg.
configure.in | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
ACK. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Thu, Nov 29, 2007 at 05:22:06PM +0000, Daniel P. Berrange wrote:
This patch will automatically disable the Xen setuid proxy if PolicyKit support is found to be enabled. It is possible to override this and force use of the proxy with --with-xen-proxy. Likewise it is possible to force disabling the proxy with the --without-xen-proxy arg.
configure.in | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
Hum, shouldn't that affect the spec file ? Or we assume RPMs are built without PolicyKit ? Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Nov 29, 2007 at 02:50:52PM -0500, Daniel Veillard wrote:
On Thu, Nov 29, 2007 at 05:22:06PM +0000, Daniel P. Berrange wrote:
This patch will automatically disable the Xen setuid proxy if PolicyKit support is found to be enabled. It is possible to override this and force use of the proxy with --with-xen-proxy. Likewise it is possible to force disabling the proxy with the --without-xen-proxy arg.
configure.in | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
Hum, shouldn't that affect the spec file ? Or we assume RPMs are built without PolicyKit ?
Yes, I should have added a BuildRequires & Requires in the spec for this too - it'll need to be conditional on Fedora 8 or later in the spec. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:22:06PM +0000, Daniel P. Berrange wrote:
This patch will automatically disable the Xen setuid proxy if PolicyKit support is found to be enabled. It is possible to override this and force use of the proxy with --with-xen-proxy. Likewise it is possible to force disabling the proxy with the --without-xen-proxy arg.
Added RPM spec magic to disable proxy too diff -r d2943aa5f7db configure.in --- a/configure.in Fri Nov 30 15:15:55 2007 -0500 +++ b/configure.in Fri Nov 30 15:21:42 2007 -0500 @@ -578,7 +578,24 @@ fi fi dnl Enable building the proxy? -AM_CONDITIONAL(WITH_PROXY,[test "x$with_xen" = "xyes"]) + +AC_ARG_WITH(xen-proxy, +[ --with-xen-proxy add XEN setuid proxy support (on)],[],[with_xen_proxy=auto]) + +AC_MSG_CHECKING([if Xen setuid proxy is needed]) +if test "$with_xen_proxy" = "auto"; then + if test "$with_polkit" = "yes"; then + with_xen_proxy="no" + else + with_xen_proxy="yes" + fi +fi +if test "$with_xen" != "yes"; then + with_xen_proxy="no" +fi +AC_MSG_RESULT([$with_xen_proxy]) + +AM_CONDITIONAL(WITH_PROXY,[test "$with_xen_proxy" = "yes"]) dnl Check for gettext AM_GNU_GETTEXT_VERSION([0.14.1]) @@ -635,6 +652,7 @@ AC_MSG_NOTICE([Drivers]) AC_MSG_NOTICE([Drivers]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ Xen: $with_xen]) +AC_MSG_NOTICE([ Proxy: $with_xen_proxy]) AC_MSG_NOTICE([ QEMU: $with_qemu]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ Test: $with_test]) diff -r d2943aa5f7db libvirt.spec.in --- a/libvirt.spec.in Fri Nov 30 15:15:55 2007 -0500 +++ b/libvirt.spec.in Fri Nov 30 15:21:42 2007 -0500 @@ -2,8 +2,10 @@ %if %{fedora} >= 8 %define with_polkit 1 +%define with_proxy no %else %define with_polkit 0 +%define with_proxy yes %endif Summary: Library providing a simple API virtualization @@ -77,7 +79,10 @@ of recent versions of Linux (and other O %setup -q %build -%configure --with-init-script=redhat --with-qemud-pid-file=%{_localstatedir}/run/libvirt_qemud.pid --with-remote-file=%{_localstatedir}/run/libvirtd.pid +%configure --with-init-script=redhat \ + --with-qemud-pid-file=%{_localstatedir}/run/libvirt_qemud.pid \ + --with-remote-file=%{_localstatedir}/run/libvirtd.pid \ + --with-xen-proxy=%{with_proxy} make %install @@ -157,7 +162,9 @@ fi %dir %{_localstatedir}/lib/libvirt/ %{_datadir}/PolicyKit/policy/libvirtd.policy %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/qemu/ +%if %{with_proxy} == "yes" %attr(4755, root, root) %{_libexecdir}/libvirt_proxy +%endif %attr(0755, root, root) %{_sbindir}/libvirtd %doc docs/*.rng %doc docs/*.xml Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Fri, Nov 30, 2007 at 10:35:17PM +0000, Daniel P. Berrange wrote:
On Thu, Nov 29, 2007 at 05:22:06PM +0000, Daniel P. Berrange wrote:
This patch will automatically disable the Xen setuid proxy if PolicyKit support is found to be enabled. It is possible to override this and force use of the proxy with --with-xen-proxy. Likewise it is possible to force disabling the proxy with the --without-xen-proxy arg.
Added RPM spec magic to disable proxy too
diff -r d2943aa5f7db configure.in --- a/configure.in Fri Nov 30 15:15:55 2007 -0500 +++ b/configure.in Fri Nov 30 15:21:42 2007 -0500 @@ -578,7 +578,24 @@ fi fi
dnl Enable building the proxy? -AM_CONDITIONAL(WITH_PROXY,[test "x$with_xen" = "xyes"]) + +AC_ARG_WITH(xen-proxy, +[ --with-xen-proxy add XEN setuid proxy support (on)],[],[with_xen_proxy=auto]) + +AC_MSG_CHECKING([if Xen setuid proxy is needed]) +if test "$with_xen_proxy" = "auto"; then + if test "$with_polkit" = "yes"; then + with_xen_proxy="no" + else + with_xen_proxy="yes" + fi +fi +if test "$with_xen" != "yes"; then + with_xen_proxy="no" +fi +AC_MSG_RESULT([$with_xen_proxy]) + +AM_CONDITIONAL(WITH_PROXY,[test "$with_xen_proxy" = "yes"])
dnl Check for gettext AM_GNU_GETTEXT_VERSION([0.14.1]) @@ -635,6 +652,7 @@ AC_MSG_NOTICE([Drivers]) AC_MSG_NOTICE([Drivers]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ Xen: $with_xen]) +AC_MSG_NOTICE([ Proxy: $with_xen_proxy]) AC_MSG_NOTICE([ QEMU: $with_qemu]) AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ Test: $with_test]) diff -r d2943aa5f7db libvirt.spec.in --- a/libvirt.spec.in Fri Nov 30 15:15:55 2007 -0500 +++ b/libvirt.spec.in Fri Nov 30 15:21:42 2007 -0500 @@ -2,8 +2,10 @@
%if %{fedora} >= 8 %define with_polkit 1 +%define with_proxy no %else %define with_polkit 0 +%define with_proxy yes %endif
Summary: Library providing a simple API virtualization @@ -77,7 +79,10 @@ of recent versions of Linux (and other O %setup -q
%build -%configure --with-init-script=redhat --with-qemud-pid-file=%{_localstatedir}/run/libvirt_qemud.pid --with-remote-file=%{_localstatedir}/run/libvirtd.pid +%configure --with-init-script=redhat \ + --with-qemud-pid-file=%{_localstatedir}/run/libvirt_qemud.pid \ + --with-remote-file=%{_localstatedir}/run/libvirtd.pid \ + --with-xen-proxy=%{with_proxy} make
%install @@ -157,7 +162,9 @@ fi %dir %{_localstatedir}/lib/libvirt/ %{_datadir}/PolicyKit/policy/libvirtd.policy %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/qemu/ +%if %{with_proxy} == "yes" %attr(4755, root, root) %{_libexecdir}/libvirt_proxy +%endif %attr(0755, root, root) %{_sbindir}/libvirtd %doc docs/*.rng %doc docs/*.xml
Great, looks just fine +1 Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

If building using --without-xen to disable the Xen driver there are a whole bunch of methods in the src/xml.c file which are not used. There are littered with #if WITH_XEN to disable small parts of the API, but not the whole APIs. This is pointless and results in many compile time warnings about unused variables. This patch just disables all the Xen specific APIs in xml.c entirely. src/xml.c | 19 +++++-------------- src/xml.h | 2 ++ tests/xml2sexprtest.c | 10 ++++++++++ 3 files changed, 17 insertions(+), 14 deletions(-) diff -r 8565c5bfacf5 src/xml.c --- a/src/xml.c Thu Nov 29 10:23:48 2007 -0500 +++ b/src/xml.c Thu Nov 29 10:42:15 2007 -0500 @@ -57,7 +57,7 @@ virXMLError(virConnectPtr conn, virError * Parser and converter for the CPUset strings used in libvirt * * * ************************************************************************/ - +#if WITH_XEN /** * skipSpaces: * @str: pointer to the char pointer used @@ -448,6 +448,7 @@ virConvertCpuSet(virConnectPtr conn, con free(cpuset); return (res); } +#endif /* WITH_XEN */ #ifndef PROXY /************************************************************************ @@ -694,7 +695,7 @@ virXPathNodeSet(const char *xpath, xmlXP * Converter functions to go from the XML tree to an S-Expr for Xen * * * ************************************************************************/ - +#if WITH_XEN /** * virtDomainParseXMLGraphicsDescImage: * @conn: pointer to the hypervisor connection @@ -1625,11 +1626,7 @@ virDomainParseXMLDesc(virConnectPtr conn str = virXPathString("string(/domain/vcpu/@cpuset)", ctxt); if (str != NULL) { -#ifdef WITH_XEN int maxcpu = xenNbCpus(conn); -#else - int maxcpu = 64; -#endif char *cpuset = NULL; char *ranges = NULL; const char *cur = str; @@ -1879,6 +1876,7 @@ virParseXMLDevice(virConnectPtr conn, co goto cleanup; } + /** * virDomainXMLDevID: * @domain: pointer to domain object @@ -1901,9 +1899,7 @@ virDomainXMLDevID(virDomainPtr domain, c xmlNodePtr node, cur; xmlChar *attr = NULL; -#ifdef WITH_XEN char *xref; -#endif /* WITH_XEN */ int ret = 0; xml = xmlReadDoc((const xmlChar *) xmldesc, "device.xml", NULL, @@ -1925,7 +1921,6 @@ virDomainXMLDevID(virDomainPtr domain, c attr = xmlGetProp(cur, BAD_CAST "dev"); if (attr == NULL) goto error; -#ifdef WITH_XEN xref = xenStoreDomainGetDiskID(domain->conn, domain->id, (char *) attr); if (xref != NULL) { @@ -1934,11 +1929,9 @@ virDomainXMLDevID(virDomainPtr domain, c ref[ref_len - 1] = '\0'; goto cleanup; } -#else /* !WITH_XEN */ /* hack to avoid the warning that domain is unused */ if (domain->id < 0) ret = -1; -#endif /* !WITH_XEN */ goto error; } @@ -1952,7 +1945,6 @@ virDomainXMLDevID(virDomainPtr domain, c if (attr == NULL) goto error; -#ifdef WITH_XEN xref = xenStoreDomainGetNetworkID(domain->conn, domain->id, (char *) attr); if (xref != NULL) { @@ -1961,11 +1953,9 @@ virDomainXMLDevID(virDomainPtr domain, c ref[ref_len - 1] = '\0'; goto cleanup; } -#else /* !WITH_XEN */ /* hack to avoid the warning that domain is unused */ if (domain->id < 0) ret = -1; -#endif /* !WITH_XEN */ goto error; } @@ -1981,6 +1971,7 @@ virDomainXMLDevID(virDomainPtr domain, c xmlFree(attr); return ret; } +#endif /* WITH_XEN */ #endif /* !PROXY */ /* diff -r 8565c5bfacf5 src/xml.h --- a/src/xml.h Thu Nov 29 10:23:48 2007 -0500 +++ b/src/xml.h Thu Nov 29 10:42:15 2007 -0500 @@ -32,6 +32,7 @@ int virXPathNodeSet (const char *xpath, xmlXPathContextPtr ctxt, xmlNodePtr **list); +#if WITH_XEN int virParseXenCpuTopology(virConnectPtr conn, virBufferPtr xml, const char *str, @@ -60,6 +61,7 @@ int virDomainXMLDevID(virDomainPtr doma char *class, char *ref, int ref_len); +#endif #ifdef __cplusplus } diff -r 8565c5bfacf5 tests/xml2sexprtest.c --- a/tests/xml2sexprtest.c Thu Nov 29 10:23:48 2007 -0500 +++ b/tests/xml2sexprtest.c Thu Nov 29 10:42:15 2007 -0500 @@ -1,7 +1,9 @@ + #include "config.h" #include <stdio.h> #include <string.h> +#include <stdlib.h> #include <sys/types.h> #include <fcntl.h> @@ -9,6 +11,8 @@ #ifdef HAVE_SYS_SYSLIMITS_H #include <sys/syslimits.h> #endif + +#if WITH_XEN #include "xml.h" #include "testutils.h" @@ -354,3 +358,9 @@ main(int argc, char **argv) exit(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); } + +#else /* WITH_XEN */ + +int main (void) { exit (77); /* means 'test skipped' for automake */ } + +#endif /* ! WITH_XEN */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
If building using --without-xen to disable the Xen driver there are a whole bunch of methods in the src/xml.c file which are not used. There are littered with #if WITH_XEN to disable small parts of the API, but not the whole APIs. This is pointless and results in many compile time warnings about unused variables. This patch just disables all the Xen specific APIs in xml.c entirely.
src/xml.c | 19 +++++-------------- src/xml.h | 2 ++ tests/xml2sexprtest.c | 10 ++++++++++ 3 files changed, 17 insertions(+), 14 deletions(-)
+1. I was getting these warnings as well, and ignoring them ... Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Thu, Nov 29, 2007 at 05:22:39PM +0000, Daniel P. Berrange wrote:
If building using --without-xen to disable the Xen driver there are a whole bunch of methods in the src/xml.c file which are not used. There are littered with #if WITH_XEN to disable small parts of the API, but not the whole APIs. This is pointless and results in many compile time warnings about unused variables. This patch just disables all the Xen specific APIs in xml.c entirely.
src/xml.c | 19 +++++-------------- src/xml.h | 2 ++ tests/xml2sexprtest.c | 10 ++++++++++ 3 files changed, 17 insertions(+), 14 deletions(-)
Well the side effect is that some of the regression tests can't be run then, we discussed that already, but I'm not against the patch, just that it reverses a previous decision, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Thu, Nov 29, 2007 at 02:53:05PM -0500, Daniel Veillard wrote:
On Thu, Nov 29, 2007 at 05:22:39PM +0000, Daniel P. Berrange wrote:
If building using --without-xen to disable the Xen driver there are a whole bunch of methods in the src/xml.c file which are not used. There are littered with #if WITH_XEN to disable small parts of the API, but not the whole APIs. This is pointless and results in many compile time warnings about unused variables. This patch just disables all the Xen specific APIs in xml.c entirely.
src/xml.c | 19 +++++-------------- src/xml.h | 2 ++ tests/xml2sexprtest.c | 10 ++++++++++ 3 files changed, 17 insertions(+), 14 deletions(-)
Well the side effect is that some of the regression tests can't be run then, we discussed that already, but I'm not against the patch, just that it reverses a previous decision,
I don't think we loose any useful test coverage. If you have disabled the Xen driver, then the Xen test cases are not actually testing any useful code, since we comment out all the bits which would use Xen. So the tests merely end up testing something which is a no-op. The nightly automated tests I have test with all drivers enable in any case. So I've committed this now. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Thu, Nov 29, 2007 at 05:15:44PM +0000, Daniel P. Berrange wrote:
This the 3rd iteration of the remote authentication / SASL patches previously provided here:
v1: http://www.redhat.com/archives/libvir-list/2007-October/msg00131.html v2: http://www.redhat.com/archives/libvir-list/2007-November/msg00005.html
This iteration has incorporate the feedback /bugs identified from previous iterations. It also adds in an API for a callback to collect credentials from the client applications, meaning username+password based authentication is now also supported alongside Kerberos/GSSAPI. I have also re-worked my previous PolicyKit patches:
http://www.redhat.com/archives/libvir-list/2007-September/msg00168.html
To fit into the generic authentication RPC mechanisms I added for SASL. The resulting incremental patch for PolicyKit is much simpler, and integrates better with client applications.
Finally, I have exposed the authentication callback support to python and prototyped its use in virt-manager as a sanity check for the whole API style.
All the auth patches in this thread have now been committed with the suggested fixes applied. I also added a page auth.html for the website describing the various auth options for administrators. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
participants (4)
-
Daniel P. Berrange
-
Daniel Veillard
-
Jim Meyering
-
Richard W.M. Jones