xref: /openbmc/qemu/ui/vnc-auth-sasl.c (revision e51d8fbb7e673e487e98327fc067700b5a3edf30)
13e230dd2SCorentin Chary /*
23e230dd2SCorentin Chary  * QEMU VNC display driver: SASL auth protocol
33e230dd2SCorentin Chary  *
43e230dd2SCorentin Chary  * Copyright (C) 2009 Red Hat, Inc
53e230dd2SCorentin Chary  *
63e230dd2SCorentin Chary  * Permission is hereby granted, free of charge, to any person obtaining a copy
73e230dd2SCorentin Chary  * of this software and associated documentation files (the "Software"), to deal
83e230dd2SCorentin Chary  * in the Software without restriction, including without limitation the rights
93e230dd2SCorentin Chary  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
103e230dd2SCorentin Chary  * copies of the Software, and to permit persons to whom the Software is
113e230dd2SCorentin Chary  * furnished to do so, subject to the following conditions:
123e230dd2SCorentin Chary  *
133e230dd2SCorentin Chary  * The above copyright notice and this permission notice shall be included in
143e230dd2SCorentin Chary  * all copies or substantial portions of the Software.
153e230dd2SCorentin Chary  *
163e230dd2SCorentin Chary  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
173e230dd2SCorentin Chary  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183e230dd2SCorentin Chary  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
193e230dd2SCorentin Chary  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
203e230dd2SCorentin Chary  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
213e230dd2SCorentin Chary  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
223e230dd2SCorentin Chary  * THE SOFTWARE.
233e230dd2SCorentin Chary  */
243e230dd2SCorentin Chary 
25e16f4c87SPeter Maydell #include "qemu/osdep.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
27b76806d4SDaniel P. Berrange #include "authz/base.h"
283e230dd2SCorentin Chary #include "vnc.h"
297364dbdaSDaniel P. Berrange #include "trace.h"
303e230dd2SCorentin Chary 
31b65310abSPaolo Bonzini /*
32b65310abSPaolo Bonzini  * Apple has deprecated sasl.h functions in OS X 10.11.  Therefore,
33b65310abSPaolo Bonzini  * files that use SASL API need to disable -Wdeprecated-declarations.
34b65310abSPaolo Bonzini  */
35b65310abSPaolo Bonzini #ifdef CONFIG_DARWIN
36b65310abSPaolo Bonzini #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
37b65310abSPaolo Bonzini #endif
38b65310abSPaolo Bonzini 
393e230dd2SCorentin Chary /* Max amount of data we send/recv for SASL steps to prevent DOS */
403e230dd2SCorentin Chary #define SASL_DATA_MAX_LEN (1024 * 1024)
413e230dd2SCorentin Chary 
423e230dd2SCorentin Chary 
vnc_sasl_server_init(Error ** errp)43b65310abSPaolo Bonzini bool vnc_sasl_server_init(Error **errp)
44b65310abSPaolo Bonzini {
45b65310abSPaolo Bonzini     int saslErr = sasl_server_init(NULL, "qemu");
46b65310abSPaolo Bonzini 
47b65310abSPaolo Bonzini     if (saslErr != SASL_OK) {
48b65310abSPaolo Bonzini         error_setg(errp, "Failed to initialize SASL auth: %s",
49b65310abSPaolo Bonzini                    sasl_errstring(saslErr, NULL, NULL));
50b65310abSPaolo Bonzini         return false;
51b65310abSPaolo Bonzini     }
52b65310abSPaolo Bonzini     return true;
53b65310abSPaolo Bonzini }
54b65310abSPaolo Bonzini 
vnc_sasl_client_cleanup(VncState * vs)553e230dd2SCorentin Chary void vnc_sasl_client_cleanup(VncState *vs)
563e230dd2SCorentin Chary {
573e230dd2SCorentin Chary     if (vs->sasl.conn) {
58ee032ca1SStefan Weil         vs->sasl.runSSF = false;
59ee032ca1SStefan Weil         vs->sasl.wantSSF = false;
60ee032ca1SStefan Weil         vs->sasl.waitWriteSSF = 0;
613e230dd2SCorentin Chary         vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
623e230dd2SCorentin Chary         vs->sasl.encoded = NULL;
63ae878b17SStefan Weil         g_free(vs->sasl.username);
64302d9d6fSMarkus Armbruster         g_free(vs->sasl.mechlist);
653e230dd2SCorentin Chary         vs->sasl.username = vs->sasl.mechlist = NULL;
663e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
673e230dd2SCorentin Chary         vs->sasl.conn = NULL;
683e230dd2SCorentin Chary     }
693e230dd2SCorentin Chary }
703e230dd2SCorentin Chary 
713e230dd2SCorentin Chary 
vnc_client_write_sasl(VncState * vs)7230b80fd5SDaniel P. Berrange size_t vnc_client_write_sasl(VncState *vs)
733e230dd2SCorentin Chary {
7430b80fd5SDaniel P. Berrange     size_t ret;
753e230dd2SCorentin Chary 
763e230dd2SCorentin Chary     VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
773e230dd2SCorentin Chary               "Encoded: %p size %d offset %d\n",
783e230dd2SCorentin Chary               vs->output.buffer, vs->output.capacity, vs->output.offset,
793e230dd2SCorentin Chary               vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
803e230dd2SCorentin Chary 
813e230dd2SCorentin Chary     if (!vs->sasl.encoded) {
823e230dd2SCorentin Chary         int err;
833e230dd2SCorentin Chary         err = sasl_encode(vs->sasl.conn,
843e230dd2SCorentin Chary                           (char *)vs->output.buffer,
853e230dd2SCorentin Chary                           vs->output.offset,
863e230dd2SCorentin Chary                           (const char **)&vs->sasl.encoded,
873e230dd2SCorentin Chary                           &vs->sasl.encodedLength);
883e230dd2SCorentin Chary         if (err != SASL_OK)
8904d2529dSDaniel P. Berrange             return vnc_client_io_error(vs, -1, NULL);
903e230dd2SCorentin Chary 
918f61f1c5SDaniel P. Berrange         vs->sasl.encodedRawLength = vs->output.offset;
923e230dd2SCorentin Chary         vs->sasl.encodedOffset = 0;
933e230dd2SCorentin Chary     }
943e230dd2SCorentin Chary 
953e230dd2SCorentin Chary     ret = vnc_client_write_buf(vs,
963e230dd2SCorentin Chary                                vs->sasl.encoded + vs->sasl.encodedOffset,
973e230dd2SCorentin Chary                                vs->sasl.encodedLength - vs->sasl.encodedOffset);
983e230dd2SCorentin Chary     if (!ret)
993e230dd2SCorentin Chary         return 0;
1003e230dd2SCorentin Chary 
1013e230dd2SCorentin Chary     vs->sasl.encodedOffset += ret;
1023e230dd2SCorentin Chary     if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
103d50f09ffSDaniel P. Berrangé         bool throttled = vs->force_update_offset != 0;
104d50f09ffSDaniel P. Berrangé         size_t offset;
105ada8d2e4SDaniel P. Berrange         if (vs->sasl.encodedRawLength >= vs->force_update_offset) {
106ada8d2e4SDaniel P. Berrange             vs->force_update_offset = 0;
107ada8d2e4SDaniel P. Berrange         } else {
108ada8d2e4SDaniel P. Berrange             vs->force_update_offset -= vs->sasl.encodedRawLength;
109ada8d2e4SDaniel P. Berrange         }
110d50f09ffSDaniel P. Berrangé         if (throttled && vs->force_update_offset == 0) {
111d50f09ffSDaniel P. Berrangé             trace_vnc_client_unthrottle_forced(vs, vs->ioc);
112d50f09ffSDaniel P. Berrangé         }
113d50f09ffSDaniel P. Berrangé         offset = vs->output.offset;
114627ebec2SDaniel P. Berrangé         buffer_advance(&vs->output, vs->sasl.encodedRawLength);
115d50f09ffSDaniel P. Berrangé         if (offset >= vs->throttle_output_offset &&
116d50f09ffSDaniel P. Berrangé             vs->output.offset < vs->throttle_output_offset) {
117d50f09ffSDaniel P. Berrangé             trace_vnc_client_unthrottle_incremental(vs, vs->ioc,
118d50f09ffSDaniel P. Berrangé                                                     vs->output.offset);
119d50f09ffSDaniel P. Berrangé         }
1203e230dd2SCorentin Chary         vs->sasl.encoded = NULL;
1213e230dd2SCorentin Chary         vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
1223e230dd2SCorentin Chary     }
1233e230dd2SCorentin Chary 
1243e230dd2SCorentin Chary     /* Can't merge this block with one above, because
1253e230dd2SCorentin Chary      * someone might have written more unencrypted
1263e230dd2SCorentin Chary      * data in vs->output while we were processing
1273e230dd2SCorentin Chary      * SASL encoded output
1283e230dd2SCorentin Chary      */
1293e230dd2SCorentin Chary     if (vs->output.offset == 0) {
13004d2529dSDaniel P. Berrange         if (vs->ioc_tag) {
13104d2529dSDaniel P. Berrange             g_source_remove(vs->ioc_tag);
13204d2529dSDaniel P. Berrange         }
13304d2529dSDaniel P. Berrange         vs->ioc_tag = qio_channel_add_watch(
1342ddafce7SDing Hui             vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
1352ddafce7SDing Hui             vnc_client_io, vs, NULL);
1363e230dd2SCorentin Chary     }
1373e230dd2SCorentin Chary 
1383e230dd2SCorentin Chary     return ret;
1393e230dd2SCorentin Chary }
1403e230dd2SCorentin Chary 
1413e230dd2SCorentin Chary 
vnc_client_read_sasl(VncState * vs)14230b80fd5SDaniel P. Berrange size_t vnc_client_read_sasl(VncState *vs)
1433e230dd2SCorentin Chary {
14430b80fd5SDaniel P. Berrange     size_t ret;
1453e230dd2SCorentin Chary     uint8_t encoded[4096];
1463e230dd2SCorentin Chary     const char *decoded;
1473e230dd2SCorentin Chary     unsigned int decodedLen;
1483e230dd2SCorentin Chary     int err;
1493e230dd2SCorentin Chary 
1503e230dd2SCorentin Chary     ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
1513e230dd2SCorentin Chary     if (!ret)
1523e230dd2SCorentin Chary         return 0;
1533e230dd2SCorentin Chary 
1543e230dd2SCorentin Chary     err = sasl_decode(vs->sasl.conn,
1553e230dd2SCorentin Chary                       (char *)encoded, ret,
1563e230dd2SCorentin Chary                       &decoded, &decodedLen);
1573e230dd2SCorentin Chary 
1583e230dd2SCorentin Chary     if (err != SASL_OK)
15904d2529dSDaniel P. Berrange         return vnc_client_io_error(vs, -1, NULL);
1603e230dd2SCorentin Chary     VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
1613e230dd2SCorentin Chary               encoded, ret, decoded, decodedLen);
1623e230dd2SCorentin Chary     buffer_reserve(&vs->input, decodedLen);
1633e230dd2SCorentin Chary     buffer_append(&vs->input, decoded, decodedLen);
1643e230dd2SCorentin Chary     return decodedLen;
1653e230dd2SCorentin Chary }
1663e230dd2SCorentin Chary 
1673e230dd2SCorentin Chary 
vnc_auth_sasl_check_access(VncState * vs)1683e230dd2SCorentin Chary static int vnc_auth_sasl_check_access(VncState *vs)
1693e230dd2SCorentin Chary {
1703e230dd2SCorentin Chary     const void *val;
171b76806d4SDaniel P. Berrange     int rv;
172b76806d4SDaniel P. Berrange     Error *err = NULL;
173b76806d4SDaniel P. Berrange     bool allow;
1743e230dd2SCorentin Chary 
175b76806d4SDaniel P. Berrange     rv = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
176b76806d4SDaniel P. Berrange     if (rv != SASL_OK) {
1777364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username",
178b76806d4SDaniel P. Berrange                             sasl_errstring(rv, NULL, NULL));
1793e230dd2SCorentin Chary         return -1;
1803e230dd2SCorentin Chary     }
1813e230dd2SCorentin Chary     if (val == NULL) {
1827364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "No SASL username set", "");
1833e230dd2SCorentin Chary         return -1;
1843e230dd2SCorentin Chary     }
1853e230dd2SCorentin Chary 
1867267c094SAnthony Liguori     vs->sasl.username = g_strdup((const char*)val);
1877364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_username(vs, vs->sasl.username);
1883e230dd2SCorentin Chary 
189b76806d4SDaniel P. Berrange     if (vs->vd->sasl.authzid == NULL) {
1907364dbdaSDaniel P. Berrange         trace_vnc_auth_sasl_acl(vs, 1);
1913e230dd2SCorentin Chary         return 0;
1923e230dd2SCorentin Chary     }
1933e230dd2SCorentin Chary 
194b76806d4SDaniel P. Berrange     allow = qauthz_is_allowed_by_id(vs->vd->sasl.authzid,
195b76806d4SDaniel P. Berrange                                     vs->sasl.username, &err);
196b76806d4SDaniel P. Berrange     if (err) {
197b76806d4SDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "Error from authz",
198b76806d4SDaniel P. Berrange                             error_get_pretty(err));
199b76806d4SDaniel P. Berrange         error_free(err);
200b76806d4SDaniel P. Berrange         return -1;
201b76806d4SDaniel P. Berrange     }
2023e230dd2SCorentin Chary 
2037364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_acl(vs, allow);
2043e230dd2SCorentin Chary     return allow ? 0 : -1;
2053e230dd2SCorentin Chary }
2063e230dd2SCorentin Chary 
vnc_auth_sasl_check_ssf(VncState * vs)2073e230dd2SCorentin Chary static int vnc_auth_sasl_check_ssf(VncState *vs)
2083e230dd2SCorentin Chary {
2093e230dd2SCorentin Chary     const void *val;
2103e230dd2SCorentin Chary     int err, ssf;
2113e230dd2SCorentin Chary 
2123e230dd2SCorentin Chary     if (!vs->sasl.wantSSF)
2133e230dd2SCorentin Chary         return 1;
2143e230dd2SCorentin Chary 
2153e230dd2SCorentin Chary     err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
2163e230dd2SCorentin Chary     if (err != SASL_OK)
2173e230dd2SCorentin Chary         return 0;
2183e230dd2SCorentin Chary 
2193e230dd2SCorentin Chary     ssf = *(const int *)val;
2207364dbdaSDaniel P. Berrange 
2217364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_ssf(vs, ssf);
2227364dbdaSDaniel P. Berrange 
2233e230dd2SCorentin Chary     if (ssf < 56)
2243e230dd2SCorentin Chary         return 0; /* 56 is good for Kerberos */
2253e230dd2SCorentin Chary 
2263e230dd2SCorentin Chary     /* Only setup for read initially, because we're about to send an RPC
2273e230dd2SCorentin Chary      * reply which must be in plain text. When the next incoming RPC
2283e230dd2SCorentin Chary      * arrives, we'll switch on writes too
2293e230dd2SCorentin Chary      *
2303e230dd2SCorentin Chary      * cf qemudClientReadSASL  in qemud.c
2313e230dd2SCorentin Chary      */
2323e230dd2SCorentin Chary     vs->sasl.runSSF = 1;
2333e230dd2SCorentin Chary 
2343e230dd2SCorentin Chary     /* We have a SSF that's good enough */
2353e230dd2SCorentin Chary     return 1;
2363e230dd2SCorentin Chary }
2373e230dd2SCorentin Chary 
2383e230dd2SCorentin Chary /*
2393e230dd2SCorentin Chary  * Step Msg
2403e230dd2SCorentin Chary  *
2413e230dd2SCorentin Chary  * Input from client:
2423e230dd2SCorentin Chary  *
2433e230dd2SCorentin Chary  * u32 clientin-length
2443e230dd2SCorentin Chary  * u8-array clientin-string
2453e230dd2SCorentin Chary  *
2463e230dd2SCorentin Chary  * Output to client:
2473e230dd2SCorentin Chary  *
2483e230dd2SCorentin Chary  * u32 serverout-length
2493e230dd2SCorentin Chary  * u8-array serverout-strin
2503e230dd2SCorentin Chary  * u8 continue
2513e230dd2SCorentin Chary  */
2523e230dd2SCorentin Chary 
2533e230dd2SCorentin Chary static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
2543e230dd2SCorentin Chary 
protocol_client_auth_sasl_step(VncState * vs,uint8_t * data,size_t len)2553e230dd2SCorentin Chary static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
2563e230dd2SCorentin Chary {
2573e230dd2SCorentin Chary     uint32_t datalen = len;
2583e230dd2SCorentin Chary     const char *serverout;
2593e230dd2SCorentin Chary     unsigned int serveroutlen;
2603e230dd2SCorentin Chary     int err;
2613e230dd2SCorentin Chary     char *clientdata = NULL;
2623e230dd2SCorentin Chary 
2633e230dd2SCorentin Chary     /* NB, distinction of NULL vs "" is *critical* in SASL */
2643e230dd2SCorentin Chary     if (datalen) {
2653e230dd2SCorentin Chary         clientdata = (char*)data;
266*1a225f57SDaniel P. Berrangé         if (clientdata[datalen - 1] != '\0') {
267*1a225f57SDaniel P. Berrangé             trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data",
268*1a225f57SDaniel P. Berrangé                                 "Missing SASL NUL padding byte");
269*1a225f57SDaniel P. Berrangé             sasl_dispose(&vs->sasl.conn);
270*1a225f57SDaniel P. Berrangé             vs->sasl.conn = NULL;
271*1a225f57SDaniel P. Berrangé             goto authabort;
272*1a225f57SDaniel P. Berrangé         }
273*1a225f57SDaniel P. Berrangé         datalen--; /* Discard the extra NUL padding byte */
2743e230dd2SCorentin Chary     }
2753e230dd2SCorentin Chary 
2763e230dd2SCorentin Chary     err = sasl_server_step(vs->sasl.conn,
2773e230dd2SCorentin Chary                            clientdata,
2783e230dd2SCorentin Chary                            datalen,
2793e230dd2SCorentin Chary                            &serverout,
2803e230dd2SCorentin Chary                            &serveroutlen);
2817364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_step(vs, data, len, serverout, serveroutlen, err);
2823e230dd2SCorentin Chary     if (err != SASL_OK &&
2833e230dd2SCorentin Chary         err != SASL_CONTINUE) {
2847364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "Cannot step SASL auth",
2857364dbdaSDaniel P. Berrange                             sasl_errdetail(vs->sasl.conn));
2863e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
2873e230dd2SCorentin Chary         vs->sasl.conn = NULL;
2883e230dd2SCorentin Chary         goto authabort;
2893e230dd2SCorentin Chary     }
2903e230dd2SCorentin Chary 
2913e230dd2SCorentin Chary     if (serveroutlen > SASL_DATA_MAX_LEN) {
2927364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
2933e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
2943e230dd2SCorentin Chary         vs->sasl.conn = NULL;
2953e230dd2SCorentin Chary         goto authabort;
2963e230dd2SCorentin Chary     }
2973e230dd2SCorentin Chary 
298829cb3d0SDaniel P. Berrangé     if (serverout) {
2993e230dd2SCorentin Chary         vnc_write_u32(vs, serveroutlen + 1);
300829cb3d0SDaniel P. Berrangé         vnc_write(vs, serverout, serveroutlen);
301829cb3d0SDaniel P. Berrangé         vnc_write_u8(vs, '\0');
3023e230dd2SCorentin Chary     } else {
3033e230dd2SCorentin Chary         vnc_write_u32(vs, 0);
3043e230dd2SCorentin Chary     }
3053e230dd2SCorentin Chary 
3063e230dd2SCorentin Chary     /* Whether auth is complete */
3073e230dd2SCorentin Chary     vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
3083e230dd2SCorentin Chary 
3093e230dd2SCorentin Chary     if (err == SASL_CONTINUE) {
3103e230dd2SCorentin Chary         /* Wait for step length */
3113e230dd2SCorentin Chary         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
3123e230dd2SCorentin Chary     } else {
3133e230dd2SCorentin Chary         if (!vnc_auth_sasl_check_ssf(vs)) {
3147364dbdaSDaniel P. Berrange             trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
3153e230dd2SCorentin Chary             goto authreject;
3163e230dd2SCorentin Chary         }
3173e230dd2SCorentin Chary 
31875ae7c46SPhilippe Mathieu-Daudé         /* Check the username access control list */
3193e230dd2SCorentin Chary         if (vnc_auth_sasl_check_access(vs) < 0) {
3203e230dd2SCorentin Chary             goto authreject;
3213e230dd2SCorentin Chary         }
3223e230dd2SCorentin Chary 
3237364dbdaSDaniel P. Berrange         trace_vnc_auth_pass(vs, vs->auth);
3243e230dd2SCorentin Chary         vnc_write_u32(vs, 0); /* Accept auth */
3253e230dd2SCorentin Chary         /*
3263e230dd2SCorentin Chary          * Delay writing in SSF encoded mode until pending output
3273e230dd2SCorentin Chary          * buffer is written
3283e230dd2SCorentin Chary          */
3293e230dd2SCorentin Chary         if (vs->sasl.runSSF)
3303e230dd2SCorentin Chary             vs->sasl.waitWriteSSF = vs->output.offset;
3313e230dd2SCorentin Chary         start_client_init(vs);
3323e230dd2SCorentin Chary     }
3333e230dd2SCorentin Chary 
3343e230dd2SCorentin Chary     return 0;
3353e230dd2SCorentin Chary 
3363e230dd2SCorentin Chary  authreject:
3373e230dd2SCorentin Chary     vnc_write_u32(vs, 1); /* Reject auth */
3383e230dd2SCorentin Chary     vnc_write_u32(vs, sizeof("Authentication failed"));
3393e230dd2SCorentin Chary     vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
3403e230dd2SCorentin Chary     vnc_flush(vs);
3413e230dd2SCorentin Chary     vnc_client_error(vs);
3423e230dd2SCorentin Chary     return -1;
3433e230dd2SCorentin Chary 
3443e230dd2SCorentin Chary  authabort:
3453e230dd2SCorentin Chary     vnc_client_error(vs);
3463e230dd2SCorentin Chary     return -1;
3473e230dd2SCorentin Chary }
3483e230dd2SCorentin Chary 
protocol_client_auth_sasl_step_len(VncState * vs,uint8_t * data,size_t len)3493e230dd2SCorentin Chary static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
3503e230dd2SCorentin Chary {
3513e230dd2SCorentin Chary     uint32_t steplen = read_u32(data, 0);
3527364dbdaSDaniel P. Berrange 
3533e230dd2SCorentin Chary     if (steplen > SASL_DATA_MAX_LEN) {
3547364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "SASL step len too large", "");
3553e230dd2SCorentin Chary         vnc_client_error(vs);
3563e230dd2SCorentin Chary         return -1;
3573e230dd2SCorentin Chary     }
3583e230dd2SCorentin Chary 
3593e230dd2SCorentin Chary     if (steplen == 0)
3603e230dd2SCorentin Chary         return protocol_client_auth_sasl_step(vs, NULL, 0);
3613e230dd2SCorentin Chary     else
3623e230dd2SCorentin Chary         vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
3633e230dd2SCorentin Chary     return 0;
3643e230dd2SCorentin Chary }
3653e230dd2SCorentin Chary 
3663e230dd2SCorentin Chary /*
3673e230dd2SCorentin Chary  * Start Msg
3683e230dd2SCorentin Chary  *
3693e230dd2SCorentin Chary  * Input from client:
3703e230dd2SCorentin Chary  *
3713e230dd2SCorentin Chary  * u32 clientin-length
3723e230dd2SCorentin Chary  * u8-array clientin-string
3733e230dd2SCorentin Chary  *
3743e230dd2SCorentin Chary  * Output to client:
3753e230dd2SCorentin Chary  *
3763e230dd2SCorentin Chary  * u32 serverout-length
3773e230dd2SCorentin Chary  * u8-array serverout-strin
3783e230dd2SCorentin Chary  * u8 continue
3793e230dd2SCorentin Chary  */
3803e230dd2SCorentin Chary 
3813e230dd2SCorentin Chary #define SASL_DATA_MAX_LEN (1024 * 1024)
3823e230dd2SCorentin Chary 
protocol_client_auth_sasl_start(VncState * vs,uint8_t * data,size_t len)3833e230dd2SCorentin Chary static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
3843e230dd2SCorentin Chary {
3853e230dd2SCorentin Chary     uint32_t datalen = len;
3863e230dd2SCorentin Chary     const char *serverout;
3873e230dd2SCorentin Chary     unsigned int serveroutlen;
3883e230dd2SCorentin Chary     int err;
3893e230dd2SCorentin Chary     char *clientdata = NULL;
3903e230dd2SCorentin Chary 
3913e230dd2SCorentin Chary     /* NB, distinction of NULL vs "" is *critical* in SASL */
3923e230dd2SCorentin Chary     if (datalen) {
3933e230dd2SCorentin Chary         clientdata = (char*)data;
394*1a225f57SDaniel P. Berrangé         if (clientdata[datalen - 1] != '\0') {
395*1a225f57SDaniel P. Berrangé             trace_vnc_auth_fail(vs, vs->auth,  "Malformed SASL client data",
396*1a225f57SDaniel P. Berrangé                                 "Missing SASL NUL padding byte");
397*1a225f57SDaniel P. Berrangé             sasl_dispose(&vs->sasl.conn);
398*1a225f57SDaniel P. Berrangé             vs->sasl.conn = NULL;
399*1a225f57SDaniel P. Berrangé             goto authabort;
400*1a225f57SDaniel P. Berrangé         }
401*1a225f57SDaniel P. Berrangé         datalen--; /* Discard the extra NUL padding byte */
4023e230dd2SCorentin Chary     }
4033e230dd2SCorentin Chary 
4043e230dd2SCorentin Chary     err = sasl_server_start(vs->sasl.conn,
4053e230dd2SCorentin Chary                             vs->sasl.mechlist,
4063e230dd2SCorentin Chary                             clientdata,
4073e230dd2SCorentin Chary                             datalen,
4083e230dd2SCorentin Chary                             &serverout,
4093e230dd2SCorentin Chary                             &serveroutlen);
4107364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_start(vs, data, len, serverout, serveroutlen, err);
4113e230dd2SCorentin Chary     if (err != SASL_OK &&
4123e230dd2SCorentin Chary         err != SASL_CONTINUE) {
4137364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "Cannot start SASL auth",
4147364dbdaSDaniel P. Berrange                             sasl_errdetail(vs->sasl.conn));
4153e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
4163e230dd2SCorentin Chary         vs->sasl.conn = NULL;
4173e230dd2SCorentin Chary         goto authabort;
4183e230dd2SCorentin Chary     }
4193e230dd2SCorentin Chary     if (serveroutlen > SASL_DATA_MAX_LEN) {
4207364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
4213e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
4223e230dd2SCorentin Chary         vs->sasl.conn = NULL;
4233e230dd2SCorentin Chary         goto authabort;
4243e230dd2SCorentin Chary     }
4253e230dd2SCorentin Chary 
426829cb3d0SDaniel P. Berrangé     if (serverout) {
4273e230dd2SCorentin Chary         vnc_write_u32(vs, serveroutlen + 1);
428829cb3d0SDaniel P. Berrangé         vnc_write(vs, serverout, serveroutlen);
429829cb3d0SDaniel P. Berrangé         vnc_write_u8(vs, '\0');
4303e230dd2SCorentin Chary     } else {
4313e230dd2SCorentin Chary         vnc_write_u32(vs, 0);
4323e230dd2SCorentin Chary     }
4333e230dd2SCorentin Chary 
4343e230dd2SCorentin Chary     /* Whether auth is complete */
4353e230dd2SCorentin Chary     vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
4363e230dd2SCorentin Chary 
4373e230dd2SCorentin Chary     if (err == SASL_CONTINUE) {
4383e230dd2SCorentin Chary         /* Wait for step length */
4393e230dd2SCorentin Chary         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
4403e230dd2SCorentin Chary     } else {
4413e230dd2SCorentin Chary         if (!vnc_auth_sasl_check_ssf(vs)) {
4427364dbdaSDaniel P. Berrange             trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
4433e230dd2SCorentin Chary             goto authreject;
4443e230dd2SCorentin Chary         }
4453e230dd2SCorentin Chary 
44675ae7c46SPhilippe Mathieu-Daudé         /* Check the username access control list */
4473e230dd2SCorentin Chary         if (vnc_auth_sasl_check_access(vs) < 0) {
4483e230dd2SCorentin Chary             goto authreject;
4493e230dd2SCorentin Chary         }
4503e230dd2SCorentin Chary 
4517364dbdaSDaniel P. Berrange         trace_vnc_auth_pass(vs, vs->auth);
4523e230dd2SCorentin Chary         vnc_write_u32(vs, 0); /* Accept auth */
4533e230dd2SCorentin Chary         start_client_init(vs);
4543e230dd2SCorentin Chary     }
4553e230dd2SCorentin Chary 
4563e230dd2SCorentin Chary     return 0;
4573e230dd2SCorentin Chary 
4583e230dd2SCorentin Chary  authreject:
4593e230dd2SCorentin Chary     vnc_write_u32(vs, 1); /* Reject auth */
4603e230dd2SCorentin Chary     vnc_write_u32(vs, sizeof("Authentication failed"));
4613e230dd2SCorentin Chary     vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
4623e230dd2SCorentin Chary     vnc_flush(vs);
4633e230dd2SCorentin Chary     vnc_client_error(vs);
4643e230dd2SCorentin Chary     return -1;
4653e230dd2SCorentin Chary 
4663e230dd2SCorentin Chary  authabort:
4673e230dd2SCorentin Chary     vnc_client_error(vs);
4683e230dd2SCorentin Chary     return -1;
4693e230dd2SCorentin Chary }
4703e230dd2SCorentin Chary 
protocol_client_auth_sasl_start_len(VncState * vs,uint8_t * data,size_t len)4713e230dd2SCorentin Chary static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
4723e230dd2SCorentin Chary {
4733e230dd2SCorentin Chary     uint32_t startlen = read_u32(data, 0);
4747364dbdaSDaniel P. Berrange 
4753e230dd2SCorentin Chary     if (startlen > SASL_DATA_MAX_LEN) {
4767364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "SASL start len too large", "");
4773e230dd2SCorentin Chary         vnc_client_error(vs);
4783e230dd2SCorentin Chary         return -1;
4793e230dd2SCorentin Chary     }
4803e230dd2SCorentin Chary 
4813e230dd2SCorentin Chary     if (startlen == 0)
4823e230dd2SCorentin Chary         return protocol_client_auth_sasl_start(vs, NULL, 0);
4833e230dd2SCorentin Chary 
4843e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
4853e230dd2SCorentin Chary     return 0;
4863e230dd2SCorentin Chary }
4873e230dd2SCorentin Chary 
protocol_client_auth_sasl_mechname(VncState * vs,uint8_t * data,size_t len)4883e230dd2SCorentin Chary static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
4893e230dd2SCorentin Chary {
4905847d9e1SJim Meyering     char *mechname = g_strndup((const char *) data, len);
4917364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_mech_choose(vs, mechname);
4923e230dd2SCorentin Chary 
4933e230dd2SCorentin Chary     if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
4943e230dd2SCorentin Chary         if (vs->sasl.mechlist[len] != '\0' &&
4953e230dd2SCorentin Chary             vs->sasl.mechlist[len] != ',') {
4968ce7d352SBlue Swirl             goto fail;
4973e230dd2SCorentin Chary         }
4983e230dd2SCorentin Chary     } else {
4993e230dd2SCorentin Chary         char *offset = strstr(vs->sasl.mechlist, mechname);
5003e230dd2SCorentin Chary         if (!offset) {
5018ce7d352SBlue Swirl             goto fail;
5023e230dd2SCorentin Chary         }
5033e230dd2SCorentin Chary         if (offset[-1] != ',' ||
5043e230dd2SCorentin Chary             (offset[len] != '\0'&&
5053e230dd2SCorentin Chary              offset[len] != ',')) {
5068ce7d352SBlue Swirl             goto fail;
5073e230dd2SCorentin Chary         }
5083e230dd2SCorentin Chary     }
5093e230dd2SCorentin Chary 
510302d9d6fSMarkus Armbruster     g_free(vs->sasl.mechlist);
5113e230dd2SCorentin Chary     vs->sasl.mechlist = mechname;
5123e230dd2SCorentin Chary 
5133e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
5143e230dd2SCorentin Chary     return 0;
5158ce7d352SBlue Swirl 
5168ce7d352SBlue Swirl  fail:
5177364dbdaSDaniel P. Berrange     trace_vnc_auth_fail(vs, vs->auth, "Unsupported mechname", mechname);
5188ce7d352SBlue Swirl     vnc_client_error(vs);
519302d9d6fSMarkus Armbruster     g_free(mechname);
5208ce7d352SBlue Swirl     return -1;
5213e230dd2SCorentin Chary }
5223e230dd2SCorentin Chary 
protocol_client_auth_sasl_mechname_len(VncState * vs,uint8_t * data,size_t len)5233e230dd2SCorentin Chary static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
5243e230dd2SCorentin Chary {
5253e230dd2SCorentin Chary     uint32_t mechlen = read_u32(data, 0);
5267364dbdaSDaniel P. Berrange 
5273e230dd2SCorentin Chary     if (mechlen > 100) {
5287364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too long", "");
5293e230dd2SCorentin Chary         vnc_client_error(vs);
5303e230dd2SCorentin Chary         return -1;
5313e230dd2SCorentin Chary     }
5323e230dd2SCorentin Chary     if (mechlen < 1) {
5337364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too short", "");
5343e230dd2SCorentin Chary         vnc_client_error(vs);
5353e230dd2SCorentin Chary         return -1;
5363e230dd2SCorentin Chary     }
5373e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
5383e230dd2SCorentin Chary     return 0;
5393e230dd2SCorentin Chary }
5403e230dd2SCorentin Chary 
541e9eabcc9SDaniel P. Berrangé static int
vnc_socket_ip_addr_string(QIOChannelSocket * ioc,bool local,char ** addrstr,Error ** errp)54204d2529dSDaniel P. Berrange vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
54304d2529dSDaniel P. Berrange                           bool local,
544e9eabcc9SDaniel P. Berrangé                           char **addrstr,
54504d2529dSDaniel P. Berrange                           Error **errp)
54604d2529dSDaniel P. Berrange {
547bd269ebcSMarkus Armbruster     SocketAddress *addr;
54804d2529dSDaniel P. Berrange 
54904d2529dSDaniel P. Berrange     if (local) {
55004d2529dSDaniel P. Berrange         addr = qio_channel_socket_get_local_address(ioc, errp);
55104d2529dSDaniel P. Berrange     } else {
55204d2529dSDaniel P. Berrange         addr = qio_channel_socket_get_remote_address(ioc, errp);
55304d2529dSDaniel P. Berrange     }
55404d2529dSDaniel P. Berrange     if (!addr) {
555e9eabcc9SDaniel P. Berrangé         return -1;
55604d2529dSDaniel P. Berrange     }
55704d2529dSDaniel P. Berrange 
558bd269ebcSMarkus Armbruster     if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
559e9eabcc9SDaniel P. Berrangé         *addrstr = NULL;
5607791acafSPan Nengyuan         qapi_free_SocketAddress(addr);
561e9eabcc9SDaniel P. Berrangé         return 0;
56204d2529dSDaniel P. Berrange     }
563e9eabcc9SDaniel P. Berrangé     *addrstr = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port);
564bd269ebcSMarkus Armbruster     qapi_free_SocketAddress(addr);
565e9eabcc9SDaniel P. Berrangé     return 0;
56604d2529dSDaniel P. Berrange }
56704d2529dSDaniel P. Berrange 
568c0a9c92bSDaniel P. Berrangé static bool
vnc_socket_is_unix(QIOChannelSocket * ioc)569c0a9c92bSDaniel P. Berrangé vnc_socket_is_unix(QIOChannelSocket *ioc)
570c0a9c92bSDaniel P. Berrangé {
571c0a9c92bSDaniel P. Berrangé     SocketAddress *addr = qio_channel_socket_get_local_address(ioc, NULL);
572c0a9c92bSDaniel P. Berrangé     return addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX;
573c0a9c92bSDaniel P. Berrangé }
574c0a9c92bSDaniel P. Berrangé 
start_auth_sasl(VncState * vs)5753e230dd2SCorentin Chary void start_auth_sasl(VncState *vs)
5763e230dd2SCorentin Chary {
5773e230dd2SCorentin Chary     const char *mechlist = NULL;
5783e230dd2SCorentin Chary     sasl_security_properties_t secprops;
5793e230dd2SCorentin Chary     int err;
5807364dbdaSDaniel P. Berrange     Error *local_err = NULL;
5813e230dd2SCorentin Chary     char *localAddr, *remoteAddr;
5823e230dd2SCorentin Chary     int mechlistlen;
5833e230dd2SCorentin Chary 
5843e230dd2SCorentin Chary     /* Get local & remote client addresses in form  IPADDR;PORT */
585e9eabcc9SDaniel P. Berrangé     if (vnc_socket_ip_addr_string(vs->sioc, true,
586e9eabcc9SDaniel P. Berrangé                                   &localAddr, &local_err) < 0) {
5877364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP",
5887364dbdaSDaniel P. Berrange                             error_get_pretty(local_err));
5893e230dd2SCorentin Chary         goto authabort;
59004d2529dSDaniel P. Berrange     }
5913e230dd2SCorentin Chary 
592e9eabcc9SDaniel P. Berrangé     if (vnc_socket_ip_addr_string(vs->sioc, false,
593e9eabcc9SDaniel P. Berrangé                                   &remoteAddr, &local_err) < 0) {
5947364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP",
5957364dbdaSDaniel P. Berrange                             error_get_pretty(local_err));
596ae878b17SStefan Weil         g_free(localAddr);
5973e230dd2SCorentin Chary         goto authabort;
5983e230dd2SCorentin Chary     }
5993e230dd2SCorentin Chary 
6003e230dd2SCorentin Chary     err = sasl_server_new("vnc",
6013e230dd2SCorentin Chary                           NULL, /* FQDN - just delegates to gethostname */
6023e230dd2SCorentin Chary                           NULL, /* User realm */
6033e230dd2SCorentin Chary                           localAddr,
6043e230dd2SCorentin Chary                           remoteAddr,
6053e230dd2SCorentin Chary                           NULL, /* Callbacks, not needed */
6063e230dd2SCorentin Chary                           SASL_SUCCESS_DATA,
6073e230dd2SCorentin Chary                           &vs->sasl.conn);
608ae878b17SStefan Weil     g_free(localAddr);
609ae878b17SStefan Weil     g_free(remoteAddr);
6103e230dd2SCorentin Chary     localAddr = remoteAddr = NULL;
6113e230dd2SCorentin Chary 
6123e230dd2SCorentin Chary     if (err != SASL_OK) {
6137364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth,  "SASL context setup failed",
6147364dbdaSDaniel P. Berrange                             sasl_errstring(err, NULL, NULL));
6153e230dd2SCorentin Chary         vs->sasl.conn = NULL;
6163e230dd2SCorentin Chary         goto authabort;
6173e230dd2SCorentin Chary     }
6183e230dd2SCorentin Chary 
6193e230dd2SCorentin Chary     /* Inform SASL that we've got an external SSF layer from TLS/x509 */
6207e7e2ebcSDaniel P. Berrange     if (vs->auth == VNC_AUTH_VENCRYPT &&
6217e7e2ebcSDaniel P. Berrange         vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
6223e305e4aSDaniel P. Berrange         int keysize;
6233e230dd2SCorentin Chary         sasl_ssf_t ssf;
6243e230dd2SCorentin Chary 
6253e305e4aSDaniel P. Berrange         keysize = qcrypto_tls_session_get_key_size(vs->tls,
6263e305e4aSDaniel P. Berrange                                                    &local_err);
6273e305e4aSDaniel P. Berrange         if (keysize < 0) {
6287364dbdaSDaniel P. Berrange             trace_vnc_auth_fail(vs, vs->auth, "cannot TLS get cipher size",
6293e305e4aSDaniel P. Berrange                                 error_get_pretty(local_err));
6303e230dd2SCorentin Chary             sasl_dispose(&vs->sasl.conn);
6313e230dd2SCorentin Chary             vs->sasl.conn = NULL;
6323e230dd2SCorentin Chary             goto authabort;
6333e230dd2SCorentin Chary         }
6343e305e4aSDaniel P. Berrange         ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */
6353e230dd2SCorentin Chary 
6363e230dd2SCorentin Chary         err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
6373e230dd2SCorentin Chary         if (err != SASL_OK) {
6387364dbdaSDaniel P. Berrange             trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL external SSF",
6397364dbdaSDaniel P. Berrange                                 sasl_errstring(err, NULL, NULL));
6403e230dd2SCorentin Chary             sasl_dispose(&vs->sasl.conn);
6413e230dd2SCorentin Chary             vs->sasl.conn = NULL;
6423e230dd2SCorentin Chary             goto authabort;
6433e230dd2SCorentin Chary         }
6443e305e4aSDaniel P. Berrange     } else {
6452b695647SDaniel P. Berrangé         vs->sasl.wantSSF = !vnc_socket_is_unix(vs->sioc);
6463e305e4aSDaniel P. Berrange     }
6473e230dd2SCorentin Chary 
6483e230dd2SCorentin Chary     memset (&secprops, 0, sizeof secprops);
6493e305e4aSDaniel P. Berrange     /* Inform SASL that we've got an external SSF layer from TLS.
6503e305e4aSDaniel P. Berrange      *
651c0a9c92bSDaniel P. Berrangé      * Disable SSF, if using TLS+x509+SASL only, or UNIX sockets.
652c0a9c92bSDaniel P. Berrangé      * TLS without x509 is not sufficiently strong, nor is plain
653c0a9c92bSDaniel P. Berrangé      * TCP
6543e305e4aSDaniel P. Berrange      */
655c0a9c92bSDaniel P. Berrangé     if (vnc_socket_is_unix(vs->sioc) ||
6563e305e4aSDaniel P. Berrange         (vs->auth == VNC_AUTH_VENCRYPT &&
6573e305e4aSDaniel P. Berrange          vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
6583e230dd2SCorentin Chary         /* If we've got TLS or UNIX domain sock, we don't care about SSF */
6593e230dd2SCorentin Chary         secprops.min_ssf = 0;
6603e230dd2SCorentin Chary         secprops.max_ssf = 0;
6613e230dd2SCorentin Chary         secprops.maxbufsize = 8192;
6623e230dd2SCorentin Chary         secprops.security_flags = 0;
6633e230dd2SCorentin Chary     } else {
6643e230dd2SCorentin Chary         /* Plain TCP, better get an SSF layer */
6653e230dd2SCorentin Chary         secprops.min_ssf = 56; /* Good enough to require kerberos */
6663e230dd2SCorentin Chary         secprops.max_ssf = 100000; /* Arbitrary big number */
6673e230dd2SCorentin Chary         secprops.maxbufsize = 8192;
6683e230dd2SCorentin Chary         /* Forbid any anonymous or trivially crackable auth */
6693e230dd2SCorentin Chary         secprops.security_flags =
6703e230dd2SCorentin Chary             SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
6713e230dd2SCorentin Chary     }
6723e230dd2SCorentin Chary 
6733e230dd2SCorentin Chary     err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
6743e230dd2SCorentin Chary     if (err != SASL_OK) {
6757364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL security props",
6767364dbdaSDaniel P. Berrange                             sasl_errstring(err, NULL, NULL));
6773e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
6783e230dd2SCorentin Chary         vs->sasl.conn = NULL;
6793e230dd2SCorentin Chary         goto authabort;
6803e230dd2SCorentin Chary     }
6813e230dd2SCorentin Chary 
6823e230dd2SCorentin Chary     err = sasl_listmech(vs->sasl.conn,
6833e230dd2SCorentin Chary                         NULL, /* Don't need to set user */
6843e230dd2SCorentin Chary                         "", /* Prefix */
6853e230dd2SCorentin Chary                         ",", /* Separator */
6863e230dd2SCorentin Chary                         "", /* Suffix */
6873e230dd2SCorentin Chary                         &mechlist,
6883e230dd2SCorentin Chary                         NULL,
6893e230dd2SCorentin Chary                         NULL);
6903e230dd2SCorentin Chary     if (err != SASL_OK) {
6917364dbdaSDaniel P. Berrange         trace_vnc_auth_fail(vs, vs->auth, "cannot list SASL mechanisms",
6927364dbdaSDaniel P. Berrange                             sasl_errdetail(vs->sasl.conn));
6933e230dd2SCorentin Chary         sasl_dispose(&vs->sasl.conn);
6943e230dd2SCorentin Chary         vs->sasl.conn = NULL;
6953e230dd2SCorentin Chary         goto authabort;
6963e230dd2SCorentin Chary     }
6977364dbdaSDaniel P. Berrange     trace_vnc_auth_sasl_mech_list(vs, mechlist);
6983e230dd2SCorentin Chary 
69946c80446SDaniel P. Berrangé     if (g_str_equal(mechlist, "")) {
70046c80446SDaniel P. Berrangé         trace_vnc_auth_fail(vs, vs->auth, "no available SASL mechanisms", "");
70146c80446SDaniel P. Berrangé         sasl_dispose(&vs->sasl.conn);
70246c80446SDaniel P. Berrangé         vs->sasl.conn = NULL;
70346c80446SDaniel P. Berrangé         goto authabort;
70446c80446SDaniel P. Berrangé     }
70546c80446SDaniel P. Berrangé 
706302d9d6fSMarkus Armbruster     vs->sasl.mechlist = g_strdup(mechlist);
7073e230dd2SCorentin Chary     mechlistlen = strlen(mechlist);
7083e230dd2SCorentin Chary     vnc_write_u32(vs, mechlistlen);
7093e230dd2SCorentin Chary     vnc_write(vs, mechlist, mechlistlen);
7103e230dd2SCorentin Chary     vnc_flush(vs);
7113e230dd2SCorentin Chary 
7123e230dd2SCorentin Chary     vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
7133e230dd2SCorentin Chary 
7143e230dd2SCorentin Chary     return;
7153e230dd2SCorentin Chary 
7163e230dd2SCorentin Chary  authabort:
7177364dbdaSDaniel P. Berrange     error_free(local_err);
7183e230dd2SCorentin Chary     vnc_client_error(vs);
7193e230dd2SCorentin Chary }
7203e230dd2SCorentin Chary 
7213e230dd2SCorentin Chary 
722