qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

vnc-auth-sasl.c (20838B)


      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     VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
     77               "Encoded: %p size %d offset %d\n",
     78               vs->output.buffer, vs->output.capacity, vs->output.offset,
     79               vs->sasl.encoded, vs->sasl.encodedLength, 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     VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
    161               encoded, ret, decoded, decodedLen);
    162     buffer_reserve(&vs->input, decodedLen);
    163     buffer_append(&vs->input, decoded, decodedLen);
    164     return decodedLen;
    165 }
    166 
    167 
    168 static int vnc_auth_sasl_check_access(VncState *vs)
    169 {
    170     const void *val;
    171     int rv;
    172     Error *err = NULL;
    173     bool allow;
    174 
    175     rv = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
    176     if (rv != SASL_OK) {
    177         trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username",
    178                             sasl_errstring(rv, NULL, NULL));
    179         return -1;
    180     }
    181     if (val == NULL) {
    182         trace_vnc_auth_fail(vs, vs->auth, "No SASL username set", "");
    183         return -1;
    184     }
    185 
    186     vs->sasl.username = g_strdup((const char*)val);
    187     trace_vnc_auth_sasl_username(vs, vs->sasl.username);
    188 
    189     if (vs->vd->sasl.authzid == NULL) {
    190         trace_vnc_auth_sasl_acl(vs, 1);
    191         return 0;
    192     }
    193 
    194     allow = qauthz_is_allowed_by_id(vs->vd->sasl.authzid,
    195                                     vs->sasl.username, &err);
    196     if (err) {
    197         trace_vnc_auth_fail(vs, vs->auth, "Error from authz",
    198                             error_get_pretty(err));
    199         error_free(err);
    200         return -1;
    201     }
    202 
    203     trace_vnc_auth_sasl_acl(vs, allow);
    204     return allow ? 0 : -1;
    205 }
    206 
    207 static int vnc_auth_sasl_check_ssf(VncState *vs)
    208 {
    209     const void *val;
    210     int err, ssf;
    211 
    212     if (!vs->sasl.wantSSF)
    213         return 1;
    214 
    215     err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
    216     if (err != SASL_OK)
    217         return 0;
    218 
    219     ssf = *(const int *)val;
    220 
    221     trace_vnc_auth_sasl_ssf(vs, ssf);
    222 
    223     if (ssf < 56)
    224         return 0; /* 56 is good for Kerberos */
    225 
    226     /* Only setup for read initially, because we're about to send an RPC
    227      * reply which must be in plain text. When the next incoming RPC
    228      * arrives, we'll switch on writes too
    229      *
    230      * cf qemudClientReadSASL  in qemud.c
    231      */
    232     vs->sasl.runSSF = 1;
    233 
    234     /* We have a SSF that's good enough */
    235     return 1;
    236 }
    237 
    238 /*
    239  * Step Msg
    240  *
    241  * Input from client:
    242  *
    243  * u32 clientin-length
    244  * u8-array clientin-string
    245  *
    246  * Output to client:
    247  *
    248  * u32 serverout-length
    249  * u8-array serverout-strin
    250  * u8 continue
    251  */
    252 
    253 static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
    254 
    255 static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
    256 {
    257     uint32_t datalen = len;
    258     const char *serverout;
    259     unsigned int serveroutlen;
    260     int err;
    261     char *clientdata = NULL;
    262 
    263     /* NB, distinction of NULL vs "" is *critical* in SASL */
    264     if (datalen) {
    265         clientdata = (char*)data;
    266         clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
    267         datalen--; /* Don't count NULL byte when passing to _start() */
    268     }
    269 
    270     err = sasl_server_step(vs->sasl.conn,
    271                            clientdata,
    272                            datalen,
    273                            &serverout,
    274                            &serveroutlen);
    275     trace_vnc_auth_sasl_step(vs, data, len, serverout, serveroutlen, err);
    276     if (err != SASL_OK &&
    277         err != SASL_CONTINUE) {
    278         trace_vnc_auth_fail(vs, vs->auth, "Cannot step SASL auth",
    279                             sasl_errdetail(vs->sasl.conn));
    280         sasl_dispose(&vs->sasl.conn);
    281         vs->sasl.conn = NULL;
    282         goto authabort;
    283     }
    284 
    285     if (serveroutlen > SASL_DATA_MAX_LEN) {
    286         trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
    287         sasl_dispose(&vs->sasl.conn);
    288         vs->sasl.conn = NULL;
    289         goto authabort;
    290     }
    291 
    292     if (serveroutlen) {
    293         vnc_write_u32(vs, serveroutlen + 1);
    294         vnc_write(vs, serverout, serveroutlen + 1);
    295     } else {
    296         vnc_write_u32(vs, 0);
    297     }
    298 
    299     /* Whether auth is complete */
    300     vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
    301 
    302     if (err == SASL_CONTINUE) {
    303         /* Wait for step length */
    304         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
    305     } else {
    306         if (!vnc_auth_sasl_check_ssf(vs)) {
    307             trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
    308             goto authreject;
    309         }
    310 
    311         /* Check the username access control list */
    312         if (vnc_auth_sasl_check_access(vs) < 0) {
    313             goto authreject;
    314         }
    315 
    316         trace_vnc_auth_pass(vs, vs->auth);
    317         vnc_write_u32(vs, 0); /* Accept auth */
    318         /*
    319          * Delay writing in SSF encoded mode until pending output
    320          * buffer is written
    321          */
    322         if (vs->sasl.runSSF)
    323             vs->sasl.waitWriteSSF = vs->output.offset;
    324         start_client_init(vs);
    325     }
    326 
    327     return 0;
    328 
    329  authreject:
    330     vnc_write_u32(vs, 1); /* Reject auth */
    331     vnc_write_u32(vs, sizeof("Authentication failed"));
    332     vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
    333     vnc_flush(vs);
    334     vnc_client_error(vs);
    335     return -1;
    336 
    337  authabort:
    338     vnc_client_error(vs);
    339     return -1;
    340 }
    341 
    342 static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
    343 {
    344     uint32_t steplen = read_u32(data, 0);
    345 
    346     if (steplen > SASL_DATA_MAX_LEN) {
    347         trace_vnc_auth_fail(vs, vs->auth, "SASL step len too large", "");
    348         vnc_client_error(vs);
    349         return -1;
    350     }
    351 
    352     if (steplen == 0)
    353         return protocol_client_auth_sasl_step(vs, NULL, 0);
    354     else
    355         vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
    356     return 0;
    357 }
    358 
    359 /*
    360  * Start Msg
    361  *
    362  * Input from client:
    363  *
    364  * u32 clientin-length
    365  * u8-array clientin-string
    366  *
    367  * Output to client:
    368  *
    369  * u32 serverout-length
    370  * u8-array serverout-strin
    371  * u8 continue
    372  */
    373 
    374 #define SASL_DATA_MAX_LEN (1024 * 1024)
    375 
    376 static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
    377 {
    378     uint32_t datalen = len;
    379     const char *serverout;
    380     unsigned int serveroutlen;
    381     int err;
    382     char *clientdata = NULL;
    383 
    384     /* NB, distinction of NULL vs "" is *critical* in SASL */
    385     if (datalen) {
    386         clientdata = (char*)data;
    387         clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
    388         datalen--; /* Don't count NULL byte when passing to _start() */
    389     }
    390 
    391     err = sasl_server_start(vs->sasl.conn,
    392                             vs->sasl.mechlist,
    393                             clientdata,
    394                             datalen,
    395                             &serverout,
    396                             &serveroutlen);
    397     trace_vnc_auth_sasl_start(vs, data, len, serverout, serveroutlen, err);
    398     if (err != SASL_OK &&
    399         err != SASL_CONTINUE) {
    400         trace_vnc_auth_fail(vs, vs->auth, "Cannot start SASL auth",
    401                             sasl_errdetail(vs->sasl.conn));
    402         sasl_dispose(&vs->sasl.conn);
    403         vs->sasl.conn = NULL;
    404         goto authabort;
    405     }
    406     if (serveroutlen > SASL_DATA_MAX_LEN) {
    407         trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
    408         sasl_dispose(&vs->sasl.conn);
    409         vs->sasl.conn = NULL;
    410         goto authabort;
    411     }
    412 
    413     if (serveroutlen) {
    414         vnc_write_u32(vs, serveroutlen + 1);
    415         vnc_write(vs, serverout, serveroutlen + 1);
    416     } else {
    417         vnc_write_u32(vs, 0);
    418     }
    419 
    420     /* Whether auth is complete */
    421     vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
    422 
    423     if (err == SASL_CONTINUE) {
    424         /* Wait for step length */
    425         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
    426     } else {
    427         if (!vnc_auth_sasl_check_ssf(vs)) {
    428             trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
    429             goto authreject;
    430         }
    431 
    432         /* Check the username access control list */
    433         if (vnc_auth_sasl_check_access(vs) < 0) {
    434             goto authreject;
    435         }
    436 
    437         trace_vnc_auth_pass(vs, vs->auth);
    438         vnc_write_u32(vs, 0); /* Accept auth */
    439         start_client_init(vs);
    440     }
    441 
    442     return 0;
    443 
    444  authreject:
    445     vnc_write_u32(vs, 1); /* Reject auth */
    446     vnc_write_u32(vs, sizeof("Authentication failed"));
    447     vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
    448     vnc_flush(vs);
    449     vnc_client_error(vs);
    450     return -1;
    451 
    452  authabort:
    453     vnc_client_error(vs);
    454     return -1;
    455 }
    456 
    457 static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
    458 {
    459     uint32_t startlen = read_u32(data, 0);
    460 
    461     if (startlen > SASL_DATA_MAX_LEN) {
    462         trace_vnc_auth_fail(vs, vs->auth, "SASL start len too large", "");
    463         vnc_client_error(vs);
    464         return -1;
    465     }
    466 
    467     if (startlen == 0)
    468         return protocol_client_auth_sasl_start(vs, NULL, 0);
    469 
    470     vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
    471     return 0;
    472 }
    473 
    474 static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
    475 {
    476     char *mechname = g_strndup((const char *) data, len);
    477     trace_vnc_auth_sasl_mech_choose(vs, mechname);
    478 
    479     if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
    480         if (vs->sasl.mechlist[len] != '\0' &&
    481             vs->sasl.mechlist[len] != ',') {
    482             goto fail;
    483         }
    484     } else {
    485         char *offset = strstr(vs->sasl.mechlist, mechname);
    486         if (!offset) {
    487             goto fail;
    488         }
    489         if (offset[-1] != ',' ||
    490             (offset[len] != '\0'&&
    491              offset[len] != ',')) {
    492             goto fail;
    493         }
    494     }
    495 
    496     g_free(vs->sasl.mechlist);
    497     vs->sasl.mechlist = mechname;
    498 
    499     vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
    500     return 0;
    501 
    502  fail:
    503     trace_vnc_auth_fail(vs, vs->auth, "Unsupported mechname", mechname);
    504     vnc_client_error(vs);
    505     g_free(mechname);
    506     return -1;
    507 }
    508 
    509 static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
    510 {
    511     uint32_t mechlen = read_u32(data, 0);
    512 
    513     if (mechlen > 100) {
    514         trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too long", "");
    515         vnc_client_error(vs);
    516         return -1;
    517     }
    518     if (mechlen < 1) {
    519         trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too short", "");
    520         vnc_client_error(vs);
    521         return -1;
    522     }
    523     vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
    524     return 0;
    525 }
    526 
    527 static char *
    528 vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
    529                           bool local,
    530                           Error **errp)
    531 {
    532     SocketAddress *addr;
    533     char *ret;
    534 
    535     if (local) {
    536         addr = qio_channel_socket_get_local_address(ioc, errp);
    537     } else {
    538         addr = qio_channel_socket_get_remote_address(ioc, errp);
    539     }
    540     if (!addr) {
    541         return NULL;
    542     }
    543 
    544     if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
    545         error_setg(errp, "Not an inet socket type");
    546         qapi_free_SocketAddress(addr);
    547         return NULL;
    548     }
    549     ret = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port);
    550     qapi_free_SocketAddress(addr);
    551     return ret;
    552 }
    553 
    554 void start_auth_sasl(VncState *vs)
    555 {
    556     const char *mechlist = NULL;
    557     sasl_security_properties_t secprops;
    558     int err;
    559     Error *local_err = NULL;
    560     char *localAddr, *remoteAddr;
    561     int mechlistlen;
    562 
    563     /* Get local & remote client addresses in form  IPADDR;PORT */
    564     localAddr = vnc_socket_ip_addr_string(vs->sioc, true, &local_err);
    565     if (!localAddr) {
    566         trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP",
    567                             error_get_pretty(local_err));
    568         goto authabort;
    569     }
    570 
    571     remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, &local_err);
    572     if (!remoteAddr) {
    573         trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP",
    574                             error_get_pretty(local_err));
    575         g_free(localAddr);
    576         goto authabort;
    577     }
    578 
    579     err = sasl_server_new("vnc",
    580                           NULL, /* FQDN - just delegates to gethostname */
    581                           NULL, /* User realm */
    582                           localAddr,
    583                           remoteAddr,
    584                           NULL, /* Callbacks, not needed */
    585                           SASL_SUCCESS_DATA,
    586                           &vs->sasl.conn);
    587     g_free(localAddr);
    588     g_free(remoteAddr);
    589     localAddr = remoteAddr = NULL;
    590 
    591     if (err != SASL_OK) {
    592         trace_vnc_auth_fail(vs, vs->auth,  "SASL context setup failed",
    593                             sasl_errstring(err, NULL, NULL));
    594         vs->sasl.conn = NULL;
    595         goto authabort;
    596     }
    597 
    598     /* Inform SASL that we've got an external SSF layer from TLS/x509 */
    599     if (vs->auth == VNC_AUTH_VENCRYPT &&
    600         vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
    601         int keysize;
    602         sasl_ssf_t ssf;
    603 
    604         keysize = qcrypto_tls_session_get_key_size(vs->tls,
    605                                                    &local_err);
    606         if (keysize < 0) {
    607             trace_vnc_auth_fail(vs, vs->auth, "cannot TLS get cipher size",
    608                                 error_get_pretty(local_err));
    609             sasl_dispose(&vs->sasl.conn);
    610             vs->sasl.conn = NULL;
    611             goto authabort;
    612         }
    613         ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */
    614 
    615         err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
    616         if (err != SASL_OK) {
    617             trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL external SSF",
    618                                 sasl_errstring(err, NULL, NULL));
    619             sasl_dispose(&vs->sasl.conn);
    620             vs->sasl.conn = NULL;
    621             goto authabort;
    622         }
    623     } else {
    624         vs->sasl.wantSSF = 1;
    625     }
    626 
    627     memset (&secprops, 0, sizeof secprops);
    628     /* Inform SASL that we've got an external SSF layer from TLS.
    629      *
    630      * Disable SSF, if using TLS+x509+SASL only. TLS without x509
    631      * is not sufficiently strong
    632      */
    633     if (vs->vd->is_unix ||
    634         (vs->auth == VNC_AUTH_VENCRYPT &&
    635          vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
    636         /* If we've got TLS or UNIX domain sock, we don't care about SSF */
    637         secprops.min_ssf = 0;
    638         secprops.max_ssf = 0;
    639         secprops.maxbufsize = 8192;
    640         secprops.security_flags = 0;
    641     } else {
    642         /* Plain TCP, better get an SSF layer */
    643         secprops.min_ssf = 56; /* Good enough to require kerberos */
    644         secprops.max_ssf = 100000; /* Arbitrary big number */
    645         secprops.maxbufsize = 8192;
    646         /* Forbid any anonymous or trivially crackable auth */
    647         secprops.security_flags =
    648             SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
    649     }
    650 
    651     err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
    652     if (err != SASL_OK) {
    653         trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL security props",
    654                             sasl_errstring(err, NULL, NULL));
    655         sasl_dispose(&vs->sasl.conn);
    656         vs->sasl.conn = NULL;
    657         goto authabort;
    658     }
    659 
    660     err = sasl_listmech(vs->sasl.conn,
    661                         NULL, /* Don't need to set user */
    662                         "", /* Prefix */
    663                         ",", /* Separator */
    664                         "", /* Suffix */
    665                         &mechlist,
    666                         NULL,
    667                         NULL);
    668     if (err != SASL_OK) {
    669         trace_vnc_auth_fail(vs, vs->auth, "cannot list SASL mechanisms",
    670                             sasl_errdetail(vs->sasl.conn));
    671         sasl_dispose(&vs->sasl.conn);
    672         vs->sasl.conn = NULL;
    673         goto authabort;
    674     }
    675     trace_vnc_auth_sasl_mech_list(vs, mechlist);
    676 
    677     vs->sasl.mechlist = g_strdup(mechlist);
    678     mechlistlen = strlen(mechlist);
    679     vnc_write_u32(vs, mechlistlen);
    680     vnc_write(vs, mechlist, mechlistlen);
    681     vnc_flush(vs);
    682 
    683     vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
    684 
    685     return;
    686 
    687  authabort:
    688     error_free(local_err);
    689     vnc_client_error(vs);
    690 }
    691 
    692