mirror of
https://github.com/xemu-project/xemu.git
synced 2025-04-02 11:11:48 -04:00
When we encode data for writing with SASL, we encode the entire pending output buffer. The subsequent write, however, may not be able to send the full encoded data in one go though, particularly with a slow network. So we delay setting the output buffer offset back to zero until all the SASL encoded data is sent. Between encoding the data and completing sending of the SASL encoded data, however, more data might have been placed on the pending output buffer. So it is not valid to set offset back to zero. Instead we must keep track of how much data we consumed during encoding and subtract only that amount. With the current bug we would be throwing away some pending data without having sent it at all. By sheer luck this did not previously cause any serious problem because appending data to the send buffer is always an atomic action, so we only ever throw away complete RFB protocol messages. In the case of frame buffer updates we'd catch up fairly quickly, so no obvious problem was visible. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Darren Kenny <darren.kenny@oracle.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-id: 20171218191228.31018-6-berrange@redhat.com Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
647 lines
19 KiB
C
647 lines
19 KiB
C
/*
|
|
* QEMU VNC display driver: SASL auth protocol
|
|
*
|
|
* Copyright (C) 2009 Red Hat, Inc
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "vnc.h"
|
|
#include "trace.h"
|
|
|
|
/* Max amount of data we send/recv for SASL steps to prevent DOS */
|
|
#define SASL_DATA_MAX_LEN (1024 * 1024)
|
|
|
|
|
|
void vnc_sasl_client_cleanup(VncState *vs)
|
|
{
|
|
if (vs->sasl.conn) {
|
|
vs->sasl.runSSF = false;
|
|
vs->sasl.wantSSF = false;
|
|
vs->sasl.waitWriteSSF = 0;
|
|
vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
|
|
vs->sasl.encoded = NULL;
|
|
g_free(vs->sasl.username);
|
|
g_free(vs->sasl.mechlist);
|
|
vs->sasl.username = vs->sasl.mechlist = NULL;
|
|
sasl_dispose(&vs->sasl.conn);
|
|
vs->sasl.conn = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
long vnc_client_write_sasl(VncState *vs)
|
|
{
|
|
long ret;
|
|
|
|
VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
|
|
"Encoded: %p size %d offset %d\n",
|
|
vs->output.buffer, vs->output.capacity, vs->output.offset,
|
|
vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
|
|
|
|
if (!vs->sasl.encoded) {
|
|
int err;
|
|
err = sasl_encode(vs->sasl.conn,
|
|
(char *)vs->output.buffer,
|
|
vs->output.offset,
|
|
(const char **)&vs->sasl.encoded,
|
|
&vs->sasl.encodedLength);
|
|
if (err != SASL_OK)
|
|
return vnc_client_io_error(vs, -1, NULL);
|
|
|
|
vs->sasl.encodedRawLength = vs->output.offset;
|
|
vs->sasl.encodedOffset = 0;
|
|
}
|
|
|
|
ret = vnc_client_write_buf(vs,
|
|
vs->sasl.encoded + vs->sasl.encodedOffset,
|
|
vs->sasl.encodedLength - vs->sasl.encodedOffset);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
vs->sasl.encodedOffset += ret;
|
|
if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
|
|
vs->output.offset -= vs->sasl.encodedRawLength;
|
|
vs->sasl.encoded = NULL;
|
|
vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
|
|
}
|
|
|
|
/* Can't merge this block with one above, because
|
|
* someone might have written more unencrypted
|
|
* data in vs->output while we were processing
|
|
* SASL encoded output
|
|
*/
|
|
if (vs->output.offset == 0) {
|
|
if (vs->ioc_tag) {
|
|
g_source_remove(vs->ioc_tag);
|
|
}
|
|
vs->ioc_tag = qio_channel_add_watch(
|
|
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
long vnc_client_read_sasl(VncState *vs)
|
|
{
|
|
long ret;
|
|
uint8_t encoded[4096];
|
|
const char *decoded;
|
|
unsigned int decodedLen;
|
|
int err;
|
|
|
|
ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
|
|
if (!ret)
|
|
return 0;
|
|
|
|
err = sasl_decode(vs->sasl.conn,
|
|
(char *)encoded, ret,
|
|
&decoded, &decodedLen);
|
|
|
|
if (err != SASL_OK)
|
|
return vnc_client_io_error(vs, -1, NULL);
|
|
VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
|
|
encoded, ret, decoded, decodedLen);
|
|
buffer_reserve(&vs->input, decodedLen);
|
|
buffer_append(&vs->input, decoded, decodedLen);
|
|
return decodedLen;
|
|
}
|
|
|
|
|
|
static int vnc_auth_sasl_check_access(VncState *vs)
|
|
{
|
|
const void *val;
|
|
int err;
|
|
int allow;
|
|
|
|
err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
|
|
if (err != SASL_OK) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username",
|
|
sasl_errstring(err, NULL, NULL));
|
|
return -1;
|
|
}
|
|
if (val == NULL) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "No SASL username set", "");
|
|
return -1;
|
|
}
|
|
|
|
vs->sasl.username = g_strdup((const char*)val);
|
|
trace_vnc_auth_sasl_username(vs, vs->sasl.username);
|
|
|
|
if (vs->vd->sasl.acl == NULL) {
|
|
trace_vnc_auth_sasl_acl(vs, 1);
|
|
return 0;
|
|
}
|
|
|
|
allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
|
|
|
|
trace_vnc_auth_sasl_acl(vs, allow);
|
|
return allow ? 0 : -1;
|
|
}
|
|
|
|
static int vnc_auth_sasl_check_ssf(VncState *vs)
|
|
{
|
|
const void *val;
|
|
int err, ssf;
|
|
|
|
if (!vs->sasl.wantSSF)
|
|
return 1;
|
|
|
|
err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
|
|
if (err != SASL_OK)
|
|
return 0;
|
|
|
|
ssf = *(const int *)val;
|
|
|
|
trace_vnc_auth_sasl_ssf(vs, ssf);
|
|
|
|
if (ssf < 56)
|
|
return 0; /* 56 is good for Kerberos */
|
|
|
|
/* Only setup for read initially, because we're about to send an RPC
|
|
* reply which must be in plain text. When the next incoming RPC
|
|
* arrives, we'll switch on writes too
|
|
*
|
|
* cf qemudClientReadSASL in qemud.c
|
|
*/
|
|
vs->sasl.runSSF = 1;
|
|
|
|
/* We have a SSF that's good enough */
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Step Msg
|
|
*
|
|
* Input from client:
|
|
*
|
|
* u32 clientin-length
|
|
* u8-array clientin-string
|
|
*
|
|
* Output to client:
|
|
*
|
|
* u32 serverout-length
|
|
* u8-array serverout-strin
|
|
* u8 continue
|
|
*/
|
|
|
|
static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
|
|
|
|
static int protocol_client_auth_sasl_step(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'; /* Wire includes '\0', but make sure */
|
|
datalen--; /* Don't count NULL byte when passing to _start() */
|
|
}
|
|
|
|
err = sasl_server_step(vs->sasl.conn,
|
|
clientdata,
|
|
datalen,
|
|
&serverout,
|
|
&serveroutlen);
|
|
trace_vnc_auth_sasl_step(vs, data, len, serverout, serveroutlen, err);
|
|
if (err != SASL_OK &&
|
|
err != SASL_CONTINUE) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot step SASL auth",
|
|
sasl_errdetail(vs->sasl.conn));
|
|
sasl_dispose(&vs->sasl.conn);
|
|
vs->sasl.conn = NULL;
|
|
goto authabort;
|
|
}
|
|
|
|
if (serveroutlen > SASL_DATA_MAX_LEN) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
|
|
sasl_dispose(&vs->sasl.conn);
|
|
vs->sasl.conn = NULL;
|
|
goto authabort;
|
|
}
|
|
|
|
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) {
|
|
/* Wait for step length */
|
|
vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
|
|
} else {
|
|
if (!vnc_auth_sasl_check_ssf(vs)) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
|
|
goto authreject;
|
|
}
|
|
|
|
/* Check username whitelist ACL */
|
|
if (vnc_auth_sasl_check_access(vs) < 0) {
|
|
goto authreject;
|
|
}
|
|
|
|
trace_vnc_auth_pass(vs, vs->auth);
|
|
vnc_write_u32(vs, 0); /* Accept auth */
|
|
/*
|
|
* Delay writing in SSF encoded mode until pending output
|
|
* buffer is written
|
|
*/
|
|
if (vs->sasl.runSSF)
|
|
vs->sasl.waitWriteSSF = vs->output.offset;
|
|
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;
|
|
}
|
|
|
|
static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
|
|
{
|
|
uint32_t steplen = read_u32(data, 0);
|
|
|
|
if (steplen > SASL_DATA_MAX_LEN) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL step len too large", "");
|
|
vnc_client_error(vs);
|
|
return -1;
|
|
}
|
|
|
|
if (steplen == 0)
|
|
return protocol_client_auth_sasl_step(vs, NULL, 0);
|
|
else
|
|
vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Start Msg
|
|
*
|
|
* Input from client:
|
|
*
|
|
* u32 clientin-length
|
|
* u8-array clientin-string
|
|
*
|
|
* Output to client:
|
|
*
|
|
* u32 serverout-length
|
|
* u8-array serverout-strin
|
|
* u8 continue
|
|
*/
|
|
|
|
#define SASL_DATA_MAX_LEN (1024 * 1024)
|
|
|
|
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() */
|
|
}
|
|
|
|
err = sasl_server_start(vs->sasl.conn,
|
|
vs->sasl.mechlist,
|
|
clientdata,
|
|
datalen,
|
|
&serverout,
|
|
&serveroutlen);
|
|
trace_vnc_auth_sasl_start(vs, data, len, serverout, serveroutlen, err);
|
|
if (err != SASL_OK &&
|
|
err != SASL_CONTINUE) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot start SASL auth",
|
|
sasl_errdetail(vs->sasl.conn));
|
|
sasl_dispose(&vs->sasl.conn);
|
|
vs->sasl.conn = NULL;
|
|
goto authabort;
|
|
}
|
|
if (serveroutlen > SASL_DATA_MAX_LEN) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL data too long", "");
|
|
sasl_dispose(&vs->sasl.conn);
|
|
vs->sasl.conn = NULL;
|
|
goto authabort;
|
|
}
|
|
|
|
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) {
|
|
/* Wait for step length */
|
|
vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
|
|
} else {
|
|
if (!vnc_auth_sasl_check_ssf(vs)) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL SSF too weak", "");
|
|
goto authreject;
|
|
}
|
|
|
|
/* Check username whitelist ACL */
|
|
if (vnc_auth_sasl_check_access(vs) < 0) {
|
|
goto authreject;
|
|
}
|
|
|
|
trace_vnc_auth_pass(vs, vs->auth);
|
|
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;
|
|
}
|
|
|
|
static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
|
|
{
|
|
uint32_t startlen = read_u32(data, 0);
|
|
|
|
if (startlen > SASL_DATA_MAX_LEN) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL start len too large", "");
|
|
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(VncState *vs, uint8_t *data, size_t len)
|
|
{
|
|
char *mechname = g_strndup((const char *) data, len);
|
|
trace_vnc_auth_sasl_mech_choose(vs, mechname);
|
|
|
|
if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
|
|
if (vs->sasl.mechlist[len] != '\0' &&
|
|
vs->sasl.mechlist[len] != ',') {
|
|
goto fail;
|
|
}
|
|
} else {
|
|
char *offset = strstr(vs->sasl.mechlist, mechname);
|
|
if (!offset) {
|
|
goto fail;
|
|
}
|
|
if (offset[-1] != ',' ||
|
|
(offset[len] != '\0'&&
|
|
offset[len] != ',')) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
g_free(vs->sasl.mechlist);
|
|
vs->sasl.mechlist = mechname;
|
|
|
|
vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
|
|
return 0;
|
|
|
|
fail:
|
|
trace_vnc_auth_fail(vs, vs->auth, "Unsupported mechname", mechname);
|
|
vnc_client_error(vs);
|
|
g_free(mechname);
|
|
return -1;
|
|
}
|
|
|
|
static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
|
|
{
|
|
uint32_t mechlen = read_u32(data, 0);
|
|
|
|
if (mechlen > 100) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too long", "");
|
|
vnc_client_error(vs);
|
|
return -1;
|
|
}
|
|
if (mechlen < 1) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL mechname too short", "");
|
|
vnc_client_error(vs);
|
|
return -1;
|
|
}
|
|
vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
|
|
bool local,
|
|
Error **errp)
|
|
{
|
|
SocketAddress *addr;
|
|
char *ret;
|
|
|
|
if (local) {
|
|
addr = qio_channel_socket_get_local_address(ioc, errp);
|
|
} else {
|
|
addr = qio_channel_socket_get_remote_address(ioc, errp);
|
|
}
|
|
if (!addr) {
|
|
return NULL;
|
|
}
|
|
|
|
if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
|
|
error_setg(errp, "Not an inet socket type");
|
|
return NULL;
|
|
}
|
|
ret = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port);
|
|
qapi_free_SocketAddress(addr);
|
|
return ret;
|
|
}
|
|
|
|
void start_auth_sasl(VncState *vs)
|
|
{
|
|
const char *mechlist = NULL;
|
|
sasl_security_properties_t secprops;
|
|
int err;
|
|
Error *local_err = NULL;
|
|
char *localAddr, *remoteAddr;
|
|
int mechlistlen;
|
|
|
|
/* Get local & remote client addresses in form IPADDR;PORT */
|
|
localAddr = vnc_socket_ip_addr_string(vs->sioc, true, &local_err);
|
|
if (!localAddr) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP",
|
|
error_get_pretty(local_err));
|
|
goto authabort;
|
|
}
|
|
|
|
remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, &local_err);
|
|
if (!remoteAddr) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP",
|
|
error_get_pretty(local_err));
|
|
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) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "SASL context setup failed",
|
|
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) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "cannot TLS get cipher size",
|
|
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) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL external SSF",
|
|
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) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "cannot set SASL security props",
|
|
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) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "cannot list SASL mechanisms",
|
|
sasl_errdetail(vs->sasl.conn));
|
|
sasl_dispose(&vs->sasl.conn);
|
|
vs->sasl.conn = NULL;
|
|
goto authabort;
|
|
}
|
|
trace_vnc_auth_sasl_mech_list(vs, mechlist);
|
|
|
|
vs->sasl.mechlist = g_strdup(mechlist);
|
|
mechlistlen = strlen(mechlist);
|
|
vnc_write_u32(vs, mechlistlen);
|
|
vnc_write(vs, mechlist, mechlistlen);
|
|
vnc_flush(vs);
|
|
|
|
vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
|
|
|
|
return;
|
|
|
|
authabort:
|
|
error_free(local_err);
|
|
vnc_client_error(vs);
|
|
}
|
|
|
|
|