Blame qemu-sasl-06-vnc-sasl.patch

Daniel P. Berrange 42af21
This patch adds the new SASL authentication protocol to the VNC server.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
It is enabled by setting the 'sasl' flag when launching VNC. SASL can
Daniel P. Berrange 42af21
optionally provide encryption via its SSF layer, if a suitable mechanism
Daniel P. Berrange 42af21
is configured (eg, GSSAPI/Kerberos, or Digest-MD5).  If an SSF layer is
Daniel P. Berrange 42af21
not available, then it should be combined with the x509 VNC authentication
Daniel P. Berrange 42af21
protocol which provides encryption.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
eg, if using GSSAPI
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
   qemu -vnc localhost:1,sasl
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
eg if using  TLS/x509 for encryption
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
   qemu -vnc localhost:1,sasl,tls,x509
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
By default the Cyrus SASL library will look for its configuration in
Daniel P. Berrange 42af21
the file /etc/sasl2/qemu.conf.  For non-root users, this can be overridden
Daniel P. Berrange 42af21
by setting the SASL_CONF_PATH environment variable, eg to make it look in
Daniel P. Berrange 42af21
$HOME/.sasl2.  NB unprivileged users may not have access to the full range
Daniel P. Berrange 42af21
of SASL mechanisms, since some of them require some administrative privileges
Daniel P. Berrange 42af21
to configure. The patch includes an example SASL configuration file which
Daniel P. Berrange 42af21
illustrates config for GSSAPI and Digest-MD5, though it should be noted that
Daniel P. Berrange 42af21
the latter is not really considered secure any more.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
Most of the SASL authentication code is located in a separate source file,
Daniel P. Berrange 42af21
vnc-auth-sasl.c.  The main vnc.c file only contains minimal integration
Daniel P. Berrange 42af21
glue, specifically parsing of command line flags / setup, and calls to
Daniel P. Berrange 42af21
start the SASL auth process, to do encoding/decoding for data.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
There are several possible stacks for reading & writing of data, depending
Daniel P. Berrange 42af21
on the combo of VNC authentication methods in use
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
 - Clear.    read/write straight to socket
Daniel P. Berrange 42af21
 - TLS.      read/write via GNUTLS helpers
Daniel P. Berrange 42af21
 - SASL.     encode/decode via SASL SSF layer, then read/write to socket
Daniel P. Berrange 42af21
 - SASL+TLS. encode/decode via SASL SSF layer, then read/write via GNUTLS
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
Hence, the vnc_client_read & vnc_client_write methods have been refactored
Daniel P. Berrange 42af21
a little.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
   vnc_client_read:  main entry point for reading, calls either
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
       - vnc_client_read_plain   reading, with no intermediate decoding
Daniel P. Berrange 42af21
       - vnc_client_read_sasl    reading, with SASL SSF decoding
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
   These two methods, then call vnc_client_read_buf(). This decides
Daniel P. Berrange 42af21
   whether to write to the socket directly or write via GNUTLS.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
The situation is the same for writing data. More extensive comments
Daniel P. Berrange 42af21
have been added in the code / patch. The vnc_client_read_sasl and
Daniel P. Berrange 42af21
vnc_client_write_sasl method implementations live in the separate
Daniel P. Berrange 42af21
vnc-auth-sasl.c file.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
The state required for the SASL auth mechanism is kept in a separate
Daniel P. Berrange 42af21
VncStateSASL struct, defined in vnc-auth-sasl.h and included in the
Daniel P. Berrange 42af21
main VncState.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
The configure script probes for SASL and automatically enables it
Daniel P. Berrange 42af21
if found, unless --disable-vnc-sasl was given to override it.
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
 Makefile            |    7 
Daniel P. Berrange 42af21
 Makefile.target     |    5 
Daniel P. Berrange 42af21
 b/qemu.sasl         |   34 ++
Daniel P. Berrange 42af21
 b/vnc-auth-sasl.c   |  626 ++++++++++++++++++++++++++++++++++++++++++++++++++++
Daniel P. Berrange 42af21
 b/vnc-auth-sasl.h   |   67 +++++
Daniel P. Berrange 42af21
 configure           |   34 ++
Daniel P. Berrange 42af21
 qemu-doc.texi       |   97 ++++++++
Daniel P. Berrange 42af21
 vnc-auth-vencrypt.c |   12 
Daniel P. Berrange 42af21
 vnc.c               |  249 ++++++++++++++++++--
Daniel P. Berrange 42af21
 vnc.h               |   31 ++
Daniel P. Berrange 42af21
 10 files changed, 1129 insertions(+), 33 deletions(-)
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
   Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Daniel P. Berrange 42af21
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 Makefile
Daniel P. Berrange 42af21
--- a/Makefile	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/Makefile	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -148,6 +148,9 @@ OBJS+=vnc.o d3des.o
Daniel P. Berrange 42af21
 ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
 OBJS+=vnc-tls.o vnc-auth-vencrypt.o
Daniel P. Berrange 42af21
 endif
Daniel P. Berrange 42af21
+ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+OBJS+=vnc-auth-sasl.o
Daniel P. Berrange 42af21
+endif
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 ifdef CONFIG_COCOA
Daniel P. Berrange 42af21
 OBJS+=cocoa.o
Daniel P. Berrange 42af21
@@ -171,7 +174,7 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-vnc.h: vnc-tls.h vnc-auth-vencrypt.h keymaps.h
Daniel P. Berrange 42af21
+vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
@@ -181,6 +184,8 @@ vnc-tls.o: vnc-tls.c vnc.h
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 curses.o: curses.c keymaps.h curses_keys.h
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 bt-host.o: CFLAGS += $(CONFIG_BLUEZ_CFLAGS)
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 Makefile.target
Daniel P. Berrange 42af21
--- a/Makefile.target	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/Makefile.target	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -554,6 +554,11 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
Daniel P. Berrange 42af21
 LIBS += $(CONFIG_VNC_TLS_LIBS)
Daniel P. Berrange 42af21
 endif
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+CPPFLAGS += $(CONFIG_VNC_SASL_CFLAGS)
Daniel P. Berrange 42af21
+LIBS += $(CONFIG_VNC_SASL_LIBS)
Daniel P. Berrange 42af21
+endif
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 ifdef CONFIG_BLUEZ
Daniel P. Berrange 42af21
 LIBS += $(CONFIG_BLUEZ_LIBS)
Daniel P. Berrange 42af21
 endif
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 configure
Daniel P. Berrange 42af21
--- a/configure	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/configure	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -164,6 +164,7 @@ fmod_lib=""
Daniel P. Berrange 42af21
 fmod_inc=""
Daniel P. Berrange 42af21
 oss_lib=""
Daniel P. Berrange 42af21
 vnc_tls="yes"
Daniel P. Berrange 42af21
+vnc_sasl="yes"
Daniel P. Berrange 42af21
 bsd="no"
Daniel P. Berrange 42af21
 linux="no"
Daniel P. Berrange 42af21
 solaris="no"
Daniel P. Berrange 42af21
@@ -387,6 +388,8 @@ for opt do
Daniel P. Berrange 42af21
   ;;
Daniel P. Berrange 42af21
   --disable-vnc-tls) vnc_tls="no"
Daniel P. Berrange 42af21
   ;;
Daniel P. Berrange 42af21
+  --disable-vnc-sasl) vnc_sasl="no"
Daniel P. Berrange 42af21
+  ;;
Daniel P. Berrange 42af21
   --disable-slirp) slirp="no"
Daniel P. Berrange 42af21
   ;;
Daniel P. Berrange 42af21
   --disable-vde) vde="no"
Daniel P. Berrange 42af21
@@ -544,6 +547,7 @@ echo "                           Availab
Daniel P. Berrange 42af21
 echo "  --enable-mixemu          enable mixer emulation"
Daniel P. Berrange 42af21
 echo "  --disable-brlapi         disable BrlAPI"
Daniel P. Berrange 42af21
 echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
Daniel P. Berrange 42af21
+echo "  --disable-vnc-sasl       disable SASL encryption for VNC server"
Daniel P. Berrange 42af21
 echo "  --disable-curses         disable curses output"
Daniel P. Berrange 42af21
 echo "  --disable-bluez          disable bluez stack connectivity"
Daniel P. Berrange 42af21
 echo "  --disable-kvm            disable KVM acceleration support"
Daniel P. Berrange 42af21
@@ -823,6 +827,25 @@ EOF
Daniel P. Berrange 42af21
 fi
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 ##########################################
Daniel P. Berrange 42af21
+# VNC SASL detection
Daniel P. Berrange 42af21
+if test "$vnc_sasl" = "yes" ; then
Daniel P. Berrange 42af21
+cat > $TMPC <
Daniel P. Berrange 42af21
+#include <sasl/sasl.h>
Daniel P. Berrange 42af21
+#include <stdio.h>
Daniel P. Berrange 42af21
+int main(void) { sasl_server_init(NULL, "qemu"); return 0; }
Daniel P. Berrange 42af21
+EOF
Daniel P. Berrange 42af21
+    # Assuming Cyrus-SASL installed in /usr prefix
Daniel P. Berrange 42af21
+    vnc_sasl_cflags=""
Daniel P. Berrange 42af21
+    vnc_sasl_libs="-lsasl2"
Daniel P. Berrange 42af21
+    if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $vnc_sasl_cflags $TMPC \
Daniel P. Berrange 42af21
+           $vnc_sasl_libs 2> /dev/null ; then
Daniel P. Berrange 42af21
+	:
Daniel P. Berrange 42af21
+    else
Daniel P. Berrange 42af21
+	vnc_sasl="no"
Daniel P. Berrange 42af21
+    fi
Daniel P. Berrange 42af21
+fi
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+##########################################
Daniel P. Berrange 42af21
 # vde libraries probe
