Commit 2f9606b3736c3be4dbd606c46525c7b770ced119

Authored by aliguori
1 parent 5fb6c7a8

Add SASL authentication support ("Daniel P. Berrange")

This patch adds the new SASL authentication protocol to the VNC server.

It is enabled by setting the 'sasl' flag when launching VNC. SASL can
optionally provide encryption via its SSF layer, if a suitable mechanism
is configured (eg, GSSAPI/Kerberos, or Digest-MD5).  If an SSF layer is
not available, then it should be combined with the x509 VNC authentication
protocol which provides encryption.

eg, if using GSSAPI

   qemu -vnc localhost:1,sasl

eg if using  TLS/x509 for encryption

   qemu -vnc localhost:1,sasl,tls,x509


By default the Cyrus SASL library will look for its configuration in
the file /etc/sasl2/qemu.conf.  For non-root users, this can be overridden
by setting the SASL_CONF_PATH environment variable, eg to make it look in
$HOME/.sasl2.  NB unprivileged users may not have access to the full range
of SASL mechanisms, since some of them require some administrative privileges
to configure. The patch includes an example SASL configuration file which
illustrates config for GSSAPI and Digest-MD5, though it should be noted that
the latter is not really considered secure any more.

Most of the SASL authentication code is located in a separate source file,
vnc-auth-sasl.c.  The main vnc.c file only contains minimal integration
glue, specifically parsing of command line flags / setup, and calls to
start the SASL auth process, to do encoding/decoding for data.

There are several possible stacks for reading & writing of data, depending
on the combo of VNC authentication methods in use

 - Clear.    read/write straight to socket
 - TLS.      read/write via GNUTLS helpers
 - SASL.     encode/decode via SASL SSF layer, then read/write to socket
 - SASL+TLS. encode/decode via SASL SSF layer, then read/write via GNUTLS

Hence, the vnc_client_read & vnc_client_write methods have been refactored
a little.

   vnc_client_read:  main entry point for reading, calls either

       - vnc_client_read_plain   reading, with no intermediate decoding
       - vnc_client_read_sasl    reading, with SASL SSF decoding

   These two methods, then call vnc_client_read_buf(). This decides
   whether to write to the socket directly or write via GNUTLS.

The situation is the same for writing data. More extensive comments
have been added in the code / patch. The vnc_client_read_sasl and
vnc_client_write_sasl method implementations live in the separate
vnc-auth-sasl.c file.

The state required for the SASL auth mechanism is kept in a separate
VncStateSASL struct, defined in vnc-auth-sasl.h and included in the
main VncState.

The configure script probes for SASL and automatically enables it
if found, unless --disable-vnc-sasl was given to override it.


 Makefile            |    7 
 Makefile.target     |    5 
 b/qemu.sasl         |   34 ++
 b/vnc-auth-sasl.c   |  626 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 b/vnc-auth-sasl.h   |   67 +++++
 configure           |   34 ++
 qemu-doc.texi       |   97 ++++++++
 vnc-auth-vencrypt.c |   12 
 vnc.c               |  249 ++++++++++++++++++--
 vnc.h               |   31 ++
 10 files changed, 1129 insertions(+), 33 deletions(-)

   Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6724 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
... ... @@ -148,6 +148,9 @@ OBJS+=vnc.o d3des.o
148 148 ifdef CONFIG_VNC_TLS
149 149 OBJS+=vnc-tls.o vnc-auth-vencrypt.o
150 150 endif
  151 +ifdef CONFIG_VNC_SASL
  152 +OBJS+=vnc-auth-sasl.o
  153 +endif
151 154  
152 155 ifdef CONFIG_COCOA
153 156 OBJS+=cocoa.o
... ... @@ -171,7 +174,7 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
171 174  
172 175 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
173 176  
174   -vnc.h: vnc-tls.h vnc-auth-vencrypt.h keymaps.h
  177 +vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
175 178  
176 179 vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h
177 180  
... ... @@ -181,6 +184,8 @@ vnc-tls.o: vnc-tls.c vnc.h
181 184  
182 185 vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
183 186  
  187 +vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h
  188 +
184 189 curses.o: curses.c keymaps.h curses_keys.h
185 190  
186 191 bt-host.o: CFLAGS += $(CONFIG_BLUEZ_CFLAGS)
... ...
Makefile.target
... ... @@ -554,6 +554,11 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
554 554 LIBS += $(CONFIG_VNC_TLS_LIBS)
555 555 endif
556 556  
  557 +ifdef CONFIG_VNC_SASL
  558 +CPPFLAGS += $(CONFIG_VNC_SASL_CFLAGS)
  559 +LIBS += $(CONFIG_VNC_SASL_LIBS)
  560 +endif
  561 +
557 562 ifdef CONFIG_BLUEZ
558 563 LIBS += $(CONFIG_BLUEZ_LIBS)
559 564 endif
... ...
configure
... ... @@ -164,6 +164,7 @@ fmod_lib=&quot;&quot;
164 164 fmod_inc=""
165 165 oss_lib=""
166 166 vnc_tls="yes"
  167 +vnc_sasl="yes"
167 168 bsd="no"
168 169 linux="no"
169 170 solaris="no"
... ... @@ -388,6 +389,8 @@ for opt do
388 389 ;;
389 390 --disable-vnc-tls) vnc_tls="no"
390 391 ;;
  392 + --disable-vnc-sasl) vnc_sasl="no"
  393 + ;;
391 394 --disable-slirp) slirp="no"
392 395 ;;
393 396 --disable-vde) vde="no"
... ... @@ -545,6 +548,7 @@ echo &quot; Available cards: $audio_possible_cards&quot;
545 548 echo " --enable-mixemu enable mixer emulation"
546 549 echo " --disable-brlapi disable BrlAPI"
547 550 echo " --disable-vnc-tls disable TLS encryption for VNC server"
  551 +echo " --disable-vnc-sasl disable SASL encryption for VNC server"
548 552 echo " --disable-curses disable curses output"
549 553 echo " --disable-bluez disable bluez stack connectivity"
550 554 echo " --disable-kvm disable KVM acceleration support"
... ... @@ -839,6 +843,25 @@ EOF
839 843 fi
840 844  
841 845 ##########################################
  846 +# VNC SASL detection
  847 +if test "$vnc_sasl" = "yes" ; then
  848 +cat > $TMPC <<EOF
  849 +#include <sasl/sasl.h>
  850 +#include <stdio.h>
  851 +int main(void) { sasl_server_init(NULL, "qemu"); return 0; }
  852 +EOF
  853 + # Assuming Cyrus-SASL installed in /usr prefix
  854 + vnc_sasl_cflags=""
  855 + vnc_sasl_libs="-lsasl2"
  856 + if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $vnc_sasl_cflags $TMPC \
  857 + $vnc_sasl_libs 2> /dev/null ; then
  858 + :
  859 + else
  860 + vnc_sasl="no"
  861 + fi
  862 +fi
  863 +
  864 +##########################################
842 865 # vde libraries probe
843 866 if test "$vde" = "yes" ; then
844 867 cat > $TMPC << EOF
... ... @@ -1146,6 +1169,11 @@ if test &quot;$vnc_tls&quot; = &quot;yes&quot; ; then
1146 1169 echo " TLS CFLAGS $vnc_tls_cflags"
1147 1170 echo " TLS LIBS $vnc_tls_libs"
1148 1171 fi
  1172 +echo "VNC SASL support $vnc_sasl"
  1173 +if test "$vnc_sasl" = "yes" ; then
  1174 + echo " SASL CFLAGS $vnc_sasl_cflags"
  1175 + echo " SASL LIBS $vnc_sasl_libs"
  1176 +fi
