This patch adds support for using x509 certificates on the server end. The server needs a CA certificate, and its own certificate and private key. A CA revocation list is optional. This this patch the file names are hardcoded. The next-but-one patch will make them configurable.
The use of x509 certificates is controlled by the 'x509' flag to the VNC arg, eg '-vnc :1,tls,x509'. This only provides encryption of the session, no authentication. diff -r b5430bd9f883 vnc.c --- a/vnc.c Tue Jul 31 11:32:19 2007 -0400 +++ b/vnc.c Tue Jul 31 11:43:59 2007 -0400 @@ -104,6 +104,14 @@ enum { VNC_AUTH_VENCRYPT_X509VNC = 261, VNC_AUTH_VENCRYPT_X509PLAIN = 262, }; + +#if CONFIG_VNC_TLS +#define CA_FILE "ca-cert.pem" +#define CRL_FILE "ca-crl.pem" +#define KEY_FILE "key.pem" +#define CERT_FILE "cert.pem" +#endif + #endif /* CONFIG_VNC_TLS */ struct VncState @@ -1369,16 +1377,59 @@ static gnutls_anon_server_credentials vn } +static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(void) +{ + gnutls_certificate_credentials_t x509_cred; + int ret; + struct stat st; + + 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, CA_FILE, 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, CERT_FILE, KEY_FILE, + 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 (stat(CRL_FILE, &st) < 0) { + if (errno != ENOENT) { + gnutls_certificate_free_credentials(x509_cred); + return NULL; + } + } else { + if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, CRL_FILE, 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; +} + static int start_auth_vencrypt_subauth(VncState *vs) { switch (vs->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 */ vnc_read_when(vs, protocol_client_init, 1); break; case VNC_AUTH_VENCRYPT_TLSVNC: + case VNC_AUTH_VENCRYPT_X509VNC: VNC_DEBUG("Start TLS auth VNC\n"); return start_auth_vnc(vs); @@ -1429,11 +1480,17 @@ static void vnc_handshake_io(void *opaqu vnc_continue_handshake(vs); } +#define NEED_X509_AUTH(vs) \ + ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN) + + static int vnc_start_tls(struct VncState *vs) { 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}; - gnutls_anon_server_credentials anon_cred = NULL; + 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) { @@ -1454,7 +1511,7 @@ static int vnc_start_tls(struct VncState return -1; } - if (gnutls_kx_set_priority(vs->tls_session, kx_anon) < 0) { + if (gnutls_kx_set_priority(vs->tls_session, NEED_X509_AUTH(vs) ? kx_x509 : kx_anon) < 0) { gnutls_deinit(vs->tls_session); vs->tls_session = NULL; vnc_client_error(vs); @@ -1475,19 +1532,36 @@ static int vnc_start_tls(struct VncState return -1; } - 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; + if (NEED_X509_AUTH(vs)) { + gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(); + 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; + } + } 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); @@ -1754,7 +1828,7 @@ int vnc_display_open(DisplayState *ds, c VncState *vs = ds ? (VncState *)ds->opaque : vnc_state; #if CONFIG_VNC_TLS const char *options; - int tls = 0; + int tls = 0, x509 = 0; #endif vnc_display_close(ds); @@ -1770,6 +1844,8 @@ int vnc_display_open(DisplayState *ds, c options++; if (strncmp(options, "tls", 3) == 0) tls = 1; /* Require TLS */ + else if (strncmp(options, "x509", 4) == 0) + x509 = 1; /* Require x509 certificates */ } #endif @@ -1781,9 +1857,14 @@ int vnc_display_open(DisplayState *ds, c } #if CONFIG_VNC_TLS if (tls) { - VNC_DEBUG("Initializing VNC server with TLS password auth\n"); vs->auth = VNC_AUTH_VENCRYPT; - vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; + if (x509) { + VNC_DEBUG("Initializing VNC server with x509 password auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; + } else { + VNC_DEBUG("Initializing VNC server with TLS password auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; + } } else { #endif VNC_DEBUG("Initializing VNC server with password auth\n"); @@ -1795,9 +1876,14 @@ int vnc_display_open(DisplayState *ds, c } else { #if CONFIG_VNC_TLS if (tls) { - VNC_DEBUG("Initializing VNC server with TLS no auth\n"); vs->auth = VNC_AUTH_VENCRYPT; - vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; + if (x509) { + VNC_DEBUG("Initializing VNC server with x509 no auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; + } else { + VNC_DEBUG("Initializing VNC server with TLS no auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; + } } else { #endif VNC_DEBUG("Initializing VNC server with no auth\n"); -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|