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