1149 1177 if test -n "$sparc_cpu"; then
1150 1178 echo "Target Sparc Arch $sparc_cpu"
1151 1179 fi
... ... @@ -1387,6 +1415,12 @@ if test &quot;$vnc_tls&quot; = &quot;yes&quot; ; then
1387 1415 echo "CONFIG_VNC_TLS_LIBS=$vnc_tls_libs" >> $config_mak
1388 1416 echo "#define CONFIG_VNC_TLS 1" >> $config_h
1389 1417 fi
  1418 +if test "$vnc_sasl" = "yes" ; then
  1419 + echo "CONFIG_VNC_SASL=yes" >> $config_mak
  1420 + echo "CONFIG_VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_mak
  1421 + echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
  1422 + echo "#define CONFIG_VNC_SASL 1" >> $config_h
  1423 +fi
1390 1424 qemu_version=`head $source_path/VERSION`
1391 1425 echo "VERSION=$qemu_version" >>$config_mak
1392 1426 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
... ...
qemu-doc.texi
... ... @@ -616,6 +616,21 @@ path following this option specifies where the x509 certificates are to
616 616 be loaded from. See the @ref{vnc_security} section for details on generating
617 617 certificates.
618 618  
  619 +@item sasl
  620 +
  621 +Require that the client use SASL to authenticate with the VNC server.
  622 +The exact choice of authentication method used is controlled from the
  623 +system / user's SASL configuration file for the 'qemu' service. This
  624 +is typically found in /etc/sasl2/qemu.conf. If running QEMU as an
  625 +unprivileged user, an environment variable SASL_CONF_PATH can be used
  626 +to make it search alternate locations for the service config.
  627 +While some SASL auth methods can also provide data encryption (eg GSSAPI),
  628 +it is recommended that SASL always be combined with the 'tls' and
  629 +'x509' settings to enable use of SSL and server certificates. This
  630 +ensures a data encryption preventing compromise of authentication
  631 +credentials. See the @ref{vnc_security} section for details on using
  632 +SASL authentication.
  633 +
619 634 @end table
620 635  
621 636 @end table
... ... @@ -2061,7 +2076,10 @@ considerations depending on the deployment scenarios.
2061 2076 * vnc_sec_certificate::
2062 2077 * vnc_sec_certificate_verify::
2063 2078 * vnc_sec_certificate_pw::
  2079 +* vnc_sec_sasl::
  2080 +* vnc_sec_certificate_sasl::
2064 2081 * vnc_generate_cert::
  2082 +* vnc_setup_sasl::
