mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 09:13:55 -06:00
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:
commit
619622424d
44 changed files with 6129 additions and 877 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
474
ui/vnc-tls.c
474
ui/vnc-tls.c
|
@ -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;
|
||||
}
|
||||
|
69
ui/vnc-tls.h
69
ui/vnc-tls.h
|
@ -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__ */
|
||||
|
82
ui/vnc-ws.c
82
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);
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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
362
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;
|
||||
}
|
||||
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) {
|
||||
|
|
21
ui/vnc.h
21
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue