[Libvir] PATCH: 1/10: SASL authentication support

Daniel P. Berrange berrange at redhat.com
Thu Nov 29 17:16:34 UTC 2007


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 at 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  -=| 




More information about the libvir-list mailing list