* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, 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
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
#endif
#include "urldata.h"
-#define SSLGEN_C
-#include "sslgen.h" /* generic SSL protos etc */
-#include "ssluse.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 "vtls.h" /* generic SSL protos etc */
#include "slist.h"
#include "sendf.h"
#include "rawstr.h"
#include "progress.h"
#include "share.h"
#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>
unsigned int Curl_rand(struct SessionHandle *data)
{
- unsigned int r;
+ unsigned int r = 0;
static unsigned int randseed;
static bool seeded = FALSE;
-#ifndef have_curlssl_random
- (void)data;
-#else
- if(data) {
- Curl_ssl_random(data, (unsigned char *)&r, sizeof(r));
- return r;
+#ifdef CURLDEBUG
+ char *force_entropy = getenv("CURL_ENTROPY");
+ if(force_entropy) {
+ if(!seeded) {
+ size_t elen = strlen(force_entropy);
+ size_t clen = sizeof(randseed);
+ size_t min = elen < clen ? elen : clen;
+ memcpy((char *)&randseed, force_entropy, min);
+ seeded = TRUE;
+ }
+ else
+ randseed++;
+ return randseed;
}
#endif
+ /* data may be NULL! */
+ if(!Curl_ssl_random(data, (unsigned char *)&r, sizeof(r)))
+ return r;
+
+ /* If Curl_ssl_random() returns non-zero it couldn't offer randomness and we
+ instead perform a "best effort" */
+
#ifdef RANDOM_FILE
if(!seeded) {
/* if there's a random file to read a seed from, use it */
if(!seeded) {
struct timeval now = curlx_tvnow();
+ infof(data, "WARNING: Using weak random seed\n");
randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
randseed = randseed * 1103515245 + 12345;
randseed = randseed * 1103515245 + 12345;
return (r << 16) | ((r >> 16) & 0xFFFF);
}
+int Curl_ssl_backend(void)
+{
+ return (int)CURL_SSL_BACKEND;
+}
+
#ifdef USE_SSL
/* "global" init done? */
CURLcode
Curl_ssl_connect(struct connectdata *conn, int sockindex)
{
- CURLcode res;
+ CURLcode result;
/* 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;
/* 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;
{
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;
}
+/*
+ * 'value' is NOT a zero terminated string
+ */
CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data,
int certnum,
const char *label,
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 */
- /* Add an information record for a particular certificate. */
- output = curl_maprintf("%s:%.*s", label, valuelen, value);
+ output = malloc(outlen);
if(!output)
return CURLE_OUT_OF_MEMORY;
+ /* sprintf the label and colon */
+ snprintf(output, outlen, "%s:", label);
+
+ /* memcpy the value (it might not be zero terminated) */
+ memcpy(&output[labellen+1], value, valuelen);
+
+ /* zero terminate the output */
+ output[labellen + 1 + valuelen] = 0;
+
nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
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;
}
/*
return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
}
-/* these functions are only provided by some SSL backends */
-
-#ifdef have_curlssl_random
-void Curl_ssl_random(struct SessionHandle *data,
+int Curl_ssl_random(struct SessionHandle *data,
unsigned char *entropy,
size_t length)
{
- curlssl_random(data, entropy, length);
+ return curlssl_random(data, entropy, length);
+}
+
+/*
+ * 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;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+ if(!pubkey || !pubkeylen)
+ return result;
+ 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;
}
-#endif
-#ifdef have_curlssl_md5sum
void 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);
+ Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen));
+ Curl_MD5_final(MD5pw, md5sum);
#endif
+}
#endif /* USE_SSL */