Compare commits

...

9 Commits

Author SHA1 Message Date
Daniel P. Berrange
4a48aaa9f5 ui: ensure VNC websockets server checks the ACL if requested
If the x509verify option is requested, the VNC websockets server
was failing to validate that the websockets client provided an
x509 certificate matching the ACL rules.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:14 +01:00
Daniel P. Berrange
7b45a00d05 ui: remove separate gnutls_session for websockets server
The previous change to the auth scheme handling guarantees we
can never have nested TLS sessions in the VNC websockets server.
Thus we can remove the separate gnutls_session instance.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:14 +01:00
Daniel P. Berrange
51941e4695 ui: enforce TLS when using websockets server
When TLS is required, the primary VNC server considers it to be
mandatory. ie the server admin decides whether or not TLS is used,
and the client has to comply with this decision. The websockets
server, however, treated it as optional, allowing non-TLS clients
to connect to a server which had setup TLS. Thus enabling websockets
lowers the security of the VNC server leaving the admin no way to
enforce use of TLS.

This removes the code that allows non-TLS fallback in the websockets
server, so that if TLS is requested for VNC it is now mandatory for
both the primary VNC server and the websockets VNC server.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:13 +01:00
Daniel P. Berrange
f9148c8ae7 ui: fix setup of VNC websockets auth scheme with TLS
The way the websockets TLS code was integrated into the VNC server
made it essentially useless. The only time that the websockets TLS
support could be used is if the primary VNC server had its existing
TLS support disabled. ie QEMU had to be launched with:

  # qemu -vnc localhost:1,websockets=5902,x509=/path/to/certs

Note the absence of the 'tls' flag. This is already a bug, because
the docs indicate that 'x509' is ignored unless 'tls' is given.

If the primary VNC server had TLS turned on via the 'tls' flag,
then this prevented the websockets TLS support from being used,
because it activates the VeNCrypt auth which would have resulted
in TLS being run over a TLS session. Of course no websockets VNC
client supported VeNCrypt so in practice, since the browser clients
cannot setup a nested TLS session over the main HTTPS connection,
so it would not even get past auth.

This patch causes us to decide our auth scheme separately for the
main VNC server vs the websockets VNC server. We take account of
the fact that if TLS is enabled, then the websockets client will
use https, so setting up VeNCrypt is thus redundant as it would
lead to nested TLS sessions.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:13 +01:00
Daniel P. Berrange
0dd72e1531 ui: split setup of VNC auth scheme into separate method
The vnc_display_open method is quite long and complex, so
move the VNC auth scheme decision logic into a separate
method for clarity.

Also update the comment to better describe what we are
trying to achieve.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:13 +01:00
Daniel P. Berrange
d169f04b8b ui: report error if user requests VNC option that is unsupported
If the VNC server is built without tls, sasl or websocket support
and the user requests one of these features, they are just silently
ignored. This is bad because it means the VNC server ends up running
in a configuration that is less secure than the user asked for.
It also leads to an tangled mass of preprocessor conditionals when
configuring the VNC server.

This ensures that the tls, sasl & websocket options are always
processed and an error is reported back to the user if any of
them were disabled at build time.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:13 +01:00
Daniel P. Berrange
153130cd4f ui: replace printf() calls with VNC_DEBUG
Handling of VNC audio messages results in printfs to the console.
This is of no use to anyone in production, so should be using the
normal VNC_DEBUG macro instead.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:13 +01:00
Daniel P. Berrange
a2f45bc02a ui: remove unused 'wiremode' variable in VncState struct
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-18 09:25:13 +01:00
Markus Armbruster
f3cf80e805 vnc: Fix QMP change not to use funky error class
Error classes are a leftover from the days of "rich" error objects.
New code should always use ERROR_CLASS_GENERIC_ERROR.  Commit 1d0d59f
added a use of ERROR_CLASS_DEVICE_NOT_FOUND.  Replace it.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2015-03-17 14:24:26 +01:00
7 changed files with 223 additions and 203 deletions

View File