Daniel P. Berrange 42af21
 if test "$vde" = "yes" ; then
Daniel P. Berrange 42af21
   cat > $TMPC << EOF
Daniel P. Berrange 42af21
@@ -1130,6 +1153,11 @@ if test "$vnc_tls" = "yes" ; then
Daniel P. Berrange 42af21
     echo "    TLS CFLAGS    $vnc_tls_cflags"
Daniel P. Berrange 42af21
     echo "    TLS LIBS      $vnc_tls_libs"
Daniel P. Berrange 42af21
 fi
Daniel P. Berrange 42af21
+echo "VNC SASL support  $vnc_sasl"
Daniel P. Berrange 42af21
+if test "$vnc_sasl" = "yes" ; then
Daniel P. Berrange 42af21
+    echo "    SASL CFLAGS    $vnc_sasl_cflags"
Daniel P. Berrange 42af21
+    echo "    SASL LIBS      $vnc_sasl_libs"
Daniel P. Berrange 42af21
+fi
Daniel P. Berrange 42af21
 if test -n "$sparc_cpu"; then
Daniel P. Berrange 42af21
     echo "Target Sparc Arch $sparc_cpu"
Daniel P. Berrange 42af21
 fi
Daniel P. Berrange 42af21
@@ -1371,6 +1399,12 @@ if test "$vnc_tls" = "yes" ; then
Daniel P. Berrange 42af21
   echo "CONFIG_VNC_TLS_LIBS=$vnc_tls_libs" >> $config_mak
Daniel P. Berrange 42af21
   echo "#define CONFIG_VNC_TLS 1" >> $config_h
Daniel P. Berrange 42af21
 fi
Daniel P. Berrange 42af21
+if test "$vnc_sasl" = "yes" ; then
Daniel P. Berrange 42af21
+  echo "CONFIG_VNC_SASL=yes" >> $config_mak
Daniel P. Berrange 42af21
+  echo "CONFIG_VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_mak
Daniel P. Berrange 42af21
+  echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
Daniel P. Berrange 42af21
+  echo "#define CONFIG_VNC_SASL 1" >> $config_h
Daniel P. Berrange 42af21
+fi
Daniel P. Berrange 42af21
 qemu_version=`head $source_path/VERSION`
Daniel P. Berrange 42af21
 echo "VERSION=$qemu_version" >>$config_mak
Daniel P. Berrange 42af21
 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 qemu-doc.texi
Daniel P. Berrange 42af21
--- a/qemu-doc.texi	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/qemu-doc.texi	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -616,6 +616,21 @@ path following this option specifies whe
Daniel P. Berrange 42af21
 be loaded from. See the @ref{vnc_security} section for details on generating
Daniel P. Berrange 42af21
 certificates.
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+@item sasl
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+Require that the client use SASL to authenticate with the VNC server.
Daniel P. Berrange 42af21
+The exact choice of authentication method used is controlled from the
Daniel P. Berrange 42af21
+system / user's SASL configuration file for the 'qemu' service. This
Daniel P. Berrange 42af21
+is typically found in /etc/sasl2/qemu.conf. If running QEMU as an
Daniel P. Berrange 42af21
+unprivileged user, an environment variable SASL_CONF_PATH can be used
Daniel P. Berrange 42af21
+to make it search alternate locations for the service config.
Daniel P. Berrange 42af21
+While some SASL auth methods can also provide data encryption (eg GSSAPI),
Daniel P. Berrange 42af21
+it is recommended that SASL always be combined with the 'tls' and
Daniel P. Berrange 42af21
+'x509' settings to enable use of SSL and server certificates. This
Daniel P. Berrange 42af21
+ensures a data encryption preventing compromise of authentication
Daniel P. Berrange 42af21
+credentials. See the @ref{vnc_security} section for details on using
Daniel P. Berrange 42af21
+SASL authentication.
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 @end table
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 @end table
Daniel P. Berrange 42af21
@@ -2061,7 +2076,10 @@ considerations depending on the deployme
Daniel P. Berrange 42af21
 * vnc_sec_certificate::
Daniel P. Berrange 42af21
 * vnc_sec_certificate_verify::
Daniel P. Berrange 42af21
 * vnc_sec_certificate_pw::
Daniel P. Berrange 42af21
+* vnc_sec_sasl::
Daniel P. Berrange 42af21
+* vnc_sec_certificate_sasl::
Daniel P. Berrange 42af21
 * vnc_generate_cert::
Daniel P. Berrange 42af21
+* vnc_setup_sasl::
Daniel P. Berrange 42af21
 @end menu
Daniel P. Berrange 42af21
 @node vnc_sec_none
Daniel P. Berrange 42af21
 @subsection Without passwords
Daniel P. Berrange 42af21
@@ -2144,6 +2162,41 @@ Password: ********
Daniel P. Berrange 42af21
 (qemu)