2065 2083 @end menu
2066 2084 @node vnc_sec_none
2067 2085 @subsection Without passwords
... ... @@ -2144,6 +2162,41 @@ Password: ********
2144 2162 (qemu)
2145 2163 @end example
2146 2164  
  2165 +
  2166 +@node vnc_sec_sasl
  2167 +@subsection With SASL authentication
  2168 +
  2169 +The SASL authentication method is a VNC extension, that provides an
  2170 +easily extendable, pluggable authentication method. This allows for
  2171 +integration with a wide range of authentication mechanisms, such as
  2172 +PAM, GSSAPI/Kerberos, LDAP, SQL databases, one-time keys and more.
  2173 +The strength of the authentication depends on the exact mechanism
  2174 +configured. If the chosen mechanism also provides a SSF layer, then
  2175 +it will encrypt the datastream as well.
  2176 +
  2177 +Refer to the later docs on how to choose the exact SASL mechanism
  2178 +used for authentication, but assuming use of one supporting SSF,
  2179 +then QEMU can be launched with:
  2180 +
  2181 +@example
  2182 +qemu [...OPTIONS...] -vnc :1,sasl -monitor stdio
  2183 +@end example
  2184 +
  2185 +@node vnc_sec_certificate_sasl
  2186 +@subsection With x509 certificates and SASL authentication
  2187 +
  2188 +If the desired SASL authentication mechanism does not supported
  2189 +SSF layers, then it is strongly advised to run it in combination
  2190 +with TLS and x509 certificates. This provides securely encrypted
  2191 +data stream, avoiding risk of compromising of the security
  2192 +credentials. This can be enabled, by combining the 'sasl' option
  2193 +with the aforementioned TLS + x509 options:
  2194 +
  2195 +@example
  2196 +qemu [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio
  2197 +@end example
  2198 +
  2199 +
2147 2200 @node vnc_generate_cert
2148 2201 @subsection Generating certificates for VNC
2149 2202  
... ... @@ -2255,6 +2308,50 @@ EOF
2255 2308 The @code{client-key.pem} and @code{client-cert.pem} files should now be securely
2256 2309 copied to the client for which they were generated.
2257 2310  
  2311 +
  2312 +@node vnc_setup_sasl
  2313 +
  2314 +@subsection Configuring SASL mechanisms
  2315 +
  2316 +The following documentation assumes use of the Cyrus SASL implementation on a
  2317 +Linux host, but the principals should apply to any other SASL impl. When SASL
  2318 +is enabled, the mechanism configuration will be loaded from system default
  2319 +SASL service config /etc/sasl2/qemu.conf. If running QEMU as an
  2320 +unprivileged user, an environment variable SASL_CONF_PATH can be used
  2321 +to make it search alternate locations for the service config.
  2322 +
  2323 +The default configuration might contain
  2324 +
  2325 +@example
  2326 +mech_list: digest-md5
  2327 +sasldb_path: /etc/qemu/passwd.db
  2328 +@end example
  2329 +
  2330 +This says to use the 'Digest MD5' mechanism, which is similar to the HTTP
  2331 +Digest-MD5 mechanism. The list of valid usernames & passwords is maintained
  2332 +in the /etc/qemu/passwd.db file, and can be updated using the saslpasswd2
  2333 +command. While this mechanism is easy to configure and use, it is not
  2334 +considered secure by modern standards, so only suitable for developers /
  2335 +ad-hoc testing.
  2336 +
  2337 +A more serious deployment might use Kerberos, which is done with the 'gssapi'
  2338 +mechanism
  2339 +
  2340 +@example
  2341 +mech_list: gssapi
  2342 +keytab: /etc/qemu/krb5.tab
  2343 +@end example
  2344 +
  2345 +For this to work the administrator of your KDC must generate a Kerberos
  2346 +principal for the server, with a name of 'qemu/somehost.example.com@@EXAMPLE.COM'
  2347 +replacing 'somehost.example.com' with the fully qualified host name of the
  2348 +machine running QEMU, and 'EXAMPLE.COM' with the Keberos Realm.
  2349 +
  2350 +Other configurations will be left as an exercise for the reader. It should
  2351 +be noted that only Digest-MD5 and GSSAPI provides a SSF layer for data
  2352 +encryption. For all other mechanisms, VNC should always be configured to
  2353 +use TLS and x509 certificates to protect security credentials from snooping.
  2354 +
2258 2355 @node gdb_usage
2259 2356 @section GDB usage
2260 2357  
... ...
qemu.sasl 0 โ†’ 100644
  1 +# If you want to use the non-TLS socket, then you *must* include
  2 +# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only
  3 +# ones that can offer session encryption as well as authentication.
  4 +#
  5 +# If you're only using TLS, then you can turn on any mechanisms
  6 +# you like for authentication, because TLS provides the encryption
  7 +#
  8 +# Default to a simple username+password mechanism
  9 +# NB digest-md5 is no longer considered secure by current standards
  10 +mech_list: digest-md5
  11 +
  12 +# Before you can use GSSAPI, you need a service principle on the
  13 +# KDC server for libvirt, and that to be exported to the keytab
  14 +# file listed below
  15 +#mech_list: gssapi
  16 +#
  17 +# You can also list many mechanisms at once, then the user can choose
  18 +# by adding '?auth=sasl.gssapi' to their libvirt URI, eg
  19 +# qemu+tcp://hostname/system?auth=sasl.gssapi
  20 +#mech_list: digest-md5 gssapi
  21 +
  22 +# Some older builds of MIT kerberos on Linux ignore this option &
  23 +# instead need KRB5_KTNAME env var.
  24 +# For modern Linux, and other OS, this should be sufficient
  25 +keytab: /etc/qemu/krb5.tab
  26 +
  27 +# If using digest-md5 for username/passwds, then this is the file
  28 +# containing the passwds. Use 'saslpasswd2 -a qemu [username]'
  29 +# to add entries, and 'sasldblistusers2 -a qemu' to browse it
  30 +sasldb_path: /etc/qemu/passwd.db
  31 +
  32 +
  33 +auxprop_plugin: sasldb
  34 +
... ...
vnc-auth-sasl.c 0 โ†’ 100644
  1 +/*
  2 + * QEMU VNC display driver: SASL auth protocol
  3 + *
  4 + * Copyright (C) 2009 Red Hat, Inc
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include "vnc.h"
  26 +
  27 +/* Max amount of data we send/recv for SASL steps to prevent DOS */
  28 +#define SASL_DATA_MAX_LEN (1024 * 1024)
  29 +
  30 +
  31 +void vnc_sasl_client_cleanup(VncState *vs)
  32 +{
  33 + if (vs->sasl.conn) {
  34 + vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0;
  35 + vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
  36 + vs->sasl.encoded = NULL;
  37 + free(vs->sasl.username);
  38 + free(vs->sasl.mechlist);
  39 + vs->sasl.username = vs->sasl.mechlist = NULL;
  40 + sasl_dispose(&vs->sasl.conn);
  41 + vs->sasl.conn = NULL;
  42 + }
  43 +}
  44 +
  45 +
  46 +long vnc_client_write_sasl(VncState *vs)
  47 +{
  48 + long ret;
  49 +
  50 + VNC_DEBUG("Write SASL: Pending output %p size %d offset %d Encoded: %p size %d offset %d\n",
  51 + vs->output.buffer, vs->output.capacity, vs->output.offset,
  52 + vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
  53 +
  54 + if (!vs->sasl.encoded) {
  55 + int err;
  56 + err = sasl_encode(vs->sasl.conn,
  57 + (char *)vs->output.buffer,
  58 + vs->output.offset,
  59 + (const char **)&vs->sasl.encoded,
  60 + &vs->sasl.encodedLength);
  61 + if (err != SASL_OK)
  62 + return vnc_client_io_error(vs, -1, EIO);
  63 +
  64 + vs->sasl.encodedOffset = 0;
  65 + }
  66 +
  67 + ret = vnc_client_write_buf(vs,
  68 + vs->sasl.encoded + vs->sasl.encodedOffset,
  69 + vs->sasl.encodedLength - vs->sasl.encodedOffset);
  70 + if (!ret)
  71 + return 0;
  72 +
  73 + vs->sasl.encodedOffset += ret;
  74 + if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
  75 + vs->output.offset = 0;
  76 + vs->sasl.encoded = NULL;
  77 + vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
  78 + }
  79 +
  80 + /* Can't merge this block with one above, because
  81 + * someone might have written more unencrypted
  82 + * data in vs->output while we were processing
  83 + * SASL encoded output
  84 + */
  85 + if (vs->output.offset == 0) {
  86 + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
  87 + }
  88 +
  89 + return ret;
  90 +}
  91 +
  92 +
  93 +long vnc_client_read_sasl(VncState *vs)
  94 +{
  95 + long ret;
  96 + uint8_t encoded[4096];
  97 + const char *decoded;
  98 + unsigned int decodedLen;
  99 + int err;
  100 +
  101 + ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
  102 + if (!ret)
  103 + return 0;
  104 +
  105 + err = sasl_decode(vs->sasl.conn,
  106 + (char *)encoded, ret,
  107 + &decoded, &decodedLen);
  108 +
  109 + if (err != SASL_OK)
  110 + return vnc_client_io_error(vs, -1, -EIO);
  111 + VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
  112 + encoded, ret, decoded, decodedLen);
  113 + buffer_reserve(&vs->input, decodedLen);
  114 + buffer_append(&vs->input, decoded, decodedLen);
  115 + return decodedLen;
  116 +}
  117 +
  118 +
  119 +static int vnc_auth_sasl_check_access(VncState *vs)
  120 +{
  121 + const void *val;
  122 + int err;
  123 +
  124 + err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
  125 + if (err != SASL_OK) {
  126 + VNC_DEBUG("cannot query SASL username on connection %d (%s)\n",
  127 + err, sasl_errstring(err, NULL, NULL));
  128 + return -1;
  129 + }
  130 + if (val == NULL) {
  131 + VNC_DEBUG("no client username was found\n");
  132 + return -1;
  133 + }
  134 + VNC_DEBUG("SASL client username %s\n", (const char *)val);
  135 +
  136 + vs->sasl.username = qemu_strdup((const char*)val);
  137 +
  138 + return 0;
  139 +}
  140 +
  141 +static int vnc_auth_sasl_check_ssf(VncState *vs)
  142 +{
  143 + const void *val;
  144 + int err, ssf;
  145 +
  146 + if (!vs->sasl.wantSSF)
  147 + return 1;
  148 +
  149 + err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
  150 + if (err != SASL_OK)
  151 + return 0;
  152 +
  153 + ssf = *(const int *)val;
  154 + VNC_DEBUG("negotiated an SSF of %d\n", ssf);
  155 + if (ssf < 56)
  156 + return 0; /* 56 is good for Kerberos */
  157 +
  158 + /* Only setup for read initially, because we're about to send an RPC
  159 + * reply which must be in plain text. When the next incoming RPC
  160 + * arrives, we'll switch on writes too
  161 + *
  162 + * cf qemudClientReadSASL in qemud.c
  163 + */
  164 + vs->sasl.runSSF = 1;
  165 +
  166 + /* We have a SSF that's good enough */
  167 + return 1;
  168 +}
  169 +
  170 +/*
  171 + * Step Msg
  172 + *
  173 + * Input from client:
  174 + *
  175 + * u32 clientin-length
  176 + * u8-array clientin-string
  177 + *
  178 + * Output to client:
  179 + *
  180 + * u32 serverout-length
  181 + * u8-array serverout-strin
  182 + * u8 continue
  183 + */
  184 +
  185 +static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
  186 +
  187 +static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
  188 +{
  189 + uint32_t datalen = len;
  190 + const char *serverout;
  191 + unsigned int serveroutlen;
  192 + int err;
  193 + char *clientdata = NULL;
  194 +
  195 + /* NB, distinction of NULL vs "" is *critical* in SASL */
  196 + if (datalen) {
  197 + clientdata = (char*)data;
  198 + clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
  199 + datalen--; /* Don't count NULL byte when passing to _start() */
  200 + }
  201 +
  202 + VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
  203 + clientdata, datalen);
  204 + err = sasl_server_step(vs->sasl.conn,
  205 + clientdata,
  206 + datalen,
  207 + &serverout,
  208 + &serveroutlen);
  209 + if (err != SASL_OK &&
  210 + err != SASL_CONTINUE) {
  211 + VNC_DEBUG("sasl step failed %d (%s)\n",
  212 + err, sasl_errdetail(vs->sasl.conn));
  213 + sasl_dispose(&vs->sasl.conn);
  214 + vs->sasl.conn = NULL;
  215 + goto authabort;
  216 + }
  217 +
  218 + if (serveroutlen > SASL_DATA_MAX_LEN) {
  219 + VNC_DEBUG("sasl step reply data too long %d\n",
  220 + serveroutlen);
  221 + sasl_dispose(&vs->sasl.conn);
  222 + vs->sasl.conn = NULL;
  223 + goto authabort;
  224 + }
  225 +
  226 + VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
  227 + serveroutlen, serverout ? 0 : 1);
  228 +
  229 + if (serveroutlen) {
  230 + vnc_write_u32(vs, serveroutlen + 1);
  231 + vnc_write(vs, serverout, serveroutlen + 1);
  232 + } else {
  233 + vnc_write_u32(vs, 0);
  234 + }
  235 +
  236 + /* Whether auth is complete */
  237 + vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
  238 +
  239 + if (err == SASL_CONTINUE) {
  240 + VNC_DEBUG("%s", "Authentication must continue\n");
  241 + /* Wait for step length */
  242 + vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
  243 + } else {
  244 + if (!vnc_auth_sasl_check_ssf(vs)) {
  245 + VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
  246 + goto authreject;
  247 + }
  248 +
  249 + /* Check username whitelist ACL */
  250 + if (vnc_auth_sasl_check_access(vs) < 0) {
  251 + VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
  252 + goto authreject;
  253 + }
  254 +
  255 + VNC_DEBUG("Authentication successful %d\n", vs->csock);
  256 + vnc_write_u32(vs, 0); /* Accept auth */
  257 + /*
  258 + * Delay writing in SSF encoded mode until pending output
  259 + * buffer is written
  260 + */
  261 + if (vs->sasl.runSSF)
  262 + vs->sasl.waitWriteSSF = vs->output.offset;
  263 + start_client_init(vs);
  264 + }
  265 +
  266 + return 0;
  267 +
  268 + authreject:
  269 + vnc_write_u32(vs, 1); /* Reject auth */
  270 + vnc_write_u32(vs, sizeof("Authentication failed"));
  271 + vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
  272 + vnc_flush(vs);
  273 + vnc_client_error(vs);
  274 + return -1;
  275 +
  276 + authabort:
  277 + vnc_client_error(vs);
  278 + return -1;
  279 +}
  280 +
  281 +static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
  282 +{
  283 + uint32_t steplen = read_u32(data, 0);
  284 + VNC_DEBUG("Got client step len %d\n", steplen);
  285 + if (steplen > SASL_DATA_MAX_LEN) {
  286 + VNC_DEBUG("Too much SASL data %d\n", steplen);
  287 + vnc_client_error(vs);
  288 + return -1;
  289 + }
  290 +
  291 + if (steplen == 0)
  292 + return protocol_client_auth_sasl_step(vs, NULL, 0);
  293 + else
  294 + vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
  295 + return 0;
  296 +}
  297 +
  298 +/*
  299 + * Start Msg
  300 + *
  301 + * Input from client:
  302 + *
  303 + * u32 clientin-length
  304 + * u8-array clientin-string
  305 + *
  306 + * Output to client:
  307 + *
  308 + * u32 serverout-length
  309 + * u8-array serverout-strin
  310 + * u8 continue
  311 + */
  312 +
  313 +#define SASL_DATA_MAX_LEN (1024 * 1024)
  314 +
  315 +static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
  316 +{
  317 + uint32_t datalen = len;
  318 + const char *serverout;
  319 + unsigned int serveroutlen;
  320 + int err;
  321 + char *clientdata = NULL;
  322 +
  323 + /* NB, distinction of NULL vs "" is *critical* in SASL */
  324 + if (datalen) {
  325 + clientdata = (char*)data;
  326 + clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
  327 + datalen--; /* Don't count NULL byte when passing to _start() */
  328 + }
  329 +
  330 + VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
  331 + vs->sasl.mechlist, clientdata, datalen);
  332 + err = sasl_server_start(vs->sasl.conn,
  333 + vs->sasl.mechlist,
  334 + clientdata,
  335 + datalen,
  336 + &serverout,
  337 + &serveroutlen);
  338 + if (err != SASL_OK &&
  339 + err != SASL_CONTINUE) {
  340 + VNC_DEBUG("sasl start failed %d (%s)\n",
  341 + err, sasl_errdetail(vs->sasl.conn));
  342 + sasl_dispose(&vs->sasl.conn);
  343 + vs->sasl.conn = NULL;
  344 + goto authabort;
  345 + }
  346 + if (serveroutlen > SASL_DATA_MAX_LEN) {
  347 + VNC_DEBUG("sasl start reply data too long %d\n",
  348 + serveroutlen);
  349 + sasl_dispose(&vs->sasl.conn);
  350 + vs->sasl.conn = NULL;
  351 + goto authabort;
  352 + }
  353 +
  354 + VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
  355 + serveroutlen, serverout ? 0 : 1);
  356 +
  357 + if (serveroutlen) {
  358 + vnc_write_u32(vs, serveroutlen + 1);
  359 + vnc_write(vs, serverout, serveroutlen + 1);
  360 + } else {
  361 + vnc_write_u32(vs, 0);
  362 + }
  363 +
  364 + /* Whether auth is complete */
  365 + vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
  366 +
  367 + if (err == SASL_CONTINUE) {
  368 + VNC_DEBUG("%s", "Authentication must continue\n");
  369 + /* Wait for step length */
  370 + vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
  371 + } else {
  372 + if (!vnc_auth_sasl_check_ssf(vs)) {
  373 + VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
  374 + goto authreject;
  375 + }
  376 +
  377 + /* Check username whitelist ACL */
  378 + if (vnc_auth_sasl_check_access(vs) < 0) {
  379 + VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
  380 + goto authreject;
  381 + }
  382 +
  383 + VNC_DEBUG("Authentication successful %d\n", vs->csock);
  384 + vnc_write_u32(vs, 0); /* Accept auth */
  385 + start_client_init(vs);
  386 + }
  387 +
  388 + return 0;
  389 +
  390 + authreject:
  391 + vnc_write_u32(vs, 1); /* Reject auth */
  392 + vnc_write_u32(vs, sizeof("Authentication failed"));
  393 + vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
  394 + vnc_flush(vs);
  395 + vnc_client_error(vs);
  396 + return -1;
  397 +
  398 + authabort:
  399 + vnc_client_error(vs);
  400 + return -1;
  401 +}
  402 +
  403 +static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
  404 +{
  405 + uint32_t startlen = read_u32(data, 0);
  406 + VNC_DEBUG("Got client start len %d\n", startlen);
  407 + if (startlen > SASL_DATA_MAX_LEN) {
  408 + VNC_DEBUG("Too much SASL data %d\n", startlen);
  409 + vnc_client_error(vs);
  410 + return -1;
  411 + }
  412 +
  413 + if (startlen == 0)
  414 + return protocol_client_auth_sasl_start(vs, NULL, 0);
  415 +
  416 + vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
  417 + return 0;
  418 +}
  419 +
  420 +static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
  421 +{
  422 + char *mechname = malloc(len + 1);
  423 + if (!mechname) {
  424 + VNC_DEBUG("Out of memory reading mechname\n");
  425 + vnc_client_error(vs);
  426 + }
  427 + strncpy(mechname, (char*)data, len);
  428 + mechname[len] = '\0';
  429 + VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
  430 + mechname, vs->sasl.mechlist);
  431 +
  432 + if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
  433 + if (vs->sasl.mechlist[len] != '\0' &&
  434 + vs->sasl.mechlist[len] != ',') {
  435 + VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
  436 + vnc_client_error(vs);
  437 + return -1;
  438 + }
  439 + } else {
  440 + char *offset = strstr(vs->sasl.mechlist, mechname);
  441 + VNC_DEBUG("Two %p\n", offset);
  442 + if (!offset) {
  443 + vnc_client_error(vs);
  444 + return -1;
  445 + }
  446 + VNC_DEBUG("Two '%s'\n", offset);
  447 + if (offset[-1] != ',' ||
  448 + (offset[len] != '\0'&&
  449 + offset[len] != ',')) {
  450 + vnc_client_error(vs);
  451 + return -1;
  452 + }
  453 + }
  454 +
  455 + free(vs->sasl.mechlist);
  456 + vs->sasl.mechlist = mechname;
  457 +
  458 + VNC_DEBUG("Validated mechname '%s'\n", mechname);
  459 + vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
  460 + return 0;
  461 +}
  462 +
  463 +static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
  464 +{
  465 + uint32_t mechlen = read_u32(data, 0);
  466 + VNC_DEBUG("Got client mechname len %d\n", mechlen);
  467 + if (mechlen > 100) {
  468 + VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
  469 + vnc_client_error(vs);
  470 + return -1;
  471 + }
  472 + if (mechlen < 1) {
  473 + VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
  474 + vnc_client_error(vs);
  475 + return -1;
  476 + }
  477 + vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
  478 + return 0;
  479 +}
  480 +
  481 +#define USES_X509_AUTH(vs) \
  482 + ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
  483 + (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
  484 + (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
  485 + (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
  486 +
  487 +
  488 +void start_auth_sasl(VncState *vs)
  489 +{
  490 + const char *mechlist = NULL;
  491 + sasl_security_properties_t secprops;
  492 + int err;
  493 + char *localAddr, *remoteAddr;
  494 + int mechlistlen;
  495 +
  496 + VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
  497 +
  498 + /* Get local & remote client addresses in form IPADDR;PORT */
  499 + if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
  500 + goto authabort;
  501 +
  502 + if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
  503 + free(localAddr);
  504 + goto authabort;
  505 + }
  506 +
  507 + err = sasl_server_new("vnc",
  508 + NULL, /* FQDN - just delegates to gethostname */
  509 + NULL, /* User realm */
  510 + localAddr,
  511 + remoteAddr,
  512 + NULL, /* Callbacks, not needed */
  513 + SASL_SUCCESS_DATA,
  514 + &vs->sasl.conn);
  515 + free(localAddr);
  516 + free(remoteAddr);
  517 + localAddr = remoteAddr = NULL;
  518 +
  519 + if (err != SASL_OK) {
  520 + VNC_DEBUG("sasl context setup failed %d (%s)",
  521 + err, sasl_errstring(err, NULL, NULL));
  522 + vs->sasl.conn = NULL;
  523 + goto authabort;
  524 + }
  525 +
  526 +#ifdef CONFIG_VNC_TLS
  527 + /* Inform SASL that we've got an external SSF layer from TLS/x509 */
  528 + if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
  529 + vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
  530 + gnutls_cipher_algorithm_t cipher;
  531 + sasl_ssf_t ssf;
  532 +
  533 + cipher = gnutls_cipher_get(vs->tls.session);
  534 + if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
  535 + VNC_DEBUG("%s", "cannot TLS get cipher size\n");
  536 + sasl_dispose(&vs->sasl.conn);
  537 + vs->sasl.conn = NULL;
  538 + goto authabort;
  539 + }
  540 + ssf *= 8; /* tls key size is bytes, sasl wants bits */
  541 +
  542 + err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
  543 + if (err != SASL_OK) {
  544 + VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
  545 + err, sasl_errstring(err, NULL, NULL));
  546 + sasl_dispose(&vs->sasl.conn);
  547 + vs->sasl.conn = NULL;
  548 + goto authabort;
  549 + }
  550 + } else
  551 +#endif /* CONFIG_VNC_TLS */
  552 + vs->sasl.wantSSF = 1;
  553 +
  554 + memset (&secprops, 0, sizeof secprops);
  555 + /* Inform SASL that we've got an external SSF layer from TLS */
  556 + if (strncmp(vs->vd->display, "unix:", 5) == 0
  557 +#ifdef CONFIG_VNC_TLS
  558 + /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
  559 + is not sufficiently strong */
  560 + || (vs->vd->auth == VNC_AUTH_VENCRYPT &&
  561 + vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
  562 +#endif /* CONFIG_VNC_TLS */
  563 + ) {
  564 + /* If we've got TLS or UNIX domain sock, we don't care about SSF */
  565 + secprops.min_ssf = 0;
  566 + secprops.max_ssf = 0;
  567 + secprops.maxbufsize = 8192;
  568 + secprops.security_flags = 0;
  569 + } else {
  570 + /* Plain TCP, better get an SSF layer */
  571 + secprops.min_ssf = 56; /* Good enough to require kerberos */
  572 + secprops.max_ssf = 100000; /* Arbitrary big number */
  573 + secprops.maxbufsize = 8192;
  574 + /* Forbid any anonymous or trivially crackable auth */
  575 + secprops.security_flags =
  576 + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
  577 + }
  578 +
  579 + err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
  580 + if (err != SASL_OK) {
  581 + VNC_DEBUG("cannot set SASL security props %d (%s)\n",
  582 + err, sasl_errstring(err, NULL, NULL));
  583 + sasl_dispose(&vs->sasl.conn);
  584 + vs->sasl.conn = NULL;
  585 + goto authabort;
  586 + }
  587 +
  588 + err = sasl_listmech(vs->sasl.conn,
  589 + NULL, /* Don't need to set user */
  590 + "", /* Prefix */
  591 + ",", /* Separator */
  592 + "", /* Suffix */
  593 + &mechlist,
  594 + NULL,
  595 + NULL);
  596 + if (err != SASL_OK) {
  597 + VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
  598 + err, sasl_errdetail(vs->sasl.conn));
  599 + sasl_dispose(&vs->sasl.conn);
  600 + vs->sasl.conn = NULL;
  601 + goto authabort;
  602 + }
  603 + VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
  604 +
  605 + if (!(vs->sasl.mechlist = strdup(mechlist))) {
  606 + VNC_DEBUG("Out of memory");
  607 + sasl_dispose(&vs->sasl.conn);
  608 + vs->sasl.conn = NULL;
  609 + goto authabort;
  610 + }
  611 + mechlistlen = strlen(mechlist);
  612 + vnc_write_u32(vs, mechlistlen);
  613 + vnc_write(vs, mechlist, mechlistlen);
  614 + vnc_flush(vs);
  615 +
  616 + VNC_DEBUG("Wait for client mechname length\n");
  617 + vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
  618 +
  619 + return;
  620 +
  621 + authabort:
  622 + vnc_client_error(vs);
  623 + return;
  624 +}
  625 +
  626 +
