* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * 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
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/md5.h>
+#include <openssl/conf.h>
+#include <openssl/bn.h>
#else
#include <rand.h>
#include <x509v3.h>
#define HAVE_ERR_REMOVE_THREAD_STATE 1
#endif
-#ifndef HAVE_SSLV2_CLIENT_METHOD
+#if !defined(HAVE_SSLV2_CLIENT_METHOD) || \
+ OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0+ has no SSLv2 */
#undef OPENSSL_NO_SSL2 /* undef first to avoid compiler warnings */
#define OPENSSL_NO_SSL2
#endif
return nread;
}
-int Curl_ossl_seed(struct SessionHandle *data)
+static int Curl_ossl_seed(struct SessionHandle *data)
{
/* we have the "SSL is seeded" boolean static to prevent multiple
time-consuming seedings in vain */
case UIT_PROMPT:
case UIT_VERIFY:
password = (const char*)UI_get0_user_data(ui);
- if(NULL != password &&
- UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD) {
+ if(password && (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
UI_set_result(ui, uis, password);
return 1;
}
switch(UI_get_string_type(uis)) {
case UIT_PROMPT:
case UIT_VERIFY:
- if(NULL != UI_get0_user_data(ui) &&
- UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD) {
+ if(UI_get0_user_data(ui) &&
+ (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
return 1;
}
default:
int file_type = do_file_type(cert_type);
- if(cert_file != NULL || file_type == SSL_FILETYPE_ENGINE) {
+ if(cert_file || (file_type == SSL_FILETYPE_ENGINE)) {
SSL *ssl;
X509 *x509;
int cert_done = 0;
if(!cert_done)
return 0; /* failure! */
+ break;
#else
failf(data, "file type P12 for certificate not supported");
return 0;
case SSL_FILETYPE_PEM:
if(cert_done)
break;
- if(key_file == NULL)
+ if(!key_file)
/* cert & key can only be in PEM case in the same file */
key_file=cert_file;
case SSL_FILETYPE_ASN1:
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
UI_METHOD *ui_method =
UI_create_method((char *)"cURL user interface");
- if(NULL == ui_method) {
+ if(!ui_method) {
failf(data, "unable do create OpenSSL user-interface method");
return 0;
}
}
ssl=SSL_new(ctx);
- if(NULL == ssl) {
+ if(!ssl) {
failf(data,"unable to create an SSL structure");
return 0;
}
/* This version was provided by Evan Jordan and is supposed to not
leak memory as the previous version: */
- if(x509 != NULL) {
+ if(x509) {
EVP_PKEY *pktmp = X509_get_pubkey(x509);
EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl));
EVP_PKEY_free(pktmp);
OpenSSL_add_all_algorithms();
+
+ /* OPENSSL_config(NULL); is "strongly recommended" to use but unfortunately
+ that function makes an exit() call on wrongly formatted config files
+ which makes it hard to use in some situations. OPENSSL_config() itself
+ calls CONF_modules_load_file() and we use that instead and we ignore
+ its return code! */
+
+ /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and
+ 0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+ (void)CONF_modules_load_file(NULL, NULL,
+ CONF_MFLAGS_DEFAULT_SECTION|
+ CONF_MFLAGS_IGNORE_MISSING_FILE);
+
return 1;
}
* This function is called when the 'data' struct is going away. Close
* down everything and free all resources!
*/
-int Curl_ossl_close_all(struct SessionHandle *data)
+void Curl_ossl_close_all(struct SessionHandle *data)
{
#ifdef HAVE_OPENSSL_ENGINE_H
if(data->state.engine) {
#else
(void)data;
#endif
- return 0;
}
static int asn1_output(const ASN1_UTCTIME *tm,
in the certificate and must exactly match the IP in the URI.
*/
-static CURLcode verifyhost(struct connectdata *conn,
- X509 *server_cert)
+static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
{
int matched = -1; /* -1 is no alternative match yet, 1 means match and 0
means mismatch */
#else
struct in_addr addr;
#endif
- CURLcode res = CURLE_OK;
+ CURLcode result = CURLE_OK;
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname);
failf(data, "SSL: no alternative certificate subject name matches "
"target host name '%s'", conn->host.dispname);
- res = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
/* we have to look to the last occurrence of a commonName in the
/* there was a terminating zero before the end of string, this
cannot match and we return failure! */
failf(data, "SSL: illegal cert name field");
- res = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
}
}
}
}
- if(res)
+ if(result)
/* error already detected, pass through */
;
else if(!peer_CN) {
failf(data,
"SSL: unable to obtain common name from peer certificate");
- res = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
else if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", peer_CN, conn->host.dispname);
- res = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
infof(data, "\t common name: %s (matched)\n", peer_CN);
if(peer_CN)
OPENSSL_free(peer_CN);
}
- return res;
+
+ return result;
}
#endif /* USE_SSLEAY */
static const char *ssl_msg_type(int ssl_ver, int msg)
{
+#ifdef SSL2_VERSION_MAJOR
if(ssl_ver == SSL2_VERSION_MAJOR) {
switch (msg) {
case SSL2_MT_ERROR:
return "Client CERT";
}
}
- else if(ssl_ver == SSL3_VERSION_MAJOR) {
+ else
+#endif
+ if(ssl_ver == SSL3_VERSION_MAJOR) {
switch (msg) {
case SSL3_MT_HELLO_REQUEST:
return "Hello request";
struct SessionHandle *data;
const char *msg_name, *tls_rt_name;
char ssl_buf[1024];
- int ver, msg_type, txt_len;
+ char unknown[32];
+ int msg_type, txt_len;
+ const char *verstr;
if(!conn || !conn->data || !conn->data->set.fdebug ||
(direction != 0 && direction != 1))
return;
data = conn->data;
- ssl_ver >>= 8;
- ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
- ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
+
+ switch(ssl_ver) {
+#ifdef SSL2_VERSION_MAJOR /* removed in recent versions */
+ case SSL2_VERSION_MAJOR:
+ verstr = "SSLv2";
+ break;
+#endif
+#ifdef SSL3_VERSION
+ case SSL3_VERSION:
+ verstr = "SSLv3";
+ break;
+#endif
+ case TLS1_VERSION:
+ verstr = "TLSv1.0";
+ break;
+#ifdef TLS1_1_VERSION
+ case TLS1_1_VERSION:
+ verstr = "TLSv1.1";
+ break;
+#endif
+#ifdef TLS1_2_VERSION
+ case TLS1_2_VERSION:
+ verstr = "TLSv1.2";
+ break;
+#endif
+ default:
+ snprintf(unknown, sizeof(unknown), "(%x)", ssl_ver);
+ verstr = unknown;
+ break;
+ }
+
+ ssl_ver >>= 8; /* check the upper 8 bits only below */
/* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
* always pass-up content-type as 0. But the interesting message-type
msg_type = *(char*)buf;
msg_name = ssl_msg_type(ssl_ver, msg_type);
- txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
- ver, tls_rt_name, msg_name, msg_type);
+ txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "%s, %s%s (%d):\n",
+ verstr, tls_rt_name, msg_name, msg_type);
Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
# define use_sni(x) Curl_nop_stmt
#endif
-static CURLcode
-ossl_connect_step1(struct connectdata *conn,
- int sockindex)
+#ifdef USE_NGHTTP2
+
+/* Check for OpenSSL 1.0.2 which has ALPN support. */
+#undef HAS_ALPN
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
+ && !defined(OPENSSL_NO_TLSEXT)
+# define HAS_ALPN 1
+#endif
+
+/* Check for OpenSSL 1.0.1 which has NPN support. */
+#undef HAS_NPN
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L \
+ && !defined(OPENSSL_NO_TLSEXT) \
+ && !defined(OPENSSL_NO_NEXTPROTONEG)
+# define HAS_NPN 1
+#endif
+
+#ifdef HAS_NPN
+
+/*
+ * in is a list of lenght prefixed strings. this function has to select
+ * the protocol we want to use from the list and write its string into out.
+ */
+static int
+select_next_proto_cb(SSL *ssl,
+ unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ void *arg)
{
- CURLcode retcode = CURLE_OK;
+ struct connectdata *conn = (struct connectdata*) arg;
+ int retval = nghttp2_select_next_protocol(out, outlen, in, inlen);
+ (void)ssl;
+
+ if(retval == 1) {
+ infof(conn->data, "NPN, negotiated HTTP2 (%s)\n",
+ NGHTTP2_PROTO_VERSION_ID);
+ conn->negnpn = NPN_HTTP2;
+ }
+ else if(retval == 0) {
+ infof(conn->data, "NPN, negotiated HTTP1.1\n");
+ conn->negnpn = NPN_HTTP1_1;
+ }
+ else {
+ infof(conn->data, "NPN, no overlap, use HTTP1.1\n",
+ NGHTTP2_PROTO_VERSION_ID);
+ *out = (unsigned char*)"http/1.1";
+ *outlen = sizeof("http/1.1") - 1;
+ conn->negnpn = NPN_HTTP1_1;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif /* HAS_NPN */
+
+#endif /* USE_NGHTTP2 */
+
+static const char *
+get_ssl_version_txt(SSL_SESSION *session)
+{
+ if(!session)
+ return "";
+
+ switch(session->ssl_version) {
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ case TLS1_2_VERSION:
+ return "TLSv1.2";
+ case TLS1_1_VERSION:
+ return "TLSv1.1";
+#endif
+ case TLS1_VERSION:
+ return "TLSv1.0";
+ case SSL3_VERSION:
+ return "SSLv3";
+ case SSL2_VERSION:
+ return "SSLv2";
+ }
+ return "unknown";
+}
+
+static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
+{
+ CURLcode result = CURLE_OK;
+ char *ciphers;
struct SessionHandle *data = conn->data;
- SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
- void *ssl_sessionid=NULL;
- X509_LOOKUP *lookup=NULL;
+ SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
+ void *ssl_sessionid = NULL;
+ X509_LOOKUP *lookup = NULL;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
long ctx_options;
struct in_addr addr;
#endif
#endif
+#ifdef HAS_ALPN
+ unsigned char protocols[128];
+#endif
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
/* Make funny stuff to get random input */
Curl_ossl_seed(data);
+ data->set.ssl.certverifyresult = !X509_V_OK;
+
/* check to see if we've been told to use an explicit SSL/TLS version */
switch(data->set.ssl.version) {
#endif
switch(data->set.ssl.version) {
- case CURL_SSLVERSION_DEFAULT:
- ctx_options |= SSL_OP_NO_SSLv2;
+ case CURL_SSLVERSION_SSLv3:
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
infof(data, "Set version TLSv1.x for SRP authorisation\n");
- ctx_options |= SSL_OP_NO_SSLv3;
}
#endif
+ ctx_options |= SSL_OP_NO_SSLv2;
+ ctx_options |= SSL_OP_NO_TLSv1;
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ ctx_options |= SSL_OP_NO_TLSv1_1;
+ ctx_options |= SSL_OP_NO_TLSv1_2;
+#endif
break;
+ case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
ctx_options |= SSL_OP_NO_SSLv2;
ctx_options |= SSL_OP_NO_SSLv3;
break;
#endif
+#ifndef OPENSSL_NO_SSL2
+ case CURL_SSLVERSION_SSLv2:
+ ctx_options |= SSL_OP_NO_SSLv3;
+ ctx_options |= SSL_OP_NO_TLSv1;
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ ctx_options |= SSL_OP_NO_TLSv1_1;
+ ctx_options |= SSL_OP_NO_TLSv1_2;
+#endif
+ break;
+#endif
+
default:
failf(data, "Unsupported SSL protocol version");
return CURLE_SSL_CONNECT_ERROR;
SSL_CTX_set_options(connssl->ctx, ctx_options);
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+#ifdef HAS_NPN
+ if(data->set.ssl_enable_npn) {
+ SSL_CTX_set_next_proto_select_cb(connssl->ctx, select_next_proto_cb,
+ conn);
+ }
+#endif
+
+#ifdef HAS_ALPN
+ if(data->set.ssl_enable_alpn) {
+ protocols[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
+ memcpy(&protocols[1], NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN);
+
+ protocols[NGHTTP2_PROTO_VERSION_ID_LEN+1] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&protocols[NGHTTP2_PROTO_VERSION_ID_LEN+2], ALPN_HTTP_1_1,
+ ALPN_HTTP_1_1_LENGTH);
+
+ /* expects length prefixed preference ordered list of protocols in wire
+ * format
+ */
+ SSL_CTX_set_alpn_protos(connssl->ctx, protocols,
+ NGHTTP2_PROTO_VERSION_ID_LEN + ALPN_HTTP_1_1_LENGTH + 2);
+
+ infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID,
+ ALPN_HTTP_1_1);
+ connssl->asked_for_h2 = TRUE;
+ }
+#endif
+ }
+#endif
+
if(data->set.str[STRING_CERT] || data->set.str[STRING_CERT_TYPE]) {
if(!cert_stuff(conn,
connssl->ctx,
}
}
- if(data->set.str[STRING_SSL_CIPHER_LIST]) {
- if(!SSL_CTX_set_cipher_list(connssl->ctx,
- data->set.str[STRING_SSL_CIPHER_LIST])) {
- failf(data, "failed setting cipher list");
- return CURLE_SSL_CIPHER;
- }
+ ciphers = data->set.str[STRING_SSL_CIPHER_LIST];
+ if(!ciphers)
+ ciphers = (char *)DEFAULT_CIPHER_SELECTION;
+ if(!SSL_CTX_set_cipher_list(connssl->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ return CURLE_SSL_CIPHER;
}
#ifdef USE_TLS_SRP
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
- retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
- data->set.ssl.fsslctxp);
- if(retcode) {
+ result = (*data->set.ssl.fsslctx)(data, connssl->ctx,
+ data->set.ssl.fsslctxp);
+ if(result) {
failf(data,"error signaled by ssl ctx callback");
- return retcode;
+ return result;
}
}
}
connssl->connecting_state = ssl_connect_2;
+
return CURLE_OK;
}
-static CURLcode
-ossl_connect_step2(struct connectdata *conn, int sockindex)
+static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
{
struct SessionHandle *data = conn->data;
int err;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
unsigned long errdetail;
char error_buffer[256]; /* OpenSSL documents that this must be at least
256 bytes long. */
- CURLcode rc;
+ CURLcode result;
const char *cert_problem = NULL;
long lerr;
SSL routines:
SSL3_GET_SERVER_CERTIFICATE:
certificate verify failed */
- rc = CURLE_SSL_CACERT;
+ result = CURLE_SSL_CACERT;
lerr = SSL_get_verify_result(connssl->handle);
if(lerr != X509_V_OK) {
break;
default:
- rc = CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
break;
}
* (RST connection etc.), OpenSSL gives no explanation whatsoever and
* the SO_ERROR is also lost.
*/
- if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
+ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
failf(data, "Unknown SSL protocol error in connection to %s:%ld ",
conn->host.name, conn->remote_port);
- return rc;
+ return result;
}
- /* Could be a CERT problem */
+ /* Could be a CERT problem */
failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
- return rc;
+
+ return result;
}
}
else {
connssl->connecting_state = ssl_connect_3;
/* Informational message */
- infof (data, "SSL connection using %s\n",
- SSL_get_cipher(connssl->handle));
+ infof(data, "SSL connection using %s / %s\n",
+ get_ssl_version_txt(SSL_get_session(connssl->handle)),
+ SSL_get_cipher(connssl->handle));
+
+#ifdef HAS_ALPN
+ /* Sets data and len to negotiated protocol, len is 0 if no protocol was
+ * negotiated
+ */
+ if(data->set.ssl_enable_alpn) {
+ const unsigned char* neg_protocol;
+ unsigned int len;
+ SSL_get0_alpn_selected(connssl->handle, &neg_protocol, &len);
+ if(len != 0) {
+ infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol);
+
+ if(len == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len) == 0) {
+ conn->negnpn = NPN_HTTP2;
+ }
+ else if(len ==
+ ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1,
+ neg_protocol,
+ ALPN_HTTP_1_1_LENGTH) == 0) {
+ conn->negnpn = NPN_HTTP1_1;
+ }
+ }
+ else if(connssl->asked_for_h2)
+ infof(data, "ALPN, server did not agree to a protocol\n");
+ }
+#endif
return CURLE_OK;
}
#define print_pubkey_BN(_type, _name, _num) \
do { \
- if(pubkey->pkey._type->_name != NULL) { \
+ if(pubkey->pkey._type->_name) { \
int len = BN_num_bytes(pubkey->pkey._type->_name); \
if(len < CERTBUFFERSIZE) { \
BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)bufp); \
sep=", ";
j++; /* skip the newline */
};
- while((biomem->data[j] == ' ') && (j<(size_t)biomem->length))
+ while((j<(size_t)biomem->length) && (biomem->data[j] == ' '))
j++;
if(j<(size_t)biomem->length)
ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
char buf[1024];
char *ptr = buf;
int i;
+
for(i=0; i<sig->length; i++)
ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]);
BIO_get_mem_ptr(bio_out, &biomem);
- infof(data, "%s\n", biomem->data);
-
Curl_ssl_push_certinfo_len(data, numcert,
"Cert", biomem->data, biomem->length);
BIO_free(bio_out);
-
}
/*
struct ssl_connect_data *connssl)
{
+ CURLcode result;
STACK_OF(X509) *sk;
int i;
char *bufp;
}
numcerts = sk_X509_num(sk);
- if(Curl_ssl_init_certinfo(data, numcerts)) {
+
+ result = Curl_ssl_init_certinfo(data, numcerts);
+ if(result) {
free(bufp);
- return CURLE_OUT_OF_MEMORY;
+ return result;
}
infof(data, "--- Certificate chain\n");
}
/*
+ * Heavily modified from:
+ * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
+ */
+static CURLcode pkp_pin_peer_pubkey(X509* cert, const char *pinnedpubkey)
+{
+ /* Scratch */
+ int len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL, *temp = NULL;
+
+ /* Result is returned to caller */
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+
+ if(!cert)
+ return result;
+
+ do {
+ /* Begin Gyrations to get the subjectPublicKeyInfo */
+ /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */
+
+ /* http://groups.google.com/group/mailing.openssl.users/browse_thread
+ /thread/d61858dae102c6c7 */
+ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL);
+ if(len1 < 1)
+ break; /* failed */
+
+ /* http://www.openssl.org/docs/crypto/buffer.html */
+ buff1 = temp = OPENSSL_malloc(len1);
+ if(!buff1)
+ break; /* failed */
+
+ /* http://www.openssl.org/docs/crypto/d2i_X509.html */
+ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp);
+
+ /*
+ * These checks are verifying we got back the same values as when we
+ * sized the buffer.Its pretty weak since they should always be the
+ * same. But it gives us something to test.
+ */
+ if((len1 != len2) || !temp || ((temp - buff1) != len1))
+ break; /* failed */
+
+ /* End Gyrations */
+
+ /* The one good exit point */
+ result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1);
+ } while(0);
+
+ /* http://www.openssl.org/docs/crypto/buffer.html */
+ if(buff1)
+ OPENSSL_free(buff1);
+
+ return result;
+}
+
+/*
* Get the server cert, verify it and show it etc, only call failf() if the
* 'strict' argument is TRUE as otherwise all this is for informational
* purposes only!
struct ssl_connect_data *connssl,
bool strict)
{
- CURLcode retcode = CURLE_OK;
+ CURLcode result = CURLE_OK;
int rc;
long lerr;
ASN1_TIME *certdate;
X509 *issuer;
FILE *fp;
char *buffer = data->state.buffer;
+ const char *ptr;
if(data->set.ssl.certinfo)
/* we've been asked to gather certificate info! */
(void)get_cert_chain(conn, connssl);
- data->set.ssl.certverifyresult = !X509_V_OK;
-
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
if(strict)
failf(data, "SSL: couldn't get peer certificate!");
return CURLE_PEER_FAILED_VERIFICATION;
}
- infof (data, "Server certificate:\n");
+
+ infof(data, "Server certificate:\n");
rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
buffer, BUFSIZE);
infof(data, "\t expire date: %s\n", buffer);
if(data->set.ssl.verifyhost) {
- retcode = verifyhost(conn, connssl->server_cert);
- if(retcode) {
+ result = verifyhost(conn, connssl->server_cert);
+ if(result) {
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
- return retcode;
+ return result;
}
}
if(rc) {
if(strict)
failf(data, "SSL: couldn't get X509-issuer name!");
- retcode = CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
}
else {
infof(data, "\t issuer: %s\n", buffer);
/* e.g. match issuer name with provided issuer certificate */
if(data->set.str[STRING_SSL_ISSUERCERT]) {
- fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r");
+ fp = fopen(data->set.str[STRING_SSL_ISSUERCERT], "r");
if(!fp) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
- issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
+
+ issuer = PEM_read_X509(fp, NULL, ZERO_NULL, NULL);
if(!issuer) {
if(strict)
failf(data, "SSL: Unable to read issuer cert (%s)",
fclose(fp);
return CURLE_SSL_ISSUER_ERROR;
}
+
fclose(fp);
+
if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
if(strict)
failf(data, "SSL: Certificate issuer check failed (%s)",
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
+
infof(data, "\t SSL certificate issuer check ok (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(issuer);
}
- lerr = data->set.ssl.certverifyresult=
+ lerr = data->set.ssl.certverifyresult =
SSL_get_verify_result(connssl->handle);
+
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
/* We probably never reach this, because SSL_connect() will fail
if(strict)
failf(data, "SSL certificate verify result: %s (%ld)",
X509_verify_cert_error_string(lerr), lerr);
- retcode = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t SSL certificate verify result: %s (%ld),"
infof(data, "\t SSL certificate verify ok.\n");
}
+ ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ if(!result && ptr) {
+ result = pkp_pin_peer_pubkey(connssl->server_cert, ptr);
+ if(result)
+ failf(data, "SSL: public key does not match pinned public key!");
+ }
+
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
connssl->connecting_state = ssl_connect_done;
- return retcode;
+ return result;
}
-
-static CURLcode
-ossl_connect_step3(struct connectdata *conn,
- int sockindex)
+static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex)
{
- CURLcode retcode = CURLE_OK;
- void *old_ssl_sessionid=NULL;
+ CURLcode result = CURLE_OK;
+ void *old_ssl_sessionid = NULL;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- int incache;
+ bool incache;
SSL_SESSION *our_ssl_sessionid;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
incache = FALSE;
}
}
+
if(!incache) {
- retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
- 0 /* unknown size */);
- if(retcode) {
+ result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
+ 0 /* unknown size */);
+ if(result) {
failf(data, "failed to store ssl session");
- return retcode;
+ return result;
}
}
#ifdef HAVE_SSL_GET1_SESSION
if(!data->set.ssl.verifypeer && !data->set.ssl.verifyhost)
(void)servercert(conn, connssl, FALSE);
else
- retcode = servercert(conn, connssl, TRUE);
+ result = servercert(conn, connssl, TRUE);
- if(CURLE_OK == retcode)
+ if(!result)
connssl->connecting_state = ssl_connect_done;
- return retcode;
+
+ return result;
}
static Curl_recv ossl_recv;
static Curl_send ossl_send;
-static CURLcode
-ossl_connect_common(struct connectdata *conn,
- int sockindex,
- bool nonblocking,
- bool *done)
+static CURLcode ossl_connect_common(struct connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
{
- CURLcode retcode;
+ CURLcode result;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
return CURLE_OK;
}
- if(ssl_connect_1==connssl->connecting_state) {
+ if(ssl_connect_1 == connssl->connecting_state) {
/* Find out how much more time we're allowed */
timeout_ms = Curl_timeleft(data, NULL, TRUE);
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
- retcode = ossl_connect_step1(conn, sockindex);
- if(retcode)
- return retcode;
+
+ result = ossl_connect_step1(conn, sockindex);
+ if(result)
+ return result;
}
while(ssl_connect_2 == connssl->connecting_state ||
}
/* if ssl is expecting something, check if it's available. */
- if(connssl->connecting_state == ssl_connect_2_reading
- || connssl->connecting_state == ssl_connect_2_writing) {
+ if(connssl->connecting_state == ssl_connect_2_reading ||
+ connssl->connecting_state == ssl_connect_2_writing) {
curl_socket_t writefd = ssl_connect_2_writing==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
* before step2 has completed while ensuring that a client using select()
* or epoll() will always have a valid fdset to wait on.
*/
- retcode = ossl_connect_step2(conn, sockindex);
- if(retcode || (nonblocking &&
- (ssl_connect_2 == connssl->connecting_state ||
- ssl_connect_2_reading == connssl->connecting_state ||
- ssl_connect_2_writing == connssl->connecting_state)))
- return retcode;
+ result = ossl_connect_step2(conn, sockindex);
+ if(result || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return result;
} /* repeat step2 until all transactions are done. */
-
- if(ssl_connect_3==connssl->connecting_state) {
- retcode = ossl_connect_step3(conn, sockindex);
- if(retcode)
- return retcode;
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = ossl_connect_step3(conn, sockindex);
+ if(result)
+ return result;
}
- if(ssl_connect_done==connssl->connecting_state) {
+ if(ssl_connect_done == connssl->connecting_state) {
connssl->state = ssl_connection_complete;
conn->recv[sockindex] = ossl_recv;
conn->send[sockindex] = ossl_send;
return CURLE_OK;
}
-CURLcode
-Curl_ossl_connect_nonblocking(struct connectdata *conn,
- int sockindex,
- bool *done)
+CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn,
+ int sockindex,
+ bool *done)
{
return ossl_connect_common(conn, sockindex, TRUE, done);
}
-CURLcode
-Curl_ossl_connect(struct connectdata *conn,
- int sockindex)
+CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = ossl_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = ossl_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
return CURLE_OK;
}
-bool Curl_ossl_data_pending(const struct connectdata *conn,
- int connindex)
+bool Curl_ossl_data_pending(const struct connectdata *conn, int connindex)
{
if(conn->ssl[connindex].handle)
/* SSL is in use */
*curlcode = CURLE_SEND_ERROR;
return -1;
}
+ *curlcode = CURLE_OK;
return (ssize_t)rc; /* number of bytes */
}
#if(SSLEAY_VERSION_NUMBER >= 0x905000)
{
- char sub[2];
+ char sub[3];
unsigned long ssleay_value;
+ sub[2]='\0';
sub[1]='\0';
ssleay_value=SSLeay();
if(ssleay_value < 0x906000) {
}
else {
if(ssleay_value&0xff0) {
- sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
+ int minor_ver = (ssleay_value >> 4) & 0xff;
+ if(minor_ver > 26) {
+ /* handle extended version introduced for 0.9.8za */
+ sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
+ sub[0] = 'z';
+ }
+ else {
+ sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
+ }
}
else
sub[0]='\0';
}
- return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx%s",
- (ssleay_value>>28)&0xf,
+ return snprintf(buffer, size, "%s/%lx.%lx.%lx%s",
+#ifdef OPENSSL_IS_BORINGSSL
+ "BoringSSL"
+#else
+#ifdef LIBRESSL_VERSION_NUMBER
+ "LibreSSL"
+#else
+ "OpenSSL"
+#endif
+#endif
+ , (ssleay_value>>28)&0xf,
(ssleay_value>>20)&0xff,
(ssleay_value>>12)&0xff,
sub);
#endif /* YASSL_VERSION */
}
-void Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
- size_t length)
+/* can be called with data == NULL */
+int Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
+ size_t length)
{
- Curl_ossl_seed(data); /* Initiate the seed if not already done */
+ if(data)
+ Curl_ossl_seed(data); /* Initiate the seed if not already done */
RAND_bytes(entropy, curlx_uztosi(length));
+ return 0; /* 0 as in no problem */
}
void Curl_ossl_md5sum(unsigned char *tmp, /* input */