Daniel P. Berrange 42af21
 @end example
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@node vnc_sec_sasl
Daniel P. Berrange 42af21
+@subsection With SASL authentication
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+The SASL authentication method is a VNC extension, that provides an
Daniel P. Berrange 42af21
+easily extendable, pluggable authentication method. This allows for
Daniel P. Berrange 42af21
+integration with a wide range of authentication mechanisms, such as
Daniel P. Berrange 42af21
+PAM, GSSAPI/Kerberos, LDAP, SQL databases, one-time keys and more.
Daniel P. Berrange 42af21
+The strength of the authentication depends on the exact mechanism
Daniel P. Berrange 42af21
+configured. If the chosen mechanism also provides a SSF layer, then
Daniel P. Berrange 42af21
+it will encrypt the datastream as well.
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+Refer to the later docs on how to choose the exact SASL mechanism
Daniel P. Berrange 42af21
+used for authentication, but assuming use of one supporting SSF,
Daniel P. Berrange 42af21
+then QEMU can be launched with:
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@example
Daniel P. Berrange 42af21
+qemu [...OPTIONS...] -vnc :1,sasl -monitor stdio
Daniel P. Berrange 42af21
+@end example
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@node vnc_sec_certificate_sasl
Daniel P. Berrange 42af21
+@subsection With x509 certificates and SASL authentication
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+If the desired SASL authentication mechanism does not supported
Daniel P. Berrange 42af21
+SSF layers, then it is strongly advised to run it in combination
Daniel P. Berrange 42af21
+with TLS and x509 certificates. This provides securely encrypted
Daniel P. Berrange 42af21
+data stream, avoiding risk of compromising of the security
Daniel P. Berrange 42af21
+credentials. This can be enabled, by combining the 'sasl' option
Daniel P. Berrange 42af21
+with the aforementioned TLS + x509 options:
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@example
Daniel P. Berrange 42af21
+qemu [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio
Daniel P. Berrange 42af21
+@end example
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 @node vnc_generate_cert
Daniel P. Berrange 42af21
 @subsection Generating certificates for VNC
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
@@ -2255,6 +2308,50 @@ EOF
Daniel P. Berrange 42af21
 The @code{client-key.pem} and @code{client-cert.pem} files should now be securely
Daniel P. Berrange 42af21
 copied to the client for which they were generated.
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@node vnc_setup_sasl
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@subsection Configuring SASL mechanisms
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+The following documentation assumes use of the Cyrus SASL implementation on a
Daniel P. Berrange 42af21
+Linux host, but the principals should apply to any other SASL impl. When SASL
Daniel P. Berrange 42af21
+is enabled, the mechanism configuration will be loaded from system default
Daniel P. Berrange 42af21
+SASL service config /etc/sasl2/qemu.conf. If running QEMU as an
Daniel P. Berrange 42af21
+unprivileged user, an environment variable SASL_CONF_PATH can be used
Daniel P. Berrange 42af21
+to make it search alternate locations for the service config.
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+The default configuration might contain
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@example
Daniel P. Berrange 42af21
+mech_list: digest-md5
Daniel P. Berrange 42af21
+sasldb_path: /etc/qemu/passwd.db
Daniel P. Berrange 42af21
+@end example
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+This says to use the 'Digest MD5' mechanism, which is similar to the HTTP
Daniel P. Berrange 42af21
+Digest-MD5 mechanism. The list of valid usernames & passwords is maintained
Daniel P. Berrange 42af21
+in the /etc/qemu/passwd.db file, and can be updated using the saslpasswd2
Daniel P. Berrange 42af21
+command. While this mechanism is easy to configure and use, it is not
Daniel P. Berrange 42af21
+considered secure by modern standards, so only suitable for developers /
Daniel P. Berrange 42af21
+ad-hoc testing.
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+A more serious deployment might use Kerberos, which is done with the 'gssapi'
Daniel P. Berrange 42af21
+mechanism
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+@example
Daniel P. Berrange 42af21
+mech_list: gssapi
Daniel P. Berrange 42af21
+keytab: /etc/qemu/krb5.tab
Daniel P. Berrange 42af21
+@end example
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+For this to work the administrator of your KDC must generate a Kerberos
Daniel P. Berrange 42af21
+principal for the server, with a name of  'qemu/somehost.example.com@@EXAMPLE.COM'
Daniel P. Berrange 42af21
+replacing 'somehost.example.com' with the fully qualified host name of the
Daniel P. Berrange 42af21
+machine running QEMU, and 'EXAMPLE.COM' with the Keberos Realm.
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+Other configurations will be left as an exercise for the reader. It should
Daniel P. Berrange 42af21
+be noted that only Digest-MD5 and GSSAPI provides a SSF layer for data
Daniel P. Berrange 42af21
+encryption. For all other mechanisms, VNC should always be configured to
Daniel P. Berrange 42af21
+use TLS and x509 certificates to protect security credentials from snooping.
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 @node gdb_usage
Daniel P. Berrange 42af21
 @section GDB usage
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 qemu.sasl
Daniel P. Berrange 42af21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
Daniel P. Berrange 42af21
+++ b/qemu.sasl	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -0,0 +1,34 @@
Daniel P. Berrange 42af21
+# If you want to use the non-TLS socket, then you *must* include
Daniel P. Berrange 42af21
+# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only
Daniel P. Berrange 42af21
+# ones that can offer session encryption as well as authentication.
Daniel P. Berrange 42af21
+#
Daniel P. Berrange 42af21
+# If you're only using TLS, then you can turn on any mechanisms
Daniel P. Berrange 42af21
+# you like for authentication, because TLS provides the encryption
Daniel P. Berrange 42af21
+#
Daniel P. Berrange 42af21
+# Default to a simple username+password mechanism
Daniel P. Berrange 42af21
+# NB digest-md5 is no longer considered secure by current standards
Daniel P. Berrange 42af21
+mech_list: digest-md5
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+# Before you can use GSSAPI, you need a service principle on the
Daniel P. Berrange 42af21
+# KDC server for libvirt, and that to be exported to the keytab
Daniel P. Berrange 42af21
+# file listed below
Daniel P. Berrange 42af21
+#mech_list: gssapi
Daniel P. Berrange 42af21
+#
Daniel P. Berrange 42af21
+# You can also list many mechanisms at once, then the user can choose
Daniel P. Berrange 42af21
+# by adding  '?auth=sasl.gssapi' to their libvirt URI, eg
Daniel P. Berrange 42af21
+#   qemu+tcp://hostname/system?auth=sasl.gssapi
Daniel P. Berrange 42af21
+#mech_list: digest-md5 gssapi
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+# Some older builds of MIT kerberos on Linux ignore this option &
Daniel P. Berrange 42af21
+# instead need KRB5_KTNAME env var.
Daniel P. Berrange 42af21
+# For modern Linux, and other OS, this should be sufficient
Daniel P. Berrange 42af21
+keytab: /etc/qemu/krb5.tab
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+# If using digest-md5 for username/passwds, then this is the file
Daniel P. Berrange 42af21
+# containing the passwds. Use 'saslpasswd2 -a qemu [username]'
Daniel P. Berrange 42af21
+# to add entries, and 'sasldblistusers2 -a qemu' to browse it
Daniel P. Berrange 42af21
+sasldb_path: /etc/qemu/passwd.db
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+auxprop_plugin: sasldb
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 vnc-auth-sasl.c
Daniel P. Berrange 42af21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
Daniel P. Berrange 42af21
+++ b/vnc-auth-sasl.c	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -0,0 +1,626 @@
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * QEMU VNC display driver: SASL auth protocol
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Copyright (C) 2009 Red Hat, Inc
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Daniel P. Berrange 42af21
+ * of this software and associated documentation files (the "Software"), to deal
Daniel P. Berrange 42af21
+ * in the Software without restriction, including without limitation the rights
Daniel P. Berrange 42af21
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Daniel P. Berrange 42af21
+ * copies of the Software, and to permit persons to whom the Software is
Daniel P. Berrange 42af21
+ * furnished to do so, subject to the following conditions:
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * The above copyright notice and this permission notice shall be included in
Daniel P. Berrange 42af21
+ * all copies or substantial portions of the Software.
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Daniel P. Berrange 42af21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Daniel P. Berrange 42af21
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Daniel P. Berrange 42af21
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Daniel P. Berrange 42af21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Daniel P. Berrange 42af21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Daniel P. Berrange 42af21
+ * THE SOFTWARE.
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#include "vnc.h"
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/* Max amount of data we send/recv for SASL steps to prevent DOS */
Daniel P. Berrange 42af21
+#define SASL_DATA_MAX_LEN (1024 * 1024)
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+void vnc_sasl_client_cleanup(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    if (vs->sasl.conn) {
Daniel P. Berrange 42af21
+	vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0;
Daniel P. Berrange 42af21
+	vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
Daniel P. Berrange 42af21
+	vs->sasl.encoded = NULL;
Daniel P. Berrange 42af21
+	free(vs->sasl.username);
Daniel P. Berrange 42af21
+	free(vs->sasl.mechlist);
Daniel P. Berrange 42af21
+	vs->sasl.username = vs->sasl.mechlist = NULL;
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+long vnc_client_write_sasl(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    long ret;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("Write SASL: Pending output %p size %d offset %d Encoded: %p size %d offset %d\n",
Daniel P. Berrange 42af21
+	      vs->output.buffer, vs->output.capacity, vs->output.offset,
Daniel P. Berrange 42af21
+	      vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (!vs->sasl.encoded) {
Daniel P. Berrange 42af21
+	int err;
Daniel P. Berrange 42af21
+	err = sasl_encode(vs->sasl.conn,
Daniel P. Berrange 42af21
+			  (char *)vs->output.buffer,
Daniel P. Berrange 42af21
+			  vs->output.offset,
Daniel P. Berrange 42af21
+			  (const char **)&vs->sasl.encoded,
Daniel P. Berrange 42af21
+			  &vs->sasl.encodedLength);
Daniel P. Berrange 42af21
+	if (err != SASL_OK)
Daniel P. Berrange 42af21
+	    return vnc_client_io_error(vs, -1, EIO);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	vs->sasl.encodedOffset = 0;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    ret = vnc_client_write_buf(vs,
Daniel P. Berrange 42af21
+			       vs->sasl.encoded + vs->sasl.encodedOffset,
Daniel P. Berrange 42af21
+			       vs->sasl.encodedLength - vs->sasl.encodedOffset);
Daniel P. Berrange 42af21
+    if (!ret)
Daniel P. Berrange 42af21
+	return 0;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    vs->sasl.encodedOffset += ret;
Daniel P. Berrange 42af21
+    if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
Daniel P. Berrange 42af21
+	vs->output.offset = 0;
Daniel P. Berrange 42af21
+	vs->sasl.encoded = NULL;
Daniel P. Berrange 42af21
+	vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* Can't merge this block with one above, because
Daniel P. Berrange 42af21
+     * someone might have written more unencrypted
Daniel P. Berrange 42af21
+     * data in vs->output while we were processing
Daniel P. Berrange 42af21
+     * SASL encoded output
Daniel P. Berrange 42af21
+     */
Daniel P. Berrange 42af21
+    if (vs->output.offset == 0) {
Daniel P. Berrange 42af21
+	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    return ret;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+long vnc_client_read_sasl(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    long ret;
Daniel P. Berrange 42af21
+    uint8_t encoded[4096];
Daniel P. Berrange 42af21
+    const char *decoded;
Daniel P. Berrange 42af21
+    unsigned int decodedLen;
Daniel P. Berrange 42af21
+    int err;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
Daniel P. Berrange 42af21
+    if (!ret)
Daniel P. Berrange 42af21
+	return 0;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    err = sasl_decode(vs->sasl.conn,
Daniel P. Berrange 42af21
+		      (char *)encoded, ret,
Daniel P. Berrange 42af21
+		      &decoded, &decodedLen);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (err != SASL_OK)
Daniel P. Berrange 42af21
+	return vnc_client_io_error(vs, -1, -EIO);
Daniel P. Berrange 42af21
+    VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
Daniel P. Berrange 42af21
+	      encoded, ret, decoded, decodedLen);
Daniel P. Berrange 42af21
+    buffer_reserve(&vs->input, decodedLen);
Daniel P. Berrange 42af21
+    buffer_append(&vs->input, decoded, decodedLen);
Daniel P. Berrange 42af21
+    return decodedLen;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int vnc_auth_sasl_check_access(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    const void *val;
Daniel P. Berrange 42af21
+    int err;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val;;
Daniel P. Berrange 42af21
+    if (err != SASL_OK) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("cannot query SASL username on connection %d (%s)\n",
Daniel P. Berrange 42af21
+		  err, sasl_errstring(err, NULL, NULL));
Daniel P. Berrange 42af21
+	return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    if (val == NULL) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("no client username was found\n");
Daniel P. Berrange 42af21
+	return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    VNC_DEBUG("SASL client username %s\n", (const char *)val);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    vs->sasl.username = qemu_strdup((const char*)val);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int vnc_auth_sasl_check_ssf(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    const void *val;
Daniel P. Berrange 42af21
+    int err, ssf;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (!vs->sasl.wantSSF)
Daniel P. Berrange 42af21
+	return 1;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val;;
Daniel P. Berrange 42af21
+    if (err != SASL_OK)
Daniel P. Berrange 42af21
+	return 0;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    ssf = *(const int *)val;
Daniel P. Berrange 42af21
+    VNC_DEBUG("negotiated an SSF of %d\n", ssf);
Daniel P. Berrange 42af21
+    if (ssf < 56)
Daniel P. Berrange 42af21
+	return 0; /* 56 is good for Kerberos */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* Only setup for read initially, because we're about to send an RPC
Daniel P. Berrange 42af21
+     * reply which must be in plain text. When the next incoming RPC
Daniel P. Berrange 42af21
+     * arrives, we'll switch on writes too
Daniel P. Berrange 42af21
+     *
Daniel P. Berrange 42af21
+     * cf qemudClientReadSASL  in qemud.c
Daniel P. Berrange 42af21
+     */
Daniel P. Berrange 42af21
+    vs->sasl.runSSF = 1;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* We have a SSF that's good enough */
Daniel P. Berrange 42af21
+    return 1;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * Step Msg
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Input from client:
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * u32 clientin-length
Daniel P. Berrange 42af21
+ * u8-array clientin-string
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Output to client:
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * u32 serverout-length
Daniel P. Berrange 42af21
+ * u8-array serverout-strin
Daniel P. Berrange 42af21
+ * u8 continue
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    uint32_t datalen = len;
Daniel P. Berrange 42af21
+    const char *serverout;
Daniel P. Berrange 42af21
+    unsigned int serveroutlen;
Daniel P. Berrange 42af21
+    int err;
Daniel P. Berrange 42af21
+    char *clientdata = NULL;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* NB, distinction of NULL vs "" is *critical* in SASL */
Daniel P. Berrange 42af21
+    if (datalen) {
Daniel P. Berrange 42af21
+	clientdata = (char*)data;
Daniel P. Berrange 42af21
+	clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
Daniel P. Berrange 42af21
+	datalen--; /* Don't count NULL byte when passing to _start() */
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
Daniel P. Berrange 42af21
+	      clientdata, datalen);
Daniel P. Berrange 42af21
+    err = sasl_server_step(vs->sasl.conn,
Daniel P. Berrange 42af21
+			   clientdata,
Daniel P. Berrange 42af21
+			   datalen,
Daniel P. Berrange 42af21
+			   &serverout,
Daniel P. Berrange 42af21
+			   &serveroutlen);
Daniel P. Berrange 42af21
+    if (err != SASL_OK &&
Daniel P. Berrange 42af21
+	err != SASL_CONTINUE) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("sasl step failed %d (%s)\n",
Daniel P. Berrange 42af21
+		  err, sasl_errdetail(vs->sasl.conn));
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (serveroutlen > SASL_DATA_MAX_LEN) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("sasl step reply data too long %d\n",
Daniel P. Berrange 42af21
+		  serveroutlen);
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
Daniel P. Berrange 42af21
+	      serveroutlen, serverout ? 0 : 1);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (serveroutlen) {
Daniel P. Berrange 42af21
+	vnc_write_u32(vs, serveroutlen + 1);
Daniel P. Berrange 42af21
+	vnc_write(vs, serverout, serveroutlen + 1);
Daniel P. Berrange 42af21
+    } else {
Daniel P. Berrange 42af21
+	vnc_write_u32(vs, 0);
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* Whether auth is complete */
Daniel P. Berrange 42af21
+    vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (err == SASL_CONTINUE) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("%s", "Authentication must continue\n");
Daniel P. Berrange 42af21
+	/* Wait for step length */
Daniel P. Berrange 42af21
+	vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
Daniel P. Berrange 42af21
+    } else {
Daniel P. Berrange 42af21
+	if (!vnc_auth_sasl_check_ssf(vs)) {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
Daniel P. Berrange 42af21
+	    goto authreject;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	/* Check username whitelist ACL */
Daniel P. Berrange 42af21
+	if (vnc_auth_sasl_check_access(vs) < 0) {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
Daniel P. Berrange 42af21
+	    goto authreject;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	VNC_DEBUG("Authentication successful %d\n", vs->csock);
Daniel P. Berrange 42af21
+	vnc_write_u32(vs, 0); /* Accept auth */
Daniel P. Berrange 42af21
+	/*
Daniel P. Berrange 42af21
+	 * Delay writing in SSF encoded mode until pending output
Daniel P. Berrange 42af21
+	 * buffer is written
Daniel P. Berrange 42af21
+	 */
Daniel P. Berrange 42af21
+	if (vs->sasl.runSSF)
Daniel P. Berrange 42af21
+	    vs->sasl.waitWriteSSF = vs->output.offset;
Daniel P. Berrange 42af21
+	start_client_init(vs);
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+ authreject:
Daniel P. Berrange 42af21
+    vnc_write_u32(vs, 1); /* Reject auth */
Daniel P. Berrange 42af21
+    vnc_write_u32(vs, sizeof("Authentication failed"));
Daniel P. Berrange 42af21
+    vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
Daniel P. Berrange 42af21
+    vnc_flush(vs);
Daniel P. Berrange 42af21
+    vnc_client_error(vs);
Daniel P. Berrange 42af21
+    return -1;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+ authabort:
Daniel P. Berrange 42af21
+    vnc_client_error(vs);
Daniel P. Berrange 42af21
+    return -1;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    uint32_t steplen = read_u32(data, 0);
Daniel P. Berrange 42af21
+    VNC_DEBUG("Got client step len %d\n", steplen);
Daniel P. Berrange 42af21
+    if (steplen > SASL_DATA_MAX_LEN) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("Too much SASL data %d\n", steplen);
Daniel P. Berrange 42af21
+	vnc_client_error(vs);
Daniel P. Berrange 42af21
+	return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (steplen == 0)
Daniel P. Berrange 42af21
+	return protocol_client_auth_sasl_step(vs, NULL, 0);
Daniel P. Berrange 42af21
+    else
Daniel P. Berrange 42af21
+	vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * Start Msg
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Input from client:
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * u32 clientin-length
Daniel P. Berrange 42af21
+ * u8-array clientin-string
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Output to client:
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * u32 serverout-length
Daniel P. Berrange 42af21
+ * u8-array serverout-strin
Daniel P. Berrange 42af21
+ * u8 continue
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#define SASL_DATA_MAX_LEN (1024 * 1024)
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    uint32_t datalen = len;
Daniel P. Berrange 42af21
+    const char *serverout;
Daniel P. Berrange 42af21
+    unsigned int serveroutlen;
Daniel P. Berrange 42af21
+    int err;
Daniel P. Berrange 42af21
+    char *clientdata = NULL;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* NB, distinction of NULL vs "" is *critical* in SASL */
Daniel P. Berrange 42af21
+    if (datalen) {
Daniel P. Berrange 42af21
+	clientdata = (char*)data;
Daniel P. Berrange 42af21
+	clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
Daniel P. Berrange 42af21
+	datalen--; /* Don't count NULL byte when passing to _start() */
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
Daniel P. Berrange 42af21
+	      vs->sasl.mechlist, clientdata, datalen);
Daniel P. Berrange 42af21
+    err = sasl_server_start(vs->sasl.conn,
Daniel P. Berrange 42af21
+			    vs->sasl.mechlist,
Daniel P. Berrange 42af21
+			    clientdata,
Daniel P. Berrange 42af21
+			    datalen,
Daniel P. Berrange 42af21
+			    &serverout,
Daniel P. Berrange 42af21
+			    &serveroutlen);
Daniel P. Berrange 42af21
+    if (err != SASL_OK &&
Daniel P. Berrange 42af21
+	err != SASL_CONTINUE) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("sasl start failed %d (%s)\n",
Daniel P. Berrange 42af21
+		  err, sasl_errdetail(vs->sasl.conn));
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    if (serveroutlen > SASL_DATA_MAX_LEN) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("sasl start reply data too long %d\n",
Daniel P. Berrange 42af21
+		  serveroutlen);
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
Daniel P. Berrange 42af21
+	      serveroutlen, serverout ? 0 : 1);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (serveroutlen) {
Daniel P. Berrange 42af21
+	vnc_write_u32(vs, serveroutlen + 1);
Daniel P. Berrange 42af21
+	vnc_write(vs, serverout, serveroutlen + 1);
Daniel P. Berrange 42af21
+    } else {
Daniel P. Berrange 42af21
+	vnc_write_u32(vs, 0);
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* Whether auth is complete */
Daniel P. Berrange 42af21
+    vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (err == SASL_CONTINUE) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("%s", "Authentication must continue\n");
Daniel P. Berrange 42af21
+	/* Wait for step length */
Daniel P. Berrange 42af21
+	vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
Daniel P. Berrange 42af21
+    } else {
Daniel P. Berrange 42af21
+	if (!vnc_auth_sasl_check_ssf(vs)) {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
Daniel P. Berrange 42af21
+	    goto authreject;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	/* Check username whitelist ACL */
Daniel P. Berrange 42af21
+	if (vnc_auth_sasl_check_access(vs) < 0) {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
Daniel P. Berrange 42af21
+	    goto authreject;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	VNC_DEBUG("Authentication successful %d\n", vs->csock);
Daniel P. Berrange 42af21
+	vnc_write_u32(vs, 0); /* Accept auth */
Daniel P. Berrange 42af21
+	start_client_init(vs);
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+ authreject:
Daniel P. Berrange 42af21
+    vnc_write_u32(vs, 1); /* Reject auth */
Daniel P. Berrange 42af21
+    vnc_write_u32(vs, sizeof("Authentication failed"));
Daniel P. Berrange 42af21
+    vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
Daniel P. Berrange 42af21
+    vnc_flush(vs);
Daniel P. Berrange 42af21
+    vnc_client_error(vs);
Daniel P. Berrange 42af21
+    return -1;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+ authabort:
Daniel P. Berrange 42af21
+    vnc_client_error(vs);
Daniel P. Berrange 42af21
+    return -1;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    uint32_t startlen = read_u32(data, 0);
Daniel P. Berrange 42af21
+    VNC_DEBUG("Got client start len %d\n", startlen);
Daniel P. Berrange 42af21
+    if (startlen > SASL_DATA_MAX_LEN) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("Too much SASL data %d\n", startlen);
Daniel P. Berrange 42af21
+	vnc_client_error(vs);
Daniel P. Berrange 42af21
+	return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (startlen == 0)
Daniel P. Berrange 42af21
+	return protocol_client_auth_sasl_start(vs, NULL, 0);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    char *mechname = malloc(len + 1);
Daniel P. Berrange 42af21
+    if (!mechname) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("Out of memory reading mechname\n");
Daniel P. Berrange 42af21
+	vnc_client_error(vs);
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    strncpy(mechname, (char*)data, len);
Daniel P. Berrange 42af21
+    mechname[len] = '\0';
Daniel P. Berrange 42af21
+    VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
Daniel P. Berrange 42af21
+	      mechname, vs->sasl.mechlist);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
Daniel P. Berrange 42af21
+	if (vs->sasl.mechlist[len] != '\0' &&
Daniel P. Berrange 42af21
+	    vs->sasl.mechlist[len] != ',') {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
Daniel P. Berrange 42af21
+	    vnc_client_error(vs);
Daniel P. Berrange 42af21
+	    return -1;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+    } else {
Daniel P. Berrange 42af21
+	char *offset = strstr(vs->sasl.mechlist, mechname);
Daniel P. Berrange 42af21
+	VNC_DEBUG("Two %p\n", offset);
Daniel P. Berrange 42af21
+	if (!offset) {
Daniel P. Berrange 42af21
+	    vnc_client_error(vs);
Daniel P. Berrange 42af21
+	    return -1;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+	VNC_DEBUG("Two '%s'\n", offset);
Daniel P. Berrange 42af21
+	if (offset[-1] != ',' ||
Daniel P. Berrange 42af21
+	    (offset[len] != '\0'&&
Daniel P. Berrange 42af21
+	     offset[len] != ',')) {
Daniel P. Berrange 42af21
+	    vnc_client_error(vs);
Daniel P. Berrange 42af21
+	    return -1;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    free(vs->sasl.mechlist);
Daniel P. Berrange 42af21
+    vs->sasl.mechlist = mechname;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("Validated mechname '%s'\n", mechname);
Daniel P. Berrange 42af21
+    vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    uint32_t mechlen = read_u32(data, 0);
Daniel P. Berrange 42af21
+    VNC_DEBUG("Got client mechname len %d\n", mechlen);
Daniel P. Berrange 42af21
+    if (mechlen > 100) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
Daniel P. Berrange 42af21
+	vnc_client_error(vs);
Daniel P. Berrange 42af21
+	return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    if (mechlen < 1) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
Daniel P. Berrange 42af21
+	vnc_client_error(vs);
Daniel P. Berrange 42af21
+	return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
Daniel P. Berrange 42af21
+    return 0;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#define USES_X509_AUTH(vs)			      \
Daniel P. Berrange 42af21
+    ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
Daniel P. Berrange 42af21
+     (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
Daniel P. Berrange 42af21
+     (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN ||  \
Daniel P. Berrange 42af21
+     (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+void start_auth_sasl(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    const char *mechlist = NULL;
Daniel P. Berrange 42af21
+    sasl_security_properties_t secprops;
Daniel P. Berrange 42af21
+    int err;
Daniel P. Berrange 42af21
+    char *localAddr, *remoteAddr;
Daniel P. Berrange 42af21
+    int mechlistlen;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /* Get local & remote client addresses in form  IPADDR;PORT */
Daniel P. Berrange 42af21
+    if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
Daniel P. Berrange 42af21
+	free(localAddr);
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    err = sasl_server_new("vnc",
Daniel P. Berrange 42af21
+			  NULL, /* FQDN - just delegates to gethostname */
Daniel P. Berrange 42af21
+			  NULL, /* User realm */
Daniel P. Berrange 42af21
+			  localAddr,
Daniel P. Berrange 42af21
+			  remoteAddr,
Daniel P. Berrange 42af21
+			  NULL, /* Callbacks, not needed */
Daniel P. Berrange 42af21
+			  SASL_SUCCESS_DATA,
Daniel P. Berrange 42af21
+			  &vs->sasl.conn);
Daniel P. Berrange 42af21
+    free(localAddr);
Daniel P. Berrange 42af21
+    free(remoteAddr);
Daniel P. Berrange 42af21
+    localAddr = remoteAddr = NULL;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (err != SASL_OK) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("sasl context setup failed %d (%s)",
Daniel P. Berrange 42af21
+		  err, sasl_errstring(err, NULL, NULL));
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
+    /* Inform SASL that we've got an external SSF layer from TLS/x509 */
Daniel P. Berrange 42af21
+    if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
Daniel P. Berrange 42af21
+	vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
Daniel P. Berrange 42af21
+	gnutls_cipher_algorithm_t cipher;
Daniel P. Berrange 42af21
+	sasl_ssf_t ssf;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	cipher = gnutls_cipher_get(vs->tls.session);
Daniel P. Berrange 42af21
+	if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("%s", "cannot TLS get cipher size\n");
Daniel P. Berrange 42af21
+	    sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	    vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	    goto authabort;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+	ssf *= 8; /* tls key size is bytes, sasl wants bits */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+	err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf;;
Daniel P. Berrange 42af21
+	if (err != SASL_OK) {
Daniel P. Berrange 42af21
+	    VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
Daniel P. Berrange 42af21
+		      err, sasl_errstring(err, NULL, NULL));
Daniel P. Berrange 42af21
+	    sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	    vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	    goto authabort;
Daniel P. Berrange 42af21
+	}
Daniel P. Berrange 42af21
+    } else
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
+	vs->sasl.wantSSF = 1;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    memset (&secprops, 0, sizeof secprops);
Daniel P. Berrange 42af21
+    /* Inform SASL that we've got an external SSF layer from TLS */
Daniel P. Berrange 42af21
+    if (strncmp(vs->vd->display, "unix:", 5) == 0
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
+	/* Disable SSF, if using TLS+x509+SASL only. TLS without x509
Daniel P. Berrange 42af21
+	   is not sufficiently strong */
Daniel P. Berrange 42af21
+	|| (vs->vd->auth == VNC_AUTH_VENCRYPT &&
Daniel P. Berrange 42af21
+	    vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
+	) {
Daniel P. Berrange 42af21
+	/* If we've got TLS or UNIX domain sock, we don't care about SSF */
Daniel P. Berrange 42af21
+	secprops.min_ssf = 0;
Daniel P. Berrange 42af21
+	secprops.max_ssf = 0;
Daniel P. Berrange 42af21
+	secprops.maxbufsize = 8192;
Daniel P. Berrange 42af21
+	secprops.security_flags = 0;
Daniel P. Berrange 42af21
+    } else {
Daniel P. Berrange 42af21
+	/* Plain TCP, better get an SSF layer */
Daniel P. Berrange 42af21
+	secprops.min_ssf = 56; /* Good enough to require kerberos */
Daniel P. Berrange 42af21
+	secprops.max_ssf = 100000; /* Arbitrary big number */
Daniel P. Berrange 42af21
+	secprops.maxbufsize = 8192;
Daniel P. Berrange 42af21
+	/* Forbid any anonymous or trivially crackable auth */
Daniel P. Berrange 42af21
+	secprops.security_flags =
Daniel P. Berrange 42af21
+	    SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
Daniel P. Berrange 42af21
+    if (err != SASL_OK) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("cannot set SASL security props %d (%s)\n",
Daniel P. Berrange 42af21
+		  err, sasl_errstring(err, NULL, NULL));
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    err = sasl_listmech(vs->sasl.conn,
Daniel P. Berrange 42af21
+			NULL, /* Don't need to set user */
Daniel P. Berrange 42af21
+			"", /* Prefix */
Daniel P. Berrange 42af21
+			",", /* Separator */
Daniel P. Berrange 42af21
+			"", /* Suffix */
Daniel P. Berrange 42af21
+			&mechlist,
Daniel P. Berrange 42af21
+			NULL,
Daniel P. Berrange 42af21
+			NULL);
Daniel P. Berrange 42af21
+    if (err != SASL_OK) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
Daniel P. Berrange 42af21
+		  err, sasl_errdetail(vs->sasl.conn));
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (!(vs->sasl.mechlist = strdup(mechlist))) {
Daniel P. Berrange 42af21
+	VNC_DEBUG("Out of memory");
Daniel P. Berrange 42af21
+	sasl_dispose(&vs->sasl.conn);
Daniel P. Berrange 42af21
+	vs->sasl.conn = NULL;
Daniel P. Berrange 42af21
+	goto authabort;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+    mechlistlen = strlen(mechlist);
Daniel P. Berrange 42af21
+    vnc_write_u32(vs, mechlistlen);
Daniel P. Berrange 42af21
+    vnc_write(vs, mechlist, mechlistlen);
Daniel P. Berrange 42af21
+    vnc_flush(vs);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    VNC_DEBUG("Wait for client mechname length\n");
Daniel P. Berrange 42af21
+    vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    return;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+ authabort:
Daniel P. Berrange 42af21
+    vnc_client_error(vs);
Daniel P. Berrange 42af21
+    return;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 vnc-auth-sasl.h
Daniel P. Berrange 42af21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
Daniel P. Berrange 42af21
+++ b/vnc-auth-sasl.h	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -0,0 +1,67 @@
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * QEMU VNC display driver: SASL auth protocol
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Copyright (C) 2009 Red Hat, Inc
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Daniel P. Berrange 42af21
+ * of this software and associated documentation files (the "Software"), to deal
Daniel P. Berrange 42af21
+ * in the Software without restriction, including without limitation the rights
Daniel P. Berrange 42af21
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Daniel P. Berrange 42af21
+ * copies of the Software, and to permit persons to whom the Software is
Daniel P. Berrange 42af21
+ * furnished to do so, subject to the following conditions:
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * The above copyright notice and this permission notice shall be included in
Daniel P. Berrange 42af21
+ * all copies or substantial portions of the Software.
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Daniel P. Berrange 42af21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Daniel P. Berrange 42af21
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Daniel P. Berrange 42af21
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Daniel P. Berrange 42af21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Daniel P. Berrange 42af21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Daniel P. Berrange 42af21
+ * THE SOFTWARE.
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#ifndef __QEMU_VNC_AUTH_SASL_H__
Daniel P. Berrange 42af21
+#define __QEMU_VNC_AUTH_SASL_H__
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#include <sasl/sasl.h>
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+typedef struct VncStateSASL VncStateSASL;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+struct VncStateSASL {
Daniel P. Berrange 42af21
+    sasl_conn_t *conn;
Daniel P. Berrange 42af21
+    /* If we want to negotiate an SSF layer with client */
Daniel P. Berrange 42af21
+    int wantSSF :1;
Daniel P. Berrange 42af21
+    /* If we are now running the SSF layer */
Daniel P. Berrange 42af21
+    int runSSF :1;
Daniel P. Berrange 42af21
+    /*
Daniel P. Berrange 42af21
+     * If this is non-zero, then wait for that many bytes
Daniel P. Berrange 42af21
+     * to be written plain, before switching to SSF encoding
Daniel P. Berrange 42af21
+     * This allows the VNC auth result to finish being
Daniel P. Berrange 42af21
+     * written in plain.
Daniel P. Berrange 42af21
+     */
Daniel P. Berrange 42af21
+    unsigned int waitWriteSSF;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    /*
Daniel P. Berrange 42af21
+     * Buffering encoded data to allow more clear data
Daniel P. Berrange 42af21
+     * to be stuffed onto the output buffer
Daniel P. Berrange 42af21
+     */
Daniel P. Berrange 42af21
+    const uint8_t *encoded;
Daniel P. Berrange 42af21
+    unsigned int encodedLength;
Daniel P. Berrange 42af21
+    unsigned int encodedOffset;
Daniel P. Berrange 42af21
+    char *username;
Daniel P. Berrange 42af21
+    char *mechlist;
Daniel P. Berrange 42af21
+};
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+void vnc_sasl_client_cleanup(VncState *vs);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+long vnc_client_read_sasl(VncState *vs);
Daniel P. Berrange 42af21
+long vnc_client_write_sasl(VncState *vs);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+void start_auth_sasl(VncState *vs);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#endif /* __QEMU_VNC_AUTH_SASL_H__ */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 vnc-auth-vencrypt.c
Daniel P. Berrange 42af21
--- a/vnc-auth-vencrypt.c	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/vnc-auth-vencrypt.c	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -43,8 +43,15 @@ static void start_auth_vencrypt_subauth(
Daniel P. Berrange 42af21
        start_auth_vnc(vs);
Daniel P. Berrange 42af21
        break;
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    case VNC_AUTH_VENCRYPT_TLSSASL:
Daniel P. Berrange 42af21
+    case VNC_AUTH_VENCRYPT_X509SASL:
Daniel P. Berrange 42af21
+      VNC_DEBUG("Start TLS auth SASL\n");
Daniel P. Berrange 42af21
+      return start_auth_sasl(vs);
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
     default: /* Should not be possible, but just in case */
Daniel P. Berrange 42af21
-       VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
Daniel P. Berrange 42af21
+       VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth);
Daniel P. Berrange 42af21
        vnc_write_u8(vs, 1);
Daniel P. Berrange 42af21
        if (vs->minor >= 8) {
Daniel P. Berrange 42af21
            static const char err[] = "Unsupported authentication type";
Daniel P. Berrange 42af21
@@ -105,7 +112,8 @@ static void vnc_tls_handshake_io(void *o
Daniel P. Berrange 42af21
 #define NEED_X509_AUTH(vs)			      \
Daniel P. Berrange 42af21
     ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
Daniel P. Berrange 42af21
      (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
Daniel P. Berrange 42af21
-     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
Daniel P. Berrange 42af21
+     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN ||  \
Daniel P. Berrange 42af21
+     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 vnc.c
Daniel P. Berrange 42af21
--- a/vnc.c	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/vnc.c	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -68,7 +68,8 @@ static char *addr_to_string(const char *
Daniel P. Berrange 42af21
     return addr;
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static char *vnc_socket_local_addr(const char *format, int fd) {
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+char *vnc_socket_local_addr(const char *format, int fd) {
Daniel P. Berrange 42af21
     struct sockaddr_storage sa;
Daniel P. Berrange 42af21
     socklen_t salen;
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
@@ -79,7 +80,8 @@ static char *vnc_socket_local_addr(const
Daniel P. Berrange 42af21
     return addr_to_string(format, &sa, salen);
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static char *vnc_socket_remote_addr(const char *format, int fd) {
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+char *vnc_socket_remote_addr(const char *format, int fd) {
Daniel P. Berrange 42af21
     struct sockaddr_storage sa;
Daniel P. Berrange 42af21
     socklen_t salen;
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
@@ -125,12 +127,18 @@ static const char *vnc_auth_name(VncDisp
Daniel P. Berrange 42af21
 	    return "vencrypt+x509+vnc";
Daniel P. Berrange 42af21
 	case VNC_AUTH_VENCRYPT_X509PLAIN:
Daniel P. Berrange 42af21
 	    return "vencrypt+x509+plain";
Daniel P. Berrange 42af21
+	case VNC_AUTH_VENCRYPT_TLSSASL:
Daniel P. Berrange 42af21
+	    return "vencrypt+tls+sasl";
Daniel P. Berrange 42af21
+	case VNC_AUTH_VENCRYPT_X509SASL:
Daniel P. Berrange 42af21
+	    return "vencrypt+x509+sasl";
Daniel P. Berrange 42af21
 	default:
Daniel P. Berrange 42af21
 	    return "vencrypt";
Daniel P. Berrange 42af21
 	}
Daniel P. Berrange 42af21
 #else
Daniel P. Berrange 42af21
 	return "vencrypt";
Daniel P. Berrange 42af21
 #endif
Daniel P. Berrange 42af21
+    case VNC_AUTH_SASL:
Daniel P. Berrange 42af21
+	return "sasl";
Daniel P. Berrange 42af21
     }
Daniel P. Berrange 42af21
     return "unknown";
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
@@ -278,7 +286,7 @@ static void vnc_framebuffer_update(VncSt
Daniel P. Berrange 42af21
     vnc_write_s32(vs, encoding);
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static void buffer_reserve(Buffer *buffer, size_t len)
Daniel P. Berrange 42af21
+void buffer_reserve(Buffer *buffer, size_t len)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
     if ((buffer->capacity - buffer->offset) < len) {
Daniel P. Berrange 42af21
 	buffer->capacity += (len + 1024);
Daniel P. Berrange 42af21
@@ -290,22 +298,22 @@ static void buffer_reserve(Buffer *buffe
Daniel P. Berrange 42af21
     }
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static int buffer_empty(Buffer *buffer)
Daniel P. Berrange 42af21
+int buffer_empty(Buffer *buffer)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
     return buffer->offset == 0;
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static uint8_t *buffer_end(Buffer *buffer)
Daniel P. Berrange 42af21
+uint8_t *buffer_end(Buffer *buffer)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
     return buffer->buffer + buffer->offset;
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static void buffer_reset(Buffer *buffer)
Daniel P. Berrange 42af21
+void buffer_reset(Buffer *buffer)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
 	buffer->offset = 0;
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static void buffer_append(Buffer *buffer, const void *data, size_t len)
Daniel P. Berrange 42af21
+void buffer_append(Buffer *buffer, const void *data, size_t len)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
     memcpy(buffer->buffer + buffer->offset, data, len);
Daniel P. Berrange 42af21
     buffer->offset += len;
Daniel P. Berrange 42af21
@@ -821,7 +829,8 @@ static void audio_del(VncState *vs)
Daniel P. Berrange 42af21
     }
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+int vnc_client_io_error(VncState *vs, int ret, int last_errno)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
     if (ret == 0 || ret == -1) {
Daniel P. Berrange 42af21
         if (ret == -1) {
Daniel P. Berrange 42af21
@@ -847,6 +856,9 @@ static int vnc_client_io_error(VncState 
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
 	vnc_tls_client_cleanup(vs);
Daniel P. Berrange 42af21
 #endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+        vnc_sasl_client_cleanup(vs);
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
         audio_del(vs);
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
         VncState *p, *parent = NULL;
Daniel P. Berrange 42af21
@@ -877,14 +889,28 @@ void vnc_client_error(VncState *vs)
Daniel P. Berrange 42af21
     vnc_client_io_error(vs, -1, EINVAL);
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-void vnc_client_write(void *opaque)
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * Called to write a chunk of data to the client socket. The data may
Daniel P. Berrange 42af21
+ * be the raw data, or may have already been encoded by SASL.
Daniel P. Berrange 42af21
+ * The data will be written either straight onto the socket, or
Daniel P. Berrange 42af21
+ * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * NB, it is theoretically possible to have 2 layers of encryption,
Daniel P. Berrange 42af21
+ * both SASL, and this TLS layer. It is highly unlikely in practice
Daniel P. Berrange 42af21
+ * though, since SASL encryption will typically be a no-op if TLS
Daniel P. Berrange 42af21
+ * is active
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Returns the number of bytes written, which may be less than
Daniel P. Berrange 42af21
+ * the requested 'datalen' if the socket would block. Returns
Daniel P. Berrange 42af21
+ * -1 on error, and disconnects the client socket.
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
     long ret;
Daniel P. Berrange 42af21
-    VncState *vs = opaque;
Daniel P. Berrange 42af21
-
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
     if (vs->tls.session) {
Daniel P. Berrange 42af21
-	ret = gnutls_write(vs->tls.session, vs->output.buffer, vs->output.offset);
Daniel P. Berrange 42af21
+	ret = gnutls_write(vs->tls.session, data, datalen);
Daniel P. Berrange 42af21
 	if (ret < 0) {
Daniel P. Berrange 42af21
 	    if (ret == GNUTLS_E_AGAIN)
Daniel P. Berrange 42af21
 		errno = EAGAIN;
Daniel P. Berrange 42af21
@@ -894,10 +920,42 @@ void vnc_client_write(void *opaque)
Daniel P. Berrange 42af21
 	}
Daniel P. Berrange 42af21
     } else
Daniel P. Berrange 42af21
 #endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
-	ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0);
Daniel P. Berrange 42af21
-    ret = vnc_client_io_error(vs, ret, socket_error());
Daniel P. Berrange 42af21
+	ret = send(vs->csock, data, datalen, 0);
Daniel P. Berrange 42af21
+    VNC_DEBUG("Wrote wire %p %d -> %ld\n", data, datalen, ret);
Daniel P. Berrange 42af21
+    return vnc_client_io_error(vs, ret, socket_error());
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * Called to write buffered data to the client socket, when not
Daniel P. Berrange 42af21
+ * using any SASL SSF encryption layers. Will write as much data
Daniel P. Berrange 42af21
+ * as possible without blocking. If all buffered data is written,
Daniel P. Berrange 42af21
+ * will switch the FD poll() handler back to read monitoring.
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Returns the number of bytes written, which may be less than
Daniel P. Berrange 42af21
+ * the buffered output data if the socket would block. Returns
Daniel P. Berrange 42af21
+ * -1 on error, and disconnects the client socket.
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+static long vnc_client_write_plain(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    long ret;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    VNC_DEBUG("Write Plain: Pending output %p size %d offset %d. Wait SSF %d\n",
Daniel P. Berrange 42af21
+              vs->output.buffer, vs->output.capacity, vs->output.offset,
Daniel P. Berrange 42af21
+              vs->sasl.waitWriteSSF);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    if (vs->sasl.conn &&
Daniel P. Berrange 42af21
+        vs->sasl.runSSF &&
Daniel P. Berrange 42af21
+        vs->sasl.waitWriteSSF) {
Daniel P. Berrange 42af21
+        ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
Daniel P. Berrange 42af21
+        if (ret)
Daniel P. Berrange 42af21
+            vs->sasl.waitWriteSSF -= ret;
Daniel P. Berrange 42af21
+    } else
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
+        ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
Daniel P. Berrange 42af21
     if (!ret)
Daniel P. Berrange 42af21
-	return;
Daniel P. Berrange 42af21
+        return 0;
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
     memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
Daniel P. Berrange 42af21
     vs->output.offset -= ret;
Daniel P. Berrange 42af21
@@ -905,6 +963,29 @@ void vnc_client_write(void *opaque)
Daniel P. Berrange 42af21
     if (vs->output.offset == 0) {
Daniel P. Berrange 42af21
 	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
Daniel P. Berrange 42af21
     }
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+    return ret;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * First function called whenever there is data to be written to
Daniel P. Berrange 42af21
+ * the client socket. Will delegate actual work according to whether
Daniel P. Berrange 42af21
+ * SASL SSF layers are enabled (thus requiring encryption calls)
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+void vnc_client_write(void *opaque)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    long ret;
Daniel P. Berrange 42af21
+    VncState *vs = opaque;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    if (vs->sasl.conn &&
Daniel P. Berrange 42af21
+        vs->sasl.runSSF &&
Daniel P. Berrange 42af21
+        !vs->sasl.waitWriteSSF)
Daniel P. Berrange 42af21
+        ret = vnc_client_write_sasl(vs);
Daniel P. Berrange 42af21
+    else
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
+        ret = vnc_client_write_plain(vs);
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
Daniel P. Berrange 42af21
@@ -913,16 +994,28 @@ void vnc_read_when(VncState *vs, VncRead
Daniel P. Berrange 42af21
     vs->read_handler_expect = expecting;
Daniel P. Berrange 42af21
 }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-void vnc_client_read(void *opaque)
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * Called to read a chunk of data from the client socket. The data may
Daniel P. Berrange 42af21
+ * be the raw data, or may need to be further decoded by SASL.
Daniel P. Berrange 42af21
+ * The data will be read either straight from to the socket, or
Daniel P. Berrange 42af21
+ * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * NB, it is theoretically possible to have 2 layers of encryption,
Daniel P. Berrange 42af21
+ * both SASL, and this TLS layer. It is highly unlikely in practice
Daniel P. Berrange 42af21
+ * though, since SASL encryption will typically be a no-op if TLS
Daniel P. Berrange 42af21
+ * is active
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Returns the number of bytes read, which may be less than
Daniel P. Berrange 42af21
+ * the requested 'datalen' if the socket would block. Returns
Daniel P. Berrange 42af21
+ * -1 on error, and disconnects the client socket.
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
-    VncState *vs = opaque;
Daniel P. Berrange 42af21
     long ret;
Daniel P. Berrange 42af21
-
Daniel P. Berrange 42af21
-    buffer_reserve(&vs->input, 4096);
Daniel P. Berrange 42af21
-
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
     if (vs->tls.session) {
Daniel P. Berrange 42af21
-	ret = gnutls_read(vs->tls.session, buffer_end(&vs->input), 4096);
Daniel P. Berrange 42af21
+	ret = gnutls_read(vs->tls.session, data, datalen);
Daniel P. Berrange 42af21
 	if (ret < 0) {
Daniel P. Berrange 42af21
 	    if (ret == GNUTLS_E_AGAIN)
Daniel P. Berrange 42af21
 		errno = EAGAIN;
Daniel P. Berrange 42af21
@@ -932,13 +1025,53 @@ void vnc_client_read(void *opaque)
Daniel P. Berrange 42af21
 	}
Daniel P. Berrange 42af21
     } else
Daniel P. Berrange 42af21
 #endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
-	ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
Daniel P. Berrange 42af21
-    ret = vnc_client_io_error(vs, ret, socket_error());
Daniel P. Berrange 42af21
+	ret = recv(vs->csock, data, datalen, 0);
Daniel P. Berrange 42af21
+    VNC_DEBUG("Read wire %p %d -> %ld\n", data, datalen, ret);
Daniel P. Berrange 42af21
+    return vnc_client_io_error(vs, ret, socket_error());
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * Called to read data from the client socket to the input buffer,
Daniel P. Berrange 42af21
+ * when not using any SASL SSF encryption layers. Will read as much
Daniel P. Berrange 42af21
+ * data as possible without blocking.
Daniel P. Berrange 42af21
+ *
Daniel P. Berrange 42af21
+ * Returns the number of bytes read. Returns -1 on error, and
Daniel P. Berrange 42af21
+ * disconnects the client socket.
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+static long vnc_client_read_plain(VncState *vs)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    int ret;
Daniel P. Berrange 42af21
+    VNC_DEBUG("Read plain %p size %d offset %d\n",
Daniel P. Berrange 42af21
+              vs->input.buffer, vs->input.capacity, vs->input.offset);
Daniel P. Berrange 42af21
+    buffer_reserve(&vs->input, 4096);
Daniel P. Berrange 42af21
+    ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
Daniel P. Berrange 42af21
+    if (!ret)
Daniel P. Berrange 42af21
+        return 0;
Daniel P. Berrange 42af21
+    vs->input.offset += ret;
Daniel P. Berrange 42af21
+    return ret;
Daniel P. Berrange 42af21
+}
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/*
Daniel P. Berrange 42af21
+ * First function called whenever there is more data to be read from
Daniel P. Berrange 42af21
+ * the client socket. Will delegate actual work according to whether
Daniel P. Berrange 42af21
+ * SASL SSF layers are enabled (thus requiring decryption calls)
Daniel P. Berrange 42af21
+ */
Daniel P. Berrange 42af21
+void vnc_client_read(void *opaque)
Daniel P. Berrange 42af21
+{
Daniel P. Berrange 42af21
+    VncState *vs = opaque;
Daniel P. Berrange 42af21
+    long ret;
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    if (vs->sasl.conn && vs->sasl.runSSF)
Daniel P. Berrange 42af21
+        ret = vnc_client_read_sasl(vs);
Daniel P. Berrange 42af21
+    else
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
+        ret = vnc_client_read_plain(vs);
Daniel P. Berrange 42af21
     if (!ret)
Daniel P. Berrange 42af21
 	return;
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
-    vs->input.offset += ret;
Daniel P. Berrange 42af21
-
Daniel P. Berrange 42af21
     while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
Daniel P. Berrange 42af21
 	size_t len = vs->read_handler_expect;
Daniel P. Berrange 42af21
 	int ret;
Daniel P. Berrange 42af21
@@ -1722,6 +1855,13 @@ static int protocol_client_auth(VncState
Daniel P. Berrange 42af21
            break;
Daniel P. Berrange 42af21
 #endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+       case VNC_AUTH_SASL:
Daniel P. Berrange 42af21
+           VNC_DEBUG("Accept SASL auth\n");
Daniel P. Berrange 42af21
+           start_auth_sasl(vs);
Daniel P. Berrange 42af21
+           break;
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
        default: /* Should not be possible, but just in case */
Daniel P. Berrange 42af21
            VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
Daniel P. Berrange 42af21
            vnc_write_u8(vs, 1);
Daniel P. Berrange 42af21
@@ -1923,6 +2063,10 @@ int vnc_display_open(DisplayState *ds, c
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
     int tls = 0, x509 = 0;
Daniel P. Berrange 42af21
 #endif
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    int sasl = 0;
Daniel P. Berrange 42af21
+    int saslErr;
Daniel P. Berrange 42af21
+#endif
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
     if (!vnc_display)
Daniel P. Berrange 42af21
         return -1;
Daniel P. Berrange 42af21
@@ -1942,6 +2086,10 @@ int vnc_display_open(DisplayState *ds, c
Daniel P. Berrange 42af21
 	    reverse = 1;
Daniel P. Berrange 42af21
 	} else if (strncmp(options, "to=", 3) == 0) {
Daniel P. Berrange 42af21
             to_port = atoi(options+3) + 5900;
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+	} else if (strncmp(options, "sasl", 4) == 0) {
Daniel P. Berrange 42af21
+	    sasl = 1; /* Require SASL auth */
Daniel P. Berrange 42af21
+#endif
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
 	} else if (strncmp(options, "tls", 3) == 0) {
Daniel P. Berrange 42af21
 	    tls = 1; /* Require TLS */
Daniel P. Berrange 42af21
@@ -1978,6 +2126,22 @@ int vnc_display_open(DisplayState *ds, c
Daniel P. Berrange 42af21
 	}
Daniel P. Berrange 42af21
     }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+    /*
Daniel P. Berrange 42af21
+     * Combinations we support here:
Daniel P. Berrange 42af21
+     *
Daniel P. Berrange 42af21
+     *  - no-auth                (clear text, no auth)
Daniel P. Berrange 42af21
+     *  - password               (clear text, weak auth)
Daniel P. Berrange 42af21
+     *  - sasl                   (encrypt, good auth *IF* using Kerberos via GSSAPI)
Daniel P. Berrange 42af21
+     *  - tls                    (encrypt, weak anonymous creds, no auth)
Daniel P. Berrange 42af21
+     *  - tls + password         (encrypt, weak anonymous creds, weak auth)
Daniel P. Berrange 42af21
+     *  - tls + sasl             (encrypt, weak anonymous creds, good auth)
Daniel P. Berrange 42af21
+     *  - tls + x509             (encrypt, good x509 creds, no auth)
Daniel P. Berrange 42af21
+     *  - tls + x509 + password  (encrypt, good x509 creds, weak auth)
Daniel P. Berrange 42af21
+     *  - tls + x509 + sasl      (encrypt, good x509 creds, good auth)
Daniel P. Berrange 42af21
+     *
Daniel P. Berrange 42af21
+     * NB1. TLS is a stackable auth scheme.
Daniel P. Berrange 42af21
+     * NB2. the x509 schemes have option to validate a client cert dname
Daniel P. Berrange 42af21
+     */
Daniel P. Berrange 42af21
     if (password) {
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
 	if (tls) {
Daniel P. Berrange 42af21
@@ -1990,13 +2154,34 @@ int vnc_display_open(DisplayState *ds, c
Daniel P. Berrange 42af21
 		vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
Daniel P. Berrange 42af21
 	    }
Daniel P. Berrange 42af21
 	} else {
Daniel P. Berrange 42af21
-#endif
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
 	    VNC_DEBUG("Initializing VNC server with password auth\n");
Daniel P. Berrange 42af21
 	    vs->auth = VNC_AUTH_VNC;
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
 	    vs->subauth = VNC_AUTH_INVALID;
Daniel P. Berrange 42af21
 	}
Daniel P. Berrange 42af21
-#endif
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    } else if (sasl) {
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
+        if (tls) {
Daniel P. Berrange 42af21
+            vs->auth = VNC_AUTH_VENCRYPT;
Daniel P. Berrange 42af21
+            if (x509) {
Daniel P. Berrange 42af21
+		VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
Daniel P. Berrange 42af21
+                vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
Daniel P. Berrange 42af21
+            } else {
Daniel P. Berrange 42af21
+		VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
Daniel P. Berrange 42af21
+                vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
Daniel P. Berrange 42af21
+            }
Daniel P. Berrange 42af21
+        } else {
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
+	    VNC_DEBUG("Initializing VNC server with SASL auth\n");
Daniel P. Berrange 42af21
+            vs->auth = VNC_AUTH_SASL;
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
+            vs->subauth = VNC_AUTH_INVALID;
Daniel P. Berrange 42af21
+        }
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_TLS */
Daniel P. Berrange 42af21
+#endif /* CONFIG_VNC_SASL */
Daniel P. Berrange 42af21
     } else {
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
 	if (tls) {
Daniel P. Berrange 42af21
@@ -2018,6 +2203,16 @@ int vnc_display_open(DisplayState *ds, c
Daniel P. Berrange 42af21
 #endif
Daniel P. Berrange 42af21
     }
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
Daniel P. Berrange 42af21
+        fprintf(stderr, "Failed to initialize SASL auth %s",
Daniel P. Berrange 42af21
+                sasl_errstring(saslErr, NULL, NULL));
Daniel P. Berrange 42af21
+        free(vs->display);
Daniel P. Berrange 42af21
+        vs->display = NULL;
Daniel P. Berrange 42af21
+        return -1;
Daniel P. Berrange 42af21
+    }
Daniel P. Berrange 42af21
+#endif
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
     if (reverse) {
Daniel P. Berrange 42af21
         /* connect to viewer */
Daniel P. Berrange 42af21
         if (strncmp(display, "unix:", 5) == 0)
Daniel P. Berrange 42af21
diff -r 6981d4d832a9 vnc.h
Daniel P. Berrange 42af21
--- a/vnc.h	Mon Mar 02 11:13:33 2009 +0000
Daniel P. Berrange 42af21
+++ b/vnc.h	Mon Mar 02 11:13:38 2009 +0000
Daniel P. Berrange 42af21
@@ -79,6 +79,10 @@ typedef struct VncDisplay VncDisplay;
Daniel P. Berrange 42af21
 #include "vnc-tls.h"
Daniel P. Berrange 42af21
 #include "vnc-auth-vencrypt.h"
Daniel P. Berrange 42af21
 #endif
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+#include "vnc-auth-sasl.h"
Daniel P. Berrange 42af21
+#endif
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 struct VncDisplay
Daniel P. Berrange 42af21
 {
Daniel P. Berrange 42af21
@@ -118,10 +122,12 @@ struct VncState
Daniel P. Berrange 42af21
     int minor;
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
Daniel P. Berrange 42af21
-
Daniel P. Berrange 42af21
 #ifdef CONFIG_VNC_TLS
Daniel P. Berrange 42af21
     VncStateTLS tls;
Daniel P. Berrange 42af21
 #endif
Daniel P. Berrange 42af21
+#ifdef CONFIG_VNC_SASL
Daniel P. Berrange 42af21
+    VncStateSASL sasl;
Daniel P. Berrange 42af21
+#endif
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
     Buffer output;
Daniel P. Berrange 42af21
     Buffer input;
Daniel P. Berrange 42af21
@@ -160,8 +166,9 @@ enum {
Daniel P. Berrange 42af21
     VNC_AUTH_RA2NE = 6,
Daniel P. Berrange 42af21
     VNC_AUTH_TIGHT = 16,
Daniel P. Berrange 42af21
     VNC_AUTH_ULTRA = 17,
Daniel P. Berrange 42af21
-    VNC_AUTH_TLS = 18,
Daniel P. Berrange 42af21
-    VNC_AUTH_VENCRYPT = 19
Daniel P. Berrange 42af21
+    VNC_AUTH_TLS = 18,      /* Supported in GTK-VNC & VINO */
Daniel P. Berrange 42af21
+    VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */
Daniel P. Berrange 42af21
+    VNC_AUTH_SASL = 20,     /* Supported in GTK-VNC & VINO */
Daniel P. Berrange 42af21
 };
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 enum {
Daniel P. Berrange 42af21
@@ -172,6 +179,8 @@ enum {
Daniel P. Berrange 42af21
     VNC_AUTH_VENCRYPT_X509NONE = 260,
Daniel P. Berrange 42af21
     VNC_AUTH_VENCRYPT_X509VNC = 261,
Daniel P. Berrange 42af21
     VNC_AUTH_VENCRYPT_X509PLAIN = 262,
Daniel P. Berrange 42af21
+    VNC_AUTH_VENCRYPT_X509SASL = 263,
Daniel P. Berrange 42af21
+    VNC_AUTH_VENCRYPT_TLSSASL = 264,
Daniel P. Berrange 42af21
 };
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
@@ -255,6 +264,8 @@ enum {
Daniel P. Berrange 42af21
 void vnc_client_read(void *opaque);
Daniel P. Berrange 42af21
 void vnc_client_write(void *opaque);
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
Daniel P. Berrange 42af21
+long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 /* Protocol I/O functions */
Daniel P. Berrange 42af21
 void vnc_write(VncState *vs, const void *data, size_t len);
Daniel P. Berrange 42af21
@@ -274,8 +285,22 @@ uint32_t read_u32(uint8_t *data, size_t 
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 /* Protocol stage functions */
Daniel P. Berrange 42af21
 void vnc_client_error(VncState *vs);
Daniel P. Berrange 42af21
+int vnc_client_io_error(VncState *vs, int ret, int last_errno);
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
 void start_client_init(VncState *vs);
Daniel P. Berrange 42af21
 void start_auth_vnc(VncState *vs);
Daniel P. Berrange 42af21
 
Daniel P. Berrange 42af21
+/* Buffer management */
Daniel P. Berrange 42af21
+void buffer_reserve(Buffer *buffer, size_t len);
Daniel P. Berrange 42af21
+int buffer_empty(Buffer *buffer);
Daniel P. Berrange 42af21
+uint8_t *buffer_end(Buffer *buffer);
Daniel P. Berrange 42af21
+void buffer_reset(Buffer *buffer);
Daniel P. Berrange 42af21
+void buffer_append(Buffer *buffer, const void *data, size_t len);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+/* Misc helpers */
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
+char *vnc_socket_local_addr(const char *format, int fd);
Daniel P. Berrange 42af21
+char *vnc_socket_remote_addr(const char *format, int fd);
Daniel P. Berrange 42af21
+
Daniel P. Berrange 42af21
 #endif /* __QEMU_VNC_H */