|
@@ -263,8 +263,14 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
if (datalen) {
|
|
if (datalen) {
|
|
clientdata = (char*)data;
|
|
clientdata = (char*)data;
|
|
- clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
|
|
|
|
- datalen--; /* Don't count NULL byte when passing to _start() */
|
|
|
|
|
|
+ if (clientdata[datalen - 1] != '\0') {
|
|
|
|
+ trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data",
|
|
|
|
+ "Missing SASL NUL padding byte");
|
|
|
|
+ sasl_dispose(&vs->sasl.conn);
|
|
|
|
+ vs->sasl.conn = NULL;
|
|
|
|
+ goto authabort;
|
|
|
|
+ }
|
|
|
|
+ datalen--; /* Discard the extra NUL padding byte */
|
|
}
|
|
}
|
|
|
|
|
|
err = sasl_server_step(vs->sasl.conn,
|
|
err = sasl_server_step(vs->sasl.conn,
|
|
@@ -289,9 +295,10 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le
|
|
goto authabort;
|
|
goto authabort;
|
|
}
|
|
}
|
|
|
|
|
|
- if (serveroutlen) {
|
|
|
|
|
|
+ if (serverout) {
|
|
vnc_write_u32(vs, serveroutlen + 1);
|
|
vnc_write_u32(vs, serveroutlen + 1);
|
|
- vnc_write(vs, serverout, serveroutlen + 1);
|
|
|
|
|
|
+ vnc_write(vs, serverout, serveroutlen);
|
|
|
|
+ vnc_write_u8(vs, '\0');
|
|
} else {
|
|
} else {
|
|
vnc_write_u32(vs, 0);
|
|
vnc_write_u32(vs, 0);
|
|
}
|
|
}
|
|
@@ -384,8 +391,14 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
|
if (datalen) {
|
|
if (datalen) {
|
|
clientdata = (char*)data;
|
|
clientdata = (char*)data;
|
|
- clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
|
|
|
|
- datalen--; /* Don't count NULL byte when passing to _start() */
|
|
|
|
|
|
+ if (clientdata[datalen - 1] != '\0') {
|
|
|
|
+ trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data",
|
|
|
|
+ "Missing SASL NUL padding byte");
|
|
|
|
+ sasl_dispose(&vs->sasl.conn);
|
|
|
|
+ vs->sasl.conn = NULL;
|
|
|
|
+ goto authabort;
|
|
|
|
+ }
|
|
|
|
+ datalen--; /* Discard the extra NUL padding byte */
|
|
}
|
|
}
|
|
|
|
|
|
err = sasl_server_start(vs->sasl.conn,
|
|
err = sasl_server_start(vs->sasl.conn,
|
|
@@ -410,9 +423,10 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l
|
|
goto authabort;
|
|
goto authabort;
|
|
}
|
|
}
|
|
|
|
|
|
- if (serveroutlen) {
|
|
|
|
|
|
+ if (serverout) {
|
|
vnc_write_u32(vs, serveroutlen + 1);
|
|
vnc_write_u32(vs, serveroutlen + 1);
|
|
- vnc_write(vs, serverout, serveroutlen + 1);
|
|
|
|
|
|
+ vnc_write(vs, serverout, serveroutlen);
|
|
|
|
+ vnc_write_u8(vs, '\0');
|
|
} else {
|
|
} else {
|
|
vnc_write_u32(vs, 0);
|
|
vnc_write_u32(vs, 0);
|
|
}
|
|
}
|
|
@@ -524,13 +538,13 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static char *
|
|
|
|
|
|
+static int
|
|
vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
|
|
vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
|
|
bool local,
|
|
bool local,
|
|
|
|
+ char **addrstr,
|
|
Error **errp)
|
|
Error **errp)
|
|
{
|
|
{
|
|
SocketAddress *addr;
|
|
SocketAddress *addr;
|
|
- char *ret;
|
|
|
|
|
|
|
|
if (local) {
|
|
if (local) {
|
|
addr = qio_channel_socket_get_local_address(ioc, errp);
|
|
addr = qio_channel_socket_get_local_address(ioc, errp);
|
|
@@ -538,17 +552,24 @@ vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
|
|
addr = qio_channel_socket_get_remote_address(ioc, errp);
|
|
addr = qio_channel_socket_get_remote_address(ioc, errp);
|
|
}
|
|
}
|
|
if (!addr) {
|
|
if (!addr) {
|
|
- return NULL;
|
|
|
|
|
|
+ return -1;
|
|
}
|
|
}
|
|
|
|
|
|
if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
|
|
if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
|
|
- error_setg(errp, "Not an inet socket type");
|
|
|
|
|
|
+ *addrstr = NULL;
|
|
qapi_free_SocketAddress(addr);
|
|
qapi_free_SocketAddress(addr);
|
|
- return NULL;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
- ret = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port);
|
|
|
|
|
|
+ *addrstr = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port);
|
|
qapi_free_SocketAddress(addr);
|
|
qapi_free_SocketAddress(addr);
|
|
- return ret;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+vnc_socket_is_unix(QIOChannelSocket *ioc)
|
|
|
|
+{
|
|
|
|
+ SocketAddress *addr = qio_channel_socket_get_local_address(ioc, NULL);
|
|
|
|
+ return addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX;
|
|
}
|
|
}
|
|
|
|
|
|
void start_auth_sasl(VncState *vs)
|
|
void start_auth_sasl(VncState *vs)
|
|
@@ -561,15 +582,15 @@ void start_auth_sasl(VncState *vs)
|
|
int mechlistlen;
|
|
int mechlistlen;
|
|
|
|
|
|
/* Get local & remote client addresses in form IPADDR;PORT */
|
|
/* Get local & remote client addresses in form IPADDR;PORT */
|
|
- localAddr = vnc_socket_ip_addr_string(vs->sioc, true, &local_err);
|
|
|
|
- if (!localAddr) {
|
|
|
|
|
|
+ if (vnc_socket_ip_addr_string(vs->sioc, true,
|
|
|
|
+ &localAddr, &local_err) < 0) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP",
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP",
|
|
error_get_pretty(local_err));
|
|
error_get_pretty(local_err));
|
|
goto authabort;
|
|
goto authabort;
|
|
}
|
|
}
|
|
|
|
|
|
- remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, &local_err);
|
|
|
|
- if (!remoteAddr) {
|
|
|
|
|
|
+ if (vnc_socket_ip_addr_string(vs->sioc, false,
|
|
|
|
+ &remoteAddr, &local_err) < 0) {
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP",
|
|
trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP",
|
|
error_get_pretty(local_err));
|
|
error_get_pretty(local_err));
|
|
g_free(localAddr);
|
|
g_free(localAddr);
|
|
@@ -621,16 +642,17 @@ void start_auth_sasl(VncState *vs)
|
|
goto authabort;
|
|
goto authabort;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- vs->sasl.wantSSF = 1;
|
|
|
|
|
|
+ vs->sasl.wantSSF = !vnc_socket_is_unix(vs->sioc);
|
|
}
|
|
}
|
|
|
|
|
|
memset (&secprops, 0, sizeof secprops);
|
|
memset (&secprops, 0, sizeof secprops);
|
|
/* Inform SASL that we've got an external SSF layer from TLS.
|
|
/* 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
|
|
|
|
|
|
+ * Disable SSF, if using TLS+x509+SASL only, or UNIX sockets.
|
|
|
|
+ * TLS without x509 is not sufficiently strong, nor is plain
|
|
|
|
+ * TCP
|
|
*/
|
|
*/
|
|
- if (vs->vd->is_unix ||
|
|
|
|
|
|
+ if (vnc_socket_is_unix(vs->sioc) ||
|
|
(vs->auth == VNC_AUTH_VENCRYPT &&
|
|
(vs->auth == VNC_AUTH_VENCRYPT &&
|
|
vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
|
|
vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
|
|
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
|
|
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
|
|
@@ -674,6 +696,13 @@ void start_auth_sasl(VncState *vs)
|
|
}
|
|
}
|
|
trace_vnc_auth_sasl_mech_list(vs, mechlist);
|
|
trace_vnc_auth_sasl_mech_list(vs, mechlist);
|
|
|
|
|
|
|
|
+ if (g_str_equal(mechlist, "")) {
|
|
|
|
+ trace_vnc_auth_fail(vs, vs->auth, "no available SASL mechanisms", "");
|
|
|
|
+ sasl_dispose(&vs->sasl.conn);
|
|
|
|
+ vs->sasl.conn = NULL;
|
|
|
|
+ goto authabort;
|
|
|
|
+ }
|
|
|
|
+
|
|
vs->sasl.mechlist = g_strdup(mechlist);
|
|
vs->sasl.mechlist = g_strdup(mechlist);
|
|
mechlistlen = strlen(mechlist);
|
|
mechlistlen = strlen(mechlist);
|
|
vnc_write_u32(vs, mechlistlen);
|
|
vnc_write_u32(vs, mechlistlen);
|