... ...
vnc-auth-sasl.h 0 โ†’ 100644
  1 +/*
  2 + * QEMU VNC display driver: SASL auth protocol
  3 + *
  4 + * Copyright (C) 2009 Red Hat, Inc
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +
  26 +#ifndef __QEMU_VNC_AUTH_SASL_H__
  27 +#define __QEMU_VNC_AUTH_SASL_H__
  28 +
  29 +
  30 +#include <sasl/sasl.h>
  31 +
  32 +typedef struct VncStateSASL VncStateSASL;
  33 +
  34 +struct VncStateSASL {
  35 + sasl_conn_t *conn;
  36 + /* If we want to negotiate an SSF layer with client */
  37 + int wantSSF :1;
  38 + /* If we are now running the SSF layer */
  39 + int runSSF :1;
  40 + /*
  41 + * If this is non-zero, then wait for that many bytes
  42 + * to be written plain, before switching to SSF encoding
  43 + * This allows the VNC auth result to finish being
  44 + * written in plain.
  45 + */
  46 + unsigned int waitWriteSSF;
  47 +
  48 + /*
  49 + * Buffering encoded data to allow more clear data
  50 + * to be stuffed onto the output buffer
  51 + */
  52 + const uint8_t *encoded;
  53 + unsigned int encodedLength;
  54 + unsigned int encodedOffset;
  55 + char *username;
  56 + char *mechlist;
  57 +};
  58 +
  59 +void vnc_sasl_client_cleanup(VncState *vs);
  60 +
  61 +long vnc_client_read_sasl(VncState *vs);
  62 +long vnc_client_write_sasl(VncState *vs);
  63 +
  64 +void start_auth_sasl(VncState *vs);
  65 +
  66 +#endif /* __QEMU_VNC_AUTH_SASL_H__ */
  67 +
