aboutsummaryrefslogtreecommitdiff
path: root/lib/vtls/vtls.c
diff options
context:
space:
mode:
authormoparisthebest <admin@moparisthebest.com>2014-11-24 19:30:09 +0100
committerPatrick Monnerat <pm@datasphere.ch>2014-11-24 19:30:09 +0100
commitbe1a5051890b7ad9339b0208424b94aa32c64776 (patch)
treed98e121223d1959808a8446e9000ecacaf70a1c8 /lib/vtls/vtls.c
parent2008c92513cee98ded80ed9ffaf984edc0c97c33 (diff)
SSL: Add PEM format support for public key pinning
Diffstat (limited to 'lib/vtls/vtls.c')
-rw-r--r--lib/vtls/vtls.c112
1 files changed, 100 insertions, 12 deletions
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index 1d1c62eba..960f76c17 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -69,6 +69,7 @@
#include "timeval.h"
#include "curl_md5.h"
#include "warnless.h"
+#include "curl_base64.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -684,15 +685,75 @@ int Curl_ssl_random(struct SessionHandle *data,
}
/*
+ * Public key pem to der conversion
+ */
+
+static CURLcode pubkey_pem_to_der(const char *pem,
+ unsigned char **der, size_t *der_len)
+{
+ char *stripped_pem, *begin_pos, *end_pos;
+ size_t pem_count, stripped_pem_count = 0, pem_len;
+ CURLcode result;
+
+ /* if no pem, exit. */
+ if(!pem)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
+ if(!begin_pos)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ pem_count = begin_pos - pem;
+ /* Invalid if not at beginning AND not directly following \n */
+ if(0 != pem_count && '\n' != pem[pem_count - 1])
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* 26 is length of "-----BEGIN PUBLIC KEY-----" */
+ pem_count += 26;
+
+ /* Invalid if not directly following \n */
+ end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----");
+ if(!end_pos)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ pem_len = end_pos - pem;
+
+ stripped_pem = malloc(pem_len - pem_count + 1);
+ if(!stripped_pem)
+ return CURLE_OUT_OF_MEMORY;
+
+ /*
+ * Here we loop through the pem array one character at a time between the
+ * correct indices, and place each character that is not '\n' or '\r'
+ * into the stripped_pem array, which should represent the raw base64 string
+ */
+ while(pem_count < pem_len) {
+ if('\n' != pem[pem_count] && '\r' != pem[pem_count])
+ stripped_pem[stripped_pem_count++] = pem[pem_count];
+ ++pem_count;
+ }
+ /* Place the null terminator in the correct place */
+ stripped_pem[stripped_pem_count] = '\0';
+
+ result = Curl_base64_decode(stripped_pem, der, der_len);
+
+ Curl_safefree(stripped_pem);
+
+ return result;
+}
+
+/*
* Generic pinned public key check.
*/
CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
const unsigned char *pubkey, size_t pubkeylen)
{
- FILE *fp = NULL;
- unsigned char *buf = NULL;
- long size = 0;
+ FILE *fp;
+ unsigned char *buf = NULL, *pem_ptr = NULL;
+ long filesize;
+ size_t size, pem_len;
+ CURLcode pem_read;
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
/* if a path wasn't specified, don't pin */
@@ -708,32 +769,59 @@ CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
/* Determine the file's size */
if(fseek(fp, 0, SEEK_END))
break;
- size = ftell(fp);
+ filesize = ftell(fp);
if(fseek(fp, 0, SEEK_SET))
break;
+ if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE)
+ break;
/*
- * 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 the size of our certificate is bigger than the file
+ * size then it can't match
*/
- if((long) pubkeylen != size)
+ size = curlx_sotouz((curl_off_t) filesize);
+ if(pubkeylen > size)
break;
- /* Allocate buffer for the pinned key. */
- buf = malloc(pubkeylen);
+ /*
+ * Allocate buffer for the pinned key
+ * With 1 additional byte for null terminator in case of PEM key
+ */
+ buf = malloc(size + 1);
if(!buf)
break;
/* Returns number of elements read, which should be 1 */
- if((int) fread(buf, pubkeylen, 1, fp) != 1)
+ if((int) fread(buf, size, 1, fp) != 1)
+ break;
+
+ /* If the sizes are the same, it can't be base64 encoded, must be der */
+ if(pubkeylen == size) {
+ if(!memcmp(pubkey, buf, pubkeylen))
+ result = CURLE_OK;
break;
+ }
- /* The one good exit point */
- if(!memcmp(pubkey, buf, pubkeylen))
+ /*
+ * Otherwise we will assume it's PEM and try to decode it
+ * after placing null terminator
+ */
+ buf[size] = '\0';
+ pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len);
+ /* if it wasn't read successfully, exit */
+ if(pem_read)
+ break;
+
+ /*
+ * if the size of our certificate doesn't match the size of
+ * the decoded file, they can't be the same, otherwise compare
+ */
+ if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
result = CURLE_OK;
} while(0);
Curl_safefree(buf);
+ Curl_safefree(pem_ptr);
fclose(fp);
return result;