@@ -93,7 +93,6 @@ static int vnc_start_vencrypt_handshake(struct VncState *vs) {
} }
VNC_DEBUG("Handshake done, switching to TLS data mode\n"); VNC_DEBUG("Handshake done, switching to TLS data mode\n");
vs->tls.wiremode = VNC_WIREMODE_TLS;
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
start_auth_vencrypt_subauth(vs); start_auth_vencrypt_subauth(vs);

View File

@@ -334,82 +334,77 @@ static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
int vnc_tls_client_setup(struct VncState *vs, int vnc_tls_client_setup(struct VncState *vs,
int needX509Creds) { int needX509Creds) {
VncStateTLS *tls;
VNC_DEBUG("Do TLS setup\n"); VNC_DEBUG("Do TLS setup\n");
#ifdef CONFIG_VNC_WS
if (vs->websocket) {
tls = &vs->ws_tls;
} else
#endif /* CONFIG_VNC_WS */
{
tls = &vs->tls;
}
if (vnc_tls_initialize() < 0) { if (vnc_tls_initialize() < 0) {
VNC_DEBUG("Failed to init TLS\n"); VNC_DEBUG("Failed to init TLS\n");
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (tls->session == NULL) { if (vs->tls.session == NULL) {
if (gnutls_init(&tls->session, GNUTLS_SERVER) < 0) { if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (gnutls_set_default_priority(tls->session) < 0) { if (gnutls_set_default_priority(vs->tls.session) < 0) {
gnutls_deinit(tls->session); gnutls_deinit(vs->tls.session);
tls->session = NULL; vs->tls.session = NULL;
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (vnc_set_gnutls_priority(tls->session, needX509Creds) < 0) { if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) {
gnutls_deinit(tls->session); gnutls_deinit(vs->tls.session);
tls->session = NULL; vs->tls.session = NULL;
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (needX509Creds) { if (needX509Creds) {
gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); gnutls_certificate_server_credentials x509_cred =
vnc_tls_initialize_x509_cred(vs->vd);
if (!x509_cred) { if (!x509_cred) {
gnutls_deinit(tls->session); gnutls_deinit(vs->tls.session);
tls->session = NULL; vs->tls.session = NULL;
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { if (gnutls_credentials_set(vs->tls.session,
gnutls_deinit(tls->session); GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
tls->session = NULL; gnutls_deinit(vs->tls.session);
vs->tls.session = NULL;
gnutls_certificate_free_credentials(x509_cred); gnutls_certificate_free_credentials(x509_cred);
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (vs->vd->tls.x509verify) { if (vs->vd->tls.x509verify) {
VNC_DEBUG("Requesting a client certificate\n"); VNC_DEBUG("Requesting a client certificate\n");
gnutls_certificate_server_set_request (tls->session, GNUTLS_CERT_REQUEST); gnutls_certificate_server_set_request(vs->tls.session,
GNUTLS_CERT_REQUEST);
} }
} else { } else {
gnutls_anon_server_credentials_t anon_cred = vnc_tls_initialize_anon_cred(); gnutls_anon_server_credentials_t anon_cred =
vnc_tls_initialize_anon_cred();
if (!anon_cred) { if (!anon_cred) {
gnutls_deinit(tls->session); gnutls_deinit(vs->tls.session);
tls->session = NULL; vs->tls.session = NULL;
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
if (gnutls_credentials_set(tls->session, GNUTLS_CRD_ANON, anon_cred) < 0) { if (gnutls_credentials_set(vs->tls.session,
gnutls_deinit(tls->session); GNUTLS_CRD_ANON, anon_cred) < 0) {
tls->session = NULL; gnutls_deinit(vs->tls.session);
vs->tls.session = NULL;
gnutls_anon_free_server_credentials(anon_cred); gnutls_anon_free_server_credentials(anon_cred);
vnc_client_error(vs); vnc_client_error(vs);
return -1; return -1;
} }
} }
gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr_t)vs); gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
gnutls_transport_set_push_function(tls->session, vnc_tls_push); gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
gnutls_transport_set_pull_function(tls->session, vnc_tls_pull); gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
} }
return 0; return 0;
} }
@@ -421,16 +416,7 @@ void vnc_tls_client_cleanup(struct VncState *vs)
gnutls_deinit(vs->tls.session); gnutls_deinit(vs->tls.session);
vs->tls.session = NULL; vs->tls.session = NULL;
} }
vs->tls.wiremode = VNC_WIREMODE_CLEAR;
g_free(vs->tls.dname); g_free(vs->tls.dname);
#ifdef CONFIG_VNC_WS
if (vs->ws_tls.session) {
gnutls_deinit(vs->ws_tls.session);
vs->ws_tls.session = NULL;
}
vs->ws_tls.wiremode = VNC_WIREMODE_CLEAR;
g_free(vs->ws_tls.dname);
#endif /* CONFIG_VNC_WS */
} }

View File

@@ -33,11 +33,6 @@
#include "qemu/acl.h" #include "qemu/acl.h"
enum {
VNC_WIREMODE_CLEAR,
VNC_WIREMODE_TLS,
};
typedef struct VncDisplayTLS VncDisplayTLS; typedef struct VncDisplayTLS VncDisplayTLS;
typedef struct VncStateTLS VncStateTLS; typedef struct VncStateTLS VncStateTLS;
@@ -55,8 +50,6 @@ struct VncDisplayTLS {
/* Per client state */ /* Per client state */
struct VncStateTLS { struct VncStateTLS {
/* Whether data is being TLS encrypted yet */
int wiremode;
gnutls_session_t session; gnutls_session_t session;
/* Client's Distinguished Name from the x509 cert */ /* Client's Distinguished Name from the x509 cert */

View File

@@ -24,16 +24,14 @@
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
#include "qemu/sockets.h" #include "qemu/sockets.h"
static void vncws_tls_handshake_io(void *opaque);
static int vncws_start_tls_handshake(struct VncState *vs) static int vncws_start_tls_handshake(struct VncState *vs)
{ {
int ret = gnutls_handshake(vs->ws_tls.session); int ret = gnutls_handshake(vs->tls.session);
if (ret < 0) { if (ret < 0) {
if (!gnutls_error_is_fatal(ret)) { if (!gnutls_error_is_fatal(ret)) {
VNC_DEBUG("Handshake interrupted (blocking)\n"); VNC_DEBUG("Handshake interrupted (blocking)\n");
if (!gnutls_record_get_direction(vs->ws_tls.session)) { if (!gnutls_record_get_direction(vs->tls.session)) {
qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io,
NULL, vs); NULL, vs);
} else { } else {
@@ -47,41 +45,35 @@ static int vncws_start_tls_handshake(struct VncState *vs)
return -1; return -1;
} }
if (vs->vd->tls.x509verify) {
if (vnc_tls_validate_certificate(vs) < 0) {
VNC_DEBUG("Client verification failed\n");
vnc_client_error(vs);
return -1;
} else {
VNC_DEBUG("Client verification passed\n");
}
}
VNC_DEBUG("Handshake done, switching to TLS data mode\n"); VNC_DEBUG("Handshake done, switching to TLS data mode\n");
vs->ws_tls.wiremode = VNC_WIREMODE_TLS;
qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs);
return 0; return 0;
} }
static void vncws_tls_handshake_io(void *opaque) void vncws_tls_handshake_io(void *opaque)
{ {
struct VncState *vs = (struct VncState *)opaque; struct VncState *vs = (struct VncState *)opaque;
if (!vs->tls.session) {
VNC_DEBUG("TLS Websocket setup\n");
if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) {
return;
}
}
VNC_DEBUG("Handshake IO continue\n"); VNC_DEBUG("Handshake IO continue\n");
vncws_start_tls_handshake(vs); vncws_start_tls_handshake(vs);
} }
void vncws_tls_handshake_peek(void *opaque)
{
VncState *vs = opaque;
long ret;
if (!vs->ws_tls.session) {
char peek[4];
ret = qemu_recv(vs->csock, peek, sizeof(peek), MSG_PEEK);
if (ret && (strncmp(peek, "\x16", 1) == 0
|| strncmp(peek, "\x80", 1) == 0)) {
VNC_DEBUG("TLS Websocket connection recognized");
vnc_tls_client_setup(vs, 1);
vncws_start_tls_handshake(vs);
} else {
vncws_handshake_read(vs);
}
} else {
qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs);
}
}
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
void vncws_handshake_read(void *opaque) void vncws_handshake_read(void *opaque)

View File

@@ -75,7 +75,7 @@ enum {
}; };
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
void vncws_tls_handshake_peek(void *opaque); void vncws_tls_handshake_io(void *opaque);
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
void vncws_handshake_read(void *opaque); void vncws_handshake_read(void *opaque);
long vnc_client_write_ws(VncState *vs); long vnc_client_write_ws(VncState *vs);

289
ui/vnc.c
View File

@@ -1343,15 +1343,8 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
if (vs->tls.session) { if (vs->tls.session) {
ret = vnc_client_write_tls(&vs->tls.session, data, datalen); ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
} else { } else {
#ifdef CONFIG_VNC_WS
if (vs->ws_tls.session) {
ret = vnc_client_write_tls(&vs->ws_tls.session, data, datalen);
} else
#endif /* CONFIG_VNC_WS */
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
{ ret = send(vs->csock, (const void *)data, datalen, 0);
ret = send(vs->csock, (const void *)data, datalen, 0);
}
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
} }
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
@@ -1491,15 +1484,8 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
if (vs->tls.session) { if (vs->tls.session) {
ret = vnc_client_read_tls(&vs->tls.session, data, datalen); ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
} else { } else {
#ifdef CONFIG_VNC_WS
if (vs->ws_tls.session) {
ret = vnc_client_read_tls(&vs->ws_tls.session, data, datalen);
} else
#endif /* CONFIG_VNC_WS */
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
{ ret = qemu_recv(vs->csock, data, datalen, 0);
ret = qemu_recv(vs->csock, data, datalen, 0);
}
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
} }
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
@@ -2400,34 +2386,34 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
case 4: vs->as.fmt = AUD_FMT_U32; break; case 4: vs->as.fmt = AUD_FMT_U32; break;
case 5: vs->as.fmt = AUD_FMT_S32; break; case 5: vs->as.fmt = AUD_FMT_S32; break;
default: default:
printf("Invalid audio format %d\n", read_u8(data, 4)); VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
vnc_client_error(vs); vnc_client_error(vs);
break; break;
} }
vs->as.nchannels = read_u8(data, 5); vs->as.nchannels = read_u8(data, 5);
if (vs->as.nchannels != 1 && vs->as.nchannels != 2) { if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
printf("Invalid audio channel coount %d\n", VNC_DEBUG("Invalid audio channel coount %d\n",
read_u8(data, 5)); read_u8(data, 5));
vnc_client_error(vs); vnc_client_error(vs);
break; break;
} }
vs->as.freq = read_u32(data, 6); vs->as.freq = read_u32(data, 6);
break; break;
default: default:
printf ("Invalid audio message %d\n", read_u8(data, 4)); VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
vnc_client_error(vs); vnc_client_error(vs);
break; break;
} }
break; break;
default: default:
printf("Msg: %d\n", read_u16(data, 0)); VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
vnc_client_error(vs); vnc_client_error(vs);
break; break;
} }
break; break;
default: default:
printf("Msg: %d\n", data[0]); VNC_DEBUG("Msg: %d\n", data[0]);
vnc_client_error(vs); vnc_client_error(vs);
break; break;
} }
@@ -3010,15 +2996,18 @@ static void vnc_connect(VncDisplay *vd, int csock,
if (skipauth) { if (skipauth) {
vs->auth = VNC_AUTH_NONE; vs->auth = VNC_AUTH_NONE;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID;
#endif
} else { } else {
vs->auth = vd->auth; if (websocket) {
#ifdef CONFIG_VNC_TLS vs->auth = vd->ws_auth;
vs->subauth = vd->subauth; vs->subauth = VNC_AUTH_INVALID;
#endif } else {
vs->auth = vd->auth;
vs->subauth = vd->subauth;
}
} }
VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n",
csock, websocket, vs->auth, vs->subauth);
vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
for (i = 0; i < VNC_STAT_ROWS; ++i) { for (i = 0; i < VNC_STAT_ROWS; ++i) {
@@ -3032,8 +3021,8 @@ static void vnc_connect(VncDisplay *vd, int csock,
if (websocket) { if (websocket) {
vs->websocket = 1; vs->websocket = 1;
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
if (vd->tls.x509cert) { if (vd->ws_tls) {
qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_peek, qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io,
NULL, vs); NULL, vs);
} else } else
#endif /* CONFIG_VNC_TLS */ #endif /* CONFIG_VNC_TLS */
@@ -3206,8 +3195,8 @@ static void vnc_display_close(VncDisplay *vs)
} }
#endif /* CONFIG_VNC_WS */ #endif /* CONFIG_VNC_WS */
vs->auth = VNC_AUTH_INVALID; vs->auth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS
vs->tls.x509verify = 0; vs->tls.x509verify = 0;
#endif #endif
} }
@@ -3318,6 +3307,134 @@ static QemuOptsList qemu_vnc_opts = {
}, },
}; };
static void
vnc_display_setup_auth(VncDisplay *vs,
bool password,
bool sasl,
bool tls,
bool x509,
bool websocket)
{
/*
* We have a choice of 3 authentication options
*
* 1. none
* 2. vnc
* 3. sasl
*
* The channel can be run in 2 modes
*
* 1. clear
* 2. tls
*
* And TLS can use 2 types of credentials
*
* 1. anon
* 2. x509
*
* We thus have 9 possible logical combinations
*
* 1. clear + none
* 2. clear + vnc
* 3. clear + sasl
* 4. tls + anon + none
* 5. tls + anon + vnc
* 6. tls + anon + sasl
* 7. tls + x509 + none
* 8. tls + x509 + vnc
* 9. tls + x509 + sasl
*
* These need to be mapped into the VNC auth schemes
* in an appropriate manner. In regular VNC, all the
* TLS options get mapped into VNC_AUTH_VENCRYPT
* sub-auth types.
*
* In websockets, the https:// protocol already provides
* TLS support, so there is no need to make use of the
* VeNCrypt extension. Furthermore, websockets browser
* clients could not use VeNCrypt even if they wanted to,
* as they cannot control when the TLS handshake takes
* place. Thus there is no option but to rely on https://,
* meaning combinations 4->6 and 7->9 will be mapped to
* VNC auth schemes in the same way as combos 1->3.
*
* Regardless of fact that we have a different mapping to
* VNC auth mechs for plain VNC vs websockets VNC, the end
* result has the same security characteristics.
*/
if (password) {
if (tls) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
if (x509) {
VNC_DEBUG("Initializing VNC server with x509 password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
} else {
VNC_DEBUG("Initializing VNC server with TLS password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
}
} else {
VNC_DEBUG("Initializing VNC server with password auth\n");
vs->auth = VNC_AUTH_VNC;
vs->subauth = VNC_AUTH_INVALID;
}
if (websocket) {
vs->ws_auth = VNC_AUTH_VNC;
} else {
vs->ws_auth = VNC_AUTH_INVALID;
}
} else if (sasl) {
if (tls) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
if (x509) {
VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
} else {
VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
}
} else {
VNC_DEBUG("Initializing VNC server with SASL auth\n");
vs->auth = VNC_AUTH_SASL;
vs->subauth = VNC_AUTH_INVALID;
}
if (websocket) {
vs->ws_auth = VNC_AUTH_SASL;
} else {
vs->ws_auth = VNC_AUTH_INVALID;
}
} else {
if (tls) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
if (x509) {
VNC_DEBUG("Initializing VNC server with x509 no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
} else {
VNC_DEBUG("Initializing VNC server with TLS no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
}
} else {
VNC_DEBUG("Initializing VNC server with no auth\n");
vs->auth = VNC_AUTH_NONE;
vs->subauth = VNC_AUTH_INVALID;
}
if (websocket) {
vs->ws_auth = VNC_AUTH_NONE;
} else {
vs->ws_auth = VNC_AUTH_INVALID;
}
}
}
void vnc_display_open(const char *id, Error **errp) void vnc_display_open(const char *id, Error **errp)
{ {
VncDisplay *vs = vnc_display_find(id); VncDisplay *vs = vnc_display_find(id);
@@ -3332,15 +3449,13 @@ void vnc_display_open(const char *id, Error **errp)
char *h; char *h;
bool has_ipv4 = false; bool has_ipv4 = false;
bool has_ipv6 = false; bool has_ipv6 = false;
#ifdef CONFIG_VNC_WS
const char *websocket; const char *websocket;
#endif
#ifdef CONFIG_VNC_TLS
bool tls = false, x509 = false; bool tls = false, x509 = false;
#ifdef CONFIG_VNC_TLS
const char *path; const char *path;
#endif #endif
#ifdef CONFIG_VNC_SASL
bool sasl = false; bool sasl = false;
#ifdef CONFIG_VNC_SASL
int saslErr; int saslErr;
#endif #endif
#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
@@ -3404,11 +3519,15 @@ void vnc_display_open(const char *id, Error **errp)
reverse = qemu_opt_get_bool(opts, "reverse", false); reverse = qemu_opt_get_bool(opts, "reverse", false);
lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true); lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true);
#ifdef CONFIG_VNC_SASL
sasl = qemu_opt_get_bool(opts, "sasl", false); sasl = qemu_opt_get_bool(opts, "sasl", false);
#endif #ifndef CONFIG_VNC_SASL
#ifdef CONFIG_VNC_TLS if (sasl) {
error_setg(errp, "VNC SASL auth requires cyrus-sasl support");
goto fail;
}
#endif /* CONFIG_VNC_SASL */
tls = qemu_opt_get_bool(opts, "tls", false); tls = qemu_opt_get_bool(opts, "tls", false);
#ifdef CONFIG_VNC_TLS
path = qemu_opt_get(opts, "x509"); path = qemu_opt_get(opts, "x509");
if (!path) { if (!path) {
path = qemu_opt_get(opts, "x509verify"); path = qemu_opt_get(opts, "x509verify");
@@ -3424,7 +3543,12 @@ void vnc_display_open(const char *id, Error **errp)
goto fail; goto fail;
} }
} }
#endif #else /* ! CONFIG_VNC_TLS */
if (tls) {
error_setg(errp, "VNC TLS auth requires gnutls support");
goto fail;
}
#endif /* ! CONFIG_VNC_TLS */
#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) #if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
acl = qemu_opt_get_bool(opts, "acl", false); acl = qemu_opt_get_bool(opts, "acl", false);
#endif #endif
@@ -3446,14 +3570,16 @@ void vnc_display_open(const char *id, Error **errp)
} }
vs->connections_limit = qemu_opt_get_number(opts, "connections", 32); vs->connections_limit = qemu_opt_get_number(opts, "connections", 32);
#ifdef CONFIG_VNC_WS
websocket = qemu_opt_get(opts, "websocket"); websocket = qemu_opt_get(opts, "websocket");
if (websocket) { if (websocket) {
#ifdef CONFIG_VNC_WS
vs->ws_enabled = true; vs->ws_enabled = true;
qemu_opt_set(wsopts, "port", websocket, &error_abort); qemu_opt_set(wsopts, "port", websocket, &error_abort);
#else /* ! CONFIG_VNC_WS */
error_setg(errp, "Websockets protocol requires gnutls support");
goto fail;
#endif /* ! CONFIG_VNC_WS */
} }
#endif /* CONFIG_VNC_WS */
#ifdef CONFIG_VNC_JPEG #ifdef CONFIG_VNC_JPEG
vs->lossy = qemu_opt_get_bool(opts, "lossy", false); vs->lossy = qemu_opt_get_bool(opts, "lossy", false);
@@ -3501,82 +3627,7 @@ void vnc_display_open(const char *id, Error **errp)
} }
#endif #endif
/* vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket);
* Combinations we support here:
*
* - no-auth (clear text, no auth)
* - password (clear text, weak auth)
* - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI)
* - tls (encrypt, weak anonymous creds, no auth)
* - tls + password (encrypt, weak anonymous creds, weak auth)
* - tls + sasl (encrypt, weak anonymous creds, good auth)
* - tls + x509 (encrypt, good x509 creds, no auth)
* - tls + x509 + password (encrypt, good x509 creds, weak auth)
* - tls + x509 + sasl (encrypt, good x509 creds, good auth)
*
* NB1. TLS is a stackable auth scheme.
* NB2. the x509 schemes have option to validate a client cert dname
*/
if (password) {
#ifdef CONFIG_VNC_TLS
if (tls) {
vs->auth = VNC_AUTH_VENCRYPT;
if (x509) {
VNC_DEBUG("Initializing VNC server with x509 password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
} else {
VNC_DEBUG("Initializing VNC server with TLS password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
}
} else {
#endif /* CONFIG_VNC_TLS */
VNC_DEBUG("Initializing VNC server with password auth\n");
vs->auth = VNC_AUTH_VNC;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID;
}
#endif /* CONFIG_VNC_TLS */
#ifdef CONFIG_VNC_SASL
} else if (sasl) {
#ifdef CONFIG_VNC_TLS
if (tls) {
vs->auth = VNC_AUTH_VENCRYPT;
if (x509) {
VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
} else {
VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
}
} else {
#endif /* CONFIG_VNC_TLS */
VNC_DEBUG("Initializing VNC server with SASL auth\n");
vs->auth = VNC_AUTH_SASL;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID;
}
#endif /* CONFIG_VNC_TLS */
#endif /* CONFIG_VNC_SASL */
} else {
#ifdef CONFIG_VNC_TLS
if (tls) {
vs->auth = VNC_AUTH_VENCRYPT;
if (x509) {
VNC_DEBUG("Initializing VNC server with x509 no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
} else {
VNC_DEBUG("Initializing VNC server with TLS no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
}
} else {
#endif
VNC_DEBUG("Initializing VNC server with no auth\n");
vs->auth = VNC_AUTH_NONE;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID;
}
#endif
}
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
@@ -3594,7 +3645,7 @@ void vnc_display_open(const char *id, Error **errp)
dev = qdev_find_recursive(sysbus_get_default(), device_id); dev = qdev_find_recursive(sysbus_get_default(), device_id);
if (dev == NULL) { if (dev == NULL) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device_id); error_setg(errp, "Device '%s' not found", device_id);
goto fail; goto fail;
} }

View File

@@ -180,10 +180,12 @@ struct VncDisplay
char *password; char *password;
time_t expires; time_t expires;
int auth; int auth;
int subauth; /* Used by VeNCrypt */
int ws_auth; /* Used by websockets */
bool ws_tls; /* Used by websockets */
bool lossy; bool lossy;
bool non_adaptive; bool non_adaptive;
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
int subauth; /* Used by VeNCrypt */
VncDisplayTLS tls; VncDisplayTLS tls;
#endif #endif
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
@@ -284,18 +286,15 @@ struct VncState
int minor; int minor;
int auth; int auth;
int subauth; /* Used by VeNCrypt */
char challenge[VNC_AUTH_CHALLENGE_SIZE]; char challenge[VNC_AUTH_CHALLENGE_SIZE];
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
int subauth; /* Used by VeNCrypt */
VncStateTLS tls; VncStateTLS tls;
#endif #endif
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
VncStateSASL sasl; VncStateSASL sasl;
#endif #endif
#ifdef CONFIG_VNC_WS #ifdef CONFIG_VNC_WS
#ifdef CONFIG_VNC_TLS
VncStateTLS ws_tls;
#endif /* CONFIG_VNC_TLS */
bool encode_ws; bool encode_ws;
bool websocket; bool websocket;
#endif /* CONFIG_VNC_WS */ #endif /* CONFIG_VNC_WS */