... ...
... ... @@ -68,7 +68,8 @@ static char *addr_to_string(const char *format,
68 68 return addr;
69 69 }
70 70  
71   -static char *vnc_socket_local_addr(const char *format, int fd) {
  71 +
  72 +char *vnc_socket_local_addr(const char *format, int fd) {
72 73 struct sockaddr_storage sa;
73 74 socklen_t salen;
74 75  
... ... @@ -79,7 +80,8 @@ static char *vnc_socket_local_addr(const char *format, int fd) {
79 80 return addr_to_string(format, &sa, salen);
80 81 }
81 82  
82   -static char *vnc_socket_remote_addr(const char *format, int fd) {
  83 +
  84 +char *vnc_socket_remote_addr(const char *format, int fd) {
83 85 struct sockaddr_storage sa;
84 86 socklen_t salen;
85 87  
... ... @@ -125,12 +127,18 @@ static const char *vnc_auth_name(VncDisplay *vd) {
125 127 return "vencrypt+x509+vnc";
126 128 case VNC_AUTH_VENCRYPT_X509PLAIN:
127 129 return "vencrypt+x509+plain";
  130 + case VNC_AUTH_VENCRYPT_TLSSASL:
  131 + return "vencrypt+tls+sasl";
  132 + case VNC_AUTH_VENCRYPT_X509SASL:
  133 + return "vencrypt+x509+sasl";
128 134 default:
129 135 return "vencrypt";
130 136 }
131 137 #else
132 138 return "vencrypt";
133 139 #endif
  140 + case VNC_AUTH_SASL:
  141 + return "sasl";
134 142 }
135 143 return "unknown";
136 144 }
... ... @@ -278,7 +286,7 @@ static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
278 286 vnc_write_s32(vs, encoding);
279 287 }
280 288  
281   -static void buffer_reserve(Buffer *buffer, size_t len)
  289 +void buffer_reserve(Buffer *buffer, size_t len)
282 290 {
283 291 if ((buffer->capacity - buffer->offset) < len) {
284 292 buffer->capacity += (len + 1024);
... ... @@ -290,22 +298,22 @@ static void buffer_reserve(Buffer *buffer, size_t len)
290 298 }
291 299 }
292 300  
293   -static int buffer_empty(Buffer *buffer)
  301 +int buffer_empty(Buffer *buffer)
294 302 {
295 303 return buffer->offset == 0;
296 304 }
297 305  
298   -static uint8_t *buffer_end(Buffer *buffer)
  306 +uint8_t *buffer_end(Buffer *buffer)
299 307 {
300 308 return buffer->buffer + buffer->offset;
301 309 }
302 310  
303   -static void buffer_reset(Buffer *buffer)
  311 +void buffer_reset(Buffer *buffer)
304 312 {
305 313 buffer->offset = 0;
306 314 }
307 315  
308   -static void buffer_append(Buffer *buffer, const void *data, size_t len)
  316 +void buffer_append(Buffer *buffer, const void *data, size_t len)
309 317 {
310 318 memcpy(buffer->buffer + buffer->offset, data, len);
311 319 buffer->offset += len;
... ... @@ -822,7 +830,8 @@ static void audio_del(VncState *vs)
822 830 }
823 831 }
824 832  
825   -static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
  833 +
  834 +int vnc_client_io_error(VncState *vs, int ret, int last_errno)
