aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-09-15 15:42:58 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-09-15 15:42:58 +0100
commit619622424dba749feef752d76d79ef2569f7f250 (patch)
treeb5b3e718e5075781da039fe3f6966aaec4922cdd /ui
parent1078f5db8ada5097600443838492ef07103790c2 (diff)
parent3e305e4a4752f70c0b5c3cf5b43ec957881714f7 (diff)
downloadqemu-619622424dba749feef752d76d79ef2569f7f250.zip
qemu-619622424dba749feef752d76d79ef2569f7f250.tar.gz
qemu-619622424dba749feef752d76d79ef2569f7f250.tar.bz2
Merge remote-tracking branch 'remotes/berrange/tags/vnc-crypto-v9-for-upstream' into staging
Merge vnc-crypto-v9 # gpg: Signature made Tue 15 Sep 2015 15:32:38 BST using RSA key ID 15104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" * remotes/berrange/tags/vnc-crypto-v9-for-upstream: ui: convert VNC server to use QCryptoTLSSession ui: fix return type for VNC I/O functions to be ssize_t crypto: introduce new module for handling TLS sessions crypto: add sanity checking of TLS x509 credentials crypto: introduce new module for TLS x509 credentials crypto: introduce new module for TLS anonymous credentials crypto: introduce new base module for TLS credentials qom: allow QOM to be linked into tools binaries crypto: move crypto objects out of libqemuutil.la tests: remove repetition in unit test object deps qapi: allow override of default enum prefix naming Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'ui')
-rw-r--r--ui/Makefile.objs2
-rw-r--r--ui/vnc-auth-sasl.c36
-rw-r--r--ui/vnc-auth-vencrypt.c80
-rw-r--r--ui/vnc-tls.c474
-rw-r--r--ui/vnc-tls.h69
-rw-r--r--ui/vnc-ws.c84
-rw-r--r--ui/vnc-ws.h2
-rw-r--r--ui/vnc.c362
-rw-r--r--ui/vnc.h21
9 files changed, 350 insertions, 780 deletions
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index c62d4d9..0034fbb 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -2,7 +2,7 @@ vnc-obj-y += vnc.o
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o
-vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+vnc-obj-y += vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 62a5fc4..fc732bd 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -525,21 +525,24 @@ void start_auth_sasl(VncState *vs)
goto authabort;
}
-#ifdef CONFIG_VNC_TLS
/* 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) {
- gnutls_cipher_algorithm_t cipher;
+ Error *local_err = NULL;
+ int keysize;
sasl_ssf_t ssf;
- cipher = gnutls_cipher_get(vs->tls.session);
- if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
- VNC_DEBUG("%s", "cannot TLS get cipher size\n");
+ 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 *= 8; /* tls key size is bytes, sasl wants bits */
+ 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) {
@@ -549,20 +552,19 @@ void start_auth_sasl(VncState *vs)
vs->sasl.conn = NULL;
goto authabort;
}
- } else
-#endif /* CONFIG_VNC_TLS */
+ } else {
vs->sasl.wantSSF = 1;
+ }
memset (&secprops, 0, sizeof secprops);
- /* Inform SASL that we've got an external SSF layer from TLS */
- if (vs->vd->is_unix
-#ifdef CONFIG_VNC_TLS
- /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
- is not sufficiently strong */
- || (vs->auth == VNC_AUTH_VENCRYPT &&
- vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-#endif /* CONFIG_VNC_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
+ */
+ 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;
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 8fc965b..44ac2fa 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -67,38 +67,42 @@ static void vnc_tls_handshake_io(void *opaque);
static int vnc_start_vencrypt_handshake(VncState *vs)
{
- int ret;
-
- if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
- if (!gnutls_error_is_fatal(ret)) {
- VNC_DEBUG("Handshake interrupted (blocking)\n");
- if (!gnutls_record_get_direction(vs->tls.session))
- qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
- else
- qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
- return 0;
- }
- VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
- vnc_client_error(vs);
- return -1;
+ Error *err = NULL;
+
+ if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
+ goto error;
}
- 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");
+ switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
+ case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
+ VNC_DEBUG("Handshake done, checking credentials\n");
+ if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
+ goto error;
}
- }
+ VNC_DEBUG("Client verification passed, starting TLS I/O\n");
+ qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+
+ start_auth_vencrypt_subauth(vs);
+ break;
- VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+ case QCRYPTO_TLS_HANDSHAKE_RECVING:
+ VNC_DEBUG("Handshake interrupted (blocking read)\n");
+ qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
+ break;
- start_auth_vencrypt_subauth(vs);
+ case QCRYPTO_TLS_HANDSHAKE_SENDING:
+ VNC_DEBUG("Handshake interrupted (blocking write)\n");
+ qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
+ break;
+ }
return 0;
+
+ error:
+ VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return -1;
}
static void vnc_tls_handshake_io(void *opaque)
@@ -110,14 +114,6 @@ static void vnc_tls_handshake_io(void *opaque)
}
-
-#define NEED_X509_AUTH(vs) \
- ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-
-
static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
{
int auth = read_u32(data, 0);
@@ -128,15 +124,29 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
vnc_flush(vs);
vnc_client_error(vs);
} else {
+ Error *err = NULL;
VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
vnc_write_u8(vs, 1); /* Accept auth */
vnc_flush(vs);
- if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
- VNC_DEBUG("Failed to setup TLS\n");
+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
+ NULL,
+ vs->vd->tlsaclname,
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ &err);
+ if (!vs->tls) {
+ VNC_DEBUG("Failed to setup TLS %s\n",
+ error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
return 0;
}
+ qcrypto_tls_session_set_callbacks(vs->tls,
+ vnc_tls_push,
+ vnc_tls_pull,
+ vs);
+
VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
if (vnc_start_vencrypt_handshake(vs) < 0) {
VNC_DEBUG("Failed to start TLS handshake\n");
diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
deleted file mode 100644
index 028fc4d..0000000
--- a/ui/vnc-tls.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * QEMU VNC display driver: TLS helpers
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2006 Fabrice Bellard
- * 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-x509.h"
-#include "vnc.h"
-#include "qemu/sockets.h"
-
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
-/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
-static void vnc_debug_gnutls_log(int level, const char* str) {
- VNC_DEBUG("%d %s", level, str);
-}
-#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
-
-
-#define DH_BITS 1024
-static gnutls_dh_params_t dh_params;
-
-static int vnc_tls_initialize(void)
-{
- static int tlsinitialized = 0;
-
- if (tlsinitialized)
- return 1;
-
- if (gnutls_global_init () < 0)
- return 0;
-
- /* XXX ought to re-generate diffie-hellman params periodically */
- if (gnutls_dh_params_init (&dh_params) < 0)
- return 0;
- if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
- return 0;
-
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
- gnutls_global_set_log_level(10);
- gnutls_global_set_log_function(vnc_debug_gnutls_log);
-#endif
-
- tlsinitialized = 1;
-
- return 1;
-}
-
-static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
- const void *data,
- size_t len) {
- VncState *vs = (VncState *)transport;
- int ret;
-
- retry:
- ret = send(vs->csock, data, len, 0);
- if (ret < 0) {
- if (errno == EINTR)
- goto retry;
- return -1;
- }
- return ret;
-}
-
-
-static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
- void *data,
- size_t len) {
- VncState *vs = (VncState *)transport;
- int ret;
-
- retry:
- ret = qemu_recv(vs->csock, data, len, 0);
- if (ret < 0) {
- if (errno == EINTR)
- goto retry;
- return -1;
- }
- return ret;
-}
-
-
-static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void)
-{
- gnutls_anon_server_credentials_t anon_cred;
- int ret;
-
- if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
- VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
- return NULL;
- }
-
- gnutls_anon_set_server_dh_params(anon_cred, dh_params);
-
- return anon_cred;
-}
-
-
-static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
-{
- gnutls_certificate_credentials_t x509_cred;
- int ret;
-
- if (!vd->tls.x509cacert) {
- VNC_DEBUG("No CA x509 certificate specified\n");
- return NULL;
- }
- if (!vd->tls.x509cert) {
- VNC_DEBUG("No server x509 certificate specified\n");
- return NULL;
- }
- if (!vd->tls.x509key) {
- VNC_DEBUG("No server private key specified\n");
- return NULL;
- }
-
- if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
- VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
- return NULL;
- }
- if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
- vd->tls.x509cacert,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
-
- if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
- vd->tls.x509cert,
- vd->tls.x509key,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
-
- if (vd->tls.x509cacrl) {
- if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
- vd->tls.x509cacrl,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
- }
-
- gnutls_certificate_set_dh_params (x509_cred, dh_params);
-
- return x509_cred;
-}
-
-
-int vnc_tls_validate_certificate(VncState *vs)
-{
- int ret;
- unsigned int status;
- const gnutls_datum_t *certs;
- unsigned int nCerts, i;
- time_t now;
-
- VNC_DEBUG("Validating client certificate\n");
- if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
- VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
- return -1;
- }
-
- if ((now = time(NULL)) == ((time_t)-1)) {
- return -1;
- }
-
- if (status != 0) {
- if (status & GNUTLS_CERT_INVALID)
- VNC_DEBUG("The certificate is not trusted.\n");
-
- if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
- VNC_DEBUG("The certificate hasn't got a known issuer.\n");
-
- if (status & GNUTLS_CERT_REVOKED)
- VNC_DEBUG("The certificate has been revoked.\n");
-
- if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
- VNC_DEBUG("The certificate uses an insecure algorithm\n");
-
- return -1;
- } else {
- VNC_DEBUG("Certificate is valid!\n");
- }
-
- /* Only support x509 for now */
- if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
- return -1;
-
- if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
- return -1;
-
- for (i = 0 ; i < nCerts ; i++) {
- gnutls_x509_crt_t cert;
- VNC_DEBUG ("Checking certificate chain %d\n", i);
- if (gnutls_x509_crt_init (&cert) < 0)
- return -1;
-
- if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_expiration_time (cert) < now) {
- VNC_DEBUG("The certificate has expired\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VNC_DEBUG("The certificate is not yet activated\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VNC_DEBUG("The certificate is not yet activated\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (i == 0) {
- size_t dnameSize = 1024;
- vs->tls.dname = g_malloc(dnameSize);
- requery:
- if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
- if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
- vs->tls.dname = g_realloc(vs->tls.dname, dnameSize);
- goto requery;
- }
- gnutls_x509_crt_deinit (cert);
- VNC_DEBUG("Cannot get client distinguished name: %s",
- gnutls_strerror (ret));
- return -1;
- }
-
- if (vs->vd->tls.x509verify) {
- int allow;
- if (!vs->vd->tls.acl) {
- VNC_DEBUG("no ACL activated, allowing access");
- gnutls_x509_crt_deinit (cert);
- continue;
- }
-
- allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
- vs->tls.dname);
-
- VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
- vs->tls.dname, allow ? "allowed" : "denied");
- if (!allow) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
- }
- }
-
- gnutls_x509_crt_deinit (cert);
- }
-
- return 0;
-}
-
-#if defined(GNUTLS_VERSION_NUMBER) && \
- GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */
-
-static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
-{
- const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH";
- int rc;
-
- rc = gnutls_priority_set_direct(s, priority, NULL);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-#else
-
-static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
-{
- static const int cert_types[] = { GNUTLS_CRT_X509, 0 };
- static const int protocols[] = {
- GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0
- };
- static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 };
- static const int kx_x509[] = {
- GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA,
- GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0
- };
- int rc;
-
- rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
-
- rc = gnutls_certificate_type_set_priority(s, cert_types);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
-
- rc = gnutls_protocol_set_priority(s, protocols);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-#endif
-
-int vnc_tls_client_setup(VncState *vs,
- int needX509Creds) {
- VNC_DEBUG("Do TLS setup\n");
- if (vnc_tls_initialize() < 0) {
- VNC_DEBUG("Failed to init TLS\n");
- vnc_client_error(vs);
- return -1;
- }
- if (vs->tls.session == NULL) {
- if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
- vnc_client_error(vs);
- return -1;
- }
-
- if (gnutls_set_default_priority(vs->tls.session) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
-
- if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
-
- if (needX509Creds) {
- gnutls_certificate_server_credentials x509_cred =
- vnc_tls_initialize_x509_cred(vs->vd);
- if (!x509_cred) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
- if (gnutls_credentials_set(vs->tls.session,
- GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- gnutls_certificate_free_credentials(x509_cred);
- vnc_client_error(vs);
- return -1;
- }
- if (vs->vd->tls.x509verify) {
- VNC_DEBUG("Requesting a client certificate\n");
- gnutls_certificate_server_set_request(vs->tls.session,
- GNUTLS_CERT_REQUEST);
- }
-
- } else {
- gnutls_anon_server_credentials_t anon_cred =
- vnc_tls_initialize_anon_cred();
- if (!anon_cred) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
- if (gnutls_credentials_set(vs->tls.session,
- GNUTLS_CRD_ANON, anon_cred) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- gnutls_anon_free_server_credentials(anon_cred);
- vnc_client_error(vs);
- return -1;
- }
- }
-
- gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
- gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
- gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
- }
- return 0;
-}
-
-
-void vnc_tls_client_cleanup(VncState *vs)
-{
- if (vs->tls.session) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- }
- g_free(vs->tls.dname);
-}
-
-
-
-static int vnc_set_x509_credential(VncDisplay *vd,
- const char *certdir,
- const char *filename,
- char **cred,
- int ignoreMissing)
-{
- struct stat sb;
-
- g_free(*cred);
- *cred = g_malloc(strlen(certdir) + strlen(filename) + 2);
-
- strcpy(*cred, certdir);
- strcat(*cred, "/");
- strcat(*cred, filename);
-
- VNC_DEBUG("Check %s\n", *cred);
- if (stat(*cred, &sb) < 0) {
- g_free(*cred);
- *cred = NULL;
- if (ignoreMissing && errno == ENOENT)
- return 0;
- return -1;
- }
-
- return 0;
-}
-
-
-int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
- const char *certdir)
-{
- if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
- goto cleanup;
-
- return 0;
-
- cleanup:
- g_free(vd->tls.x509cacert);
- g_free(vd->tls.x509cacrl);
- g_free(vd->tls.x509cert);
- g_free(vd->tls.x509key);
- vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
- return -1;
-}
-
diff --git a/ui/vnc-tls.h b/ui/vnc-tls.h
deleted file mode 100644
index f9829c7..0000000
--- a/ui/vnc-tls.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * QEMU VNC display driver. TLS helpers
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2006 Fabrice Bellard
- * 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.
- */
-
-
-#ifndef __QEMU_VNC_TLS_H__
-#define __QEMU_VNC_TLS_H__
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-#include "qemu/acl.h"
-
-typedef struct VncDisplayTLS VncDisplayTLS;
-typedef struct VncStateTLS VncStateTLS;
-
-/* Server state */
-struct VncDisplayTLS {
- int x509verify; /* Non-zero if server requests & validates client cert */
- qemu_acl *acl;
-
- /* Paths to x509 certs/keys */
- char *x509cacert;
- char *x509cacrl;
- char *x509cert;
- char *x509key;
-};
-
-/* Per client state */
-struct VncStateTLS {
- gnutls_session_t session;
-
- /* Client's Distinguished Name from the x509 cert */
- char *dname;
-};
-
-int vnc_tls_client_setup(VncState *vs, int x509Creds);
-void vnc_tls_client_cleanup(VncState *vs);
-
-int vnc_tls_validate_certificate(VncState *vs);
-
-int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
- const char *path);
-
-
-#endif /* __QEMU_VNC_TLS_H__ */
-
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index b4cb6bd..175ea50 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -22,60 +22,70 @@
#include "qemu/main-loop.h"
#include "crypto/hash.h"
-#ifdef CONFIG_VNC_TLS
-#include "qemu/sockets.h"
-
static int vncws_start_tls_handshake(VncState *vs)
{
- int ret = gnutls_handshake(vs->tls.session);
-
- if (ret < 0) {
- if (!gnutls_error_is_fatal(ret)) {
- VNC_DEBUG("Handshake interrupted (blocking)\n");
- if (!gnutls_record_get_direction(vs->tls.session)) {
- qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io,
- NULL, vs);
- } else {
- qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io,
- vs);
- }
- return 0;
- }
- VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
- vnc_client_error(vs);
- return -1;
+ Error *err = NULL;
+
+ if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
+ goto error;
}
- 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");
+ switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
+ case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
+ VNC_DEBUG("Handshake done, checking credentials\n");
+ if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
+ goto error;
}
+ VNC_DEBUG("Client verification passed, starting TLS I/O\n");
+ qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
+ break;
+
+ case QCRYPTO_TLS_HANDSHAKE_RECVING:
+ VNC_DEBUG("Handshake interrupted (blocking read)\n");
+ qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
+ break;
+
+ case QCRYPTO_TLS_HANDSHAKE_SENDING:
+ VNC_DEBUG("Handshake interrupted (blocking write)\n");
+ qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, vs);
+ break;
}
- VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
-
return 0;
+
+ error:
+ VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return -1;
}
void vncws_tls_handshake_io(void *opaque)
{
VncState *vs = (VncState *)opaque;
+ Error *err = NULL;
- if (!vs->tls.session) {
- VNC_DEBUG("TLS Websocket setup\n");
- if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) {
- return;
- }
+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
+ NULL,
+ vs->vd->tlsaclname,
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ &err);
+ if (!vs->tls) {
+ VNC_DEBUG("Failed to setup TLS %s\n",
+ error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
+ return;
}
- VNC_DEBUG("Handshake IO continue\n");
+
+ qcrypto_tls_session_set_callbacks(vs->tls,
+ vnc_tls_push,
+ vnc_tls_pull,
+ vs);
+
+ VNC_DEBUG("Start TLS WS handshake process\n");
vncws_start_tls_handshake(vs);
}
-#endif /* CONFIG_VNC_TLS */
void vncws_handshake_read(void *opaque)
{
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 9494225..4ab0a8c 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -72,9 +72,7 @@ enum {
WS_OPCODE_PONG = 0xA
};
-#ifdef CONFIG_VNC_TLS
void vncws_tls_handshake_io(void *opaque);
-#endif /* CONFIG_VNC_TLS */
void vncws_handshake_read(void *opaque);
long vnc_client_write_ws(VncState *vs);
long vnc_client_read_ws(VncState *vs);
diff --git a/ui/vnc.c b/ui/vnc.c
index caf82f5..d73966a 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -41,6 +41,9 @@
#include "ui/input.h"
#include "qapi-event.h"
#include "crypto/hash.h"
+#include "crypto/tlscredsanon.h"
+#include "crypto/tlscredsx509.h"
+#include "qom/object_interfaces.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
@@ -222,7 +225,6 @@ static const char *vnc_auth_name(VncDisplay *vd) {
case VNC_AUTH_TLS:
return "tls";
case VNC_AUTH_VENCRYPT:
-#ifdef CONFIG_VNC_TLS
switch (vd->subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
return "vencrypt+plain";
@@ -245,9 +247,6 @@ static const char *vnc_auth_name(VncDisplay *vd) {
default:
return "vencrypt";
}
-#else
- return "vencrypt";
-#endif
case VNC_AUTH_SASL:
return "sasl";
}
@@ -275,13 +274,12 @@ static void vnc_client_cache_auth(VncState *client)
return;
}
-#ifdef CONFIG_VNC_TLS
- if (client->tls.session &&
- client->tls.dname) {
- client->info->has_x509_dname = true;
- client->info->x509_dname = g_strdup(client->tls.dname);
+ if (client->tls) {
+ client->info->x509_dname =
+ qcrypto_tls_session_get_peer_name(client->tls);
+ client->info->has_x509_dname =
+ client->info->x509_dname != NULL;
}
-#endif
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn &&
client->sasl.username) {
@@ -358,12 +356,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
info->base->family = inet_netfamily(sa.ss_family);
info->base->websocket = client->websocket;
-#ifdef CONFIG_VNC_TLS
- if (client->tls.session && client->tls.dname) {
- info->has_x509_dname = true;
- info->x509_dname = g_strdup(client->tls.dname);
+ if (client->tls) {
+ info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
+ info->has_x509_dname = info->x509_dname != NULL;
}
-#endif
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn && client->sasl.username) {
info->has_sasl_username = true;
@@ -513,7 +509,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
break;
case VNC_AUTH_VENCRYPT:
info->auth = VNC_PRIMARY_AUTH_VENCRYPT;
-#ifdef CONFIG_VNC_TLS
info->has_vencrypt = true;
switch (vd->subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
@@ -547,7 +542,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
info->has_vencrypt = false;
break;
}
-#endif
break;
case VNC_AUTH_SASL:
info->auth = VNC_PRIMARY_AUTH_SASL;
@@ -1237,9 +1231,7 @@ void vnc_disconnect_finish(VncState *vs)
vnc_tight_clear(vs);
vnc_zrle_clear(vs);
-#ifdef CONFIG_VNC_TLS
- vnc_tls_client_cleanup(vs);
-#endif /* CONFIG_VNC_TLS */
+ qcrypto_tls_session_free(vs->tls);
#ifdef CONFIG_VNC_SASL
vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */
@@ -1268,7 +1260,7 @@ void vnc_disconnect_finish(VncState *vs)
g_free(vs);
}
-int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno)
{
if (ret == 0 || ret == -1) {
if (ret == -1) {
@@ -1284,7 +1276,7 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno)
}
}
- VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
+ VNC_DEBUG("Closing down client sock: ret %zd, errno %d\n",
ret, ret < 0 ? last_errno : 0);
vnc_disconnect_start(vs);
@@ -1300,23 +1292,40 @@ void vnc_client_error(VncState *vs)
vnc_disconnect_start(vs);
}
-#ifdef CONFIG_VNC_TLS
-static long vnc_client_write_tls(gnutls_session_t *session,
- const uint8_t *data,
- size_t datalen)
+
+ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
{
- long ret = gnutls_write(*session, data, datalen);
+ VncState *vs = opaque;
+ ssize_t ret;
+
+ retry:
+ ret = qemu_recv(vs->csock, buf, len, 0);
if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN) {
- errno = EAGAIN;
- } else {
- errno = EIO;
+ if (errno == EINTR) {
+ goto retry;
+ }
+ return -1;
+ }
+ return ret;
+}
+
+
+ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
+{
+ VncState *vs = opaque;
+ ssize_t ret;
+
+ retry:
+ ret = send(vs->csock, buf, len, 0);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ goto retry;
}
- ret = -1;
+ return -1;
}
return ret;
}
-#endif /* CONFIG_VNC_TLS */
+
/*
* Called to write a chunk of data to the client socket. The data may
@@ -1333,20 +1342,23 @@ static long vnc_client_write_tls(gnutls_session_t *session,
* the requested 'datalen' if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
{
- long ret;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls.session) {
- ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
+ ssize_t ret;
+ int err = 0;
+ if (vs->tls) {
+ ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen);
+ if (ret < 0) {
+ err = errno;
+ }
} else {
-#endif /* CONFIG_VNC_TLS */
ret = send(vs->csock, (const void *)data, datalen, 0);
-#ifdef CONFIG_VNC_TLS
+ if (ret < 0) {
+ err = socket_error();
+ }
}
-#endif /* CONFIG_VNC_TLS */
VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
- return vnc_client_io_error(vs, ret, socket_error());
+ return vnc_client_io_error(vs, ret, err);
}
@@ -1360,9 +1372,9 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
* the buffered output data if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-static long vnc_client_write_plain(VncState *vs)
+static ssize_t vnc_client_write_plain(VncState *vs)
{
- long ret;
+ ssize_t ret;
#ifdef CONFIG_VNC_SASL
VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
@@ -1435,22 +1447,6 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
vs->read_handler_expect = expecting;
}
-#ifdef CONFIG_VNC_TLS
-static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
- size_t datalen)
-{
- long ret = gnutls_read(*session, data, datalen);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN) {
- errno = EAGAIN;
- } else {
- errno = EIO;
- }
- ret = -1;
- }
- return ret;
-}
-#endif /* CONFIG_VNC_TLS */
/*
* Called to read a chunk of data from the client socket. The data may
@@ -1467,20 +1463,23 @@ static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
* the requested 'datalen' if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
{
- long ret;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls.session) {
- ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
+ ssize_t ret;
+ int err = -1;
+ if (vs->tls) {
+ ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen);
+ if (ret < 0) {
+ err = errno;
+ }
} else {
-#endif /* CONFIG_VNC_TLS */
ret = qemu_recv(vs->csock, data, datalen, 0);
-#ifdef CONFIG_VNC_TLS
+ if (ret < 0) {
+ err = socket_error();
+ }
}
-#endif /* CONFIG_VNC_TLS */
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
- return vnc_client_io_error(vs, ret, socket_error());
+ return vnc_client_io_error(vs, ret, err);
}
@@ -1492,9 +1491,9 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
* Returns the number of bytes read. Returns -1 on error, and
* disconnects the client socket.
*/
-static long vnc_client_read_plain(VncState *vs)
+static ssize_t vnc_client_read_plain(VncState *vs)
{
- int ret;
+ ssize_t ret;
VNC_DEBUG("Read plain %p size %zd offset %zd\n",
vs->input.buffer, vs->input.capacity, vs->input.offset);
buffer_reserve(&vs->input, 4096);
@@ -1520,7 +1519,7 @@ static void vnc_jobs_bh(void *opaque)
void vnc_client_read(void *opaque)
{
VncState *vs = opaque;
- long ret;
+ ssize_t ret;
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn && vs->sasl.runSSF)
@@ -2631,12 +2630,10 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
start_auth_vnc(vs);
break;
-#ifdef CONFIG_VNC_TLS
case VNC_AUTH_VENCRYPT:
VNC_DEBUG("Accept VeNCrypt auth\n");
start_auth_vencrypt(vs);
break;
-#endif /* CONFIG_VNC_TLS */
#ifdef CONFIG_VNC_SASL
case VNC_AUTH_SASL:
@@ -3033,12 +3030,9 @@ static void vnc_connect(VncDisplay *vd, int csock,
qemu_set_nonblock(vs->csock);
if (websocket) {
vs->websocket = 1;
-#ifdef CONFIG_VNC_TLS
if (vd->ws_tls) {
qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
- } else
-#endif /* CONFIG_VNC_TLS */
- {
+ } else {
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
}
} else
@@ -3194,9 +3188,11 @@ static void vnc_display_close(VncDisplay *vs)
}
vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID;
-#ifdef CONFIG_VNC_TLS
- vs->tls.x509verify = 0;
-#endif
+ if (vs->tlscreds) {
+ object_unparent(OBJECT(vs->tlscreds));
+ }
+ g_free(vs->tlsaclname);
+ vs->tlsaclname = NULL;
}
int vnc_display_password(const char *id, const char *password)
@@ -3250,6 +3246,10 @@ static QemuOptsList qemu_vnc_opts = {
.name = "websocket",
.type = QEMU_OPT_STRING,
},{
+ .name = "tls-creds",
+ .type = QEMU_OPT_STRING,
+ },{
+ /* Deprecated in favour of tls-creds */
.name = "x509",
.type = QEMU_OPT_STRING,
},{
@@ -3286,9 +3286,11 @@ static QemuOptsList qemu_vnc_opts = {
.name = "sasl",
.type = QEMU_OPT_BOOL,
},{
+ /* Deprecated in favour of tls-creds */
.name = "tls",
.type = QEMU_OPT_BOOL,
},{
+ /* Deprecated in favour of tls-creds */
.name = "x509verify",
.type = QEMU_OPT_STRING,
},{
@@ -3306,13 +3308,12 @@ static QemuOptsList qemu_vnc_opts = {
};
-static void
+static int
vnc_display_setup_auth(VncDisplay *vs,
bool password,
bool sasl,
- bool tls,
- bool x509,
- bool websocket)
+ bool websocket,
+ Error **errp)
{
/*
* We have a choice of 3 authentication options
@@ -3362,17 +3363,24 @@ vnc_display_setup_auth(VncDisplay *vs,
* result has the same security characteristics.
*/
if (password) {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with password auth\n");
@@ -3385,17 +3393,24 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
} else if (sasl) {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with SASL auth\n");
@@ -3408,17 +3423,24 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
} else {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with no auth\n");
@@ -3431,8 +3453,55 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
}
+ return 0;
+}
+
+
+/*
+ * Handle back compat with old CLI syntax by creating some
+ * suitable QCryptoTLSCreds objects
+ */
+static QCryptoTLSCreds *
+vnc_display_create_creds(bool x509,
+ bool x509verify,
+ const char *dir,
+ const char *id,
+ Error **errp)
+{
+ gchar *credsid = g_strdup_printf("tlsvnc%s", id);
+ Object *parent = object_get_objects_root();
+ Object *creds;
+ Error *err = NULL;
+
+ if (x509) {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_X509,
+ parent,
+ credsid,
+ &err,
+ "endpoint", "server",
+ "dir", dir,
+ "verify-peer", x509verify ? "yes" : "no",
+ NULL);
+ } else {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
+ parent,
+ credsid,
+ &err,
+ "endpoint", "server",
+ NULL);
+ }
+
+ g_free(credsid);
+
+ if (err) {
+ error_propagate(errp, err);
+ return NULL;
+ }
+
+ return QCRYPTO_TLS_CREDS(creds);
}
+
void vnc_display_open(const char *id, Error **errp)
{
VncDisplay *vs = vnc_display_find(id);
@@ -3447,18 +3516,13 @@ void vnc_display_open(const char *id, Error **errp)
char *h;
bool has_ipv4 = false;
bool has_ipv6 = false;
+ const char *credid;
const char *websocket;
- bool tls = false, x509 = false;
-#ifdef CONFIG_VNC_TLS
- const char *path;
-#endif
bool sasl = false;
#ifdef CONFIG_VNC_SASL
int saslErr;
#endif
-#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
int acl = 0;
-#endif
int lock_key_sync = 1;
if (!vs) {
@@ -3539,32 +3603,67 @@ void vnc_display_open(const char *id, Error **errp)
goto fail;
}
#endif /* CONFIG_VNC_SASL */
- tls = qemu_opt_get_bool(opts, "tls", false);
-#ifdef CONFIG_VNC_TLS
- path = qemu_opt_get(opts, "x509");
- if (!path) {
- path = qemu_opt_get(opts, "x509verify");
- if (path) {
- vs->tls.x509verify = true;
- }
- }
- if (path) {
- x509 = true;
- if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
- error_setg(errp, "Failed to find x509 certificates/keys in %s",
- path);
+ credid = qemu_opt_get(opts, "tls-creds");
+ if (credid) {
+ Object *creds;
+ if (qemu_opt_get(opts, "tls") ||
+ qemu_opt_get(opts, "x509") ||
+ qemu_opt_get(opts, "x509verify")) {
+ error_setg(errp,
+ "'credid' parameter is mutually exclusive with "
+ "'tls', 'x509' and 'x509verify' parameters");
goto fail;
}
+
+ creds = object_resolve_path_component(
+ object_get_objects_root(), credid);
+ if (!creds) {
+ error_setg(errp, "No TLS credentials with id '%s'",
+ credid);
+ goto fail;
+ }
+ vs->tlscreds = (QCryptoTLSCreds *)
+ object_dynamic_cast(creds,
+ TYPE_QCRYPTO_TLS_CREDS);
+ if (!vs->tlscreds) {
+ error_setg(errp, "Object with id '%s' is not TLS credentials",
+ credid);
+ goto fail;
+ }
+ object_ref(OBJECT(vs->tlscreds));
+
+ if (vs->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ error_setg(errp,
+ "Expecting TLS credentials with a server endpoint");
+ goto fail;
+ }
+ } else {
+ const char *path;
+ bool tls = false, x509 = false, x509verify = false;
+ tls = qemu_opt_get_bool(opts, "tls", false);
+ if (tls) {
+ path = qemu_opt_get(opts, "x509");
+
+ if (path) {
+ x509 = true;
+ } else {
+ path = qemu_opt_get(opts, "x509verify");
+ if (path) {
+ x509 = true;
+ x509verify = true;
+ }
+ }
+ vs->tlscreds = vnc_display_create_creds(x509,
+ x509verify,
+ path,
+ vs->id,
+ errp);
+ if (!vs->tlscreds) {
+ goto fail;
+ }
+ }
}
-#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)
acl = qemu_opt_get_bool(opts, "acl", false);
-#endif
share = qemu_opt_get(opts, "share");
if (share) {
@@ -3604,19 +3703,14 @@ void vnc_display_open(const char *id, Error **errp)
vs->non_adaptive = true;
}
-#ifdef CONFIG_VNC_TLS
- if (acl && x509 && vs->tls.x509verify) {
- char *aclname;
-
+ if (acl) {
if (strcmp(vs->id, "default") == 0) {
- aclname = g_strdup("vnc.x509dname");
+ vs->tlsaclname = g_strdup("vnc.x509dname");
} else {
- aclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
+ vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
}
- vs->tls.acl = qemu_acl_init(aclname);
- g_free(aclname);
- }
-#endif
+ qemu_acl_init(vs->tlsaclname);
+ }
#ifdef CONFIG_VNC_SASL
if (acl && sasl) {
char *aclname;
@@ -3631,7 +3725,9 @@ void vnc_display_open(const char *id, Error **errp)
}
#endif
- vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket);
+ if (vnc_display_setup_auth(vs, password, sasl, websocket, errp) < 0) {
+ goto fail;
+ }
#ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
diff --git a/ui/vnc.h b/ui/vnc.h
index 814d720..4dd769c 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -33,6 +33,7 @@
#include "ui/console.h"
#include "audio/audio.h"
#include "qemu/bitmap.h"
+#include "crypto/tlssession.h"
#include <zlib.h>
#include <stdbool.h>
@@ -101,10 +102,7 @@ typedef void VncSendHextileTile(VncState *vs,
typedef struct VncDisplay VncDisplay;
-#ifdef CONFIG_VNC_TLS
-#include "vnc-tls.h"
#include "vnc-auth-vencrypt.h"
-#endif
#ifdef CONFIG_VNC_SASL
#include "vnc-auth-sasl.h"
#endif
@@ -181,9 +179,8 @@ struct VncDisplay
bool ws_tls; /* Used by websockets */
bool lossy;
bool non_adaptive;
-#ifdef CONFIG_VNC_TLS
- VncDisplayTLS tls;
-#endif
+ QCryptoTLSCreds *tlscreds;
+ char *tlsaclname;
#ifdef CONFIG_VNC_SASL
VncDisplaySASL sasl;
#endif
@@ -284,9 +281,7 @@ struct VncState
int auth;
int subauth; /* Used by VeNCrypt */
char challenge[VNC_AUTH_CHALLENGE_SIZE];
-#ifdef CONFIG_VNC_TLS
- VncStateTLS tls;
-#endif
+ QCryptoTLSSession *tls;
#ifdef CONFIG_VNC_SASL
VncStateSASL sasl;
#endif
@@ -513,8 +508,10 @@ enum {
void vnc_client_read(void *opaque);
void vnc_client_write(void *opaque);
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque);
+ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque);
/* Protocol I/O functions */
void vnc_write(VncState *vs, const void *data, size_t len);
@@ -533,7 +530,7 @@ uint32_t read_u32(uint8_t *data, size_t offset);
/* Protocol stage functions */
void vnc_client_error(VncState *vs);
-int vnc_client_io_error(VncState *vs, int ret, int last_errno);
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno);
void start_client_init(VncState *vs);
void start_auth_vnc(VncState *vs);