aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoparisthebest <admin@moparisthebest.com>2014-10-01 02:14:49 -0400
committerDaniel Stenberg <daniel@haxx.se>2014-10-07 14:55:39 +0200
commite644866caf4cabaf766910316184bb1c84ee2f3c (patch)
treeca5c31a1eb8bc86ded3b528a4fa16d6a3232e52a
parent93e450793ce289925dfd1d5e3b2d14e781f8dfd4 (diff)
GnuTLS: Implement public key pinning
-rw-r--r--lib/vtls/gtls.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index d64f95dcb..8364f4458 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -32,6 +32,7 @@
#ifdef USE_GNUTLS
+#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
@@ -677,6 +678,102 @@ gtls_connect_step1(struct connectdata *conn,
return CURLE_OK;
}
+static int pkp_pin_peer_pubkey(gnutls_x509_crt_t cert, char *pinnedpubkey)
+{
+ /* Scratch */
+ FILE* fp = NULL;
+ size_t len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL, *buff2 = NULL;
+ long size = 0;
+
+ gnutls_pubkey_t key = NULL;
+
+ /* Result is returned to caller */
+ int ret = 0, result = FALSE;
+
+ /* if a path wasn't specified, don't pin */
+ if(NULL == pinnedpubkey) return TRUE;
+ if(NULL == cert) return FALSE;
+
+ do {
+ /* Begin Gyrations to get the public key */
+ gnutls_pubkey_init(&key);
+
+ ret = gnutls_pubkey_import_x509(key, cert, 0);
+ if(ret < 0)
+ break; /* failed */
+
+ ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1);
+ if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0)
+ break; /* failed */
+
+ buff1 = malloc(len1);
+ if(NULL == buff1)
+ break; /* failed */
+
+ len2 = len1;
+
+ ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2);
+ if(ret < 0 || len1 != len2)
+ break; /* failed */
+
+ /* End Gyrations */
+
+ fp = fopen(pinnedpubkey, "r");
+
+ if(NULL == fp)
+ break; /* failed */
+
+ /* Seek to eof to determine the file's size */
+ ret = fseek(fp, 0, SEEK_END);
+ if(0 != ret)
+ break; /* failed */
+
+ /* Fetch the file's size */
+ size = ftell(fp);
+
+ /*
+ * if the size of our certificate doesn't match the size of
+ * the file, they can't be the same, don't bother reading it
+ */
+ if(size > 0 && len2 != (size_t)size)
+ break; /* failed */
+
+ /* Rewind to beginning to perform the read */
+ ret = fseek(fp, 0, SEEK_SET);
+ if(0 != ret)
+ break; /* failed */
+
+ /* http://www.openssl.org/docs/crypto/buffer.html */
+ buff2 = malloc(len2);
+ if(NULL == buff2)
+ break; /* failed */
+
+ /* Returns number of elements read, which should be 1 */
+ ret = (int)fread(buff2, (size_t)len2, 1, fp);
+ if(1 != ret)
+ break; /* failed */
+
+ /* The one good exit point */
+ result = (0 == memcmp(buff1, buff2, (size_t)len2));
+
+ } while(0);
+
+ if(NULL != fp)
+ fclose(fp);
+
+ if(NULL != key)
+ gnutls_pubkey_deinit(key);
+
+ if(NULL != buff2)
+ free(buff2);
+
+ if(NULL != buff1)
+ free(buff1);
+
+ return result;
+}
+
static Curl_recv gtls_recv;
static Curl_send gtls_send;
@@ -909,6 +1006,13 @@ gtls_connect_step3(struct connectdata *conn,
infof(data, "\t server certificate activation date OK\n");
}
+ if(data->set.str[STRING_SSL_PINNEDPUBLICKEY] != NULL &&
+ TRUE != pkp_pin_peer_pubkey(x509_cert,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY])) {
+ failf(data, "SSL: public key does not matched pinned public key!");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
/* Show:
- ciphers used