diff options
author | Christian Beier <dontmind@freeshell.org> | 2017-05-14 16:48:21 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-14 16:48:21 +0200 |
commit | 0fcd4a3b5270cfaa36049ba68aa1cae75063e064 (patch) | |
tree | 52bc9b0e413758973d39a33d77ba8f642261ae91 | |
parent | edd1acec7c01d668907cbeac003e8754c1b94c2d (diff) | |
parent | cc10eab7ebb6162c5621c458eb1037b9ec814800 (diff) | |
download | libtdevnc-0fcd4a3b5270cfaa36049ba68aa1cae75063e064.tar.gz libtdevnc-0fcd4a3b5270cfaa36049ba68aa1cae75063e064.zip |
Merge pull request #175 from simonwaterman/x509verify
Added support for X509 server certificate verification
-rw-r--r-- | libvncclient/tls_gnutls.c | 104 |
1 files changed, 100 insertions, 4 deletions
diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index b9ffe89..f49fa85 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -18,6 +18,7 @@ */ #include <gnutls/gnutls.h> +#include <gnutls/x509.h> #include <rfb/rfbclient.h> #include <errno.h> #ifdef WIN32 @@ -39,6 +40,98 @@ 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; + } + + 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 +145,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; } @@ -170,7 +263,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 +293,7 @@ HandshakeTLS(rfbClient* client) continue; } rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret)); + FreeTLS(client); return FALSE; } @@ -449,6 +543,10 @@ 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)); @@ -459,8 +557,6 @@ HandleVeNCryptAuth(rfbClient* client) if (!HandshakeTLS(client)) return FALSE; - /* TODO: validate certificate */ - /* We are done here. The caller should continue with client->subAuthScheme * to do actual sub authentication. */ |