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 GSSAPI mechanism. This 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. If suitabl callbacks were supplied the same code would work for any other auth mechanism, so we just need to agree on a callback API. 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 throw errors. 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. The wire protocol for the SASL calls is changed so there is no longer a fixed maximum string size for the auth negotiation packets. We also now supply the info about the local & remote IP address & port to the SASL api so it can enable any auth mechanism which require that info. b/qemud/libvirtd.sasl | 11 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 | 26 +- qemud/remote.c | 381 +++++++++++++++++++++++++++++-- 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 | 442 ++++++++++++++++++++++++++++++++---- src/virsh.c | 4 src/virterror.c | 6 tests/Makefile.am | 2 21 files changed, 1139 insertions(+), 70 deletions(-) Dan. diff -r b5fe91c98e78 configure.in --- a/configure.in Tue Oct 30 16:14:32 2007 -0400 +++ b/configure.in Thu Nov 01 15:03:01 2007 -0400 @@ -329,6 +329,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 @@ -537,6 +571,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 b5fe91c98e78 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Tue Oct 30 16:14:32 2007 -0400 +++ b/include/libvirt/virterror.h Thu Nov 01 15:03:01 2007 -0400 @@ -129,6 +129,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 b5fe91c98e78 libvirt.spec.in --- a/libvirt.spec.in Tue Oct 30 16:14:32 2007 -0400 +++ b/libvirt.spec.in Thu Nov 01 15:03:01 2007 -0400 @@ -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 b5fe91c98e78 qemud/Makefile.am --- a/qemud/Makefile.am Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/Makefile.am Thu Nov 01 15:03:01 2007 -0400 @@ -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 @@ -45,7 +46,7 @@ libvirtd_LDADD += $(AVAHI_LIBS) libvirtd_LDADD += $(AVAHI_LIBS) endif -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)/libvirt/qemu/networks/default.xml sed -i -e "s,</name>,</name>\n <uuid>$(UUID)</uuid>," $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/default.xml @@ -55,7 +56,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)/libvirt/qemu/networks/default.xml rmdir $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart || : @@ -63,6 +64,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 .x.c: rm -f $@ diff -r b5fe91c98e78 qemud/internal.h --- a/qemud/internal.h Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/internal.h Thu Nov 01 16:54:13 2007 -0400 @@ -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 #include "remote_protocol.h" #include "../config.h" @@ -85,6 +88,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; @@ -110,6 +117,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 b5fe91c98e78 qemud/libvirtd.init.in --- a/qemud/libvirtd.init.in Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/libvirtd.init.in Thu Nov 01 15:03:01 2007 -0400 @@ -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 b5fe91c98e78 qemud/libvirtd.sasl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qemud/libvirtd.sasl Thu Nov 01 15:03:01 2007 -0400 @@ -0,0 +1,11 @@ +# If you want to use the non-TLS socket, then you *must* include +# the GSSAPI mechanism, because its the only one 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 +mech_list: 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 diff -r b5fe91c98e78 qemud/libvirtd.sysconf --- a/qemud/libvirtd.sysconf Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/libvirtd.sysconf Thu Nov 01 15:03:01 2007 -0400 @@ -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 b5fe91c98e78 qemud/qemud.c --- a/qemud/qemud.c Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/qemud.c Thu Nov 01 16:54:13 2007 -0400 @@ -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; @@ -699,6 +701,7 @@ static struct qemud_server *qemudInitial struct qemud_socket *sock; char sockname[PATH_MAX]; char roSockname[PATH_MAX]; + int err; if (!(server = calloc(1, sizeof(struct qemud_server)))) { qemudLog(QEMUD_ERR, "Failed to allocate struct qemud_server"); @@ -728,15 +731,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; } } @@ -1046,6 +1062,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; @@ -1126,6 +1143,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 b5fe91c98e78 qemud/remote.c --- a/qemud/remote.c Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote.c Thu Nov 01 16:54:13 2007 -0400 @@ -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, ...); @@ -115,6 +117,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 @@ -274,23 +291,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. @@ -312,12 +320,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; @@ -362,6 +370,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. -----*/ @@ -1945,6 +1978,324 @@ 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; + ret->types.types_val = calloc (ret->types.types_len, sizeof (remote_auth_type)); + ret->types.types_val[0] = client->auth; + return 0; +} + + +#if HAVE_SASL +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; + + if (getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV) < 0) { + remoteDispatchError(client, req, + "Cannot resolve address %d: %s", errno, strerror(errno)); + 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 illegal 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) { + 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 illegal 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 illegal 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 +static int +remoteDispatchAuthSaslInit (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 init request"); + remoteDispatchFailAuth(client, req); + return -1; +} + +static int +remoteDispatchAuthSaslStart (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 start request"); + remoteDispatchFailAuth(client, req); + return -1; +} + +static int +remoteDispatchAuthSaslStep (struct qemud_client *client, + remote_message_header *req, + remote_auth_sasl_step_args *args, + remote_auth_sasl_step_ret *ret) + qemudLog(QEMUD_ERR, "client tried unsupported SASL step request"); + remoteDispatchFailAuth(client, req); + return -1; +} +#endif + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff -r b5fe91c98e78 qemud/remote_dispatch_localvars.h --- a/qemud/remote_dispatch_localvars.h Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote_dispatch_localvars.h Thu Nov 01 16:36:53 2007 -0400 @@ -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 b5fe91c98e78 qemud/remote_dispatch_proc_switch.h --- a/qemud/remote_dispatch_proc_switch.h Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote_dispatch_proc_switch.h Thu Nov 01 16:36:53 2007 -0400 @@ -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 b5fe91c98e78 qemud/remote_dispatch_prototypes.h --- a/qemud/remote_dispatch_prototypes.h Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote_dispatch_prototypes.h Thu Nov 01 16:54:10 2007 -0400 @@ -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 b5fe91c98e78 qemud/remote_protocol.c --- a/qemud/remote_protocol.c Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote_protocol.c Thu Nov 01 16:36:53 2007 -0400 @@ -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 b5fe91c98e78 qemud/remote_protocol.h --- a/qemud/remote_protocol.h Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote_protocol.h Thu Nov 01 16:36:52 2007 -0400 @@ -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 b5fe91c98e78 qemud/remote_protocol.x --- a/qemud/remote_protocol.x Tue Oct 30 16:14:32 2007 -0400 +++ b/qemud/remote_protocol.x Thu Nov 01 16:36:51 2007 -0400 @@ -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 b5fe91c98e78 src/Makefile.am --- a/src/Makefile.am Tue Oct 30 16:14:32 2007 -0400 +++ b/src/Makefile.am Thu Nov 01 15:03:01 2007 -0400 @@ -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 b5fe91c98e78 src/remote_internal.c --- a/src/remote_internal.c Tue Oct 30 16:14:32 2007 -0400 +++ b/src/remote_internal.c Thu Nov 01 16:54:45 2007 -0400 @@ -46,6 +46,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" @@ -71,6 +74,11 @@ struct private_data { int counter; /* Generates serial numbers for RPC. */ char *uri; /* Original (remote) URI. */ 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) \ @@ -89,7 +97,12 @@ 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); +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); +static int remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open); +static int remoteAuthSASL (virConnectPtr conn, struct private_data *priv, int in_open); 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); @@ -120,7 +133,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) @@ -131,6 +144,20 @@ remoteStartup(void) inside_daemon = 1; return 0; } + +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"); +} + /** * remoteFindServerPath: @@ -302,7 +329,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; @@ -310,12 +337,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 (NULL, 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) { @@ -330,6 +351,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; @@ -372,6 +399,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 @@ -441,7 +474,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 (NULL, VIR_ERR_INVALID_ARG, gai_strerror (e)); goto failed; @@ -481,7 +514,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; @@ -595,7 +628,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); @@ -648,6 +681,11 @@ 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 }; @@ -659,19 +697,46 @@ doRemoteOpen (virConnectPtr conn, struct /* Duplicate and save the uri_str. */ priv->uri = strdup (uri_str); if (!priv->uri) { + free(priv->hostname); + priv->hostname = NULL; error (NULL, VIR_ERR_NO_MEMORY, "allocating priv->uri"); goto failed; } /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; + + cleanup: + /* Free up the URL and strings. */ + xmlFreeURI (uri); + 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"); /*FALLTHROUGH*/ 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; @@ -682,26 +747,12 @@ doRemoteOpen (virConnectPtr conn, struct } while (reap != -1 && reap != priv->pid); } } - - /* Free up the URL and strings. */ - xmlFreeURI (uri); - 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 @@ -1005,11 +1056,12 @@ initialise_gnutls (virConnectPtr conn AT 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, @@ -1050,7 +1102,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: @@ -1063,7 +1115,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; @@ -1098,8 +1150,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; @@ -1177,14 +1229,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 (NULL, 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; } @@ -1206,8 +1258,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) { @@ -1224,6 +1282,9 @@ doRemoteClose (virConnectPtr conn, struc /* Free URI copy. */ if (priv->uri) free (priv->uri); + + /* Free hostname copy */ + if (priv->hostname) free (priv->hostname); /* Free private data. */ priv->magic = DEAD; @@ -2756,6 +2817,299 @@ remoteNetworkSetAutostart (virNetworkPtr return 0; } + +/*----------------------------------------------------------------------*/ + +static int +remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open) +{ + struct remote_auth_list_ret ret; + + memset(&ret, 0, sizeof ret); + if (call (conn, priv, in_open, REMOTE_PROC_AUTH_LIST, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_auth_list_ret, (char *) &ret) == -1) { + virErrorPtr err = virGetLastError(); + if (!err) + return -1; + if (err->domain == VIR_FROM_REMOTE && + err->code == VIR_ERR_RPC && + err->level == VIR_ERR_ERROR && + STREQLEN(err->message, "unknown procedure", 17) == 0) { + virResetLastError(); + /* Old server. Does not do authentication */ + return 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 +static char *addrToString(struct sockaddr_storage *sa, socklen_t salen) +{ + char host[1024], port[20]; + char *addr; + + if (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", errno, strerror(errno)); + 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; + 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; + } + printf("'%s' '%s' '%s'\n", priv->hostname, localAddr, remoteAddr); + /* 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 /*----------------------------------------------------------------------*/ diff -r b5fe91c98e78 src/virsh.c --- a/src/virsh.c Tue Oct 30 16:14:32 2007 -0400 +++ b/src/virsh.c Thu Nov 01 15:03:01 2007 -0400 @@ -4512,8 +4512,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 b5fe91c98e78 src/virterror.c --- a/src/virterror.c Tue Oct 30 16:14:32 2007 -0400 +++ b/src/virterror.c Thu Nov 01 15:03:01 2007 -0400 @@ -652,6 +652,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 b5fe91c98e78 tests/Makefile.am --- a/tests/Makefile.am Tue Oct 30 16:14:32 2007 -0400 +++ b/tests/Makefile.am Thu Nov 01 15:03:01 2007 -0400 @@ -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 -=| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list