826 835 {
827 836 if (ret == 0 || ret == -1) {
828 837 if (ret == -1) {
... ... @@ -848,6 +857,9 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
848 857 #ifdef CONFIG_VNC_TLS
849 858 vnc_tls_client_cleanup(vs);
850 859 #endif /* CONFIG_VNC_TLS */
  860 +#ifdef CONFIG_VNC_SASL
  861 + vnc_sasl_client_cleanup(vs);
  862 +#endif /* CONFIG_VNC_SASL */
851 863 audio_del(vs);
852 864  
853 865 VncState *p, *parent = NULL;
... ... @@ -878,14 +890,28 @@ void vnc_client_error(VncState *vs)
878 890 vnc_client_io_error(vs, -1, EINVAL);
879 891 }
880 892  
881   -void vnc_client_write(void *opaque)
  893 +
  894 +/*
  895 + * Called to write a chunk of data to the client socket. The data may
  896 + * be the raw data, or may have already been encoded by SASL.
  897 + * The data will be written either straight onto the socket, or
  898 + * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
  899 + *
  900 + * NB, it is theoretically possible to have 2 layers of encryption,
  901 + * both SASL, and this TLS layer. It is highly unlikely in practice
  902 + * though, since SASL encryption will typically be a no-op if TLS
  903 + * is active
  904 + *
  905 + * Returns the number of bytes written, which may be less than
  906 + * the requested 'datalen' if the socket would block. Returns
  907 + * -1 on error, and disconnects the client socket.
  908 + */
  909 +long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
882 910 {
883 911 long ret;
884   - VncState *vs = opaque;
885   -
886 912 #ifdef CONFIG_VNC_TLS
887 913 if (vs->tls.session) {
888   - ret = gnutls_write(vs->tls.session, vs->output.buffer, vs->output.offset);
  914 + ret = gnutls_write(vs->tls.session, data, datalen);
889 915 if (ret < 0) {
890 916 if (ret == GNUTLS_E_AGAIN)
891 917 errno = EAGAIN;
... ... @@ -895,10 +921,42 @@ void vnc_client_write(void *opaque)
895 921 }
896 922 } else
897 923 #endif /* CONFIG_VNC_TLS */
898   - ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0);
899   - ret = vnc_client_io_error(vs, ret, socket_error());
  924 + ret = send(vs->csock, data, datalen, 0);
  925 + VNC_DEBUG("Wrote wire %p %d -> %ld\n", data, datalen, ret);
  926 + return vnc_client_io_error(vs, ret, socket_error());
  927 +}
  928 +
  929 +
  930 +/*
  931 + * Called to write buffered data to the client socket, when not
  932 + * using any SASL SSF encryption layers. Will write as much data
  933 + * as possible without blocking. If all buffered data is written,
  934 + * will switch the FD poll() handler back to read monitoring.
  935 + *
  936 + * Returns the number of bytes written, which may be less than
  937 + * the buffered output data if the socket would block. Returns
  938 + * -1 on error, and disconnects the client socket.
  939 + */
  940 +static long vnc_client_write_plain(VncState *vs)
  941 +{
  942 + long ret;
  943 +
  944 +#ifdef CONFIG_VNC_SASL
  945 + VNC_DEBUG("Write Plain: Pending output %p size %d offset %d. Wait SSF %d\n",
  946 + vs->output.buffer, vs->output.capacity, vs->output.offset,
  947 + vs->sasl.waitWriteSSF);
  948 +
  949 + if (vs->sasl.conn &&
  950 + vs->sasl.runSSF &&
  951 + vs->sasl.waitWriteSSF) {
  952 + ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
  953 + if (ret)
  954 + vs->sasl.waitWriteSSF -= ret;
  955 + } else
  956 +#endif /* CONFIG_VNC_SASL */
  957 + ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
900 958 if (!ret)
901   - return;
  959 + return 0;
902 960  
903 961 memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
904 962 vs->output.offset -= ret;
... ... @@ -906,6 +964,29 @@ void vnc_client_write(void *opaque)
906 964 if (vs->output.offset == 0) {
907 965 qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
908 966 }
  967 +
  968 + return ret;
  969 +}
  970 +
  971 +
  972 +/*
  973 + * First function called whenever there is data to be written to
  974 + * the client socket. Will delegate actual work according to whether
  975 + * SASL SSF layers are enabled (thus requiring encryption calls)
  976 + */
  977 +void vnc_client_write(void *opaque)
  978 +{
  979 + long ret;
  980 + VncState *vs = opaque;
  981 +
  982 +#ifdef CONFIG_VNC_SASL
  983 + if (vs->sasl.conn &&
  984 + vs->sasl.runSSF &&
  985 + !vs->sasl.waitWriteSSF)
  986 + ret = vnc_client_write_sasl(vs);
  987 + else
  988 +#endif /* CONFIG_VNC_SASL */
  989 + ret = vnc_client_write_plain(vs);
909 990 }
910 991  
911 992 void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
... ... @@ -914,16 +995,28 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
914 995 vs->read_handler_expect = expecting;
915 996 }
916 997  
917   -void vnc_client_read(void *opaque)
  998 +
  999 +/*
  1000 + * Called to read a chunk of data from the client socket. The data may
  1001 + * be the raw data, or may need to be further decoded by SASL.
  1002 + * The data will be read either straight from to the socket, or
  1003 + * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
  1004 + *
  1005 + * NB, it is theoretically possible to have 2 layers of encryption,
  1006 + * both SASL, and this TLS layer. It is highly unlikely in practice
  1007 + * though, since SASL encryption will typically be a no-op if TLS
  1008 + * is active
  1009 + *
  1010 + * Returns the number of bytes read, which may be less than
  1011 + * the requested 'datalen' if the socket would block. Returns
  1012 + * -1 on error, and disconnects the client socket.
  1013 + */
  1014 +long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
