From 1b1d647439059b7e10fb94e1ade227fb695d7110 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 5 Dec 2007 15:24:15 +0000 Subject: [PATCH] Initial integration of SASL authentication, working for Kerberos only --- ChangeLog | 17 + configure.in | 39 +++ include/libvirt/virterror.h | 1 + libvirt.spec.in | 6 + 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 | 492 ++++++++++++++++++++++++---- src/virterror.c | 6 + tests/Makefile.am | 2 + 20 files changed, 1190 insertions(+), 86 deletions(-) diff --git a/ChangeLog b/ChangeLog index 195a987281..455e615ca4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +Wed Dec 5 10:20:00 EST 2007 Daniel P. Berrange + + * configure.in: Add checks for SASL library + * include/libvirt/virterror.h: Add VIR_ERR_AUTH_FAILED + * libvirt.spec.in: Add deps on cyrus-sasl & md5 plugin + and add SASL config file + * qemud/Makefile.am: Add SASL config file & build flags + * qemud/remote_protocol.x: new RPC calls for SASL + * qemud/internal.h, qemud/qemud.c, qemud/remote.c: Server + side of SASL authentication support + * qemud/libvirtd.init.in, qemud/libvirtd.sysconf: Set + KRB5_KTNAME to tell daemon where kerberos keytab lives + * qemud/libvirtd.sasl: example SASL config file + * src/Makefile.am, tests/Makefile.am: add SASL build flags + * src/remote_internal.c: Add support for SASL auth client + * src/virterror.c: Add VOIR_ERR_AUTH_FAILED string + Wed Dec 5 10:07:00 EST 2007 Daniel P. Berrange * src/qemu_conf.c: Strip out NIC interfaces named vnetXXX since diff --git a/configure.in b/configure.in index 6cd7fe2cb6..659eb0bc3b 100644 --- a/configure.in +++ b/configure.in @@ -361,6 +361,40 @@ 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 AC_ARG_WITH(avahi, [ --with-avahi use avahi to advertise remote daemon], @@ -583,6 +617,11 @@ AC_MSG_NOTICE([Libraries]) 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 --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 0b22c8f69a..7ade727159 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -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 --git a/libvirt.spec.in b/libvirt.spec.in index e0bf2f90f6..637dc77639 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -16,6 +16,10 @@ Requires: ncurses 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: avahi-devel 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 --git a/qemud/Makefile.am b/qemud/Makefile.am index 1737176f4d..acfd2bbcc3 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -17,6 +17,7 @@ EXTRA_DIST = libvirtd.init.in libvirtd.sysconf default-network.xml \ 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 @@ libvirtd_LDADD += $(AVAHI_LIBS) 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 --git a/qemud/internal.h b/qemud/internal.h index 390cc27f8a..7bb83ef493 100644 --- a/qemud/internal.h +++ b/qemud/internal.h @@ -28,6 +28,9 @@ #include #include #include "../src/gnutls_1_0_compat.h" +#if HAVE_SASL +#include +#endif #ifdef HAVE_SYS_SYSLIMITS_H #include @@ -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 --git a/qemud/libvirtd.init.in b/qemud/libvirtd.init.in index 1c58e939e3..c522f1104f 100644 --- a/qemud/libvirtd.init.in +++ b/qemud/libvirtd.init.in @@ -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 --git a/qemud/libvirtd.sysconf b/qemud/libvirtd.sysconf index a0afd64c36..fe4596a0c4 100644 --- a/qemud/libvirtd.sysconf +++ b/qemud/libvirtd.sysconf @@ -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 --git a/qemud/qemud.c b/qemud/qemud.c index b983e54786..273352faf6 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -575,7 +575,8 @@ remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *service) static int remoteListenTCP (struct qemud_server *server, const char *port, - int tls) + int tls, + int auth) { int fds[2]; int nfds = 0; @@ -604,6 +605,7 @@ remoteListenTCP (struct qemud_server *server, sock->fd = fds[i]; sock->tls = tls; + sock->auth = auth; if (getsockname(sock->fd, (struct sockaddr *)(&sa), &salen) < 0) return -1; @@ -699,6 +701,9 @@ static struct qemud_server *qemudInitialize(int sigread) { 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"); @@ -728,15 +733,28 @@ static struct qemud_server *qemudInitialize(int sigread) { 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 qemud_server *server, struct qemud_socket 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(struct qemud_server *server, struct qemud 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 --git a/qemud/remote.c b/qemud/remote.c index 93af5104b6..be256425bb 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -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, ...) @@ -118,6 +120,21 @@ remoteDispatchClientRequest (struct qemud_server *server ATTRIBUTE_UNUSED, 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 * this on the version number as well. */ @@ -275,24 +292,15 @@ remoteDispatchClientRequest (struct qemud_server *server ATTRIBUTE_UNUSED, * 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 *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; @@ -364,6 +372,31 @@ remoteDispatchError (struct qemud_client *client, 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. -----*/ static int @@ -1946,6 +1979,335 @@ remoteDispatchNumOfNetworks (struct qemud_client *client, 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 --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h index 92ec482f85..534de78f60 100644 --- a/qemud/remote_dispatch_localvars.h +++ b/qemud/remote_dispatch_localvars.h @@ -9,6 +9,7 @@ remote_list_defined_domains_args lv_remote_list_defined_domains_args; 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_set_memory_args lv_remote_domain_set_memory_args; 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_domain_resume_args lv_remote_domain_resume_args; 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_domain_suspend_args lv_remote_domain_suspend_args; 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 --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h index ba000f1f11..b01e6d6ac7 100644 --- a/qemud/remote_dispatch_proc_switch.h +++ b/qemud/remote_dispatch_proc_switch.h @@ -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 --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index c1925fcc7a..74c71f4edb 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -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 --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index b0f7c7c996..d3710327e1 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -104,6 +104,15 @@ xdr_remote_error (XDR *xdrs, remote_error *objp) 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; +} + bool_t xdr_remote_vcpu_info (XDR *xdrs, remote_vcpu_info *objp) { @@ -1221,6 +1230,84 @@ xdr_remote_network_set_autostart_args (XDR *xdrs, remote_network_set_autostart_a return TRUE; } +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 --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 908f97c694..9222ad3945 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -28,6 +28,8 @@ typedef remote_nonnull_string *remote_string; #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_nonnull_network (XDR *, remote_nonnull_network*); 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_bridge_name_ret (XDR *, remote_network_get 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_nonnull_network (); 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_bridge_name_ret (); 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 --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 6874af8182..2029d6e15d 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -81,6 +81,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]; @@ -123,6 +129,13 @@ struct remote_error { 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 { unsigned int number; @@ -612,6 +625,37 @@ struct remote_network_set_autostart_args { int autostart; }; +struct remote_auth_list_ret { + remote_auth_type types; +}; + +struct remote_auth_sasl_init_ret { + remote_nonnull_string mechlist; +}; + +struct remote_auth_sasl_start_args { + remote_nonnull_string mech; + int nil; + char data; +}; + +struct remote_auth_sasl_start_ret { + int complete; + int nil; + char data; +}; + +struct remote_auth_sasl_step_args { + int nil; + char data; +}; + +struct remote_auth_sasl_step_ret { + int complete; + int nil; + char data; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -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 --git a/src/Makefile.am b/src/Makefile.am index 52ffbaf67a..666a737e11 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 --git a/src/remote_internal.c b/src/remote_internal.c index a8227f3a82..f05fd86743 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -48,6 +48,9 @@ #include #include #include "gnutls_1_0_compat.h" +#if HAVE_SASL +#include +#endif #include #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_fields *fields); /* 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) @@ -133,6 +154,22 @@ remoteStartup(void) 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 private_data *priv, * 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 private_data *priv, 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 private_data *priv, } 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 private_data *priv, } 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 private_data *priv, 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 private_data *priv, 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 private_data *priv, 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 private_data *priv, } } /* 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,30 +724,13 @@ doRemoteOpen (virConnectPtr conn, struct private_data *priv, /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; - /*FALLTHROUGH*/ - failed: - /* Close the socket if we failed. */ - if (retcode != VIR_DRV_OPEN_SUCCESS && priv->sock >= 0) { - if (priv->uses_tls && priv->session) - gnutls_bye (priv->session, GNUTLS_SHUT_RDWR); - close (priv->sock); - if (priv->pid > 0) { - pid_t reap; - do { - reap = waitpid(priv->pid, NULL, 0); - if (reap == -1 && errno == EINTR) - continue; - } while (reap != -1 && reap != priv->pid); - } - } - + 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 (server) free (server); if (port) free (port); if (cmd_argv) { char **cmd_argv_ptr = cmd_argv; @@ -711,6 +742,34 @@ doRemoteOpen (virConnectPtr conn, struct private_data *priv, } return retcode; + + out_of_memory: + error (NULL, VIR_ERR_NO_MEMORY, "uri params"); + + failed: + /* Close the socket if we failed. */ + 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; + do { + reap = waitpid(priv->pid, NULL, 0); + if (reap == -1 && errno == EINTR) + continue; + } while (reap != -1 && reap != priv->pid); + } + } + + 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 (virConnectPtr conn, } 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 (virConnectPtr conn, } /* 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 (virConnectPtr conn, 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 ATTRIBUTE_UNUSED, } 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, struct private_data *priv) 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) { @@ -1231,6 +1297,9 @@ doRemoteClose (virConnectPtr conn, struct private_data *priv) } 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 network, int autostart) /*----------------------------------------------------------------------*/ +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 conn, struct private_data *priv, */ 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_data *priv, /* 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_data *priv, /* 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_data *priv, 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_data *priv, * 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_data *priv, 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_data *priv, 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 --git a/src/virterror.c b/src/virterror.c index 6bc64951a8..105d65f776 100644 --- a/src/virterror.c +++ b/src/virterror.c @@ -671,6 +671,12 @@ __virErrorMsg(virErrorNumber error, const char *info) 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 --git a/tests/Makefile.am b/tests/Makefile.am index 512162bf5e..fd9ab1ede2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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) -- GitLab