Annotation of qemu/ui/vnc-auth-sasl.c, revision 1.1

1.1     ! root        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 "vnc.h"
        !            26: 
        !            27: /* Max amount of data we send/recv for SASL steps to prevent DOS */
        !            28: #define SASL_DATA_MAX_LEN (1024 * 1024)
        !            29: 
        !            30: 
        !            31: void vnc_sasl_client_cleanup(VncState *vs)
        !            32: {
        !            33:     if (vs->sasl.conn) {
        !            34:         vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0;
        !            35:         vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
        !            36:         vs->sasl.encoded = NULL;
        !            37:         free(vs->sasl.username);
        !            38:         free(vs->sasl.mechlist);
        !            39:         vs->sasl.username = vs->sasl.mechlist = NULL;
        !            40:         sasl_dispose(&vs->sasl.conn);
        !            41:         vs->sasl.conn = NULL;
        !            42:     }
        !            43: }
        !            44: 
        !            45: 
        !            46: long vnc_client_write_sasl(VncState *vs)
        !            47: {
        !            48:     long ret;
        !            49: 
        !            50:     VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
        !            51:               "Encoded: %p size %d offset %d\n",
        !            52:               vs->output.buffer, vs->output.capacity, vs->output.offset,
        !            53:               vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
        !            54: 
        !            55:     if (!vs->sasl.encoded) {
        !            56:         int err;
        !            57:         err = sasl_encode(vs->sasl.conn,
        !            58:                           (char *)vs->output.buffer,
        !            59:                           vs->output.offset,
        !            60:                           (const char **)&vs->sasl.encoded,
        !            61:                           &vs->sasl.encodedLength);
        !            62:         if (err != SASL_OK)
        !            63:             return vnc_client_io_error(vs, -1, EIO);
        !            64: 
        !            65:         vs->sasl.encodedOffset = 0;
        !            66:     }
        !            67: 
        !            68:     ret = vnc_client_write_buf(vs,
        !            69:                                vs->sasl.encoded + vs->sasl.encodedOffset,
        !            70:                                vs->sasl.encodedLength - vs->sasl.encodedOffset);
        !            71:     if (!ret)
        !            72:         return 0;
        !            73: 
        !            74:     vs->sasl.encodedOffset += ret;
        !            75:     if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
        !            76:         vs->output.offset = 0;
        !            77:         vs->sasl.encoded = NULL;
        !            78:         vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
        !            79:     }
        !            80: 
        !            81:     /* Can't merge this block with one above, because
        !            82:      * someone might have written more unencrypted
        !            83:      * data in vs->output while we were processing
        !            84:      * SASL encoded output
        !            85:      */
        !            86:     if (vs->output.offset == 0) {
        !            87:         qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
        !            88:     }
        !            89: 
        !            90:     return ret;
        !            91: }
        !            92: 
        !            93: 
        !            94: long vnc_client_read_sasl(VncState *vs)
        !            95: {
        !            96:     long ret;
        !            97:     uint8_t encoded[4096];
        !            98:     const char *decoded;
        !            99:     unsigned int decodedLen;
        !           100:     int err;
        !           101: 
        !           102:     ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
        !           103:     if (!ret)
        !           104:         return 0;
        !           105: 
        !           106:     err = sasl_decode(vs->sasl.conn,
        !           107:                       (char *)encoded, ret,
        !           108:                       &decoded, &decodedLen);
        !           109: 
        !           110:     if (err != SASL_OK)
        !           111:         return vnc_client_io_error(vs, -1, -EIO);
        !           112:     VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
        !           113:               encoded, ret, decoded, decodedLen);
        !           114:     buffer_reserve(&vs->input, decodedLen);
        !           115:     buffer_append(&vs->input, decoded, decodedLen);
        !           116:     return decodedLen;
        !           117: }
        !           118: 
        !           119: 
        !           120: static int vnc_auth_sasl_check_access(VncState *vs)
        !           121: {
        !           122:     const void *val;
        !           123:     int err;
        !           124:     int allow;
        !           125: 
        !           126:     err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
        !           127:     if (err != SASL_OK) {
        !           128:         VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
        !           129:                   err, sasl_errstring(err, NULL, NULL));
        !           130:         return -1;
        !           131:     }
        !           132:     if (val == NULL) {
        !           133:         VNC_DEBUG("no client username was found, denying access\n");
        !           134:         return -1;
        !           135:     }
        !           136:     VNC_DEBUG("SASL client username %s\n", (const char *)val);
        !           137: 
        !           138:     vs->sasl.username = qemu_strdup((const char*)val);
        !           139: 
        !           140:     if (vs->vd->sasl.acl == NULL) {
        !           141:         VNC_DEBUG("no ACL activated, allowing access\n");
        !           142:         return 0;
        !           143:     }
        !           144: 
        !           145:     allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
        !           146: 
        !           147:     VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
        !           148:               allow ? "allowed" : "denied");
        !           149:     return allow ? 0 : -1;
        !           150: }
        !           151: 
        !           152: static int vnc_auth_sasl_check_ssf(VncState *vs)
        !           153: {
        !           154:     const void *val;
        !           155:     int err, ssf;
        !           156: 
        !           157:     if (!vs->sasl.wantSSF)
        !           158:         return 1;
        !           159: 
        !           160:     err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
        !           161:     if (err != SASL_OK)
        !           162:         return 0;
        !           163: 
        !           164:     ssf = *(const int *)val;
        !           165:     VNC_DEBUG("negotiated an SSF of %d\n", ssf);
        !           166:     if (ssf < 56)
        !           167:         return 0; /* 56 is good for Kerberos */
        !           168: 
        !           169:     /* Only setup for read initially, because we're about to send an RPC
        !           170:      * reply which must be in plain text. When the next incoming RPC
        !           171:      * arrives, we'll switch on writes too
        !           172:      *
        !           173:      * cf qemudClientReadSASL  in qemud.c
        !           174:      */
        !           175:     vs->sasl.runSSF = 1;
        !           176: 
        !           177:     /* We have a SSF that's good enough */
        !           178:     return 1;
        !           179: }
        !           180: 
        !           181: /*
        !           182:  * Step Msg
        !           183:  *
        !           184:  * Input from client:
        !           185:  *
        !           186:  * u32 clientin-length
        !           187:  * u8-array clientin-string
        !           188:  *
        !           189:  * Output to client:
        !           190:  *
        !           191:  * u32 serverout-length
        !           192:  * u8-array serverout-strin
        !           193:  * u8 continue
        !           194:  */
        !           195: 
        !           196: static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
        !           197: 
        !           198: static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
        !           199: {
        !           200:     uint32_t datalen = len;
        !           201:     const char *serverout;
        !           202:     unsigned int serveroutlen;
        !           203:     int err;
        !           204:     char *clientdata = NULL;
        !           205: 
        !           206:     /* NB, distinction of NULL vs "" is *critical* in SASL */
        !           207:     if (datalen) {
        !           208:         clientdata = (char*)data;
        !           209:         clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
        !           210:         datalen--; /* Don't count NULL byte when passing to _start() */
        !           211:     }
        !           212: 
        !           213:     VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
        !           214:               clientdata, datalen);
        !           215:     err = sasl_server_step(vs->sasl.conn,
        !           216:                            clientdata,
        !           217:                            datalen,
        !           218:                            &serverout,
        !           219:                            &serveroutlen);
        !           220:     if (err != SASL_OK &&
        !           221:         err != SASL_CONTINUE) {
        !           222:         VNC_DEBUG("sasl step failed %d (%s)\n",
        !           223:                   err, sasl_errdetail(vs->sasl.conn));
        !           224:         sasl_dispose(&vs->sasl.conn);
        !           225:         vs->sasl.conn = NULL;
        !           226:         goto authabort;
        !           227:     }
        !           228: 
        !           229:     if (serveroutlen > SASL_DATA_MAX_LEN) {
        !           230:         VNC_DEBUG("sasl step reply data too long %d\n",
        !           231:                   serveroutlen);
        !           232:         sasl_dispose(&vs->sasl.conn);
        !           233:         vs->sasl.conn = NULL;
        !           234:         goto authabort;
        !           235:     }
        !           236: 
        !           237:     VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
        !           238:               serveroutlen, serverout ? 0 : 1);
        !           239: 
        !           240:     if (serveroutlen) {
        !           241:         vnc_write_u32(vs, serveroutlen + 1);
        !           242:         vnc_write(vs, serverout, serveroutlen + 1);
        !           243:     } else {
        !           244:         vnc_write_u32(vs, 0);
        !           245:     }
        !           246: 
        !           247:     /* Whether auth is complete */
        !           248:     vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
        !           249: 
        !           250:     if (err == SASL_CONTINUE) {
        !           251:         VNC_DEBUG("%s", "Authentication must continue\n");
        !           252:         /* Wait for step length */
        !           253:         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
        !           254:     } else {
        !           255:         if (!vnc_auth_sasl_check_ssf(vs)) {
        !           256:             VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
        !           257:             goto authreject;
        !           258:         }
        !           259: 
        !           260:         /* Check username whitelist ACL */
        !           261:         if (vnc_auth_sasl_check_access(vs) < 0) {
        !           262:             VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
        !           263:             goto authreject;
        !           264:         }
        !           265: 
        !           266:         VNC_DEBUG("Authentication successful %d\n", vs->csock);
        !           267:         vnc_write_u32(vs, 0); /* Accept auth */
        !           268:         /*
        !           269:          * Delay writing in SSF encoded mode until pending output
        !           270:          * buffer is written
        !           271:          */
        !           272:         if (vs->sasl.runSSF)
        !           273:             vs->sasl.waitWriteSSF = vs->output.offset;
        !           274:         start_client_init(vs);
        !           275:     }
        !           276: 
        !           277:     return 0;
        !           278: 
        !           279:  authreject:
        !           280:     vnc_write_u32(vs, 1); /* Reject auth */
        !           281:     vnc_write_u32(vs, sizeof("Authentication failed"));
        !           282:     vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
        !           283:     vnc_flush(vs);
        !           284:     vnc_client_error(vs);
        !           285:     return -1;
        !           286: 
        !           287:  authabort:
        !           288:     vnc_client_error(vs);
        !           289:     return -1;
        !           290: }
        !           291: 
        !           292: static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
        !           293: {
        !           294:     uint32_t steplen = read_u32(data, 0);
        !           295:     VNC_DEBUG("Got client step len %d\n", steplen);
        !           296:     if (steplen > SASL_DATA_MAX_LEN) {
        !           297:         VNC_DEBUG("Too much SASL data %d\n", steplen);
        !           298:         vnc_client_error(vs);
        !           299:         return -1;
        !           300:     }
        !           301: 
        !           302:     if (steplen == 0)
        !           303:         return protocol_client_auth_sasl_step(vs, NULL, 0);
        !           304:     else
        !           305:         vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
        !           306:     return 0;
        !           307: }
        !           308: 
        !           309: /*
        !           310:  * Start Msg
        !           311:  *
        !           312:  * Input from client:
        !           313:  *
        !           314:  * u32 clientin-length
        !           315:  * u8-array clientin-string
        !           316:  *
        !           317:  * Output to client:
        !           318:  *
        !           319:  * u32 serverout-length
        !           320:  * u8-array serverout-strin
        !           321:  * u8 continue
        !           322:  */
        !           323: 
        !           324: #define SASL_DATA_MAX_LEN (1024 * 1024)
        !           325: 
        !           326: static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
        !           327: {
        !           328:     uint32_t datalen = len;
        !           329:     const char *serverout;
        !           330:     unsigned int serveroutlen;
        !           331:     int err;
        !           332:     char *clientdata = NULL;
        !           333: 
        !           334:     /* NB, distinction of NULL vs "" is *critical* in SASL */
        !           335:     if (datalen) {
        !           336:         clientdata = (char*)data;
        !           337:         clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
        !           338:         datalen--; /* Don't count NULL byte when passing to _start() */
        !           339:     }
        !           340: 
        !           341:     VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
        !           342:               vs->sasl.mechlist, clientdata, datalen);
        !           343:     err = sasl_server_start(vs->sasl.conn,
        !           344:                             vs->sasl.mechlist,
        !           345:                             clientdata,
        !           346:                             datalen,
        !           347:                             &serverout,
        !           348:                             &serveroutlen);
        !           349:     if (err != SASL_OK &&
        !           350:         err != SASL_CONTINUE) {
        !           351:         VNC_DEBUG("sasl start failed %d (%s)\n",
        !           352:                   err, sasl_errdetail(vs->sasl.conn));
        !           353:         sasl_dispose(&vs->sasl.conn);
        !           354:         vs->sasl.conn = NULL;
        !           355:         goto authabort;
        !           356:     }
        !           357:     if (serveroutlen > SASL_DATA_MAX_LEN) {
        !           358:         VNC_DEBUG("sasl start reply data too long %d\n",
        !           359:                   serveroutlen);
        !           360:         sasl_dispose(&vs->sasl.conn);
        !           361:         vs->sasl.conn = NULL;
        !           362:         goto authabort;
        !           363:     }
        !           364: 
        !           365:     VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
        !           366:               serveroutlen, serverout ? 0 : 1);
        !           367: 
        !           368:     if (serveroutlen) {
        !           369:         vnc_write_u32(vs, serveroutlen + 1);
        !           370:         vnc_write(vs, serverout, serveroutlen + 1);
        !           371:     } else {
        !           372:         vnc_write_u32(vs, 0);
        !           373:     }
        !           374: 
        !           375:     /* Whether auth is complete */
        !           376:     vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
        !           377: 
        !           378:     if (err == SASL_CONTINUE) {
        !           379:         VNC_DEBUG("%s", "Authentication must continue\n");
        !           380:         /* Wait for step length */
        !           381:         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
        !           382:     } else {
        !           383:         if (!vnc_auth_sasl_check_ssf(vs)) {
        !           384:             VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
        !           385:             goto authreject;
        !           386:         }
        !           387: 
        !           388:         /* Check username whitelist ACL */
        !           389:         if (vnc_auth_sasl_check_access(vs) < 0) {
        !           390:             VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
        !           391:             goto authreject;
        !           392:         }
        !           393: 
        !           394:         VNC_DEBUG("Authentication successful %d\n", vs->csock);
        !           395:         vnc_write_u32(vs, 0); /* Accept auth */
        !           396:         start_client_init(vs);
        !           397:     }
        !           398: 
        !           399:     return 0;
        !           400: 
        !           401:  authreject:
        !           402:     vnc_write_u32(vs, 1); /* Reject auth */
        !           403:     vnc_write_u32(vs, sizeof("Authentication failed"));
        !           404:     vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
        !           405:     vnc_flush(vs);
        !           406:     vnc_client_error(vs);
        !           407:     return -1;
        !           408: 
        !           409:  authabort:
        !           410:     vnc_client_error(vs);
        !           411:     return -1;
        !           412: }
        !           413: 
        !           414: static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
        !           415: {
        !           416:     uint32_t startlen = read_u32(data, 0);
        !           417:     VNC_DEBUG("Got client start len %d\n", startlen);
        !           418:     if (startlen > SASL_DATA_MAX_LEN) {
        !           419:         VNC_DEBUG("Too much SASL data %d\n", startlen);
        !           420:         vnc_client_error(vs);
        !           421:         return -1;
        !           422:     }
        !           423: 
        !           424:     if (startlen == 0)
        !           425:         return protocol_client_auth_sasl_start(vs, NULL, 0);
        !           426: 
        !           427:     vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
        !           428:     return 0;
        !           429: }
        !           430: 
        !           431: static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
        !           432: {
        !           433:     char *mechname = malloc(len + 1);
        !           434:     if (!mechname) {
        !           435:         VNC_DEBUG("Out of memory reading mechname\n");
        !           436:         vnc_client_error(vs);
        !           437:     }
        !           438:     strncpy(mechname, (char*)data, len);
        !           439:     mechname[len] = '\0';
        !           440:     VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
        !           441:               mechname, vs->sasl.mechlist);
        !           442: 
        !           443:     if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
        !           444:         if (vs->sasl.mechlist[len] != '\0' &&
        !           445:             vs->sasl.mechlist[len] != ',') {
        !           446:             VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
        !           447:             vnc_client_error(vs);
        !           448:             return -1;
        !           449:         }
        !           450:     } else {
        !           451:         char *offset = strstr(vs->sasl.mechlist, mechname);
        !           452:         VNC_DEBUG("Two %p\n", offset);
        !           453:         if (!offset) {
        !           454:             vnc_client_error(vs);
        !           455:             return -1;
        !           456:         }
        !           457:         VNC_DEBUG("Two '%s'\n", offset);
        !           458:         if (offset[-1] != ',' ||
        !           459:             (offset[len] != '\0'&&
        !           460:              offset[len] != ',')) {
        !           461:             vnc_client_error(vs);
        !           462:             return -1;
        !           463:         }
        !           464:     }
        !           465: 
        !           466:     free(vs->sasl.mechlist);
        !           467:     vs->sasl.mechlist = mechname;
        !           468: 
        !           469:     VNC_DEBUG("Validated mechname '%s'\n", mechname);
        !           470:     vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
        !           471:     return 0;
        !           472: }
        !           473: 
        !           474: static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
        !           475: {
        !           476:     uint32_t mechlen = read_u32(data, 0);
        !           477:     VNC_DEBUG("Got client mechname len %d\n", mechlen);
        !           478:     if (mechlen > 100) {
        !           479:         VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
        !           480:         vnc_client_error(vs);
        !           481:         return -1;
        !           482:     }
        !           483:     if (mechlen < 1) {
        !           484:         VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
        !           485:         vnc_client_error(vs);
        !           486:         return -1;
        !           487:     }
        !           488:     vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
        !           489:     return 0;
        !           490: }
        !           491: 
        !           492: #define USES_X509_AUTH(vs)                              \
        !           493:     ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
        !           494:      (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
        !           495:      (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN ||  \
        !           496:      (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
        !           497: 
        !           498: 
        !           499: void start_auth_sasl(VncState *vs)
        !           500: {
        !           501:     const char *mechlist = NULL;
        !           502:     sasl_security_properties_t secprops;
        !           503:     int err;
        !           504:     char *localAddr, *remoteAddr;
        !           505:     int mechlistlen;
        !           506: 
        !           507:     VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
        !           508: 
        !           509:     /* Get local & remote client addresses in form  IPADDR;PORT */
        !           510:     if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
        !           511:         goto authabort;
        !           512: 
        !           513:     if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
        !           514:         free(localAddr);
        !           515:         goto authabort;
        !           516:     }
        !           517: 
        !           518:     err = sasl_server_new("vnc",
        !           519:                           NULL, /* FQDN - just delegates to gethostname */
        !           520:                           NULL, /* User realm */
        !           521:                           localAddr,
        !           522:                           remoteAddr,
        !           523:                           NULL, /* Callbacks, not needed */
        !           524:                           SASL_SUCCESS_DATA,
        !           525:                           &vs->sasl.conn);
        !           526:     free(localAddr);
        !           527:     free(remoteAddr);
        !           528:     localAddr = remoteAddr = NULL;
        !           529: 
        !           530:     if (err != SASL_OK) {
        !           531:         VNC_DEBUG("sasl context setup failed %d (%s)",
        !           532:                   err, sasl_errstring(err, NULL, NULL));
        !           533:         vs->sasl.conn = NULL;
        !           534:         goto authabort;
        !           535:     }
        !           536: 
        !           537: #ifdef CONFIG_VNC_TLS
        !           538:     /* Inform SASL that we've got an external SSF layer from TLS/x509 */
        !           539:     if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
        !           540:         vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
        !           541:         gnutls_cipher_algorithm_t cipher;
        !           542:         sasl_ssf_t ssf;
        !           543: 
        !           544:         cipher = gnutls_cipher_get(vs->tls.session);
        !           545:         if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
        !           546:             VNC_DEBUG("%s", "cannot TLS get cipher size\n");
        !           547:             sasl_dispose(&vs->sasl.conn);
        !           548:             vs->sasl.conn = NULL;
        !           549:             goto authabort;
        !           550:         }
        !           551:         ssf *= 8; /* tls key size is bytes, sasl wants bits */
        !           552: 
        !           553:         err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
        !           554:         if (err != SASL_OK) {
        !           555:             VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
        !           556:                       err, sasl_errstring(err, NULL, NULL));
        !           557:             sasl_dispose(&vs->sasl.conn);
        !           558:             vs->sasl.conn = NULL;
        !           559:             goto authabort;
        !           560:         }
        !           561:     } else
        !           562: #endif /* CONFIG_VNC_TLS */
        !           563:         vs->sasl.wantSSF = 1;
        !           564: 
        !           565:     memset (&secprops, 0, sizeof secprops);
        !           566:     /* Inform SASL that we've got an external SSF layer from TLS */
        !           567:     if (strncmp(vs->vd->display, "unix:", 5) == 0
        !           568: #ifdef CONFIG_VNC_TLS
        !           569:         /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
        !           570:            is not sufficiently strong */
        !           571:         || (vs->vd->auth == VNC_AUTH_VENCRYPT &&
        !           572:             vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
        !           573: #endif /* CONFIG_VNC_TLS */
        !           574:         ) {
        !           575:         /* If we've got TLS or UNIX domain sock, we don't care about SSF */
        !           576:         secprops.min_ssf = 0;
        !           577:         secprops.max_ssf = 0;
        !           578:         secprops.maxbufsize = 8192;
        !           579:         secprops.security_flags = 0;
        !           580:     } else {
        !           581:         /* Plain TCP, better get an SSF layer */
        !           582:         secprops.min_ssf = 56; /* Good enough to require kerberos */
        !           583:         secprops.max_ssf = 100000; /* Arbitrary big number */
        !           584:         secprops.maxbufsize = 8192;
        !           585:         /* Forbid any anonymous or trivially crackable auth */
        !           586:         secprops.security_flags =
        !           587:             SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
        !           588:     }
        !           589: 
        !           590:     err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
        !           591:     if (err != SASL_OK) {
        !           592:         VNC_DEBUG("cannot set SASL security props %d (%s)\n",
        !           593:                   err, sasl_errstring(err, NULL, NULL));
        !           594:         sasl_dispose(&vs->sasl.conn);
        !           595:         vs->sasl.conn = NULL;
        !           596:         goto authabort;
        !           597:     }
        !           598: 
        !           599:     err = sasl_listmech(vs->sasl.conn,
        !           600:                         NULL, /* Don't need to set user */
        !           601:                         "", /* Prefix */
        !           602:                         ",", /* Separator */
        !           603:                         "", /* Suffix */
        !           604:                         &mechlist,
        !           605:                         NULL,
        !           606:                         NULL);
        !           607:     if (err != SASL_OK) {
        !           608:         VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
        !           609:                   err, sasl_errdetail(vs->sasl.conn));
        !           610:         sasl_dispose(&vs->sasl.conn);
        !           611:         vs->sasl.conn = NULL;
        !           612:         goto authabort;
        !           613:     }
        !           614:     VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
        !           615: 
        !           616:     if (!(vs->sasl.mechlist = strdup(mechlist))) {
        !           617:         VNC_DEBUG("Out of memory");
        !           618:         sasl_dispose(&vs->sasl.conn);
        !           619:         vs->sasl.conn = NULL;
        !           620:         goto authabort;
        !           621:     }
        !           622:     mechlistlen = strlen(mechlist);
        !           623:     vnc_write_u32(vs, mechlistlen);
        !           624:     vnc_write(vs, mechlist, mechlistlen);
        !           625:     vnc_flush(vs);
        !           626: 
        !           627:     VNC_DEBUG("Wait for client mechname length\n");
        !           628:     vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
        !           629: 
        !           630:     return;
        !           631: 
        !           632:  authabort:
        !           633:     vnc_client_error(vs);
        !           634:     return;
        !           635: }
        !           636: 
        !           637: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.