From 6c312aaf5be2e1db3bb414d430ab9338d4efced6 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 20 Apr 2017 21:08:23 +0100 Subject: Added support for X509 server certificate verification as part of the handshake process. --- libvncclient/tls_gnutls.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index b9ffe89..4a798f4 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -170,7 +170,7 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS) static rfbBool SetTLSAnonCredential(rfbClient* client) { - gnutls_anon_client_credentials anonCred; + gnutls_anon_client_credentials_t anonCred; int ret; if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 || @@ -200,6 +200,21 @@ HandshakeTLS(rfbClient* client) continue; } rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret)); + if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { + gnutls_datum_t out; + unsigned status; + int type; + + type = gnutls_certificate_type_get((gnutls_session_t)client->tlsSession); + status = gnutls_session_get_verify_cert_status((gnutls_session_t)client->tlsSession); + + if (gnutls_certificate_verification_status_print(status, type, &out, 0)) + rfbClientLog("Certificate verification failed but could not determine reason"); + else { + rfbClientLog("Certificate verification failed: %s\n", out.data); + gnutls_free(out.data); + } + } FreeTLS(client); return FALSE; } @@ -212,6 +227,11 @@ HandshakeTLS(rfbClient* client) } rfbClientLog("TLS handshake done.\n"); + char *desc; + desc = gnutls_session_get_desc((gnutls_session_t)client->tlsSession); + rfbClientLog("Session info: %s\n", desc); + gnutls_free(desc); + return TRUE; } @@ -455,12 +475,11 @@ HandleVeNCryptAuth(rfbClient* client) FreeTLS(client); return FALSE; } + gnutls_session_set_verify_cert((gnutls_session_t)client->tlsSession, client->serverHost, 0); } if (!HandshakeTLS(client)) return FALSE; - /* TODO: validate certificate */ - /* We are done here. The caller should continue with client->subAuthScheme * to do actual sub authentication. */ -- cgit v1.2.1 From cc69ee96e113c8b86f132f6316f22faa3e9205ce Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Apr 2017 00:45:05 +0100 Subject: Modified certificate verification for compatibility with GnuTLS 2.12.23 --- libvncclient/tls_gnutls.c | 124 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 22 deletions(-) diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index 4a798f4..2fc65a5 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #ifdef WIN32 @@ -39,6 +40,101 @@ static gnutls_dh_params_t rfbDHParams; static rfbBool rfbTLSInitialized = FALSE; +static int +verify_certificate_callback (gnutls_session_t session) +{ + unsigned int status; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + int ret; + gnutls_x509_crt_t cert; + rfbClient *sptr; + char *hostname; + + sptr = (rfbClient *)gnutls_session_get_ptr(session); + if (!sptr) { + rfbClientLog("Failed to validate certificate - missing client data\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + hostname = sptr->serverHost; + if (!hostname) { + rfbClientLog("No server hostname found for client\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* This verification function uses the trusted CAs in the credentials + * structure. So you must have installed one or more CA certificates. + */ + ret = gnutls_certificate_verify_peers2 (session, &status); + if (ret < 0) + { + rfbClientLog ("Certificate validation call failed\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (status & GNUTLS_CERT_INVALID) + rfbClientLog("The certificate is not trusted.\n"); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + rfbClientLog("The certificate hasn't got a known issuer.\n"); + + if (status & GNUTLS_CERT_REVOKED) + rfbClientLog("The certificate has been revoked.\n"); + + if (status & GNUTLS_CERT_EXPIRED) + rfbClientLog("The certificate has expired\n"); + + if (status & GNUTLS_CERT_NOT_ACTIVATED) + rfbClientLog("The certificate is not yet activated\n"); + + if (status) + return GNUTLS_E_CERTIFICATE_ERROR; + + /* Up to here the process is the same for X.509 certificates and + * OpenPGP keys. From now on X.509 certificates are assumed. This can + * be easily extended to work with openpgp keys as well. + */ + if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) { + rfbClientLog("The certificate was not X509\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (gnutls_x509_crt_init (&cert) < 0) + { + rfbClientLog("Error initialising certificate structure\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + cert_list = gnutls_certificate_get_peers (session, &cert_list_size); + if (cert_list == NULL) + { + rfbClientLog("No certificate was found!\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* This is not a real world example, since we only check the first + * certificate in the given chain. + */ + if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) + { + rfbClientLog("Error parsing certificate\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (!gnutls_x509_crt_check_hostname (cert, hostname)) + { + rfbClientLog("The certificate's owner does not match hostname '%s'\n", + hostname); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + gnutls_x509_crt_deinit (cert); + + /* notify gnutls to continue handshake normally */ + return 0; +} + static rfbBool InitializeTLS(void) { @@ -52,7 +148,7 @@ InitializeTLS(void) rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret)); return FALSE; } - rfbClientLog("GnuTLS initialized.\n"); + rfbClientLog("GnuTLS version %s initialized.\n", gnutls_check_version(NULL)); rfbTLSInitialized = TRUE; return TRUE; } @@ -200,21 +296,7 @@ HandshakeTLS(rfbClient* client) continue; } rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret)); - if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { - gnutls_datum_t out; - unsigned status; - int type; - - type = gnutls_certificate_type_get((gnutls_session_t)client->tlsSession); - status = gnutls_session_get_verify_cert_status((gnutls_session_t)client->tlsSession); - - if (gnutls_certificate_verification_status_print(status, type, &out, 0)) - rfbClientLog("Certificate verification failed but could not determine reason"); - else { - rfbClientLog("Certificate verification failed: %s\n", out.data); - gnutls_free(out.data); - } - } + FreeTLS(client); return FALSE; } @@ -227,11 +309,6 @@ HandshakeTLS(rfbClient* client) } rfbClientLog("TLS handshake done.\n"); - char *desc; - desc = gnutls_session_get_desc((gnutls_session_t)client->tlsSession); - rfbClientLog("Session info: %s\n", desc); - gnutls_free(desc); - return TRUE; } @@ -469,13 +546,16 @@ HandleVeNCryptAuth(rfbClient* client) } else { + /* Set the certificate verification callback. */ + gnutls_certificate_set_verify_function (x509_cred, verify_certificate_callback); + gnutls_session_set_ptr ((gnutls_session_t)client->tlsSession, (void *)client); + if ((ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0) { rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret)); FreeTLS(client); return FALSE; } - gnutls_session_set_verify_cert((gnutls_session_t)client->tlsSession, client->serverHost, 0); } if (!HandshakeTLS(client)) return FALSE; -- cgit v1.2.1 From cc10eab7ebb6162c5621c458eb1037b9ec814800 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Apr 2017 01:53:37 +0100 Subject: Removed comment left over from development --- libvncclient/tls_gnutls.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index 2fc65a5..f49fa85 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -113,9 +113,6 @@ verify_certificate_callback (gnutls_session_t session) return GNUTLS_E_CERTIFICATE_ERROR; } - /* This is not a real world example, since we only check the first - * certificate in the given chain. - */ if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) { rfbClientLog("Error parsing certificate\n"); -- cgit v1.2.1