Add more missing files
Major FAIL with my checkin scripts. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6731 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
5f5aed2278
commit
274b6fcc78
175
vnc-auth-vencrypt.c
Normal file
175
vnc-auth-vencrypt.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* QEMU VNC display driver: VeNCrypt authentication setup
|
||||
*
|
||||
* 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 "vnc.h"
|
||||
|
||||
|
||||
static void start_auth_vencrypt_subauth(VncState *vs)
|
||||
{
|
||||
switch (vs->vd->subauth) {
|
||||
case VNC_AUTH_VENCRYPT_TLSNONE:
|
||||
case VNC_AUTH_VENCRYPT_X509NONE:
|
||||
VNC_DEBUG("Accept TLS auth none\n");
|
||||
vnc_write_u32(vs, 0); /* Accept auth completion */
|
||||
start_client_init(vs);
|
||||
break;
|
||||
|
||||
case VNC_AUTH_VENCRYPT_TLSVNC:
|
||||
case VNC_AUTH_VENCRYPT_X509VNC:
|
||||
VNC_DEBUG("Start TLS auth VNC\n");
|
||||
start_auth_vnc(vs);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_VNC_SASL
|
||||
case VNC_AUTH_VENCRYPT_TLSSASL:
|
||||
case VNC_AUTH_VENCRYPT_X509SASL:
|
||||
VNC_DEBUG("Start TLS auth SASL\n");
|
||||
return start_auth_sasl(vs);
|
||||
#endif /* CONFIG_VNC_SASL */
|
||||
|
||||
default: /* Should not be possible, but just in case */
|
||||
VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth);
|
||||
vnc_write_u8(vs, 1);
|
||||
if (vs->minor >= 8) {
|
||||
static const char err[] = "Unsupported authentication type";
|
||||
vnc_write_u32(vs, sizeof(err));
|
||||
vnc_write(vs, err, sizeof(err));
|
||||
}
|
||||
vnc_client_error(vs);
|
||||
}
|
||||
}
|
||||
|
||||
static void vnc_tls_handshake_io(void *opaque);
|
||||
|
||||
static int vnc_start_vencrypt_handshake(struct VncState *vs) {
|
||||
int ret;
|
||||
|
||||
if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
|
||||
if (!gnutls_error_is_fatal(ret)) {
|
||||
VNC_DEBUG("Handshake interrupted (blocking)\n");
|
||||
if (!gnutls_record_get_direction(vs->tls.session))
|
||||
qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
|
||||
else
|
||||
qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
|
||||
return 0;
|
||||
}
|
||||
VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
|
||||
vnc_client_error(vs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vs->vd->tls.x509verify) {
|
||||
if (vnc_tls_validate_certificate(vs) < 0) {
|
||||
VNC_DEBUG("Client verification failed\n");
|
||||
vnc_client_error(vs);
|
||||
return -1;
|
||||
} else {
|
||||
VNC_DEBUG("Client verification passed\n");
|
||||
}
|
||||
}
|
||||
|
||||
VNC_DEBUG("Handshake done, switching to TLS data mode\n");
|
||||
vs->tls.wiremode = VNC_WIREMODE_TLS;
|
||||
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
|
||||
|
||||
start_auth_vencrypt_subauth(vs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vnc_tls_handshake_io(void *opaque) {
|
||||
struct VncState *vs = (struct VncState *)opaque;
|
||||
|
||||
VNC_DEBUG("Handshake IO continue\n");
|
||||
vnc_start_vencrypt_handshake(vs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define NEED_X509_AUTH(vs) \
|
||||
((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
|
||||
(vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
|
||||
(vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
|
||||
(vs)->vd->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);
|
||||
|
||||
if (auth != vs->vd->subauth) {
|
||||
VNC_DEBUG("Rejecting auth %d\n", auth);
|
||||
vnc_write_u8(vs, 0); /* Reject auth */
|
||||
vnc_flush(vs);
|
||||
vnc_client_error(vs);
|
||||
} else {
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
|
||||
VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
|
||||
if (vnc_start_vencrypt_handshake(vs) < 0) {
|
||||
VNC_DEBUG("Failed to start TLS handshake\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
|
||||
{
|
||||
if (data[0] != 0 ||
|
||||
data[1] != 2) {
|
||||
VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
|
||||
vnc_write_u8(vs, 1); /* Reject version */
|
||||
vnc_flush(vs);
|
||||
vnc_client_error(vs);
|
||||
} else {
|
||||
VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
|
||||
vnc_write_u8(vs, 0); /* Accept version */
|
||||
vnc_write_u8(vs, 1); /* Number of sub-auths */
|
||||
vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
|
||||
vnc_flush(vs);
|
||||
vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void start_auth_vencrypt(VncState *vs)
|
||||
{
|
||||
/* Send VeNCrypt version 0.2 */
|
||||
vnc_write_u8(vs, 0);
|
||||
vnc_write_u8(vs, 2);
|
||||
|
||||
vnc_read_when(vs, protocol_client_vencrypt_init, 2);
|
||||
}
|
||||
|
33
vnc-auth-vencrypt.h
Normal file
33
vnc-auth-vencrypt.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* QEMU VNC display driver
|
||||
*
|
||||
* 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_AUTH_VENCRYPT_H__
|
||||
#define __QEMU_VNC_AUTH_VENCRYPT_H__
|
||||
|
||||
void start_auth_vencrypt(VncState *vs);
|
||||
|
||||
#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */
|
450
vnc-tls.c
Normal file
450
vnc-tls.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
* 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 "vnc.h"
|
||||
#include "qemu_socket.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-hellmen 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) {
|
||||
struct VncState *vs = (struct 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) {
|
||||
struct VncState *vs = (struct VncState *)transport;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
ret = recv(vs->csock, data, len, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
|
||||
{
|
||||
gnutls_anon_server_credentials 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(struct 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 = qemu_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 = qemu_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;
|
||||
}
|
||||
|
||||
|
||||
int vnc_tls_client_setup(struct VncState *vs,
|
||||
int needX509Creds) {
|
||||
static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
|
||||
static const int protocol_priority[]= { 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};
|
||||
|
||||
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 (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
|
||||
gnutls_deinit(vs->tls.session);
|
||||
vs->tls.session = NULL;
|
||||
vnc_client_error(vs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
|
||||
gnutls_deinit(vs->tls.session);
|
||||
vs->tls.session = NULL;
|
||||
vnc_client_error(vs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 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 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(struct VncState *vs)
|
||||
{
|
||||
if (vs->tls.session) {
|
||||
gnutls_deinit(vs->tls.session);
|
||||
vs->tls.session = NULL;
|
||||
}
|
||||
vs->tls.wiremode = VNC_WIREMODE_CLEAR;
|
||||
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;
|
||||
|
||||
if (*cred) {
|
||||
qemu_free(*cred);
|
||||
*cred = NULL;
|
||||
}
|
||||
|
||||
*cred = qemu_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) {
|
||||
qemu_free(*cred);
|
||||
*cred = NULL;
|
||||
if (ignoreMissing && errno == ENOENT)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define X509_CA_CERT_FILE "ca-cert.pem"
|
||||
#define X509_CA_CRL_FILE "ca-crl.pem"
|
||||
#define X509_SERVER_KEY_FILE "server-key.pem"
|
||||
#define X509_SERVER_CERT_FILE "server-cert.pem"
|
||||
|
||||
|
||||
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:
|
||||
qemu_free(vd->tls.x509cacert);
|
||||
qemu_free(vd->tls.x509cacrl);
|
||||
qemu_free(vd->tls.x509cert);
|
||||
qemu_free(vd->tls.x509key);
|
||||
vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
76
vnc-tls.h
Normal file
76
vnc-tls.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "acl.h"
|
||||
|
||||
enum {
|
||||
VNC_WIREMODE_CLEAR,
|
||||
VNC_WIREMODE_TLS,
|
||||
};
|
||||
|
||||
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 {
|
||||
/* Whether data is being TLS encrypted yet */
|
||||
int wiremode;
|
||||
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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user