master
c 720 lines 21.4 KB
Raw
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 "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "authz/base.h"
28 #include "vnc.h"
29 #include "trace.h"
30
31 /*
32 * Apple has deprecated sasl.h functions in OS X 10.11. Therefore,
33 * files that use SASL API need to disable -Wdeprecated-declarations.
34 */
35 #ifdef CONFIG_DARWIN
36 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
37 #endif
38
39 /* Max amount of data we send/recv for SASL steps to prevent DOS */
40 #define SASL_DATA_MAX_LEN (1024 * 1024)
41
42
43 bool vnc_sasl_server_init(Error **errp)
44 {
45 int saslErr = sasl_server_init(NULL, "qemu");
46
47 if (saslErr != SASL_OK) {
48 error_setg(errp, "Failed to initialize SASL auth: %s",
49 sasl_errstring(saslErr, NULL, NULL));
50 return false;
51 }
52 return true;
53 }
54
55 void vnc_sasl_client_cleanup(VncState *vs)
56 {
57 if (vs->sasl.conn) {
58 vs->sasl.runSSF = false;
59 vs->sasl.wantSSF = false;
60 vs->sasl.waitWriteSSF = 0;
61 vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
62 vs->sasl.encoded = NULL;
63 g_free(vs->sasl.username);
64 g_free(vs->sasl.mechlist);
65 vs->sasl.username = vs->sasl.mechlist = NULL;
66 sasl_dispose(&vs->sasl.conn);
67 vs->sasl.conn = NULL;
68 }
69 }
70
71
72 size_t vnc_client_write_sasl(VncState *vs)
73 {
74 size_t ret;
75
76 trace_vnc_sasl_write_pending(vs, vs->output.buffer, vs->output.capacity,
77 vs->output.offset, vs->sasl.encoded,
78 vs->sasl.encodedLength,
79 vs->sasl.encodedOffset);
80
81 if (!vs->sasl.encoded) {
82 int err;
83 err = sasl_encode(vs->sasl.conn,
84 (char *)vs->output.buffer,
85 vs->output.offset,
86 (const char **)&vs->sasl.encoded,
87 &vs->sasl.encodedLength);
88 if (err != SASL_OK)
89 return vnc_client_io_error(vs, -1, NULL);
90
91 vs->sasl.encodedRawLength = vs->output.offset;
92 vs->sasl.encodedOffset = 0;
93 }
94
95 ret = vnc_client_write_buf(vs,
96 vs->sasl.encoded + vs->sasl.encodedOffset,
97 vs->sasl.encodedLength - vs->sasl.encodedOffset);
98 if (!ret)
99 return 0;
100
101 vs->sasl.encodedOffset += ret;
102 if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
103 bool throttled = vs->force_update_offset != 0;
104 size_t offset;
105 if (vs->sasl.encodedRawLength >= vs->force_update_offset) {
106 vs->force_update_offset = 0;
107 } else {
108 vs->force_update_offset -= vs->sasl.encodedRawLength;
109 }
110 if (throttled && vs->force_update_offset == 0) {
111 trace_vnc_client_unthrottle_forced(vs, vs->ioc);
112 }
113 offset = vs->output.offset;
114 buffer_advance(&vs->output, vs->sasl.encodedRawLength);
115 if (offset >= vs->throttle_output_offset &&
116 vs->output.offset < vs->throttle_output_offset) {
117 trace_vnc_client_unthrottle_incremental(vs, vs->ioc,
118 vs->output.offset);
119 }
120 vs->sasl.encoded = NULL;
121 vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
122 }
123
124 /* Can't merge this block with one above, because
125 * someone might have written more unencrypted
126 * data in vs->output while we were processing
127 * SASL encoded output
128 */
129 if (vs->output.offset == 0) {
130 if (vs->ioc_tag) {
131 g_source_remove(vs->ioc_tag);
132 }
133 vs->ioc_tag = qio_channel_add_watch(
134 vs->ioc, G_IO_IN | G_IO_HUP | G_IO_ERR,
135 vnc_client_io, vs, NULL);
136 }
137
138 return ret;
139 }
140
141
142 size_t vnc_client_read_sasl(VncState *vs)
143 {
144 size_t ret;
145 uint8_t encoded[4096];
146 const char *decoded;
147 unsigned int decodedLen;
148 int err;
149
150 ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
151 if (!ret)
152 return 0;
153
154 err = sasl_decode(vs->sasl.conn,
155 (char *)encoded, ret,
156 &decoded, &decodedLen);
157
158 if (err != SASL_OK)
159 return vnc_client_io_error(vs, -1, NULL);
160 trace_vnc_sasl_read_decoded(vs, encoded, ret, decoded, decodedLen);
161 buffer_reserve(&vs->input, decodedLen);
162 buffer_append(&vs->input, decoded, decodedLen);
163 return decodedLen;
164 }
165
166
167 static int vnc_auth_sasl_check_access(VncState *vs)
168 {
169 const void *val;
170 int rv;
171 Error *err = NULL;
172 bool allow;
173
174 rv = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
175 if (rv != SASL_OK) {
176 trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username",
177 sasl_errstring(rv, NULL, NULL));
178 return -1;
179 }
180 if (val == NULL) {
181 trace_vnc_auth_fail(vs, vs->auth, "No SASL username set", "");
182 return -1;
183 }
184
185 vs->sasl.username = g_strdup((const char*)val);
186 trace_vnc_auth_sasl_username(vs, vs->sasl.username);
187
188 if (vs->vd->sasl.authzid == NULL) {
189 trace_vnc_auth_sasl_acl(vs, 1);
190 return 0;
191 }
192
193 allow = qauthz_is_allowed_by_id(vs->vd->sasl.authzid,
194 vs->sasl.username, &err);
195 if (err) {
196 trace_vnc_auth_fail(vs, vs->auth, "Error from authz",
197 error_get_pretty(err));
198 error_free(err);
199 return -1;
200 }
201
202 trace_vnc_auth_sasl_acl(vs, allow);
203 return allow ? 0 : -1;
204 }
205
206 static int vnc_auth_sasl_check_ssf(VncState *vs)
207 {
208 const void *val;
209 int err, ssf;
210
211 if (!vs->sasl.wantSSF)
212 return 1;
213
214 err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
215 if (err != SASL_OK)
216 return 0;
217
218 ssf = *(const int *)val;
219
220 trace_vnc_auth_sasl_ssf(vs, ssf);
221
222 if (ssf < 56)
223 return 0; /* 56 is good for Kerberos */
224
225 /* Only setup for read initially, because we're about to send an RPC
226 * reply which must be in plain text. When the next incoming RPC
227 * arrives, we'll switch on writes too
228 *
229 * cf qemudClientReadSASL in qemud.c
230 */
231 vs->sasl.runSSF = 1;
232
233 /* We have a SSF that's good enough */
234 return 1;
235 }
236
237 /*
238 * Step Msg
239 *
240 * Input from client:
241 *
242 * u32 clientin-length
243 * u8-array clientin-string
244 *
245 * Output to client:
246 *
247 * u32 serverout-length
248 * u8-array serverout-strin
249 * u8 continue
250 */
251
252 static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
253
254 static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
255 {
256 uint32_t datalen = len;
257 const char *serverout;
258 unsigned int serveroutlen;
259 int err;
260 char *clientdata = NULL;
261
262 /* NB, distinction of NULL vs "" is *critical* in SASL */
263 if (datalen) {
264 clientdata = (char*)data;
265 if (clientdata[datalen - 1] != '\0') {
266 trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data",
267 "Missing SASL NUL padding byte");
268 sasl_dispose(&vs->sasl.conn);
269 vs->sasl.conn = NULL;
270 goto authabort;
271 }
272 datalen--; /* Discard the extra NUL padding byte */
273 }
274
275 err = sasl_server_step(vs->sasl.conn,
276 clientdata,
277 datalen,
278 &serverout,
279 &serveroutlen);
280 trace_vnc_auth_sasl_step(vs, data, len, serverout, serveroutlen, err);
281 if (err != SASL_OK &&
282 err != SASL_CONTINUE) {
283 trace_vnc_auth_fail(vs, vs->auth, "Cannot step SASL auth",
284 sasl_errdetail(vs->sasl.conn));
285 sasl_dispose(&vs->sasl.conn);
286 vs->sasl.conn = NULL;
287 goto authabort;
288 }
289
290 if (serveroutlen > SASL_DATA_MAX_LEN) {
291 trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
292 sasl_dispose(&vs->sasl.conn);
293 vs->sasl.conn = NULL;
294 goto authabort;
295 }
296
297 if (serverout) {
298 vnc_write_u32(vs, serveroutlen + 1);
299 vnc_write(vs, serverout, serveroutlen);
300 vnc_write_u8(vs, '\0');
301 } else {
302 vnc_write_u32(vs, 0);
303 }
304
305 /* Whether auth is complete */
306 vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
307
308 if (err == SASL_CONTINUE) {
309 /* Wait for step length */
310 vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
311 } else {
312 if (!vnc_auth_sasl_check_ssf(vs)) {
313 trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
314 goto authreject;
315 }
316
317 /* Check the username access control list */
318 if (vnc_auth_sasl_check_access(vs) < 0) {
319 goto authreject;
320 }
321
322 trace_vnc_auth_pass(vs, vs->auth);
323 vnc_write_u32(vs, 0); /* Accept auth */
324 /*
325 * Delay writing in SSF encoded mode until pending output
326 * buffer is written
327 */
328 if (vs->sasl.runSSF)
329 vs->sasl.waitWriteSSF = vs->output.offset;
330 start_client_init(vs);
331 }
332
333 return 0;
334
335 authreject:
336 vnc_write_u32(vs, 1); /* Reject auth */
337 vnc_write_u32(vs, sizeof("Authentication failed"));
338 vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
339 vnc_flush(vs);
340 vnc_client_error(vs);
341 return -1;
342
343 authabort:
344 vnc_client_error(vs);
345 return -1;
346 }
347
348 static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
349 {
350 uint32_t steplen = read_u32(data, 0);
351
352 if (steplen > SASL_DATA_MAX_LEN) {
353 trace_vnc_auth_fail(vs, vs->auth, "SASL step len too large", "");
354 vnc_client_error(vs);
355 return -1;
356 }
357
358 if (steplen == 0)
359 return protocol_client_auth_sasl_step(vs, NULL, 0);
360 else
361 vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
362 return 0;
363 }
364
365 /*
366 * Start Msg
367 *
368 * Input from client:
369 *
370 * u32 clientin-length
371 * u8-array clientin-string
372 *
373 * Output to client:
374 *
375 * u32 serverout-length
376 * u8-array serverout-strin
377 * u8 continue
378 */
379
380 #define SASL_DATA_MAX_LEN (1024 * 1024)
381
382 static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
383 {
384 uint32_t datalen = len;
385 const char *serverout;
386 unsigned int serveroutlen;
387 int err;
388 char *clientdata = NULL;
389
390 /* NB, distinction of NULL vs "" is *critical* in SASL */
391 if (datalen) {
392 clientdata = (char*)data;
393 if (clientdata[datalen - 1] != '\0') {
394 trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data",
395 "Missing SASL NUL padding byte");
396 sasl_dispose(&vs->sasl.conn);
397 vs->sasl.conn = NULL;
398 goto authabort;
399 }
400 datalen--; /* Discard the extra NUL padding byte */
401 }
402
403 err = sasl_server_start(vs->sasl.conn,
404 vs->sasl.mechlist,
405 clientdata,
406 datalen,
407 &serverout,
408 &serveroutlen);
409 trace_vnc_auth_sasl_start(vs, data, len, serverout, serveroutlen, err);
410 if (err != SASL_OK &&
411 err != SASL_CONTINUE) {
412 trace_vnc_auth_fail(vs, vs->auth, "Cannot start SASL auth",
413 sasl_errdetail(vs->sasl.conn));
414 sasl_dispose(&vs->sasl.conn);
415 vs->sasl.conn = NULL;
416 goto authabort;
417 }
418 if (serveroutlen > SASL_DATA_MAX_LEN) {
419 trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
420 sasl_dispose(&vs->sasl.conn);
421 vs->sasl.conn = NULL;
422 goto authabort;
423 }
424
425 if (serverout) {
426 vnc_write_u32(vs, serveroutlen + 1);
427 vnc_write(vs, serverout, serveroutlen);
428 vnc_write_u8(vs, '\0');
429 } else {
430 vnc_write_u32(vs, 0);
431 }
432
433 /* Whether auth is complete */
434 vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
435
436 if (err == SASL_CONTINUE) {
437 /* Wait for step length */
438 vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
439 } else {
440 if (!vnc_auth_sasl_check_ssf(vs)) {
441 trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
442 goto authreject;
443 }
444
445 /* Check the username access control list */
446 if (vnc_auth_sasl_check_access(vs) < 0) {
447 goto authreject;
448 }
449
450 trace_vnc_auth_pass(vs, vs->auth);
451 vnc_write_u32(vs, 0); /* Accept auth */
452 start_client_init(vs);
453 }
454
455 return 0;
456
457 authreject:
458 vnc_write_u32(vs, 1); /* Reject auth */
459 vnc_write_u32(vs, sizeof("Authentication failed"));
460 vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
461 vnc_flush(vs);
462 vnc_client_error(vs);
463 return -1;
464
465 authabort:
466 vnc_client_error(vs);
467 return -1;
468 }
469
470 static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
471 {
472 uint32_t startlen = read_u32(data, 0);
473
474 if (startlen > SASL_DATA_MAX_LEN) {
475 trace_vnc_auth_fail(vs, vs->auth, "SASL start len too large", "");
476 vnc_client_error(vs);
477 return -1;
478 }
479
480 if (startlen == 0)
481 return protocol_client_auth_sasl_start(vs, NULL, 0);
482
483 vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
484 return 0;
485 }
486
487 static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
488 {
489 char *mechname = g_strndup((const char *) data, len);
490 trace_vnc_auth_sasl_mech_choose(vs, mechname);
491
492 /* If 'data' had embedded NUL the dup'd string might now be shorter */
493 len = strlen(mechname);
494 if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
495 if (vs->sasl.mechlist[len] != '\0' &&
496 vs->sasl.mechlist[len] != ',') {
497 goto fail;
498 }
499 } else {
500 char *offset = strstr(vs->sasl.mechlist, mechname);
501 if (!offset) {
502 goto fail;
503 }
504 if (offset[-1] != ',' ||
505 (offset[len] != '\0'&&
506 offset[len] != ',')) {
507 goto fail;
508 }
509 }
510
511 g_free(vs->sasl.mechlist);
512 vs->sasl.mechlist = mechname;
513
514 vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
515 return 0;
516
517 fail:
518 trace_vnc_auth_fail(vs, vs->auth, "Unsupported mechname", mechname);
519 vnc_client_error(vs);
520 g_free(mechname);
521 return -1;
522 }
523
524 static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
525 {
526 uint32_t mechlen = read_u32(data, 0);
527
528 if (mechlen > 100) {
529 trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too long", "");
530 vnc_client_error(vs);
531 return -1;
532 }
533 if (mechlen < 1) {
534 trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too short", "");
535 vnc_client_error(vs);
536 return -1;
537 }
538 vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
539 return 0;
540 }
541
542 static int
543 vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
544 bool local,
545 char **addrstr,
546 Error **errp)
547 {
548 SocketAddress *addr;
549
550 if (local) {
551 addr = qio_channel_socket_get_local_address(ioc, errp);
552 } else {
553 addr = qio_channel_socket_get_remote_address(ioc, errp);
554 }
555 if (!addr) {
556 return -1;
557 }
558
559 if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
560 *addrstr = NULL;
561 qapi_free_SocketAddress(addr);
562 return 0;
563 }
564 *addrstr = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port);
565 qapi_free_SocketAddress(addr);
566 return 0;
567 }
568
569 static bool
570 vnc_socket_is_unix(QIOChannelSocket *ioc)
571 {
572 SocketAddress *addr = qio_channel_socket_get_local_address(ioc, NULL);
573 return addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX;
574 }
575
576 void start_auth_sasl(VncState *vs)
577 {
578 const char *mechlist = NULL;
579 sasl_security_properties_t secprops;
580 int err;
581 Error *local_err = NULL;
582 char *localAddr, *remoteAddr;
583 int mechlistlen;
584
585 /* Get local & remote client addresses in form IPADDR;PORT */
586 if (vnc_socket_ip_addr_string(vs->sioc, true,
587 &localAddr, &local_err) < 0) {
588 trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP",
589 error_get_pretty(local_err));
590 goto authabort;
591 }
592
593 if (vnc_socket_ip_addr_string(vs->sioc, false,
594 &remoteAddr, &local_err) < 0) {
595 trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP",
596 error_get_pretty(local_err));
597 g_free(localAddr);
598 goto authabort;
599 }
600
601 err = sasl_server_new("vnc",
602 NULL, /* FQDN - just delegates to gethostname */
603 NULL, /* User realm */
604 localAddr,
605 remoteAddr,
606 NULL, /* Callbacks, not needed */
607 SASL_SUCCESS_DATA,
608 &vs->sasl.conn);
609 g_free(localAddr);
610 g_free(remoteAddr);
611 localAddr = remoteAddr = NULL;
612
613 if (err != SASL_OK) {
614 trace_vnc_auth_fail(vs, vs->auth, "SASL context setup failed",
615 sasl_errstring(err, NULL, NULL));
616 vs->sasl.conn = NULL;
617 goto authabort;
618 }
619
620 /* Inform SASL that we've got an external SSF layer from TLS/x509 */
621 if (vs->auth == VNC_AUTH_VENCRYPT &&
622 vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
623 int keysize;
624 sasl_ssf_t ssf;
625
626 keysize = qcrypto_tls_session_get_key_size(vs->tls,
627 &local_err);
628 if (keysize < 0) {
629 trace_vnc_auth_fail(vs, vs->auth, "cannot TLS get cipher size",
630 error_get_pretty(local_err));
631 sasl_dispose(&vs->sasl.conn);
632 vs->sasl.conn = NULL;
633 goto authabort;
634 }
635 ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */
636
637 err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
638 if (err != SASL_OK) {
639 trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL external SSF",
640 sasl_errstring(err, NULL, NULL));
641 sasl_dispose(&vs->sasl.conn);
642 vs->sasl.conn = NULL;
643 goto authabort;
644 }
645 } else {
646 vs->sasl.wantSSF = !vnc_socket_is_unix(vs->sioc);
647 }
648
649 memset (&secprops, 0, sizeof secprops);
650 /* Inform SASL that we've got an external SSF layer from TLS.
651 *
652 * Disable SSF, if using TLS+x509+SASL only, or UNIX sockets.
653 * TLS without x509 is not sufficiently strong, nor is plain
654 * TCP
655 */
656 if (vnc_socket_is_unix(vs->sioc) ||
657 (vs->auth == VNC_AUTH_VENCRYPT &&
658 vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
659 /* If we've got TLS or UNIX domain sock, we don't care about SSF */
660 secprops.min_ssf = 0;
661 secprops.max_ssf = 0;
662 secprops.maxbufsize = 8192;
663 secprops.security_flags = 0;
664 } else {
665 /* Plain TCP, better get an SSF layer */
666 secprops.min_ssf = 56; /* Good enough to require kerberos */
667 secprops.max_ssf = 100000; /* Arbitrary big number */
668 secprops.maxbufsize = 8192;
669 /* Forbid any anonymous or trivially crackable auth */
670 secprops.security_flags =
671 SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
672 }
673
674 err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
675 if (err != SASL_OK) {
676 trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL security props",
677 sasl_errstring(err, NULL, NULL));
678 sasl_dispose(&vs->sasl.conn);
679 vs->sasl.conn = NULL;
680 goto authabort;
681 }
682
683 err = sasl_listmech(vs->sasl.conn,
684 NULL, /* Don't need to set user */
685 "", /* Prefix */
686 ",", /* Separator */
687 "", /* Suffix */
688 &mechlist,
689 NULL,
690 NULL);
691 if (err != SASL_OK) {
692 trace_vnc_auth_fail(vs, vs->auth, "cannot list SASL mechanisms",
693 sasl_errdetail(vs->sasl.conn));
694 sasl_dispose(&vs->sasl.conn);
695 vs->sasl.conn = NULL;
696 goto authabort;
697 }
698 trace_vnc_auth_sasl_mech_list(vs, mechlist);
699
700 if (g_str_equal(mechlist, "")) {
701 trace_vnc_auth_fail(vs, vs->auth, "no available SASL mechanisms", "");
702 sasl_dispose(&vs->sasl.conn);
703 vs->sasl.conn = NULL;
704 goto authabort;
705 }
706
707 vs->sasl.mechlist = g_strdup(mechlist);
708 mechlistlen = strlen(mechlist);
709 vnc_write_u32(vs, mechlistlen);
710 vnc_write(vs, mechlist, mechlistlen);
711 vnc_flush(vs);
712
713 vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
714
715 return;
716
717 authabort:
718 error_free(local_err);
719 vnc_client_error(vs);
720 }