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