This patch adds support for requesting and validating client certificates. In effect the client certificates are being used as the authentication mechansim, making VNC passwords unccessary, though using a combination of both is also possible.
The verification of client certificates is enabled using the 'x509verify' flag to VNC arg eg '-vnc :1,tls,x509verify' diff -r f8cd8bd742ee vnc.c --- a/vnc.c Tue Jul 31 11:45:22 2007 -0400 +++ b/vnc.c Tue Jul 31 11:45:30 2007 -0400 @@ -37,7 +37,7 @@ #include <gnutls/x509.h> #endif /* CONFIG_VNC_TLS */ -// #define _VNC_DEBUG 1 + #define _VNC_DEBUG 1 #if _VNC_DEBUG #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) @@ -141,6 +141,7 @@ struct VncState int auth; #if CONFIG_VNC_TLS int subauth; + int x509verify; #endif char challenge[VNC_AUTH_CHALLENGE_SIZE]; @@ -1418,6 +1419,85 @@ static gnutls_certificate_credentials_t return x509_cred; } +static int vnc_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; + } + + gnutls_x509_crt_deinit (cert); + } + + return 0; +} + + static int start_auth_vencrypt_subauth(VncState *vs) { switch (vs->subauth) { @@ -1464,6 +1544,16 @@ static int vnc_continue_handshake(struct VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); vnc_client_error(vs); return -1; + } + + if (vs->x509verify) { + if (vnc_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"); @@ -1547,6 +1637,11 @@ static int vnc_start_tls(struct VncState vnc_client_error(vs); return -1; } + if (vs->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) { @@ -1808,6 +1903,7 @@ void vnc_display_close(DisplayState *ds) vs->auth = VNC_AUTH_INVALID; #if CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; + vs->x509verify = 0; #endif if (vs->lsock != -1) { close(vs->lsock); @@ -1842,10 +1938,14 @@ int vnc_display_open(DisplayState *ds, c options = display; while ((options = strchr(options, ','))) { options++; - if (strncmp(options, "tls", 3) == 0) + if (strncmp(options, "tls", 3) == 0) { tls = 1; /* Require TLS */ - else if (strncmp(options, "x509", 4) == 0) + } else if (strncmp(options, "x509verify", 10) == 0) { + x509 = 1; + vs->x509verify = 1; /* Require x509 certificates & verify client */ + } else if (strncmp(options, "x509", 4) == 0) { x509 = 1; /* Require x509 certificates */ + } } #endif -- |=- 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 -=|