cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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
     43bool 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
     55void 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
     72size_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
    142size_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
    168static 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
    207static 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
    253static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
    254
    255static 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
    342static 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
    376static 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
    457static 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
    474static 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
    509static 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
    527static char *
    528vnc_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
    554void 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