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