summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/vtls/vtls.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/vtls/vtls.c')
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.c355
1 files changed, 306 insertions, 49 deletions
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index 88511b8..01bbc61 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,7 +31,6 @@
Curl_ossl_ - prefix for OpenSSL ones
Curl_gtls_ - prefix for GnuTLS ones
Curl_nss_ - prefix for NSS ones
- Curl_qssl_ - prefix for QsoSSL ones
Curl_gskit_ - prefix for GSKit ones
Curl_polarssl_ - prefix for PolarSSL ones
Curl_cyassl_ - prefix for CyaSSL ones
@@ -60,29 +59,20 @@
#include "urldata.h"
#include "vtls.h" /* generic SSL protos etc */
-#include "openssl.h" /* OpenSSL versions */
-#include "gtls.h" /* GnuTLS versions */
-#include "nssg.h" /* NSS versions */
-#include "qssl.h" /* QSOSSL versions */
-#include "gskit.h" /* Global Secure ToolKit versions */
-#include "polarssl.h" /* PolarSSL versions */
-#include "axtls.h" /* axTLS versions */
-#include "cyassl.h" /* CyaSSL versions */
-#include "curl_schannel.h" /* Schannel SSPI version */
-#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */
#include "slist.h"
#include "sendf.h"
#include "rawstr.h"
#include "url.h"
-#include "curl_memory.h"
#include "progress.h"
#include "share.h"
#include "timeval.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "curl_base64.h"
+#include "curl_printf.h"
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
-/* The last #include file should be: */
+/* The last #include files should be: */
+#include "curl_memory.h"
#include "memdebug.h"
/* convenience macro to check if this handle is using a shared SSL session */
@@ -193,7 +183,7 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc)
unsigned int Curl_rand(struct SessionHandle *data)
{
- unsigned int r;
+ unsigned int r = 0;
static unsigned int randseed;
static bool seeded = FALSE;
@@ -286,47 +276,66 @@ void Curl_ssl_cleanup(void)
}
}
+static bool ssl_prefs_check(struct SessionHandle *data)
+{
+ /* check for CURLOPT_SSLVERSION invalid parameter value */
+ if((data->set.ssl.version < 0)
+ || (data->set.ssl.version >= CURL_SSLVERSION_LAST)) {
+ failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
+ return FALSE;
+ }
+ return TRUE;
+}
+
CURLcode
Curl_ssl_connect(struct connectdata *conn, int sockindex)
{
- CURLcode res;
+ CURLcode result;
+
+ if(!ssl_prefs_check(conn->data))
+ return CURLE_SSL_CONNECT_ERROR;
+
/* mark this is being ssl-enabled from here on. */
conn->ssl[sockindex].use = TRUE;
conn->ssl[sockindex].state = ssl_connection_negotiating;
- res = curlssl_connect(conn, sockindex);
+ result = curlssl_connect(conn, sockindex);
- if(!res)
+ if(!result)
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
- return res;
+ return result;
}
CURLcode
Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
bool *done)
{
- CURLcode res;
+ CURLcode result;
+
+ if(!ssl_prefs_check(conn->data))
+ return CURLE_SSL_CONNECT_ERROR;
+
/* mark this is being ssl requested from here on. */
conn->ssl[sockindex].use = TRUE;
#ifdef curlssl_connect_nonblocking
- res = curlssl_connect_nonblocking(conn, sockindex, done);
+ result = curlssl_connect_nonblocking(conn, sockindex, done);
#else
*done = TRUE; /* fallback to BLOCKING */
- res = curlssl_connect(conn, sockindex);
+ result = curlssl_connect(conn, sockindex);
#endif /* non-blocking connect support */
- if(!res && *done)
+ if(!result && *done)
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
- return res;
+ return result;
}
/*
* Check if there's a session ID for the given connection in the cache, and if
* there's one suitable, it is provided. Returns TRUE when no entry matched.
*/
-int Curl_ssl_getsessionid(struct connectdata *conn,
- void **ssl_sessionid,
- size_t *idsize) /* set 0 if unknown */
+bool Curl_ssl_getsessionid(struct connectdata *conn,
+ void **ssl_sessionid,
+ size_t *idsize) /* set 0 if unknown */
{
struct curl_ssl_session *check;
struct SessionHandle *data = conn->data;
@@ -473,9 +482,8 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
store->sessionid = ssl_sessionid;
store->idsize = idsize;
store->age = *general_age; /* set current age */
- if(store->name)
/* free it if there's one already present */
- free(store->name);
+ free(store->name);
store->name = clone_host; /* clone host name */
store->remote_port = conn->remote_port; /* port number */
@@ -601,34 +609,37 @@ void Curl_ssl_free_certinfo(struct SessionHandle *data)
{
int i;
struct curl_certinfo *ci = &data->info.certs;
+
if(ci->num_of_certs) {
/* free all individual lists used */
for(i=0; i<ci->num_of_certs; i++) {
curl_slist_free_all(ci->certinfo[i]);
ci->certinfo[i] = NULL;
}
+
free(ci->certinfo); /* free the actual array too */
ci->certinfo = NULL;
ci->num_of_certs = 0;
}
}
-int Curl_ssl_init_certinfo(struct SessionHandle * data,
- int num)
+CURLcode Curl_ssl_init_certinfo(struct SessionHandle *data, int num)
{
- struct curl_certinfo * ci = &data->info.certs;
- struct curl_slist * * table;
+ struct curl_certinfo *ci = &data->info.certs;
+ struct curl_slist **table;
- /* Initialize the certificate information structures. Return 0 if OK, else 1.
- */
+ /* Free any previous certificate information structures */
Curl_ssl_free_certinfo(data);
- ci->num_of_certs = num;
+
+ /* Allocate the required certificate information structures */
table = calloc((size_t) num, sizeof(struct curl_slist *));
if(!table)
- return 1;
+ return CURLE_OUT_OF_MEMORY;
+ ci->num_of_certs = num;
ci->certinfo = table;
- return 0;
+
+ return CURLE_OK;
}
/*
@@ -643,7 +654,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data,
struct curl_certinfo * ci = &data->info.certs;
char * output;
struct curl_slist * nl;
- CURLcode res = CURLE_OK;
+ CURLcode result = CURLE_OK;
size_t labellen = strlen(label);
size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
@@ -664,11 +675,11 @@ CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data,
if(!nl) {
free(output);
curl_slist_free_all(ci->certinfo[certnum]);
- res = CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
}
ci->certinfo[certnum] = nl;
- return res;
+ return result;
}
/*
@@ -692,14 +703,260 @@ int Curl_ssl_random(struct SessionHandle *data,
return curlssl_random(data, entropy, length);
}
-#ifdef have_curlssl_md5sum
-void Curl_ssl_md5sum(unsigned char *tmp, /* input */
- size_t tmplen,
- unsigned char *md5sum, /* output */
- size_t md5len)
+/*
+ * 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;
+ unsigned char *buf = NULL, *pem_ptr = NULL;
+ long filesize;
+ size_t size, pem_len;
+ CURLcode pem_read;
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+#ifdef curlssl_sha256sum
+ size_t pinkeylen;
+ char *pinkeycopy, *begin_pos, *end_pos;
+ unsigned char *sha256sumdigest = NULL, *expectedsha256sumdigest = NULL;
+#endif
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+ if(!pubkey || !pubkeylen)
+ return result;
+
+#ifdef curlssl_sha256sum
+ /* only do this if pinnedpubkey starts with "sha256//", length 8 */
+ if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
+ /* compute sha256sum of public key */
+ sha256sumdigest = malloc(SHA256_DIGEST_LENGTH);
+ if(!sha256sumdigest)
+ return CURLE_OUT_OF_MEMORY;
+ curlssl_sha256sum(pubkey, pubkeylen,
+ sha256sumdigest, SHA256_DIGEST_LENGTH);
+
+ /* it starts with sha256//, copy so we can modify it */
+ pinkeylen = strlen(pinnedpubkey) + 1;
+ pinkeycopy = malloc(pinkeylen);
+ if(!pinkeycopy) {
+ Curl_safefree(sha256sumdigest);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
+ /* point begin_pos to the copy, and start extracting keys */
+ begin_pos = pinkeycopy;
+ do {
+ end_pos = strstr(begin_pos, ";sha256//");
+ /*
+ * if there is an end_pos, null terminate,
+ * otherwise it'll go to the end of the original string
+ */
+ if(end_pos)
+ end_pos[0] = '\0';
+
+ /* decode base64 pinnedpubkey, 8 is length of "sha256//" */
+ pem_read = Curl_base64_decode(begin_pos + 8,
+ &expectedsha256sumdigest, &size);
+ /* if not valid base64, don't bother comparing or freeing */
+ if(!pem_read) {
+ /* compare sha256 digests directly */
+ if(SHA256_DIGEST_LENGTH == size &&
+ !memcmp(sha256sumdigest, expectedsha256sumdigest,
+ SHA256_DIGEST_LENGTH)) {
+ result = CURLE_OK;
+ Curl_safefree(expectedsha256sumdigest);
+ break;
+ }
+ Curl_safefree(expectedsha256sumdigest);
+ }
+
+ /*
+ * change back the null-terminator we changed earlier,
+ * and look for next begin
+ */
+ if(end_pos) {
+ end_pos[0] = ';';
+ begin_pos = strstr(end_pos, "sha256//");
+ }
+ } while(end_pos && begin_pos);
+ Curl_safefree(sha256sumdigest);
+ Curl_safefree(pinkeycopy);
+ return result;
+ }
+#endif
+
+ fp = fopen(pinnedpubkey, "rb");
+ if(!fp)
+ return result;
+
+ do {
+ /* Determine the file's size */
+ if(fseek(fp, 0, SEEK_END))
+ break;
+ 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 is bigger than the file
+ * size then it can't match
+ */
+ size = curlx_sotouz((curl_off_t) filesize);
+ if(pubkeylen > size)
+ break;
+
+ /*
+ * 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, 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;
+ }
+
+ /*
+ * 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;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len)
+{
+#ifdef curlssl_md5sum
curlssl_md5sum(tmp, tmplen, md5sum, md5len);
+#else
+ MD5_context *MD5pw;
+
+ (void) md5len;
+
+ MD5pw = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!MD5pw)
+ return CURLE_OUT_OF_MEMORY;
+ Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen));
+ Curl_MD5_final(MD5pw, md5sum);
+#endif
+ return CURLE_OK;
+}
+#endif
+
+/*
+ * Check whether the SSL backend supports the status_request extension.
+ */
+bool Curl_ssl_cert_status_request(void)
+{
+#ifdef curlssl_cert_status_request
+ return curlssl_cert_status_request();
+#else
+ return FALSE;
+#endif
}
+
+/*
+ * Check whether the SSL backend supports false start.
+ */
+bool Curl_ssl_false_start(void)
+{
+#ifdef curlssl_false_start
+ return curlssl_false_start();
+#else
+ return FALSE;
#endif
+}
#endif /* USE_SSL */