void start_auth_vencrypt(VncState *vs) { /* Send VeNCrypt version 0.2 */ vnc_write_u8(vs, 0); vnc_write_u8(vs, 2); vnc_read_when(vs, protocol_client_vencrypt_init, 2); }
static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len) { uint32_t startlen = read_u32(data, 0); VNC_DEBUG("Got client start len %d\n", startlen); if (startlen > SASL_DATA_MAX_LEN) { VNC_DEBUG("Too much SASL data %d\n", startlen); vnc_client_error(vs); return -1; } if (startlen == 0) return protocol_client_auth_sasl_start(vs, NULL, 0); vnc_read_when(vs, protocol_client_auth_sasl_start, startlen); return 0; }
static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len) { uint32_t mechlen = read_u32(data, 0); VNC_DEBUG("Got client mechname len %d\n", mechlen); if (mechlen > 100) { VNC_DEBUG("Too long SASL mechname data %d\n", mechlen); vnc_client_error(vs); return -1; } if (mechlen < 1) { VNC_DEBUG("Too short SASL mechname %d\n", mechlen); vnc_client_error(vs); return -1; } vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen); return 0; }
static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) { trace_vnc_auth_vencrypt_version(vs, (int)data[0], (int)data[1]); if (data[0] != 0 || data[1] != 2) { trace_vnc_auth_fail(vs, vs->auth, "Unsupported version", ""); vnc_write_u8(vs, 1); /* Reject version */ vnc_flush(vs); vnc_client_error(vs); } else { vnc_write_u8(vs, 0); /* Accept version */ vnc_write_u8(vs, 1); /* Number of sub-auths */ vnc_write_u32(vs, vs->subauth); /* The supported auth */ vnc_flush(vs); vnc_read_when(vs, protocol_client_vencrypt_auth, 4); } return 0; }
static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) { if (data[0] != 0 || data[1] != 2) { VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]); vnc_write_u8(vs, 1); /* Reject version */ vnc_flush(vs); vnc_client_error(vs); } else { VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); vnc_write_u8(vs, 0); /* Accept version */ vnc_write_u8(vs, 1); /* Number of sub-auths */ vnc_write_u32(vs, vs->subauth); /* The supported auth */ vnc_flush(vs); vnc_read_when(vs, protocol_client_vencrypt_auth, 4); } return 0; }
static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len) { char *mechname = malloc(len + 1); if (!mechname) { VNC_DEBUG("Out of memory reading mechname\n"); vnc_client_error(vs); } strncpy(mechname, (char*)data, len); mechname[len] = '\0'; VNC_DEBUG("Got client mechname '%s' check against '%s'\n", mechname, vs->sasl.mechlist); if (strncmp(vs->sasl.mechlist, mechname, len) == 0) { if (vs->sasl.mechlist[len] != '\0' && vs->sasl.mechlist[len] != ',') { VNC_DEBUG("One %d", vs->sasl.mechlist[len]); vnc_client_error(vs); return -1; } } else { char *offset = strstr(vs->sasl.mechlist, mechname); VNC_DEBUG("Two %p\n", offset); if (!offset) { vnc_client_error(vs); return -1; } VNC_DEBUG("Two '%s'\n", offset); if (offset[-1] != ',' || (offset[len] != '\0'&& offset[len] != ',')) { vnc_client_error(vs); return -1; } } free(vs->sasl.mechlist); vs->sasl.mechlist = mechname; VNC_DEBUG("Validated mechname '%s'\n", mechname); vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4); return 0; }
static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len) { char *mechname = g_strndup((const char *) data, len); VNC_DEBUG("Got client mechname '%s' check against '%s'\n", mechname, vs->sasl.mechlist); if (strncmp(vs->sasl.mechlist, mechname, len) == 0) { if (vs->sasl.mechlist[len] != '\0' && vs->sasl.mechlist[len] != ',') { VNC_DEBUG("One %d", vs->sasl.mechlist[len]); goto fail; } } else { char *offset = strstr(vs->sasl.mechlist, mechname); VNC_DEBUG("Two %p\n", offset); if (!offset) { goto fail; } VNC_DEBUG("Two '%s'\n", offset); if (offset[-1] != ',' || (offset[len] != '\0'&& offset[len] != ',')) { goto fail; } } g_free(vs->sasl.mechlist); vs->sasl.mechlist = mechname; VNC_DEBUG("Validated mechname '%s'\n", mechname); vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4); return 0; fail: vnc_client_error(vs); g_free(mechname); return -1; }
void start_auth_sasl(VncState *vs) { const char *mechlist = NULL; sasl_security_properties_t secprops; int err; char *localAddr, *remoteAddr; int mechlistlen; VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc); /* Get local & remote client addresses in form IPADDR;PORT */ localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL); if (!localAddr) { goto authabort; } remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL); if (!remoteAddr) { g_free(localAddr); goto authabort; } err = sasl_server_new("vnc", NULL, /* FQDN - just delegates to gethostname */ NULL, /* User realm */ localAddr, remoteAddr, NULL, /* Callbacks, not needed */ SASL_SUCCESS_DATA, &vs->sasl.conn); g_free(localAddr); g_free(remoteAddr); localAddr = remoteAddr = NULL; if (err != SASL_OK) { VNC_DEBUG("sasl context setup failed %d (%s)", err, sasl_errstring(err, NULL, NULL)); vs->sasl.conn = NULL; goto authabort; } /* Inform SASL that we've got an external SSF layer from TLS/x509 */ if (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { Error *local_err = NULL; int keysize; sasl_ssf_t ssf; keysize = qcrypto_tls_session_get_key_size(vs->tls, &local_err); if (keysize < 0) { VNC_DEBUG("cannot TLS get cipher size: %s\n", error_get_pretty(local_err)); error_free(local_err); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */ err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { VNC_DEBUG("cannot set SASL external SSF %d (%s)\n", err, sasl_errstring(err, NULL, NULL)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } } else { vs->sasl.wantSSF = 1; } memset (&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS. * * Disable SSF, if using TLS+x509+SASL only. TLS without x509 * is not sufficiently strong */ if (vs->vd->is_unix || (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ secprops.min_ssf = 0; secprops.max_ssf = 0; secprops.maxbufsize = 8192; secprops.security_flags = 0; } else { /* Plain TCP, better get an SSF layer */ secprops.min_ssf = 56; /* Good enough to require kerberos */ secprops.max_ssf = 100000; /* Arbitrary big number */ secprops.maxbufsize = 8192; /* Forbid any anonymous or trivially crackable auth */ secprops.security_flags = SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; } err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops); if (err != SASL_OK) { VNC_DEBUG("cannot set SASL security props %d (%s)\n", err, sasl_errstring(err, NULL, NULL)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } err = sasl_listmech(vs->sasl.conn, NULL, /* Don't need to set user */ "", /* Prefix */ ",", /* Separator */ "", /* Suffix */ &mechlist, NULL, NULL); if (err != SASL_OK) { VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n", err, sasl_errdetail(vs->sasl.conn)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist); vs->sasl.mechlist = g_strdup(mechlist); mechlistlen = strlen(mechlist); vnc_write_u32(vs, mechlistlen); vnc_write(vs, mechlist, mechlistlen); vnc_flush(vs); VNC_DEBUG("Wait for client mechname length\n"); vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4); return; authabort: vnc_client_error(vs); }
static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len) { uint32_t datalen = len; const char *serverout; unsigned int serveroutlen; int err; char *clientdata = NULL; /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = (char*)data; clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ datalen--; /* Don't count NULL byte when passing to _start() */ } VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n", vs->sasl.mechlist, clientdata, datalen); err = sasl_server_start(vs->sasl.conn, vs->sasl.mechlist, clientdata, datalen, &serverout, &serveroutlen); if (err != SASL_OK && err != SASL_CONTINUE) { VNC_DEBUG("sasl start failed %d (%s)\n", err, sasl_errdetail(vs->sasl.conn)); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } if (serveroutlen > SASL_DATA_MAX_LEN) { VNC_DEBUG("sasl start reply data too long %d\n", serveroutlen); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } VNC_DEBUG("SASL return data %d bytes, nil; %d\n", serveroutlen, serverout ? 0 : 1); if (serveroutlen) { vnc_write_u32(vs, serveroutlen + 1); vnc_write(vs, serverout, serveroutlen + 1); } else { vnc_write_u32(vs, 0); } /* Whether auth is complete */ vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); if (err == SASL_CONTINUE) { VNC_DEBUG("%s", "Authentication must continue\n"); /* Wait for step length */ vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); } else { if (!vnc_auth_sasl_check_ssf(vs)) { VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc); goto authreject; } /* Check username whitelist ACL */ if (vnc_auth_sasl_check_access(vs) < 0) { VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc); goto authreject; } VNC_DEBUG("Authentication successful %p\n", vs->ioc); vnc_write_u32(vs, 0); /* Accept auth */ start_client_init(vs); } return 0; authreject: vnc_write_u32(vs, 1); /* Reject auth */ vnc_write_u32(vs, sizeof("Authentication failed")); vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); vnc_flush(vs); vnc_client_error(vs); return -1; authabort: vnc_client_error(vs); return -1; }