918 1015 {
919   - VncState *vs = opaque;
920 1016 long ret;
921   -
922   - buffer_reserve(&vs->input, 4096);
923   -
924 1017 #ifdef CONFIG_VNC_TLS
925 1018 if (vs->tls.session) {
926   - ret = gnutls_read(vs->tls.session, buffer_end(&vs->input), 4096);
  1019 + ret = gnutls_read(vs->tls.session, data, datalen);
927 1020 if (ret < 0) {
928 1021 if (ret == GNUTLS_E_AGAIN)
929 1022 errno = EAGAIN;
... ... @@ -933,12 +1026,52 @@ void vnc_client_read(void *opaque)
933 1026 }
934 1027 } else
935 1028 #endif /* CONFIG_VNC_TLS */
936   - ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
937   - ret = vnc_client_io_error(vs, ret, socket_error());
938   - if (!ret)
939   - return;
  1029 + ret = recv(vs->csock, data, datalen, 0);
  1030 + VNC_DEBUG("Read wire %p %d -> %ld\n", data, datalen, ret);
  1031 + return vnc_client_io_error(vs, ret, socket_error());
  1032 +}
940 1033  
  1034 +
  1035 +/*
  1036 + * Called to read data from the client socket to the input buffer,
  1037 + * when not using any SASL SSF encryption layers. Will read as much
  1038 + * data as possible without blocking.
  1039 + *
  1040 + * Returns the number of bytes read. Returns -1 on error, and
  1041 + * disconnects the client socket.
  1042 + */
  1043 +static long vnc_client_read_plain(VncState *vs)
  1044 +{
  1045 + int ret;
  1046 + VNC_DEBUG("Read plain %p size %d offset %d\n",
  1047 + vs->input.buffer, vs->input.capacity, vs->input.offset);
  1048 + buffer_reserve(&vs->input, 4096);
  1049 + ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
  1050 + if (!ret)
  1051 + return 0;
941 1052 vs->input.offset += ret;
  1053 + return ret;
  1054 +}
  1055 +
  1056 +
  1057 +/*
  1058 + * First function called whenever there is more data to be read from
  1059 + * the client socket. Will delegate actual work according to whether
  1060 + * SASL SSF layers are enabled (thus requiring decryption calls)
  1061 + */
  1062 +void vnc_client_read(void *opaque)
  1063 +{
  1064 + VncState *vs = opaque;
  1065 + long ret;
  1066 +
  1067 +#ifdef CONFIG_VNC_SASL
  1068 + if (vs->sasl.conn && vs->sasl.runSSF)
  1069 + ret = vnc_client_read_sasl(vs);
  1070 + else
  1071 +#endif /* CONFIG_VNC_SASL */
  1072 + ret = vnc_client_read_plain(vs);
  1073 + if (!ret)
  1074 + return;
942 1075  
943 1076 while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
944 1077 size_t len = vs->read_handler_expect;
... ... @@ -1723,6 +1856,13 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
1723 1856 break;
1724 1857 #endif /* CONFIG_VNC_TLS */
1725 1858  
  1859 +#ifdef CONFIG_VNC_SASL
  1860 + case VNC_AUTH_SASL:
  1861 + VNC_DEBUG("Accept SASL auth\n");
  1862 + start_auth_sasl(vs);
  1863 + break;
  1864 +#endif /* CONFIG_VNC_SASL */
  1865 +
1726 1866 default: /* Should not be possible, but just in case */
1727 1867 VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
1728 1868 vnc_write_u8(vs, 1);
... ... @@ -1924,6 +2064,10 @@ int vnc_display_open(DisplayState *ds, const char *display)
1924 2064 #ifdef CONFIG_VNC_TLS
1925 2065 int tls = 0, x509 = 0;
1926 2066 #endif
  2067 +#ifdef CONFIG_VNC_SASL
  2068 + int sasl = 0;
  2069 + int saslErr;
  2070 +#endif
1927 2071  
1928 2072 if (!vnc_display)
1929 2073 return -1;
... ... @@ -1943,6 +2087,10 @@ int vnc_display_open(DisplayState *ds, const char *display)
1943 2087 reverse = 1;
1944 2088 } else if (strncmp(options, "to=", 3) == 0) {
1945 2089 to_port = atoi(options+3) + 5900;
  2090 +#ifdef CONFIG_VNC_SASL
  2091 + } else if (strncmp(options, "sasl", 4) == 0) {
  2092 + sasl = 1; /* Require SASL auth */
  2093 +#endif
1946 2094 #ifdef CONFIG_VNC_TLS
1947 2095 } else if (strncmp(options, "tls", 3) == 0) {
1948 2096 tls = 1; /* Require TLS */
... ... @@ -1979,6 +2127,22 @@ int vnc_display_open(DisplayState *ds, const char *display)
1979 2127 }
1980 2128 }
1981 2129  
  2130 + /*
  2131 + * Combinations we support here:
  2132 + *
  2133 + * - no-auth (clear text, no auth)
  2134 + * - password (clear text, weak auth)
  2135 + * - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI)
  2136 + * - tls (encrypt, weak anonymous creds, no auth)
  2137 + * - tls + password (encrypt, weak anonymous creds, weak auth)
  2138 + * - tls + sasl (encrypt, weak anonymous creds, good auth)
  2139 + * - tls + x509 (encrypt, good x509 creds, no auth)
  2140 + * - tls + x509 + password (encrypt, good x509 creds, weak auth)
  2141 + * - tls + x509 + sasl (encrypt, good x509 creds, good auth)
  2142 + *
  2143 + * NB1. TLS is a stackable auth scheme.
  2144 + * NB2. the x509 schemes have option to validate a client cert dname
  2145 + */
