Merge vnc-crypto-v9

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJV+CwGAAoJEL6G67QVEE/fResQAKiHbjRRPjtCNjAvVixd2ewa
 O39TXlgQol4EiMKgsrIJf33yaEJQIj5ElNfKOUysgcLdGfL69+XWGQ5WgoHZx40d
 0Iiy8rGOTmCAQMgQYkRmJyayPTkK96jt8rl9psE0ab7JhS4CA2NbgnPWLLzVFwEx
 0BJ0SgHvzIGYy0N+9aQ7lVVUUja/Ksg64/6AAPpBHMkBZkOruk132E9B0D0mL7kL
 rka3OMgLpKqginKD4t3MKII1CnR5iSS2NNB/fJxVzrWK84Wv1/SbD1QnSlHPFWl6
 ffeD9j3F8ihFVdi0nssxK6kHYZW+dAeC8VPxpLcnFffHiNa7yU4XGQxmMuR3F/W/
 Su/R6W9JSP1dY6MCvCPjJNa2t9AW5iG0pGm4MckoZp4H6F46OPuxb0/GWoz/9prU
 S7BPLoB3h7h3otmokIL2MvqlU/5lfqUhlhW7w7ZS6fTNXUT2amFlq2UJZpFuEt0b
 3kAsAaGAq4wk5QB04lSbxW+u/F669L0dobu2FtOHiHECe3bihrCxk0OckzdA0fOP
 kZ14jIsvagXgWG2NAMQFKKXL3OCpfbObEm+mQp6JR6y108TwdXR3XYCudVHAHyK7
 GS+rhTdOtUgtQgpJG97RgdBd1nvil2dZ+NizX9DXu5EhT6le3PKijIOkq/6TLw5H
 5qAYBZCGQXl1bNrmifcH
 =6TWk
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2015-09-15 15:42:58 +01:00
commit 619622424d
44 changed files with 6129 additions and 877 deletions

View file

@ -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

View file

@ -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;

View file

@ -67,38 +67,42 @@ static void vnc_tls_handshake_io(void *opaque);
static int vnc_start_vencrypt_handshake(VncState *vs)
{
int ret;
Error *err = NULL;
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;
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;
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;
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;
}
VNC_DEBUG("Handshake done, switching to TLS data mode\n");
qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
start_auth_vencrypt_subauth(vs);
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");

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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);
Error *err = NULL;
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;
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;
VNC_DEBUG("Handshake done, switching to TLS data mode\n");
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
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;
}
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)
{

View file

@ -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);

362
ui/vnc.c
View file

@ -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;
}
ret = -1;
return -1;
}
return ret;
}
#endif /* CONFIG_VNC_TLS */
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;
}
return -1;
}
return ret;
}
/*
* 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) {

View file

@ -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);