1982 2146 if (password) {
1983 2147 #ifdef CONFIG_VNC_TLS
1984 2148 if (tls) {
... ... @@ -1991,13 +2155,34 @@ int vnc_display_open(DisplayState *ds, const char *display)
1991 2155 vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
1992 2156 }
1993 2157 } else {
1994   -#endif
  2158 +#endif /* CONFIG_VNC_TLS */
1995 2159 VNC_DEBUG("Initializing VNC server with password auth\n");
1996 2160 vs->auth = VNC_AUTH_VNC;
1997 2161 #ifdef CONFIG_VNC_TLS
1998 2162 vs->subauth = VNC_AUTH_INVALID;
1999 2163 }
2000   -#endif
  2164 +#endif /* CONFIG_VNC_TLS */
  2165 +#ifdef CONFIG_VNC_SASL
  2166 + } else if (sasl) {
  2167 +#ifdef CONFIG_VNC_TLS
  2168 + if (tls) {
  2169 + vs->auth = VNC_AUTH_VENCRYPT;
  2170 + if (x509) {
  2171 + VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
  2172 + vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
  2173 + } else {
  2174 + VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
  2175 + vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
  2176 + }
  2177 + } else {
  2178 +#endif /* CONFIG_VNC_TLS */
  2179 + VNC_DEBUG("Initializing VNC server with SASL auth\n");
  2180 + vs->auth = VNC_AUTH_SASL;
  2181 +#ifdef CONFIG_VNC_TLS
  2182 + vs->subauth = VNC_AUTH_INVALID;
  2183 + }
  2184 +#endif /* CONFIG_VNC_TLS */
  2185 +#endif /* CONFIG_VNC_SASL */
2001 2186 } else {
2002 2187 #ifdef CONFIG_VNC_TLS
2003 2188 if (tls) {
... ... @@ -2019,6 +2204,16 @@ int vnc_display_open(DisplayState *ds, const char *display)
2019 2204 #endif
2020 2205 }
2021 2206  
  2207 +#ifdef CONFIG_VNC_SASL
  2208 + if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
  2209 + fprintf(stderr, "Failed to initialize SASL auth %s",
  2210 + sasl_errstring(saslErr, NULL, NULL));
  2211 + free(vs->display);
  2212 + vs->display = NULL;
  2213 + return -1;
  2214 + }
  2215 +#endif
  2216 +
2022 2217 if (reverse) {
2023 2218 /* connect to viewer */
2024 2219 if (strncmp(display, "unix:", 5) == 0)
... ...
... ... @@ -80,6 +80,10 @@ typedef struct VncDisplay VncDisplay;
80 80 #include "vnc-tls.h"
81 81 #include "vnc-auth-vencrypt.h"
82 82 #endif
  83 +#ifdef CONFIG_VNC_SASL
  84 +#include "vnc-auth-sasl.h"
  85 +#endif
  86 +
83 87  
84 88 struct VncDisplay
85 89 {
... ... @@ -119,10 +123,12 @@ struct VncState
119 123 int minor;
120 124  
121 125 char challenge[VNC_AUTH_CHALLENGE_SIZE];
122   -
123 126 #ifdef CONFIG_VNC_TLS
124 127 VncStateTLS tls;
125 128 #endif
  129 +#ifdef CONFIG_VNC_SASL
  130 + VncStateSASL sasl;
  131 +#endif
126 132  
127 133 Buffer output;
128 134 Buffer input;
... ... @@ -161,8 +167,9 @@ enum {
161 167 VNC_AUTH_RA2NE = 6,
162 168 VNC_AUTH_TIGHT = 16,
163 169 VNC_AUTH_ULTRA = 17,
164   - VNC_AUTH_TLS = 18,
165   - VNC_AUTH_VENCRYPT = 19
  170 + VNC_AUTH_TLS = 18, /* Supported in GTK-VNC & VINO */
  171 + VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */
  172 + VNC_AUTH_SASL = 20, /* Supported in GTK-VNC & VINO */
166 173 };
167 174  
168 175 enum {
... ... @@ -173,6 +180,8 @@ enum {
173 180 VNC_AUTH_VENCRYPT_X509NONE = 260,
174 181 VNC_AUTH_VENCRYPT_X509VNC = 261,
175 182 VNC_AUTH_VENCRYPT_X509PLAIN = 262,
  183 + VNC_AUTH_VENCRYPT_X509SASL = 263,
  184 + VNC_AUTH_VENCRYPT_TLSSASL = 264,
176 185 };
177 186  
178 187  
... ... @@ -256,6 +265,8 @@ enum {
256 265 void vnc_client_read(void *opaque);
257 266 void vnc_client_write(void *opaque);
258 267  
  268 +long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
  269 +long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
259 270  
260 271 /* Protocol I/O functions */
261 272 void vnc_write(VncState *vs, const void *data, size_t len);
... ... @@ -275,8 +286,22 @@ uint32_t read_u32(uint8_t *data, size_t offset);
275 286  
276 287 /* Protocol stage functions */
277 288 void vnc_client_error(VncState *vs);
  289 +int vnc_client_io_error(VncState *vs, int ret, int last_errno);
278 290  
279 291 void start_client_init(VncState *vs);
280 292 void start_auth_vnc(VncState *vs);
281 293  
  294 +/* Buffer management */
  295 +void buffer_reserve(Buffer *buffer, size_t len);
  296 +int buffer_empty(Buffer *buffer);
  297 +uint8_t *buffer_end(Buffer *buffer);
  298 +void buffer_reset(Buffer *buffer);
  299 +void buffer_append(Buffer *buffer, const void *data, size_t len);
  300 +
  301 +
  302 +/* Misc helpers */
  303 +
  304 +char *vnc_socket_local_addr(const char *format, int fd);
  305 +char *vnc_socket_remote_addr(const char *format, int fd);
  306 +
282 307 #endif /* __QEMU_VNC_H */
... ...