summaryrefslogtreecommitdiffstats
path: root/lib/vtls
diff options
context:
space:
mode:
authorCurl Upstream <curl-library@cool.haxx.se>2015-08-11 18:13:01 (GMT)
committerBrad King <brad.king@kitware.com>2015-08-12 18:18:06 (GMT)
commit706542615828488a5ad197d0ef3dd5e42eb739c4 (patch)
tree66bac7b097b4502a0e41eaba51c6b82312bc7f16 /lib/vtls
parent3fe5d9bff98b4716e219516c30d71462495324f4 (diff)
downloadCMake-706542615828488a5ad197d0ef3dd5e42eb739c4.zip
CMake-706542615828488a5ad197d0ef3dd5e42eb739c4.tar.gz
CMake-706542615828488a5ad197d0ef3dd5e42eb739c4.tar.bz2
curl 7.44.0 (reduced)
Extract upstream curl using the following shell code. url=git://github.com/bagder/curl.git && v=7.44.0 && r=1a7f66a3 && paths=" CMake/* CMakeLists.txt COPYING include/curl/*.h include/curl/curlbuild.h.cmake lib/*.c lib/*.h lib/CMakeLists.txt lib/Makefile.inc lib/curl_config.h.cmake lib/libcurl.rc lib/vtls/*.c lib/vtls/*.h " && mkdir curl-$v-g$r-reduced && git clone $url curl-git && date=$(cd curl-git && git log -n 1 --format='%cd' $r) && (cd curl-git && git checkout $r && git archive --format=tar $r -- $paths) | (cd curl-$v-g$r-reduced && tar xv && rm lib/config-*.h) && echo "g$r date: $date"
Diffstat (limited to 'lib/vtls')
-rw-r--r--lib/vtls/axtls.c34
-rw-r--r--lib/vtls/axtls.h23
-rw-r--r--lib/vtls/cyassl.c273
-rw-r--r--lib/vtls/cyassl.h30
-rw-r--r--lib/vtls/darwinssl.c (renamed from lib/vtls/curl_darwinssl.c)137
-rw-r--r--lib/vtls/darwinssl.h (renamed from lib/vtls/curl_darwinssl.h)21
-rw-r--r--lib/vtls/gskit.c326
-rw-r--r--lib/vtls/gskit.h26
-rw-r--r--lib/vtls/gtls.c398
-rw-r--r--lib/vtls/gtls.h37
-rw-r--r--lib/vtls/nss.c599
-rw-r--r--lib/vtls/nssg.h41
-rw-r--r--lib/vtls/openssl.c1091
-rw-r--r--lib/vtls/openssl.h35
-rw-r--r--lib/vtls/polarssl.c197
-rw-r--r--lib/vtls/polarssl.h26
-rw-r--r--lib/vtls/polarssl_threadlock.c5
-rw-r--r--lib/vtls/qssl.c527
-rw-r--r--lib/vtls/qssl.h62
-rw-r--r--lib/vtls/schannel.c (renamed from lib/vtls/curl_schannel.c)755
-rw-r--r--lib/vtls/schannel.h (renamed from lib/vtls/curl_schannel.h)39
-rw-r--r--lib/vtls/vtls.c355
-rw-r--r--lib/vtls/vtls.h55
23 files changed, 2909 insertions, 2183 deletions
diff --git a/lib/vtls/axtls.c b/lib/vtls/axtls.c
index 1b577b1..1038432 100644
--- a/lib/vtls/axtls.c
+++ b/lib/vtls/axtls.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010, DirecTV, Contact: Eric Hu, <ehu@directv.com>.
- * Copyright (C) 2010 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010 - 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
@@ -29,6 +29,7 @@
#include "curl_setup.h"
#ifdef USE_AXTLS
+#include <axTLS/config.h>
#include <axTLS/ssl.h>
#include "axtls.h"
@@ -38,13 +39,13 @@
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
#include "select.h"
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-#include "curl_memory.h"
+#include "curl_printf.h"
+#include "hostcheck.h"
#include <unistd.h>
-/* The last #include file should be: */
+
+/* The last #include files should be: */
+#include "curl_memory.h"
#include "memdebug.h"
-#include "hostcheck.h"
/* Global axTLS init, called from Curl_ssl_init() */
@@ -463,9 +464,11 @@ Curl_axtls_connect(struct connectdata *conn,
int sockindex)
{
+ struct SessionHandle *data = conn->data;
CURLcode conn_step = connect_prep(conn, sockindex);
int ssl_fcn_return;
SSL *ssl = conn->ssl[sockindex].ssl;
+ long timeout_ms;
if(conn_step != CURLE_OK) {
Curl_axtls_close(conn, sockindex);
@@ -474,14 +477,23 @@ Curl_axtls_connect(struct connectdata *conn,
/* Check to make sure handshake was ok. */
while(ssl_handshake_status(ssl) != SSL_OK) {
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
ssl_fcn_return = ssl_read(ssl, NULL);
if(ssl_fcn_return < 0) {
Curl_axtls_close(conn, sockindex);
ssl_display_error(ssl_fcn_return); /* goes to stdout. */
return map_error_to_curl(ssl_fcn_return);
}
+ /* TODO: avoid polling */
usleep(10000);
- /* TODO: check for timeout as this could hang indefinitely otherwise */
}
infof (conn->data, "handshake completed successfully\n");
@@ -515,12 +527,6 @@ static ssize_t axtls_send(struct connectdata *conn,
return rc;
}
-void Curl_axtls_close_all(struct SessionHandle *data)
-{
- (void)data;
- infof(data, " Curl_axtls_close_all\n");
-}
-
void Curl_axtls_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
@@ -677,7 +683,7 @@ int Curl_axtls_random(struct SessionHandle *data,
* race condition is that some global resources will leak. */
RNG_initialize();
}
- get_random(length, entropy);
+ get_random((int)length, entropy);
return 0;
}
diff --git a/lib/vtls/axtls.h b/lib/vtls/axtls.h
index 0459cf2..223ecb8 100644
--- a/lib/vtls/axtls.h
+++ b/lib/vtls/axtls.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010, DirecTV
- * contact: Eric Hu <ehu@directv.com>
+ * Copyright (C) 2010, DirecTV, Contact: Eric Hu <ehu@directv.com>
+ * Copyright (C) 2010 - 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
@@ -35,10 +35,6 @@ CURLcode Curl_axtls_connect_nonblocking(
int sockindex,
bool *done);
-/* tell axTLS to close down all open information regarding connections (and
- thus session ID caching etc) */
-void Curl_axtls_close_all(struct SessionHandle *data);
-
/* close a SSL connection */
void Curl_axtls_close(struct connectdata *conn, int sockindex);
@@ -50,23 +46,26 @@ int Curl_axtls_random(struct SessionHandle *data,
unsigned char *entropy,
size_t length);
+/* Set the API backend definition to axTLS */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_AXTLS
+
/* API setup for axTLS */
#define curlssl_init Curl_axtls_init
#define curlssl_cleanup Curl_axtls_cleanup
#define curlssl_connect Curl_axtls_connect
#define curlssl_connect_nonblocking Curl_axtls_connect_nonblocking
#define curlssl_session_free(x) Curl_axtls_session_free(x)
-#define curlssl_close_all Curl_axtls_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_axtls_close
#define curlssl_shutdown(x,y) Curl_axtls_shutdown(x,y)
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_axtls_version
#define curlssl_check_cxn(x) Curl_axtls_check_cxn(x)
-#define curlssl_data_pending(x,y) (x=x, y=y, 0)
+#define curlssl_data_pending(x,y) ((void)x, (void)y, 0)
#define curlssl_random(x,y,z) Curl_axtls_random(x,y,z)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_AXTLS
+
#endif /* USE_AXTLS */
#endif /* HEADER_CURL_AXTLS_H */
diff --git a/lib/vtls/cyassl.c b/lib/vtls/cyassl.c
index 9b5c7c6..3ded7f1 100644
--- a/lib/vtls/cyassl.c
+++ b/lib/vtls/cyassl.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
@@ -30,6 +30,20 @@
#ifdef USE_CYASSL
+#define WOLFSSL_OPTIONS_IGNORE_SYS
+/* CyaSSL's version.h, which should contain only the version, should come
+before all other CyaSSL includes and be immediately followed by build config
+aka options.h. http://curl.haxx.se/mail/lib-2015-04/0069.html */
+#include <cyassl/version.h>
+#if defined(HAVE_CYASSL_OPTIONS_H) && (LIBCYASSL_VERSION_HEX > 0x03004008)
+#if defined(CYASSL_API) || defined(WOLFSSL_API)
+/* Safety measure. If either is defined some API include was already included
+and that's a problem since options.h hasn't been included yet. */
+#error "CyaSSL API was included before the CyaSSL build options."
+#endif
+#include <cyassl/options.h>
+#endif
+
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
@@ -43,10 +57,8 @@
#include "connect.h" /* for the connect timeout */
#include "select.h"
#include "rawstr.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-#include "curl_memory.h"
+#include "x509asn1.h"
+#include "curl_printf.h"
#include <cyassl/ssl.h>
#ifdef HAVE_CYASSL_ERROR_SSL_H
@@ -55,10 +67,16 @@
#include <cyassl/error.h>
#endif
#include <cyassl/ctaocrypt/random.h>
+#include <cyassl/ctaocrypt/sha256.h>
-/* The last #include file should be: */
+/* The last #include files should be: */
+#include "curl_memory.h"
#include "memdebug.h"
+#if LIBCYASSL_VERSION_HEX < 0x02007002 /* < 2.7.2 */
+#define CYASSL_MAX_ERROR_SZ 80
+#endif
+
static Curl_recv cyassl_recv;
static Curl_send cyassl_send;
@@ -82,46 +100,58 @@ static CURLcode
cyassl_connect_step1(struct connectdata *conn,
int sockindex)
{
+ char error_buffer[CYASSL_MAX_ERROR_SZ];
struct SessionHandle *data = conn->data;
struct ssl_connect_data* conssl = &conn->ssl[sockindex];
SSL_METHOD* req_method = NULL;
void* ssl_sessionid = NULL;
curl_socket_t sockfd = conn->sock[sockindex];
+#ifdef HAVE_SNI
+ bool sni = FALSE;
+#define use_sni(x) sni = (x)
+#else
+#define use_sni(x) Curl_nop_stmt
+#endif
if(conssl->state == ssl_connection_complete)
return CURLE_OK;
- /* CyaSSL doesn't support SSLv2 */
- if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
- failf(data, "CyaSSL does not support SSLv2");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
/* check to see if we've been told to use an explicit SSL/TLS version */
switch(data->set.ssl.version) {
case CURL_SSLVERSION_DEFAULT:
- /* we try to figure out version */
- req_method = SSLv23_client_method();
- break;
case CURL_SSLVERSION_TLSv1:
- infof(data, "CyaSSL cannot be configured to use TLS 1.0-1.2, "
+#if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
+ /* minimum protocol version is set later after the CTX object is created */
+ req_method = SSLv23_client_method();
+#else
+ infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
"TLS 1.0 is used exclusively\n");
req_method = TLSv1_client_method();
+#endif
+ use_sni(TRUE);
break;
case CURL_SSLVERSION_TLSv1_0:
req_method = TLSv1_client_method();
+ use_sni(TRUE);
break;
case CURL_SSLVERSION_TLSv1_1:
req_method = TLSv1_1_client_method();
+ use_sni(TRUE);
break;
case CURL_SSLVERSION_TLSv1_2:
req_method = TLSv1_2_client_method();
+ use_sni(TRUE);
break;
case CURL_SSLVERSION_SSLv3:
req_method = SSLv3_client_method();
+ use_sni(FALSE);
break;
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "CyaSSL does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
default:
- req_method = TLSv1_client_method();
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
}
if(!req_method) {
@@ -138,15 +168,36 @@ cyassl_connect_step1(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY;
}
+ switch(data->set.ssl.version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+#if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
+ /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever
+ minimum version of TLS was built in and at least TLS 1.0. For later library
+ versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so
+ we have this short circuit evaluation to find the minimum supported TLS
+ version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion
+ because only the former will work before the user's CTX callback is called.
+ */
+ if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) &&
+ (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) &&
+ (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) {
+ failf(data, "SSL: couldn't set the minimum protocol version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif
+ break;
+ }
+
#ifndef NO_FILESYSTEM
/* load trusted cacert */
if(data->set.str[STRING_SSL_CAFILE]) {
- if(!SSL_CTX_load_verify_locations(conssl->ctx,
- data->set.str[STRING_SSL_CAFILE],
- data->set.str[STRING_SSL_CAPATH])) {
+ if(1 != SSL_CTX_load_verify_locations(conssl->ctx,
+ data->set.str[STRING_SSL_CAFILE],
+ data->set.str[STRING_SSL_CAPATH])) {
if(data->set.ssl.verifypeer) {
/* Fail if we insist on successfully verifying the server. */
- failf(data,"error setting certificate verify locations:\n"
+ failf(data, "error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s",
data->set.str[STRING_SSL_CAFILE]?
data->set.str[STRING_SSL_CAFILE]: "none",
@@ -192,11 +243,7 @@ cyassl_connect_step1(struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR;
}
}
-#else
- if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) {
- return CURLE_SSL_CONNECT_ERROR;
- }
-#endif /* NO_FILESYSTEM */
+#endif /* !NO_FILESYSTEM */
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
@@ -206,6 +253,46 @@ cyassl_connect_step1(struct connectdata *conn,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
NULL);
+#ifdef HAVE_SNI
+ if(sni) {
+ struct in_addr addr4;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+#endif
+ size_t hostname_len = strlen(conn->host.name);
+ if((hostname_len < USHRT_MAX) &&
+ (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) &&
+#ifdef ENABLE_IPV6
+ (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) &&
+#endif
+ (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name,
+ (unsigned short)hostname_len) != 1)) {
+ infof(data, "WARNING: failed to configure server name indication (SNI) "
+ "TLS extension\n");
+ }
+ }
+#endif
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ CURLcode result = CURLE_OK;
+ result = (*data->set.ssl.fsslctx)(data, conssl->ctx,
+ data->set.ssl.fsslctxp);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ return result;
+ }
+ }
+#ifdef NO_FILESYSTEM
+ else if(data->set.ssl.verifypeer) {
+ failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built"
+ " with \"no filesystem\". Either disable peer verification"
+ " (insecure) or if you are building an application with libcurl you"
+ " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif
+
/* Let's make an SSL structure */
if(conssl->handle)
SSL_free(conssl->handle);
@@ -220,7 +307,7 @@ cyassl_connect_step1(struct connectdata *conn,
/* we got a session id, use it! */
if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
- ERR_error_string(SSL_get_error(conssl->handle, 0),NULL));
+ ERR_error_string(SSL_get_error(conssl->handle, 0), error_buffer));
return CURLE_SSL_CONNECT_ERROR;
}
/* Informational message */
@@ -246,9 +333,6 @@ cyassl_connect_step2(struct connectdata *conn,
struct SessionHandle *data = conn->data;
struct ssl_connect_data* conssl = &conn->ssl[sockindex];
- infof(data, "CyaSSL: Connecting to %s:%d\n",
- conn->host.name, conn->remote_port);
-
conn->recv[sockindex] = cyassl_recv;
conn->send[sockindex] = cyassl_send;
@@ -261,7 +345,7 @@ cyassl_connect_step2(struct connectdata *conn,
ret = SSL_connect(conssl->handle);
if(ret != 1) {
- char error_buffer[80];
+ char error_buffer[CYASSL_MAX_ERROR_SZ];
int detail = SSL_get_error(conssl->handle, ret);
if(SSL_ERROR_WANT_READ == detail) {
@@ -321,6 +405,44 @@ cyassl_connect_step2(struct connectdata *conn,
}
}
+ if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
+ X509 *x509;
+ const char *x509_der;
+ int x509_der_len;
+ curl_X509certificate x509_parsed;
+ curl_asn1Element *pubkey;
+ CURLcode result;
+
+ x509 = SSL_get_peer_certificate(conssl->handle);
+ if(!x509) {
+ failf(data, "SSL: failed retrieving server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ x509_der = (const char *)CyaSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ memset(&x509_parsed, 0, sizeof x509_parsed);
+ Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len);
+
+ pubkey = &x509_parsed.subjectPublicKeyInfo;
+ if(!pubkey->header || pubkey->end <= pubkey->header) {
+ failf(data, "SSL: failed retrieving public key from server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ result = Curl_pin_peer_pubkey(data->set.str[STRING_SSL_PINNEDPUBLICKEY],
+ (const unsigned char *)pubkey->header,
+ (size_t)(pubkey->end - pubkey->header));
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key!");
+ return result;
+ }
+ }
+
conssl->connecting_state = ssl_connect_3;
infof(data, "SSL connected\n");
@@ -332,11 +454,11 @@ static CURLcode
cyassl_connect_step3(struct connectdata *conn,
int sockindex)
{
- CURLcode retcode = CURLE_OK;
+ 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);
@@ -351,18 +473,19 @@ cyassl_connect_step3(struct connectdata *conn,
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;
}
}
connssl->connecting_state = ssl_connect_done;
- return retcode;
+ return result;
}
@@ -372,7 +495,7 @@ static ssize_t cyassl_send(struct connectdata *conn,
size_t len,
CURLcode *curlcode)
{
- char error_buffer[80];
+ char error_buffer[CYASSL_MAX_ERROR_SZ];
int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
int rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
@@ -396,11 +519,6 @@ static ssize_t cyassl_send(struct connectdata *conn,
return rc;
}
-void Curl_cyassl_close_all(struct SessionHandle *data)
-{
- (void)data;
-}
-
void Curl_cyassl_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *conssl = &conn->ssl[sockindex];
@@ -422,7 +540,7 @@ static ssize_t cyassl_recv(struct connectdata *conn,
size_t buffersize,
CURLcode *curlcode)
{
- char error_buffer[80];
+ char error_buffer[CYASSL_MAX_ERROR_SZ];
int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
int nread = SSL_read(conn->ssl[num].handle, buf, buffsize);
@@ -458,7 +576,9 @@ void Curl_cyassl_session_free(void *ptr)
size_t Curl_cyassl_version(char *buffer, size_t size)
{
-#ifdef CYASSL_VERSION
+#ifdef WOLFSSL_VERSION
+ return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
+#elif defined(CYASSL_VERSION)
return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
#else
return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
@@ -468,10 +588,7 @@ size_t Curl_cyassl_version(char *buffer, size_t size)
int Curl_cyassl_init(void)
{
- if(CyaSSL_Init() == 0)
- return 1;
-
- return -1;
+ return (CyaSSL_Init() == SSL_SUCCESS);
}
@@ -507,7 +624,7 @@ cyassl_connect_common(struct connectdata *conn,
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];
@@ -529,9 +646,10 @@ cyassl_connect_common(struct connectdata *conn,
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
- retcode = cyassl_connect_step1(conn, sockindex);
- if(retcode)
- return retcode;
+
+ result = cyassl_connect_step1(conn, sockindex);
+ if(result)
+ return result;
}
while(ssl_connect_2 == connssl->connecting_state ||
@@ -583,22 +701,21 @@ cyassl_connect_common(struct connectdata *conn,
* ensuring that a client using select() or epoll() will always
* have a valid fdset to wait on.
*/
- retcode = cyassl_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 = cyassl_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 = cyassl_connect_step3(conn, sockindex);
- if(retcode)
- return retcode;
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = cyassl_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] = cyassl_recv;
conn->send[sockindex] = cyassl_send;
@@ -627,12 +744,12 @@ CURLcode
Curl_cyassl_connect(struct connectdata *conn,
int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = cyassl_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = cyassl_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
@@ -647,9 +764,23 @@ int Curl_cyassl_random(struct SessionHandle *data,
(void)data;
if(InitRng(&rng))
return 1;
- if(RNG_GenerateBlock(&rng, entropy, length))
+ if(length > UINT_MAX)
+ return 1;
+ if(RNG_GenerateBlock(&rng, entropy, (unsigned)length))
return 1;
return 0;
}
+void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
+{
+ Sha256 SHA256pw;
+ (void)unused;
+ InitSha256(&SHA256pw);
+ Sha256Update(&SHA256pw, tmp, tmplen);
+ Sha256Final(&SHA256pw, sha256sum);
+}
+
#endif
diff --git a/lib/vtls/cyassl.h b/lib/vtls/cyassl.h
index b10b607..167de74 100644
--- a/lib/vtls/cyassl.h
+++ b/lib/vtls/cyassl.h
@@ -7,7 +7,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
@@ -26,13 +26,9 @@
#ifdef USE_CYASSL
CURLcode Curl_cyassl_connect(struct connectdata *conn, int sockindex);
-bool Curl_cyassl_data_pending(const struct connectdata* conn,int connindex);
+bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex);
int Curl_cyassl_shutdown(struct connectdata* conn, int sockindex);
-/* tell CyaSSL to close down all open information regarding connections (and
- thus session ID caching etc) */
-void Curl_cyassl_close_all(struct SessionHandle *data);
-
/* close a SSL connection */
void Curl_cyassl_close(struct connectdata *conn, int sockindex);
@@ -46,6 +42,16 @@ CURLcode Curl_cyassl_connect_nonblocking(struct connectdata *conn,
int Curl_cyassl_random(struct SessionHandle *data,
unsigned char *entropy,
size_t length);
+void Curl_cyassl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t unused);
+
+/* Set the API backend definition to Schannel */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_CYASSL
+
+/* this backend supports CURLOPT_SSL_CTX_* */
+#define have_curlssl_ssl_ctx 1
/* API setup for CyaSSL */
#define curlssl_init Curl_cyassl_init
@@ -53,17 +59,17 @@ int Curl_cyassl_random(struct SessionHandle *data,
#define curlssl_connect Curl_cyassl_connect
#define curlssl_connect_nonblocking Curl_cyassl_connect_nonblocking
#define curlssl_session_free(x) Curl_cyassl_session_free(x)
-#define curlssl_close_all Curl_cyassl_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_cyassl_close
#define curlssl_shutdown(x,y) Curl_cyassl_shutdown(x,y)
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_cyassl_version
-#define curlssl_check_cxn(x) (x=x, -1)
+#define curlssl_check_cxn(x) ((void)x, -1)
#define curlssl_data_pending(x,y) Curl_cyassl_data_pending(x,y)
#define curlssl_random(x,y,z) Curl_cyassl_random(x,y,z)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_CYASSL
+#define curlssl_sha256sum(a,b,c,d) Curl_cyassl_sha256sum(a,b,c,d)
#endif /* USE_CYASSL */
#endif /* HEADER_CURL_CYASSL_H */
diff --git a/lib/vtls/curl_darwinssl.c b/lib/vtls/darwinssl.c
index f229c6f..03adcef 100644
--- a/lib/vtls/curl_darwinssl.c
+++ b/lib/vtls/darwinssl.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 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
@@ -86,6 +86,7 @@
#define CURL_SUPPORT_MAC_10_6 0
#define CURL_SUPPORT_MAC_10_7 0
#define CURL_SUPPORT_MAC_10_8 0
+#define CURL_SUPPORT_MAC_10_9 0
#else
#error "The darwinssl back-end requires iOS or OS X."
@@ -101,10 +102,8 @@
#include "connect.h"
#include "select.h"
#include "vtls.h"
-#include "curl_darwinssl.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "darwinssl.h"
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
@@ -1057,10 +1056,8 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
if(SSLSetProtocolVersionMax != NULL) {
switch(data->set.ssl.version) {
- case CURL_SSLVERSION_DEFAULT: default:
- (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3);
- (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12);
- break;
+ default:
+ case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
(void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kTLSProtocol1);
(void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12);
@@ -1078,7 +1075,11 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
(void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12);
break;
case CURL_SSLVERSION_SSLv3:
- (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3);
+ err = SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
(void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kSSLProtocol3);
break;
case CURL_SSLVERSION_SSLv2:
@@ -1096,20 +1097,8 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
kSSLProtocolAll,
false);
switch (data->set.ssl.version) {
- case CURL_SSLVERSION_DEFAULT: default:
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
- kSSLProtocol3,
- true);
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
- kTLSProtocol1,
- true);
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
- kTLSProtocol11,
- true);
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
- kTLSProtocol12,
- true);
- break;
+ default:
+ case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
(void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
kTLSProtocol1,
@@ -1137,9 +1126,13 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
true);
break;
case CURL_SSLVERSION_SSLv3:
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+ err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
kSSLProtocol3,
true);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
break;
case CURL_SSLVERSION_SSLv2:
err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
@@ -1158,13 +1151,6 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
switch(data->set.ssl.version) {
default:
case CURL_SSLVERSION_DEFAULT:
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
- kSSLProtocol3,
- true);
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
- kTLSProtocol1,
- true);
- break;
case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
(void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
@@ -1187,9 +1173,13 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
}
break;
case CURL_SSLVERSION_SSLv3:
- (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
+ err = SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
kSSLProtocol3,
true);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
break;
}
#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
@@ -1469,14 +1459,17 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
/* We want to enable 1/n-1 when using a CBC cipher unless the user
specifically doesn't want us doing that: */
- if(SSLSetSessionOption != NULL)
+ if(SSLSetSessionOption != NULL) {
SSLSetSessionOption(connssl->ssl_ctx, kSSLSessionOptionSendOneByteRecord,
!data->set.ssl_enable_beast);
+ SSLSetSessionOption(connssl->ssl_ctx, kSSLSessionOptionFalseStart,
+ data->set.ssl.falsestart); /* false start support */
+ }
#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
/* Check if there's a cached ID we can/should use here! */
if(!Curl_ssl_getsessionid(conn, (void **)&ssl_sessionid,
- &ssl_sessionid_len)) {
+ &ssl_sessionid_len)) {
/* we got a session id, use it! */
err = SSLSetPeerID(connssl->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
if(err != noErr) {
@@ -1489,20 +1482,23 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
/* If there isn't one, then let's make one up! This has to be done prior
to starting the handshake. */
else {
- CURLcode retcode;
+ CURLcode result;
+ ssl_sessionid =
+ aprintf("%s:%d:%d:%s:%hu", data->set.str[STRING_SSL_CAFILE],
+ data->set.ssl.verifypeer, data->set.ssl.verifyhost,
+ conn->host.name, conn->remote_port);
+ ssl_sessionid_len = strlen(ssl_sessionid);
- ssl_sessionid = malloc(256*sizeof(char));
- ssl_sessionid_len = snprintf(ssl_sessionid, 256, "curl:%s:%hu",
- conn->host.name, conn->remote_port);
err = SSLSetPeerID(connssl->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
if(err != noErr) {
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- retcode = Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_sessionid_len);
- if(retcode!= CURLE_OK) {
+
+ result = Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_sessionid_len);
+ if(result) {
failf(data, "failed to store ssl session");
- return retcode;
+ return result;
}
}
@@ -2081,7 +2077,7 @@ darwinssl_connect_common(struct connectdata *conn,
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];
@@ -2103,9 +2099,10 @@ darwinssl_connect_common(struct connectdata *conn,
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
- retcode = darwinssl_connect_step1(conn, sockindex);
- if(retcode)
- return retcode;
+
+ result = darwinssl_connect_step1(conn, sockindex);
+ if(result)
+ return result;
}
while(ssl_connect_2 == connssl->connecting_state ||
@@ -2122,8 +2119,8 @@ darwinssl_connect_common(struct connectdata *conn,
}
/* 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;
@@ -2156,23 +2153,23 @@ darwinssl_connect_common(struct connectdata *conn,
* before step2 has completed while ensuring that a client using select()
* or epoll() will always have a valid fdset to wait on.
*/
- retcode = darwinssl_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 = darwinssl_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 = darwinssl_connect_step3(conn, sockindex);
- if(retcode)
- return retcode;
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = darwinssl_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] = darwinssl_recv;
conn->send[sockindex] = darwinssl_send;
@@ -2199,13 +2196,13 @@ CURLcode
Curl_darwinssl_connect(struct connectdata *conn,
int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = darwinssl_connect_common(conn, sockindex, FALSE, &done);
+ result = darwinssl_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ if(result)
+ return result;
DEBUGASSERT(done);
@@ -2233,12 +2230,6 @@ void Curl_darwinssl_close(struct connectdata *conn, int sockindex)
connssl->ssl_sockfd = 0;
}
-void Curl_darwinssl_close_all(struct SessionHandle *data)
-{
- /* SecureTransport doesn't separate sessions from contexts, so... */
- (void)data;
-}
-
int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
@@ -2376,6 +2367,14 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
(void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum);
}
+bool Curl_darwinssl_false_start(void) {
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ if(SSLSetSessionOption != NULL)
+ return TRUE;
+#endif
+ return FALSE;
+}
+
static ssize_t darwinssl_send(struct connectdata *conn,
int sockindex,
const void *mem,
diff --git a/lib/vtls/curl_darwinssl.h b/lib/vtls/darwinssl.h
index f5c03d8..3bb69c0 100644
--- a/lib/vtls/curl_darwinssl.h
+++ b/lib/vtls/darwinssl.h
@@ -8,6 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) 2012 - 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,9 +32,6 @@ CURLcode Curl_darwinssl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done);
-/* this function doesn't actually do anything */
-void Curl_darwinssl_close_all(struct SessionHandle *data);
-
/* close a SSL connection */
void Curl_darwinssl_close(struct connectdata *conn, int sockindex);
@@ -50,9 +48,10 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *md5sum, /* output */
size_t md5len);
+bool Curl_darwinssl_false_start(void);
-/* this backend provides these functions: */
-#define have_curlssl_md5sum 1
+/* Set the API backend definition to SecureTransport */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL
/* API setup for SecureTransport */
#define curlssl_init() (1)
@@ -60,18 +59,18 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
#define curlssl_connect Curl_darwinssl_connect
#define curlssl_connect_nonblocking Curl_darwinssl_connect_nonblocking
#define curlssl_session_free(x) Curl_darwinssl_session_free(x)
-#define curlssl_close_all Curl_darwinssl_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_darwinssl_close
#define curlssl_shutdown(x,y) 0
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_darwinssl_version
#define curlssl_check_cxn Curl_darwinssl_check_cxn
#define curlssl_data_pending(x,y) Curl_darwinssl_data_pending(x, y)
-#define curlssl_random(x,y,z) Curl_darwinssl_random(y,z)
+#define curlssl_random(x,y,z) ((void)x, Curl_darwinssl_random(y,z))
#define curlssl_md5sum(a,b,c,d) Curl_darwinssl_md5sum(a,b,c,d)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL
+#define curlssl_false_start() Curl_darwinssl_false_start()
#endif /* USE_DARWINSSL */
#endif /* HEADER_CURL_DARWINSSL_H */
diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c
index 0f8b08f..d884bd4 100644
--- a/lib/vtls/gskit.c
+++ b/lib/vtls/gskit.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, 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
@@ -74,9 +74,7 @@
#include "select.h"
#include "strequal.h"
#include "x509asn1.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
@@ -134,8 +132,12 @@ static const gskit_cipher ciphertable[] = {
CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
CURL_GSKPROTO_TLSV12_MASK },
{ "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK },
- { "aes128-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK },
{ "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes128-gcm-sha256",
+ "9C", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes256-gcm-sha384",
+ "9D", CURL_GSKPROTO_TLSV12_MASK },
{ "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK },
{ "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK },
{ "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK },
@@ -164,8 +166,6 @@ static bool is_separator(char c)
static CURLcode gskit_status(struct SessionHandle *data, int rc,
const char *procname, CURLcode defcode)
{
- CURLcode cc;
-
/* Process GSKit status and map it to a CURLcode. */
switch (rc) {
case GSK_OK:
@@ -298,7 +298,7 @@ static CURLcode set_ciphers(struct SessionHandle *data,
int i;
int l;
bool unsupported;
- CURLcode cc;
+ CURLcode result;
struct {
char *buf;
char *ptr;
@@ -331,7 +331,7 @@ static CURLcode set_ciphers(struct SessionHandle *data,
/* Process each cipher in input string. */
unsupported = FALSE;
- cc = CURLE_OK;
+ result = CURLE_OK;
for(;;) {
for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);)
cipherlist++;
@@ -344,7 +344,7 @@ static CURLcode set_ciphers(struct SessionHandle *data,
break;
if(!ctp->name) {
failf(data, "Unknown cipher %.*s", l, clp);
- cc = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
}
else {
unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK |
@@ -372,53 +372,53 @@ static CURLcode set_ciphers(struct SessionHandle *data,
/* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */
if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) {
- cc = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS,
- ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE);
- if(cc == CURLE_UNSUPPORTED_PROTOCOL) {
- cc = CURLE_OK;
+ result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
if(unsupported) {
failf(data, "TLSv1.1-only ciphers are not yet supported");
- cc = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
}
}
}
- if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) {
- cc = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS,
- ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE);
- if(cc == CURLE_UNSUPPORTED_PROTOCOL) {
- cc = CURLE_OK;
+ if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) {
+ result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
if(unsupported) {
failf(data, "TLSv1.2-only ciphers are not yet supported");
- cc = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
}
}
}
/* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to
the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */
- if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) {
- cc = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS,
- ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE);
- if(cc == CURLE_UNSUPPORTED_PROTOCOL) {
- cc = CURLE_OK;
+ if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) {
+ result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr,
ciphers[CURL_GSKPROTO_TLSV10].ptr);
}
}
/* Set-up other ciphers. */
- if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_SSLV3_MASK))
- cc = set_buffer(data, h, GSK_V3_CIPHER_SPECS,
- ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE);
- if(cc == CURLE_OK && (*protoflags & CURL_GSKPROTO_SSLV2_MASK))
- cc = set_buffer(data, h, GSK_V2_CIPHER_SPECS,
- ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE);
+ if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK))
+ result = set_buffer(data, h, GSK_V3_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE);
+ if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK))
+ result = set_buffer(data, h, GSK_V2_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE);
/* Clean-up. */
for(i = 0; i < CURL_GSKPROTO_LAST; i++)
free(ciphers[i].buf);
- return cc;
+ return result;
}
@@ -442,7 +442,7 @@ static CURLcode init_environment(struct SessionHandle *data,
const char *password)
{
int rc;
- CURLcode c;
+ CURLcode result;
gsk_handle h;
/* Creates the GSKit environment. */
@@ -458,29 +458,29 @@ static CURLcode init_environment(struct SessionHandle *data,
return CURLE_SSL_CONNECT_ERROR;
}
- c = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE);
- if(c == CURLE_OK && appid)
- c = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE);
- if(c == CURLE_OK && file)
- c = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE);
- if(c == CURLE_OK && label)
- c = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE);
- if(c == CURLE_OK && password)
- c = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE);
-
- if(c == CURLE_OK) {
+ result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE);
+ if(!result && appid)
+ result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE);
+ if(!result && file)
+ result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE);
+ if(!result && label)
+ result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE);
+ if(!result && password)
+ result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE);
+
+ if(!result) {
/* Locate CAs, Client certificate and key according to our settings.
Note: this call may be blocking for some tenths of seconds. */
- c = gskit_status(data, gsk_environment_init(h),
- "gsk_environment_init()", CURLE_SSL_CERTPROBLEM);
- if(c == CURLE_OK) {
+ result = gskit_status(data, gsk_environment_init(h),
+ "gsk_environment_init()", CURLE_SSL_CERTPROBLEM);
+ if(!result) {
*envir = h;
- return c;
+ return result;
}
}
/* Error: rollback. */
gsk_environment_close(&h);
- return c;
+ return result;
}
@@ -558,7 +558,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
gsk_handle envir;
- CURLcode cc;
+ CURLcode result;
int rc;
char *keyringfile;
char *keyringpwd;
@@ -600,22 +600,22 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
if(!envir) {
/* Use keyring mode. */
- cc = init_environment(data, &envir, (const char *) NULL,
- keyringfile, keyringlabel, keyringpwd);
- if(cc != CURLE_OK)
- return cc;
+ result = init_environment(data, &envir, (const char *) NULL,
+ keyringfile, keyringlabel, keyringpwd);
+ if(result)
+ return result;
}
/* Create secure session. */
- cc = gskit_status(data, gsk_secure_soc_open(envir, &connssl->handle),
- "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR);
+ result = gskit_status(data, gsk_secure_soc_open(envir, &connssl->handle),
+ "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR);
gsk_environment_close(&envir);
- if(cc != CURLE_OK)
- return cc;
+ if(result)
+ return result;
/* Determine which SSL/TLS version should be enabled. */
- protoflags = CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
- CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK;
+ protoflags = CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
+ CURL_GSKPROTO_TLSV12_MASK;
sni = conn->host.name;
switch (data->set.ssl.version) {
case CURL_SSLVERSION_SSLv2:
@@ -623,7 +623,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
sni = (char *) NULL;
break;
case CURL_SSLVERSION_SSLv3:
- protoflags = CURL_GSKPROTO_SSLV2_MASK;
+ protoflags = CURL_GSKPROTO_SSLV3_MASK;
sni = (char *) NULL;
break;
case CURL_SSLVERSION_TLSv1:
@@ -643,81 +643,84 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
/* Process SNI. Ignore if not supported (on OS400 < V7R1). */
if(sni) {
- cc = set_buffer(data, connssl->handle,
- GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE);
- if(cc == CURLE_UNSUPPORTED_PROTOCOL)
- cc = CURLE_OK;
+ result = set_buffer(data, connssl->handle,
+ GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL)
+ result = CURLE_OK;
}
/* Set session parameters. */
- if(cc == CURLE_OK) {
+ if(!result) {
/* Compute the handshake timeout. Since GSKit granularity is 1 second,
we round up the required value. */
timeout = Curl_timeleft(data, NULL, TRUE);
if(timeout < 0)
- cc = CURLE_OPERATION_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
else
- cc = set_numeric(data, connssl->handle, GSK_HANDSHAKE_TIMEOUT,
- (timeout + 999) / 1000);
+ result = set_numeric(data, connssl->handle, GSK_HANDSHAKE_TIMEOUT,
+ (timeout + 999) / 1000);
}
- if(cc == CURLE_OK)
- cc = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]);
- if(cc == CURLE_OK)
- cc = set_ciphers(data, connssl->handle, &protoflags);
+ if(!result)
+ result = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]);
+ if(!result)
+ result = set_ciphers(data, connssl->handle, &protoflags);
if(!protoflags) {
failf(data, "No SSL protocol/cipher combination enabled");
- cc = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
}
- if(cc == CURLE_OK)
- cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV2,
- (protoflags & CURL_GSKPROTO_SSLV2_MASK)?
- GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE);
- if(cc == CURLE_OK)
- cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV3,
- (protoflags & CURL_GSKPROTO_SSLV3_MASK)?
- GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE);
- if(cc == CURLE_OK)
- cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV1,
- (protoflags & CURL_GSKPROTO_TLSV10_MASK)?
- GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE);
- if(cc == CURLE_OK) {
- cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV11,
- (protoflags & CURL_GSKPROTO_TLSV11_MASK)?
- GSK_TRUE: GSK_FALSE, TRUE);
- if(cc == CURLE_UNSUPPORTED_PROTOCOL) {
- cc = CURLE_OK;
+ if(!result)
+ result = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV2,
+ (protoflags & CURL_GSKPROTO_SSLV2_MASK)?
+ GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE);
+ if(!result)
+ result = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV3,
+ (protoflags & CURL_GSKPROTO_SSLV3_MASK)?
+ GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE);
+ if(!result)
+ result = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV1,
+ (protoflags & CURL_GSKPROTO_TLSV10_MASK)?
+ GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE);
+ if(!result) {
+ result = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV11,
+ (protoflags & CURL_GSKPROTO_TLSV11_MASK)?
+ GSK_TRUE: GSK_FALSE, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
if(protoflags == CURL_GSKPROTO_TLSV11_MASK) {
failf(data, "TLS 1.1 not yet supported");
- cc = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
}
}
}
- if(cc == CURLE_OK) {
- cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV12,
- (protoflags & CURL_GSKPROTO_TLSV12_MASK)?
- GSK_TRUE: GSK_FALSE, TRUE);
- if(cc == CURLE_UNSUPPORTED_PROTOCOL) {
- cc = CURLE_OK;
+ if(!result) {
+ result = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV12,
+ (protoflags & CURL_GSKPROTO_TLSV12_MASK)?
+ GSK_TRUE: GSK_FALSE, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
if(protoflags == CURL_GSKPROTO_TLSV12_MASK) {
failf(data, "TLS 1.2 not yet supported");
- cc = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
}
}
}
- if(cc == CURLE_OK)
- cc = set_enum(data, connssl->handle, GSK_SERVER_AUTH_TYPE,
- data->set.ssl.verifypeer? GSK_SERVER_AUTH_FULL:
- GSK_SERVER_AUTH_PASSTHRU, FALSE);
+ if(!result)
+ result = set_enum(data, connssl->handle, GSK_SERVER_AUTH_TYPE,
+ data->set.ssl.verifypeer? GSK_SERVER_AUTH_FULL:
+ GSK_SERVER_AUTH_PASSTHRU, FALSE);
- if(cc == CURLE_OK) {
+ if(!result) {
/* Start handshake. Try asynchronous first. */
memset(&commarea, 0, sizeof commarea);
connssl->iocport = QsoCreateIOCompletionPort();
if(connssl->iocport != -1) {
- cc = gskit_status(data, gsk_secure_soc_startInit(connssl->handle,
- connssl->iocport, &commarea),
- "gsk_secure_soc_startInit()", CURLE_SSL_CONNECT_ERROR);
- if(cc == CURLE_OK) {
+ result = gskit_status(data,
+ gsk_secure_soc_startInit(connssl->handle,
+ connssl->iocport,
+ &commarea),
+ "gsk_secure_soc_startInit()",
+ CURLE_SSL_CONNECT_ERROR);
+ if(!result) {
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
@@ -725,12 +728,13 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
close_async_handshake(connssl);
}
else if(errno != ENOBUFS)
- cc = gskit_status(data, GSK_ERROR_IO, "QsoCreateIOCompletionPort()", 0);
+ result = gskit_status(data, GSK_ERROR_IO,
+ "QsoCreateIOCompletionPort()", 0);
else {
/* No more completion port available. Use synchronous IO. */
- cc = gskit_status(data, gsk_secure_soc_init(connssl->handle),
- "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR);
- if(cc == CURLE_OK) {
+ result = gskit_status(data, gsk_secure_soc_init(connssl->handle),
+ "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR);
+ if(!result) {
connssl->connecting_state = ssl_connect_3;
return CURLE_OK;
}
@@ -739,7 +743,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
/* Error: rollback. */
close_one(connssl, data);
- return cc;
+ return result;
}
@@ -751,7 +755,7 @@ static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex,
Qso_OverlappedIO_t cstat;
long timeout_ms;
struct timeval stmv;
- CURLcode cc;
+ CURLcode result;
/* Poll or wait for end of SSL asynchronous handshake. */
@@ -786,12 +790,12 @@ static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex,
}
break;
}
- cc = gskit_status(data, cstat.returnValue, "SSL handshake",
- CURLE_SSL_CONNECT_ERROR);
- if(cc == CURLE_OK)
+ result = gskit_status(data, cstat.returnValue, "SSL handshake",
+ CURLE_SSL_CONNECT_ERROR);
+ if(!result)
connssl->connecting_state = ssl_connect_3;
close_async_handshake(connssl);
- return cc;
+ return result;
}
@@ -804,8 +808,9 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
const gsk_cert_data_elem *p;
const char *cert = (const char *) NULL;
const char *certend;
+ const char *ptr;
int i;
- CURLcode cc;
+ CURLcode result;
/* SSL handshake done: gather certificate info and verify host. */
@@ -838,9 +843,9 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
}
/* Verify host. */
- cc = Curl_verifyhost(conn, cert, certend);
- if(cc != CURLE_OK)
- return cc;
+ result = Curl_verifyhost(conn, cert, certend);
+ if(result)
+ return result;
/* The only place GSKit can get the whole CA chain is a validation
callback where no user data pointer is available. Therefore it's not
@@ -848,12 +853,31 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
However the server certificate may be available, thus we can return
info about it. */
if(data->set.ssl.certinfo) {
- if(Curl_ssl_init_certinfo(data, 1))
- return CURLE_OUT_OF_MEMORY;
+ result = Curl_ssl_init_certinfo(data, 1);
+ if(result)
+ return result;
+
if(cert) {
- cc = Curl_extract_certinfo(conn, 0, cert, certend);
- if(cc != CURLE_OK)
- return cc;
+ result = Curl_extract_certinfo(conn, 0, cert, certend);
+ if(result)
+ return result;
+ }
+ }
+
+ /* Check pinned public key. */
+ ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ if(!result && ptr) {
+ curl_X509certificate x509;
+ curl_asn1Element *p;
+
+ if(!cert)
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ Curl_parseX509(&x509, cert, certend);
+ p = &x509.subjectPublicKeyInfo;
+ result = Curl_pin_peer_pubkey(ptr, p->header, p->end - p->header);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key!");
+ return result;
}
}
@@ -869,7 +893,7 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
long timeout_ms;
Qso_OverlappedIO_t cstat;
- CURLcode cc = CURLE_OK;
+ CURLcode result = CURLE_OK;
*done = connssl->state == ssl_connection_complete;
if(*done)
@@ -883,31 +907,31 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
- cc = CURLE_OPERATION_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
}
else
- cc = gskit_connect_step1(conn, sockindex);
+ result = gskit_connect_step1(conn, sockindex);
}
/* Step 2: check if handshake is over. */
- if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_2) {
+ if(!result && connssl->connecting_state == ssl_connect_2) {
/* check allowed time left */
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
- cc = CURLE_OPERATION_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
}
else
- cc = gskit_connect_step2(conn, sockindex, nonblocking);
+ result = gskit_connect_step2(conn, sockindex, nonblocking);
}
/* Step 3: gather certificate info, verify host. */
- if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_3)
- cc = gskit_connect_step3(conn, sockindex);
+ if(!result && connssl->connecting_state == ssl_connect_3)
+ result = gskit_connect_step3(conn, sockindex);
- if(cc != CURLE_OK)
+ if(result)
close_one(connssl, data);
else if(connssl->connecting_state == ssl_connect_done) {
connssl->state = ssl_connection_complete;
@@ -917,7 +941,7 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
*done = TRUE;
}
- return cc;
+ return result;
}
@@ -925,24 +949,24 @@ CURLcode Curl_gskit_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done)
{
- CURLcode cc;
+ CURLcode result;
- cc = gskit_connect_common(conn, sockindex, TRUE, done);
- if(*done || cc != CURLE_OK)
+ result = gskit_connect_common(conn, sockindex, TRUE, done);
+ if(*done || result)
conn->ssl[sockindex].connecting_state = ssl_connect_1;
- return cc;
+ return result;
}
CURLcode Curl_gskit_connect(struct connectdata *conn, int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done;
conn->ssl[sockindex].connecting_state = ssl_connect_1;
- retcode = gskit_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = gskit_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
@@ -960,14 +984,6 @@ void Curl_gskit_close(struct connectdata *conn, int sockindex)
}
-int Curl_gskit_close_all(struct SessionHandle *data)
-{
- /* Unimplemented. */
- (void) data;
- return 0;
-}
-
-
int Curl_gskit_shutdown(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
diff --git a/lib/vtls/gskit.h b/lib/vtls/gskit.h
index a4caa6f..af31faf 100644
--- a/lib/vtls/gskit.h
+++ b/lib/vtls/gskit.h
@@ -7,7 +7,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
@@ -32,15 +32,20 @@
#ifdef USE_GSKIT
int Curl_gskit_init(void);
void Curl_gskit_cleanup(void);
-CURLcode Curl_gskit_connect(struct connectdata * conn, int sockindex);
-CURLcode Curl_gskit_connect_nonblocking(struct connectdata * conn,
- int sockindex, bool * done);
+CURLcode Curl_gskit_connect(struct connectdata *conn, int sockindex);
+CURLcode Curl_gskit_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done);
void Curl_gskit_close(struct connectdata *conn, int sockindex);
-int Curl_gskit_close_all(struct SessionHandle * data);
-int Curl_gskit_shutdown(struct connectdata * conn, int sockindex);
+int Curl_gskit_shutdown(struct connectdata *conn, int sockindex);
-size_t Curl_gskit_version(char * buffer, size_t size);
-int Curl_gskit_check_cxn(struct connectdata * cxn);
+size_t Curl_gskit_version(char *buffer, size_t size);
+int Curl_gskit_check_cxn(struct connectdata *cxn);
+
+/* Set the API backend definition to GSKit */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_GSKIT
+
+/* this backend supports CURLOPT_CERTINFO */
+#define have_curlssl_certinfo 1
/* API setup for GSKit */
#define curlssl_init Curl_gskit_init
@@ -50,7 +55,7 @@ int Curl_gskit_check_cxn(struct connectdata * cxn);
/* No session handling for GSKit */
#define curlssl_session_free(x) Curl_nop_stmt
-#define curlssl_close_all Curl_gskit_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_gskit_close
#define curlssl_shutdown(x,y) Curl_gskit_shutdown(x,y)
#define curlssl_set_engine(x,y) CURLE_NOT_BUILT_IN
@@ -59,7 +64,8 @@ int Curl_gskit_check_cxn(struct connectdata * cxn);
#define curlssl_version Curl_gskit_version
#define curlssl_check_cxn(x) Curl_gskit_check_cxn(x)
#define curlssl_data_pending(x,y) 0
-#define CURL_SSL_BACKEND CURLSSLBACKEND_GSKIT
+#define curlssl_random(x,y,z) -1
+
#endif /* USE_GSKIT */
#endif /* HEADER_CURL_GSKIT_H */
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index d64f95d..c54dfc1 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.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
@@ -32,12 +32,14 @@
#ifdef USE_GNUTLS
+#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#ifdef USE_GNUTLS_NETTLE
#include <gnutls/crypto.h>
#include <nettle/md5.h>
+#include <nettle/sha2.h>
#else
#include <gcrypt.h>
#endif
@@ -52,9 +54,8 @@
#include "select.h"
#include "rawstr.h"
#include "warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "x509asn1.h"
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -91,14 +92,23 @@ static bool gtls_inited = FALSE;
# define GNUTLS_MAPS_WINSOCK_ERRORS 1
# endif
-# ifdef USE_NGHTTP2
-# undef HAS_ALPN
-# if (GNUTLS_VERSION_NUMBER >= 0x030200)
-# define HAS_ALPN
-# endif
+# if (GNUTLS_VERSION_NUMBER >= 0x030200)
+# define HAS_ALPN
+# endif
+
+# if (GNUTLS_VERSION_NUMBER >= 0x03020d)
+# define HAS_OCSP
+# endif
+
+# if (GNUTLS_VERSION_NUMBER >= 0x030306)
+# define HAS_CAPATH
# endif
#endif
+#ifdef HAS_OCSP
+# include <gnutls/ocsp.h>
+#endif
+
/*
* Custom push and pull callback functions used by GNU TLS to read and write
* to the socket. These functions are simple wrappers to send() and recv()
@@ -203,7 +213,7 @@ static void showtime(struct SessionHandle *data,
snprintf(data->state.buffer,
BUFSIZE,
- "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n",
+ "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT",
text,
Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
tm->tm_mday,
@@ -222,7 +232,7 @@ static gnutls_datum_t load_file (const char *file)
long filelen;
void *ptr;
- if(!(f = fopen(file, "r")))
+ if(!(f = fopen(file, "rb")))
return loaded_file;
if(fseek(f, 0, SEEK_END) != 0
|| (filelen = ftell(f)) < 0
@@ -306,8 +316,6 @@ static CURLcode handshake(struct connectdata *conn,
gnutls_record_get_direction(session)?
ssl_connect_2_writing:ssl_connect_2_reading;
continue;
- if(nonblocking)
- return CURLE_OK;
}
else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
const char *strerr = NULL;
@@ -320,7 +328,8 @@ static CURLcode handshake(struct connectdata *conn,
if(strerr == NULL)
strerr = gnutls_strerror(rc);
- failf(data, "gnutls_handshake() warning: %s", strerr);
+ infof(data, "gnutls_handshake() warning: %s\n", strerr);
+ continue;
}
else if(rc < 0) {
const char *strerr = NULL;
@@ -393,10 +402,6 @@ gtls_connect_step1(struct connectdata *conn,
const char* prioritylist;
const char *err = NULL;
#endif
-#ifdef HAS_ALPN
- int protocols_size = 2;
- gnutls_datum_t protocols[2];
-#endif
if(conn->ssl[sockindex].state == ssl_connection_complete)
/* to make us tolerant against being called more than once for the
@@ -464,6 +469,24 @@ gtls_connect_step1(struct connectdata *conn,
rc, data->set.ssl.CAfile);
}
+#ifdef HAS_CAPATH
+ if(data->set.ssl.CApath) {
+ /* set the trusted CA cert directory */
+ rc = gnutls_certificate_set_x509_trust_dir(conn->ssl[sockindex].cred,
+ data->set.ssl.CApath,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ infof(data, "error reading ca cert file %s (%s)\n",
+ data->set.ssl.CAfile, gnutls_strerror(rc));
+ if(data->set.ssl.verifypeer)
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else
+ infof(data, "found %d certificates in %s\n",
+ rc, data->set.ssl.CApath);
+ }
+#endif
+
if(data->set.ssl.CRLfile) {
/* set the CRL list file */
rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred,
@@ -610,19 +633,25 @@ gtls_connect_step1(struct connectdata *conn,
#endif
#ifdef HAS_ALPN
- if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
- if(data->set.ssl_enable_alpn) {
- protocols[0].data = NGHTTP2_PROTO_VERSION_ID;
- protocols[0].size = NGHTTP2_PROTO_VERSION_ID_LEN;
- protocols[1].data = ALPN_HTTP_1_1;
- protocols[1].size = ALPN_HTTP_1_1_LENGTH;
- gnutls_alpn_set_protocols(session, protocols, protocols_size, 0);
- infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID,
- ALPN_HTTP_1_1);
- }
- else {
- infof(data, "SSL, can't negotiate HTTP/2.0 without ALPN\n");
+ if(data->set.ssl_enable_alpn) {
+ int cur = 0;
+ gnutls_datum_t protocols[2];
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+ protocols[cur].data = (unsigned char *)NGHTTP2_PROTO_VERSION_ID;
+ protocols[cur].size = NGHTTP2_PROTO_VERSION_ID_LEN;
+ cur++;
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
}
+#endif
+
+ protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
+ protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
+ cur++;
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ gnutls_alpn_set_protocols(session, protocols, cur, 0);
}
#endif
@@ -644,13 +673,21 @@ gtls_connect_step1(struct connectdata *conn,
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP,
conn->ssl[sockindex].srp_client_cred);
- if(rc != GNUTLS_E_SUCCESS)
+ if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
}
else
#endif
+ {
rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
conn->ssl[sockindex].cred);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
/* set the connection handle (file descriptor for the socket) */
gnutls_transport_set_ptr(session,
@@ -663,6 +700,16 @@ gtls_connect_step1(struct connectdata *conn,
/* lowat must be set to zero when using custom push and pull functions. */
gnutls_transport_set_lowat(session, 0);
+#ifdef HAS_OCSP
+ if(data->set.ssl.verifystatus) {
+ rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
/* This might be a reconnect, so we check for a session ID in the cache
to speed up things */
@@ -677,6 +724,62 @@ gtls_connect_step1(struct connectdata *conn,
return CURLE_OK;
}
+static CURLcode pkp_pin_peer_pubkey(gnutls_x509_crt_t cert,
+ const char *pinnedpubkey)
+{
+ /* Scratch */
+ size_t len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL;
+
+ gnutls_pubkey_t key = NULL;
+
+ /* Result is returned to caller */
+ int ret = 0;
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(NULL == pinnedpubkey)
+ return CURLE_OK;
+
+ if(NULL == cert)
+ return result;
+
+ 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 */
+
+ /* The one good exit point */
+ result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1);
+ } while(0);
+
+ if(NULL != key)
+ gnutls_pubkey_deinit(key);
+
+ Curl_safefree(buff1);
+
+ return result;
+}
+
static Curl_recv gtls_recv;
static Curl_send gtls_send;
@@ -686,8 +789,8 @@ gtls_connect_step3(struct connectdata *conn,
{
unsigned int cert_list_size;
const gnutls_datum_t *chainp;
- unsigned int verify_status;
- gnutls_x509_crt_t x509_cert,x509_issuer;
+ unsigned int verify_status = 0;
+ gnutls_x509_crt_t x509_cert, x509_issuer;
gnutls_datum_t issuerp;
char certbuf[256] = ""; /* big enough? */
size_t size;
@@ -698,13 +801,23 @@ gtls_connect_step3(struct connectdata *conn,
struct SessionHandle *data = conn->data;
gnutls_session_t session = conn->ssl[sockindex].session;
int rc;
- int incache;
+ bool incache;
void *ssl_sessionid;
#ifdef HAS_ALPN
gnutls_datum_t proto;
#endif
CURLcode result = CURLE_OK;
+ gnutls_protocol_t version = gnutls_protocol_get_version(session);
+
+ /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
+ ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session),
+ gnutls_cipher_get(session),
+ gnutls_mac_get(session));
+
+ infof(data, "SSL connection using %s / %s\n",
+ gnutls_protocol_get_name(version), ptr);
+
/* This function will return the peer's raw certificate (chain) as sent by
the peer. These certificates are in raw format (DER encoded for
X.509). In case of a X.509 then a certificate list may be present. The
@@ -735,6 +848,23 @@ gtls_connect_step3(struct connectdata *conn,
infof(data, "\t common name: WARNING couldn't obtain\n");
}
+ if(data->set.ssl.certinfo && chainp) {
+ unsigned int i;
+
+ result = Curl_ssl_init_certinfo(data, cert_list_size);
+ if(result)
+ return result;
+
+ for(i = 0; i < cert_list_size; i++) {
+ const char *beg = (const char *) chainp[i].data;
+ const char *end = beg + chainp[i].size;
+
+ result = Curl_extract_certinfo(conn, i, beg, end);
+ if(result)
+ return result;
+ }
+ }
+
if(data->set.ssl.verifypeer) {
/* This function will try to verify the peer's certificate and return its
status (trusted, invalid etc.). The value of status should be one or
@@ -766,6 +896,111 @@ gtls_connect_step3(struct connectdata *conn,
else
infof(data, "\t server certificate verification SKIPPED\n");
+#ifdef HAS_OCSP
+ if(data->set.ssl.verifystatus) {
+ if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
+ gnutls_datum_t status_request;
+ gnutls_ocsp_resp_t ocsp_resp;
+
+ gnutls_ocsp_cert_status_t status;
+ gnutls_x509_crl_reason_t reason;
+
+ rc = gnutls_ocsp_status_request_get(session, &status_request);
+
+ infof(data, "\t server certificate status verification FAILED\n");
+
+ if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ failf(data, "No OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ if(rc < 0) {
+ failf(data, "Invalid OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ gnutls_ocsp_resp_init(&ocsp_resp);
+
+ rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request);
+ if(rc < 0) {
+ failf(data, "Invalid OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ rc = gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL,
+ &status, NULL, NULL, NULL, &reason);
+
+ switch(status) {
+ case GNUTLS_OCSP_CERT_GOOD:
+ break;
+
+ case GNUTLS_OCSP_CERT_REVOKED: {
+ const char *crl_reason;
+
+ switch(reason) {
+ default:
+ case GNUTLS_X509_CRLREASON_UNSPECIFIED:
+ crl_reason = "unspecified reason";
+ break;
+
+ case GNUTLS_X509_CRLREASON_KEYCOMPROMISE:
+ crl_reason = "private key compromised";
+ break;
+
+ case GNUTLS_X509_CRLREASON_CACOMPROMISE:
+ crl_reason = "CA compromised";
+ break;
+
+ case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED:
+ crl_reason = "affiliation has changed";
+ break;
+
+ case GNUTLS_X509_CRLREASON_SUPERSEDED:
+ crl_reason = "certificate superseded";
+ break;
+
+ case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION:
+ crl_reason = "operation has ceased";
+ break;
+
+ case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD:
+ crl_reason = "certificate is on hold";
+ break;
+
+ case GNUTLS_X509_CRLREASON_REMOVEFROMCRL:
+ crl_reason = "will be removed from delta CRL";
+ break;
+
+ case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN:
+ crl_reason = "privilege withdrawn";
+ break;
+
+ case GNUTLS_X509_CRLREASON_AACOMPROMISE:
+ crl_reason = "AA compromised";
+ break;
+ }
+
+ failf(data, "Server certificate was revoked: %s", crl_reason);
+ break;
+ }
+
+ default:
+ case GNUTLS_OCSP_CERT_UNKNOWN:
+ failf(data, "Server certificate status is unknown");
+ break;
+ }
+
+ gnutls_ocsp_resp_deinit(ocsp_resp);
+
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+ else
+ infof(data, "\t server certificate status verification OK\n");
+ }
+ else
+ infof(data, "\t server certificate status verification SKIPPED\n");
+#endif
+
/* initialize an X.509 certificate structure. */
gnutls_x509_crt_init(&x509_cert);
@@ -778,14 +1013,16 @@ gtls_connect_step3(struct connectdata *conn,
gnutls_x509_crt_init(&x509_issuer);
issuerp = load_file(data->set.ssl.issuercert);
gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
- rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
+ rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer);
+ gnutls_x509_crt_deinit(x509_issuer);
unload_file(issuerp);
if(rc <= 0) {
failf(data, "server certificate issuer check failed (IssuerCert: %s)",
data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
+ gnutls_x509_crt_deinit(x509_cert);
return CURLE_SSL_ISSUER_ERROR;
}
- infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
+ infof(data, "\t server certificate issuer check OK (Issuer Cert: %s)\n",
data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
}
@@ -868,6 +1105,7 @@ gtls_connect_step3(struct connectdata *conn,
if(certclock == (time_t)-1) {
if(data->set.ssl.verifypeer) {
failf(data, "server cert expiration date verify failed");
+ gnutls_x509_crt_deinit(x509_cert);
return CURLE_SSL_CONNECT_ERROR;
}
else
@@ -877,6 +1115,7 @@ gtls_connect_step3(struct connectdata *conn,
if(certclock < time(NULL)) {
if(data->set.ssl.verifypeer) {
failf(data, "server certificate expiration date has passed.");
+ gnutls_x509_crt_deinit(x509_cert);
return CURLE_PEER_FAILED_VERIFICATION;
}
else
@@ -891,6 +1130,7 @@ gtls_connect_step3(struct connectdata *conn,
if(certclock == (time_t)-1) {
if(data->set.ssl.verifypeer) {
failf(data, "server cert activation date verify failed");
+ gnutls_x509_crt_deinit(x509_cert);
return CURLE_SSL_CONNECT_ERROR;
}
else
@@ -900,6 +1140,7 @@ gtls_connect_step3(struct connectdata *conn,
if(certclock > time(NULL)) {
if(data->set.ssl.verifypeer) {
failf(data, "server certificate not activated yet.");
+ gnutls_x509_crt_deinit(x509_cert);
return CURLE_PEER_FAILED_VERIFICATION;
}
else
@@ -909,9 +1150,18 @@ gtls_connect_step3(struct connectdata *conn,
infof(data, "\t server certificate activation date OK\n");
}
+ ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ if(ptr) {
+ result = pkp_pin_peer_pubkey(x509_cert, ptr);
+ if(result != CURLE_OK) {
+ failf(data, "SSL: public key does not match pinned public key!");
+ gnutls_x509_crt_deinit(x509_cert);
+ return result;
+ }
+ }
+
/* Show:
- - ciphers used
- subject
- start date
- expire date
@@ -951,14 +1201,6 @@ gtls_connect_step3(struct connectdata *conn,
/* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
infof(data, "\t compression: %s\n", ptr);
- /* the name of the cipher used. ie 3DES. */
- ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
- infof(data, "\t cipher: %s\n", ptr);
-
- /* the MAC algorithms name. ie SHA1 */
- ptr = gnutls_mac_get_name(gnutls_mac_get(session));
- infof(data, "\t MAC: %s\n", ptr);
-
#ifdef HAS_ALPN
if(data->set.ssl_enable_alpn) {
rc = gnutls_alpn_get_selected_protocol(session, &proto);
@@ -966,19 +1208,21 @@ gtls_connect_step3(struct connectdata *conn,
infof(data, "ALPN, server accepted to use %.*s\n", proto.size,
proto.data);
+#ifdef USE_NGHTTP2
if(proto.size == NGHTTP2_PROTO_VERSION_ID_LEN &&
- memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data,
- NGHTTP2_PROTO_VERSION_ID_LEN) == 0) {
- conn->negnpn = NPN_HTTP2;
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data,
+ NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ conn->negnpn = CURL_HTTP_VERSION_2_0;
}
- else if(proto.size == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1,
- proto.data, ALPN_HTTP_1_1_LENGTH) == 0) {
- conn->negnpn = NPN_HTTP1_1;
+ else
+#endif
+ if(proto.size == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
}
}
- else {
+ else
infof(data, "ALPN, server did not agree to a protocol\n");
- }
}
#endif
@@ -1079,12 +1323,12 @@ Curl_gtls_connect(struct connectdata *conn,
int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = gtls_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = gtls_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
@@ -1110,12 +1354,6 @@ static ssize_t gtls_send(struct connectdata *conn,
return rc;
}
-void Curl_gtls_close_all(struct SessionHandle *data)
-{
- /* FIX: make the OpenSSL code more generic and use parts of it here */
- (void)data;
-}
-
static void close_one(struct connectdata *conn,
int idx)
{
@@ -1232,10 +1470,10 @@ static ssize_t gtls_recv(struct connectdata *conn, /* connection data */
if(ret == GNUTLS_E_REHANDSHAKE) {
/* BLOCKING call, this is bad but a work-around for now. Fixing this "the
proper way" takes a whole lot of work. */
- CURLcode rc = handshake(conn, num, FALSE, FALSE);
- if(rc)
+ CURLcode result = handshake(conn, num, FALSE, FALSE);
+ if(result)
/* handshake() writes error message on its own */
- *curlcode = rc;
+ *curlcode = result;
else
*curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
return -1;
@@ -1320,4 +1558,32 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */
#endif
}
+void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+#if defined(USE_GNUTLS_NETTLE)
+ struct sha256_ctx SHA256pw;
+ sha256_init(&SHA256pw);
+ sha256_update(&SHA256pw, (unsigned int)tmplen, tmp);
+ sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum);
+#elif defined(USE_GNUTLS)
+ gcry_md_hd_t SHA256pw;
+ gcry_md_open(&SHA256pw, GCRY_MD_SHA256, 0);
+ gcry_md_write(SHA256pw, tmp, tmplen);
+ memcpy(sha256sum, gcry_md_read (SHA256pw, 0), sha256len);
+ gcry_md_close(SHA256pw);
+#endif
+}
+
+bool Curl_gtls_cert_status_request(void)
+{
+#ifdef HAS_OCSP
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
#endif /* USE_GNUTLS */
diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h
index cd6152c..0afd9b9 100644
--- a/lib/vtls/gtls.h
+++ b/lib/vtls/gtls.h
@@ -7,7 +7,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
@@ -35,10 +35,6 @@ CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done);
-/* tell GnuTLS to close down all open information regarding connections (and
- thus session ID caching etc) */
-void Curl_gtls_close_all(struct SessionHandle *data);
-
/* close a SSL connection */
void Curl_gtls_close(struct connectdata *conn, int sockindex);
@@ -52,9 +48,21 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *md5sum, /* output */
size_t md5len);
+void Curl_gtls_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len);
-/* this backend provides these functions: */
-#define have_curlssl_md5sum 1
+bool Curl_gtls_cert_status_request(void);
+
+/* Set the API backend definition to GnuTLS */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_GNUTLS
+
+/* this backend supports the CAPATH option */
+#define have_curlssl_ca_path 1
+
+/* this backend supports CURLOPT_CERTINFO */
+#define have_curlssl_certinfo 1
/* API setup for GnuTLS */
#define curlssl_init Curl_gtls_init
@@ -62,18 +70,19 @@ void Curl_gtls_md5sum(unsigned char *tmp, /* input */
#define curlssl_connect Curl_gtls_connect
#define curlssl_connect_nonblocking Curl_gtls_connect_nonblocking
#define curlssl_session_free(x) Curl_gtls_session_free(x)
-#define curlssl_close_all Curl_gtls_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_gtls_close
#define curlssl_shutdown(x,y) Curl_gtls_shutdown(x,y)
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_gtls_version
-#define curlssl_check_cxn(x) (x=x, -1)
-#define curlssl_data_pending(x,y) (x=x, y=y, 0)
+#define curlssl_check_cxn(x) ((void)x, -1)
+#define curlssl_data_pending(x,y) ((void)x, (void)y, 0)
#define curlssl_random(x,y,z) Curl_gtls_random(x,y,z)
#define curlssl_md5sum(a,b,c,d) Curl_gtls_md5sum(a,b,c,d)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_GNUTLS
+#define curlssl_sha256sum(a,b,c,d) Curl_gtls_sha256sum(a,b,c,d)
+#define curlssl_cert_status_request() Curl_gtls_cert_status_request()
#endif /* USE_GNUTLS */
#endif /* HEADER_CURL_GTLS_H */
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index 83b3e32..91727c7 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.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
@@ -38,10 +38,7 @@
#include "select.h"
#include "vtls.h"
#include "llist.h"
-
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
+#include "curl_printf.h"
#include "nssg.h"
#include <nspr.h>
#include <nss.h>
@@ -59,13 +56,20 @@
#include <base64.h>
#include <cert.h>
#include <prerror.h>
+#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */
+
+#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
+
+#if NSSVERNUM >= 0x030f00 /* 3.15.0 */
+#include <ocsp.h>
+#endif
-#include "curl_memory.h"
#include "rawstr.h"
#include "warnless.h"
#include "x509asn1.h"
-/* The last #include file should be: */
+/* The last #include files should be: */
+#include "curl_memory.h"
#include "memdebug.h"
#define SSL_DIR "/etc/pki/nssdb"
@@ -343,7 +347,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
CK_BBOOL ckfalse = CK_FALSE;
CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
int attr_cnt = 0;
- CURLcode err = (cacert)
+ CURLcode result = (cacert)
? CURLE_SSL_CACERT_BADFILE
: CURLE_SSL_CERTPROBLEM;
@@ -355,7 +359,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
slot = PK11_FindSlotByName(slot_name);
free(slot_name);
if(!slot)
- return err;
+ return result;
PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
@@ -370,7 +374,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
PK11_FreeSlot(slot);
if(!obj)
- return err;
+ return result;
if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
PK11_DestroyGenericObject(obj);
@@ -405,16 +409,16 @@ static void nss_destroy_crl_item(void *user, void *ptr)
static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
const char *filename, PRBool cacert)
{
- CURLcode err = (cacert)
+ CURLcode result = (cacert)
? CURLE_SSL_CACERT_BADFILE
: CURLE_SSL_CERTPROBLEM;
/* libnsspem.so leaks memory if the requested file does not exist. For more
* details, go to <https://bugzilla.redhat.com/734760>. */
if(is_file(filename))
- err = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
+ result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
- if(CURLE_OK == err && !cacert) {
+ if(!result && !cacert) {
/* we have successfully loaded a client certificate */
CERTCertificate *cert;
char *nickname = NULL;
@@ -436,7 +440,7 @@ static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
}
}
- return err;
+ return result;
}
/* add given CRL to cache if it is not already there */
@@ -448,7 +452,7 @@ static CURLcode nss_cache_crl(SECItem *crl_der)
/* CRL already cached */
SEC_DestroyCrl(crl);
SECITEM_FreeItem(crl_der, PR_TRUE);
- return CURLE_SSL_CRL_BADFILE;
+ return CURLE_OK;
}
/* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */
@@ -542,14 +546,15 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
{
PK11SlotInfo *slot;
SECStatus status;
- CURLcode rv;
+ CURLcode result;
struct ssl_connect_data *ssl = conn->ssl;
+
(void)sockindex; /* unused */
- rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE);
- if(CURLE_OK != rv) {
+ result = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE);
+ if(result) {
PR_SetError(SEC_ERROR_BAD_KEY, 0);
- return rv;
+ return result;
}
slot = PK11_FindSlotByName("PEM Token #1");
@@ -563,9 +568,8 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
status = PK11_Authenticate(slot, PR_TRUE,
conn->data->set.str[STRING_KEY_PASSWD]);
PK11_FreeSlot(slot);
- return (SECSuccess == status)
- ? CURLE_OK
- : CURLE_SSL_CERTPROBLEM;
+
+ return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM;
}
static int display_error(struct connectdata *conn, PRInt32 err,
@@ -588,35 +592,35 @@ static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
char *cert_file, char *key_file)
{
struct SessionHandle *data = conn->data;
- CURLcode rv;
+ CURLcode result;
if(cert_file) {
- rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE);
- if(CURLE_OK != rv) {
+ result = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE);
+ if(result) {
const PRErrorCode err = PR_GetError();
if(!display_error(conn, err, cert_file)) {
const char *err_name = nss_error_to_name(err);
failf(data, "unable to load client cert: %d (%s)", err, err_name);
}
- return rv;
+ return result;
}
}
if(key_file || (is_file(cert_file))) {
if(key_file)
- rv = nss_load_key(conn, sockindex, key_file);
+ result = nss_load_key(conn, sockindex, key_file);
else
/* In case the cert file also has the key */
- rv = nss_load_key(conn, sockindex, cert_file);
- if(CURLE_OK != rv) {
+ result = nss_load_key(conn, sockindex, cert_file);
+ if(result) {
const PRErrorCode err = PR_GetError();
if(!display_error(conn, err, key_file)) {
const char *err_name = nss_error_to_name(err);
failf(data, "unable to load client key: %d (%s)", err, err_name);
}
- return rv;
+ return result;
}
}
@@ -626,6 +630,7 @@ static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
{
(void)slot; /* unused */
+
if(retry || NULL == arg)
return NULL;
else
@@ -638,6 +643,34 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
PRBool isServer)
{
struct connectdata *conn = (struct connectdata *)arg;
+
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ if(conn->data->set.ssl.verifystatus) {
+ SECStatus cacheResult;
+
+ const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
+ if(!csa) {
+ failf(conn->data, "Invalid OCSP response");
+ return SECFailure;
+ }
+
+ if(csa->len == 0) {
+ failf(conn->data, "No OCSP response received");
+ return SECFailure;
+ }
+
+ cacheResult = CERT_CacheOCSPResponseFromSideChannel(
+ CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd),
+ PR_Now(), &csa->items[0], arg
+ );
+
+ if(cacheResult != SECSuccess) {
+ failf(conn->data, "Invalid OCSP response");
+ return cacheResult;
+ }
+ }
+#endif
+
if(!conn->data->set.ssl.verifypeer) {
infof(conn->data, "skipping SSL peer certificate verification\n");
return SECSuccess;
@@ -651,7 +684,6 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
*/
static void HandshakeCallback(PRFileDesc *sock, void *arg)
{
-#ifdef USE_NGHTTP2
struct connectdata *conn = (struct connectdata*) arg;
unsigned int buflenmax = 50;
unsigned char buf[50];
@@ -665,36 +697,94 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) {
switch(state) {
- case SSL_NEXT_PROTO_NO_SUPPORT:
- case SSL_NEXT_PROTO_NO_OVERLAP:
- infof(conn->data, "TLS, neither ALPN nor NPN succeeded\n");
- return;
+ case SSL_NEXT_PROTO_NO_SUPPORT:
+ case SSL_NEXT_PROTO_NO_OVERLAP:
+ infof(conn->data, "ALPN/NPN, server did not agree to a protocol\n");
+ return;
#ifdef SSL_ENABLE_ALPN
- case SSL_NEXT_PROTO_SELECTED:
- infof(conn->data, "ALPN, server accepted to use %.*s\n", buflen, buf);
- break;
+ case SSL_NEXT_PROTO_SELECTED:
+ infof(conn->data, "ALPN, server accepted to use %.*s\n", buflen, buf);
+ break;
#endif
- case SSL_NEXT_PROTO_NEGOTIATED:
- infof(conn->data, "NPN, server accepted to use %.*s\n", buflen, buf);
- break;
+ case SSL_NEXT_PROTO_NEGOTIATED:
+ infof(conn->data, "NPN, server accepted to use %.*s\n", buflen, buf);
+ break;
}
+#ifdef USE_NGHTTP2
if(buflen == NGHTTP2_PROTO_VERSION_ID_LEN &&
- memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN)
- == 0) {
- conn->negnpn = NPN_HTTP2;
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ conn->negnpn = CURL_HTTP_VERSION_2_0;
}
- else if(buflen == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1, buf,
- ALPN_HTTP_1_1_LENGTH)) {
- conn->negnpn = NPN_HTTP1_1;
+ else
+#endif
+ if(buflen == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
}
}
-#else
- (void)sock;
- (void)arg;
-#endif
}
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data,
+ PRBool *canFalseStart)
+{
+ struct connectdata *conn = client_data;
+ struct SessionHandle *data = conn->data;
+
+ SSLChannelInfo channelInfo;
+ SSLCipherSuiteInfo cipherInfo;
+
+ SECStatus rv;
+ PRBool negotiatedExtension;
+
+ *canFalseStart = PR_FALSE;
+
+ if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess)
+ return SECFailure;
+
+ if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
+ sizeof(cipherInfo)) != SECSuccess)
+ return SECFailure;
+
+ /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for
+ * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310
+ */
+ if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2)
+ goto end;
+
+ /* Only allow ECDHE key exchange algorithm.
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */
+ if(cipherInfo.keaType != ssl_kea_ecdh)
+ goto end;
+
+ /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC
+ * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt
+ * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */
+ if(cipherInfo.symCipher != ssl_calg_aes_gcm)
+ goto end;
+
+ /* Enforce ALPN or NPN to do False Start, as an indicator of server
+ * compatibility. */
+ rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn,
+ &negotiatedExtension);
+ if(rv != SECSuccess || !negotiatedExtension) {
+ rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn,
+ &negotiatedExtension);
+ }
+
+ if(rv != SECSuccess || !negotiatedExtension)
+ goto end;
+
+ *canFalseStart = PR_TRUE;
+
+ infof(data, "Trying TLS False Start\n");
+
+end:
+ return SECSuccess;
+}
+#endif
+
static void display_cert_info(struct SessionHandle *data,
CERTCertificate *cert)
{
@@ -723,8 +813,9 @@ static void display_cert_info(struct SessionHandle *data,
PR_Free(common_name);
}
-static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
+static CURLcode display_conn_info(struct connectdata *conn, PRFileDesc *sock)
{
+ CURLcode result = CURLE_OK;
SSLChannelInfo channel;
SSLCipherSuiteInfo suite;
CERTCertificate *cert;
@@ -743,7 +834,6 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
}
cert = SSL_PeerCertificate(sock);
-
if(cert) {
infof(conn->data, "Server certificate:\n");
@@ -768,21 +858,29 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
cert2 = cert3;
}
}
- Curl_ssl_init_certinfo(conn->data, i);
- for(i = 0; cert; cert = cert2) {
- Curl_extract_certinfo(conn, i++, (char *)cert->derCert.data,
- (char *)cert->derCert.data + cert->derCert.len);
- if(cert->isRoot) {
+
+ result = Curl_ssl_init_certinfo(conn->data, i);
+ if(!result) {
+ for(i = 0; cert; cert = cert2) {
+ result = Curl_extract_certinfo(conn, i++, (char *)cert->derCert.data,
+ (char *)cert->derCert.data +
+ cert->derCert.len);
+ if(result)
+ break;
+
+ if(cert->isRoot) {
+ CERT_DestroyCertificate(cert);
+ break;
+ }
+
+ cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
CERT_DestroyCertificate(cert);
- break;
}
- cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
- CERT_DestroyCertificate(cert);
}
}
}
- return;
+ return result;
}
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
@@ -820,7 +918,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
static SECStatus check_issuer_cert(PRFileDesc *sock,
char *issuer_nickname)
{
- CERTCertificate *cert,*cert_issuer,*issuer;
+ CERTCertificate *cert, *cert_issuer, *issuer;
SECStatus res=SECSuccess;
void *proto_win = NULL;
@@ -831,7 +929,7 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
*/
cert = SSL_PeerCertificate(sock);
- cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
+ cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner);
proto_win = SSL_RevealPinArg(sock);
issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
@@ -848,6 +946,53 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
return res;
}
+static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
+ const char *pinnedpubkey)
+{
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ struct SessionHandle *data = connssl->data;
+ CERTCertificate *cert;
+
+ if(!pinnedpubkey)
+ /* no pinned public key specified */
+ return CURLE_OK;
+
+ /* get peer certificate */
+ cert = SSL_PeerCertificate(connssl->handle);
+ if(cert) {
+ /* extract public key from peer certificate */
+ SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert);
+ if(pubkey) {
+ /* encode the public key as DER */
+ SECItem *cert_der = PK11_DEREncodePublicKey(pubkey);
+ if(cert_der) {
+ /* compare the public key with the pinned public key */
+ result = Curl_pin_peer_pubkey(pinnedpubkey,
+ cert_der->data,
+ cert_der->len);
+ SECITEM_FreeItem(cert_der, PR_TRUE);
+ }
+ SECKEY_DestroyPublicKey(pubkey);
+ }
+ CERT_DestroyCertificate(cert);
+ }
+
+ /* report the resulting status */
+ switch(result) {
+ case CURLE_OK:
+ infof(data, "pinned public key verified successfully!\n");
+ break;
+ case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+ failf(data, "failed to verify pinned public key");
+ break;
+ default:
+ /* OOM, etc. */
+ break;
+ }
+
+ return result;
+}
+
/**
*
* Callback to pick the SSL client certificate.
@@ -935,36 +1080,6 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
return SECSuccess;
}
-/* This function is supposed to decide, which error codes should be used
- * to conclude server is TLS intolerant.
- *
- * taken from xulrunner - nsNSSIOLayer.cpp
- */
-static PRBool
-isTLSIntoleranceError(PRInt32 err)
-{
- switch (err) {
- case SSL_ERROR_BAD_MAC_ALERT:
- case SSL_ERROR_BAD_MAC_READ:
- case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
- case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
- case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE:
- case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
- case SSL_ERROR_NO_CYPHER_OVERLAP:
- case SSL_ERROR_BAD_SERVER:
- case SSL_ERROR_BAD_BLOCK_PADDING:
- case SSL_ERROR_UNSUPPORTED_VERSION:
- case SSL_ERROR_PROTOCOL_VERSION_ALERT:
- case SSL_ERROR_RX_MALFORMED_FINISHED:
- case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE:
- case SSL_ERROR_DECODE_ERROR_ALERT:
- case SSL_ERROR_RX_UNKNOWN_ALERT:
- return PR_TRUE;
- default:
- return PR_FALSE;
- }
-}
-
/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */
static void nss_update_connecting_state(ssl_connect_state state, void *secret)
{
@@ -1019,6 +1134,7 @@ static PRStatus nspr_io_close(PRFileDesc *fd)
return close_fn(fd);
}
+/* data might be NULL */
static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir)
{
NSSInitParameters initparams;
@@ -1056,11 +1172,12 @@ static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir)
return CURLE_SSL_CACERT_BADFILE;
}
+/* data might be NULL */
static CURLcode nss_init(struct SessionHandle *data)
{
char *cert_dir;
struct_stat st;
- CURLcode rv;
+ CURLcode result;
if(initialized)
return CURLE_OK;
@@ -1102,14 +1219,15 @@ static CURLcode nss_init(struct SessionHandle *data)
nspr_io_methods.close = nspr_io_close;
}
- rv = nss_init_core(data, cert_dir);
- if(rv)
- return rv;
+ result = nss_init_core(data, cert_dir);
+ if(result)
+ return result;
if(num_enabled_ciphers() == 0)
NSS_SetDomesticPolicy();
initialized = 1;
+
return CURLE_OK;
}
@@ -1133,20 +1251,22 @@ int Curl_nss_init(void)
return 1;
}
+/* data might be NULL */
CURLcode Curl_nss_force_init(struct SessionHandle *data)
{
- CURLcode rv;
+ CURLcode result;
if(!nss_initlock) {
- failf(data,
- "unable to initialize NSS, curl_global_init() should have been "
- "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL");
+ if(data)
+ failf(data, "unable to initialize NSS, curl_global_init() should have "
+ "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL");
return CURLE_FAILED_INIT;
}
PR_Lock(nss_initlock);
- rv = nss_init(data);
+ result = nss_init(data);
PR_Unlock(nss_initlock);
- return rv;
+
+ return result;
}
/* Global cleanup */
@@ -1229,10 +1349,8 @@ void Curl_nss_close(struct connectdata *conn, int sockindex)
* authentication data from a previous connection. */
SSL_InvalidateSession(connssl->handle);
- if(connssl->client_nickname != NULL) {
- free(connssl->client_nickname);
- connssl->client_nickname = NULL;
- }
+ free(connssl->client_nickname);
+ connssl->client_nickname = NULL;
/* destroy all NSS objects in order to avoid failure of NSS shutdown */
Curl_llist_destroy(connssl->obj_list, NULL);
connssl->obj_list = NULL;
@@ -1243,16 +1361,6 @@ void Curl_nss_close(struct connectdata *conn, int sockindex)
}
}
-/*
- * This function is called when the 'data' struct is going away. Close
- * down everything and free all resources!
- */
-int Curl_nss_close_all(struct SessionHandle *data)
-{
- (void)data;
- return 0;
-}
-
/* return true if NSS can provide error code (and possibly msg) for the
error */
static bool is_nss_error(CURLcode err)
@@ -1295,9 +1403,9 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
const char *capath = data->set.ssl.CApath;
if(cafile) {
- CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
- if(CURLE_OK != rv)
- return rv;
+ CURLcode result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
+ if(result)
+ return result;
}
if(capath) {
@@ -1342,18 +1450,11 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
static CURLcode nss_init_sslver(SSLVersionRange *sslver,
struct SessionHandle *data)
{
- switch (data->set.ssl.version) {
+ switch(data->set.ssl.version) {
default:
case CURL_SSLVERSION_DEFAULT:
- sslver->min = SSL_LIBRARY_VERSION_3_0;
- if(data->state.ssl_connect_retry) {
- infof(data, "TLS disabled due to previous handshake failure\n");
- sslver->max = SSL_LIBRARY_VERSION_3_0;
- return CURLE_OK;
- }
- /* intentional fall-through to default to highest TLS version if possible */
-
case CURL_SSLVERSION_TLSv1:
+ sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
#ifdef SSL_LIBRARY_VERSION_TLS_1_2
sslver->max = SSL_LIBRARY_VERSION_TLS_1_2;
#elif defined SSL_LIBRARY_VERSION_TLS_1_1
@@ -1403,12 +1504,8 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl,
struct SessionHandle *data,
CURLcode curlerr)
{
- SSLVersionRange sslver;
PRErrorCode err = 0;
- /* reset the flag to avoid an infinite loop */
- data->state.ssl_connect_retry = FALSE;
-
if(is_nss_error(curlerr)) {
/* read NSPR error code */
err = PR_GetError();
@@ -1426,17 +1523,6 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl,
Curl_llist_destroy(connssl->obj_list, NULL);
connssl->obj_list = NULL;
- if(connssl->handle
- && (SSL_VersionRangeGet(connssl->handle, &sslver) == SECSuccess)
- && (sslver.min == SSL_LIBRARY_VERSION_3_0)
- && (sslver.max != SSL_LIBRARY_VERSION_3_0)
- && isTLSIntoleranceError(err)) {
- /* schedule reconnect through Curl_retry_request() */
- data->state.ssl_connect_retry = TRUE;
- infof(data, "Error in TLS handshake, trying SSLv3...\n");
- return CURLE_OK;
- }
-
return curlerr;
}
@@ -1464,27 +1550,13 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
struct SessionHandle *data = conn->data;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- CURLcode curlerr;
+ CURLcode result;
SSLVersionRange sslver = {
SSL_LIBRARY_VERSION_TLS_1_0, /* min */
SSL_LIBRARY_VERSION_TLS_1_0 /* max */
};
-#ifdef USE_NGHTTP2
-#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN)
- unsigned int alpn_protos_len = NGHTTP2_PROTO_VERSION_ID_LEN +
- ALPN_HTTP_1_1_LENGTH + 2;
- unsigned char alpn_protos[NGHTTP2_PROTO_VERSION_ID_LEN + ALPN_HTTP_1_1_LENGTH
- + 2];
- int cur = 0;
-#endif
-#endif
-
-
- if(connssl->state == ssl_connection_complete)
- return CURLE_OK;
-
connssl->data = data;
/* list of all NSS objects we need to destroy in Curl_nss_close() */
@@ -1494,13 +1566,13 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
/* FIXME. NSS doesn't support multiple databases open at the same time. */
PR_Lock(nss_initlock);
- curlerr = nss_init(conn->data);
- if(CURLE_OK != curlerr) {
+ result = nss_init(conn->data);
+ if(result) {
PR_Unlock(nss_initlock);
goto error;
}
- curlerr = CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
if(!mod) {
char *configstring = aprintf("library=%s name=PEM", pem_library);
@@ -1517,7 +1589,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
mod = NULL;
}
infof(data, "WARNING: failed to load NSS PEM library %s. Using "
- "OpenSSL PEM certificates will not work.\n", pem_library);
+ "OpenSSL PEM certificates will not work.\n", pem_library);
}
}
@@ -1560,12 +1632,9 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
#endif
- /* reset the flag to avoid an infinite loop */
- data->state.ssl_connect_retry = FALSE;
-
if(data->set.ssl.cipher_list) {
if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
- curlerr = CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
goto error;
}
}
@@ -1587,16 +1656,16 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
if(data->set.ssl.verifypeer) {
const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
- if(CURLE_OK != rv) {
- curlerr = rv;
+ if(rv) {
+ result = rv;
goto error;
}
}
if(data->set.ssl.CRLfile) {
const CURLcode rv = nss_load_crl(data->set.ssl.CRLfile);
- if(CURLE_OK != rv) {
- curlerr = rv;
+ if(rv) {
+ result = rv;
goto error;
}
infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile);
@@ -1611,9 +1680,9 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
else {
CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
data->set.str[STRING_KEY]);
- if(CURLE_OK != rv) {
+ if(rv) {
/* failf() is already done in cert_stuff() */
- curlerr = rv;
+ result = rv;
goto error;
}
}
@@ -1626,7 +1695,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
if(SSL_GetClientAuthDataHook(model, SelectClientCert,
(void *)connssl) != SECSuccess) {
- curlerr = CURLE_SSL_CERTPROBLEM;
+ result = CURLE_SSL_CERTPROBLEM;
goto error;
}
@@ -1667,42 +1736,57 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
}
-#ifdef USE_NGHTTP2
- if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ if(data->set.ssl.verifystatus) {
+ if(SSL_OptionSet(connssl->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE)
+ != SECSuccess)
+ goto error;
+ }
+#endif
+
#ifdef SSL_ENABLE_NPN
- if(data->set.ssl_enable_npn) {
- if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, PR_TRUE) != SECSuccess)
- goto error;
- }
+ if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, data->set.ssl_enable_npn
+ ? PR_TRUE : PR_FALSE) != SECSuccess)
+ goto error;
#endif
#ifdef SSL_ENABLE_ALPN
- if(data->set.ssl_enable_alpn) {
- if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, PR_TRUE)
- != SECSuccess)
- goto error;
- }
+ if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, data->set.ssl_enable_alpn
+ ? PR_TRUE : PR_FALSE) != SECSuccess)
+ goto error;
+#endif
+
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+ if(data->set.ssl.falsestart) {
+ if(SSL_OptionSet(connssl->handle, SSL_ENABLE_FALSE_START, PR_TRUE)
+ != SECSuccess)
+ goto error;
+
+ if(SSL_SetCanFalseStartCallback(connssl->handle, CanFalseStartCallback,
+ conn) != SECSuccess)
+ goto error;
+ }
#endif
#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN)
- if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) {
- alpn_protos[cur] = NGHTTP2_PROTO_VERSION_ID_LEN;
- cur++;
- memcpy(&alpn_protos[cur], NGHTTP2_PROTO_VERSION_ID,
+ if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) {
+ int cur = 0;
+ unsigned char protocols[128];
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+ protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
+ memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
NGHTTP2_PROTO_VERSION_ID_LEN);
cur += NGHTTP2_PROTO_VERSION_ID_LEN;
- alpn_protos[cur] = ALPN_HTTP_1_1_LENGTH;
- cur++;
- memcpy(&alpn_protos[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
-
- if(SSL_SetNextProtoNego(connssl->handle, alpn_protos, alpn_protos_len)
- != SECSuccess)
- goto error;
- }
- else {
- infof(data, "SSL, can't negotiate HTTP/2.0 with neither NPN nor ALPN\n");
}
#endif
+ protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+
+ if(SSL_SetNextProtoNego(connssl->handle, protocols, cur) != SECSuccess)
+ goto error;
}
#endif
@@ -1718,21 +1802,21 @@ error:
if(model)
PR_Close(model);
- return nss_fail_connect(connssl, data, curlerr);
+ return nss_fail_connect(connssl, data, result);
}
static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct SessionHandle *data = conn->data;
- CURLcode curlerr = CURLE_SSL_CONNECT_ERROR;
+ CURLcode result = CURLE_SSL_CONNECT_ERROR;
PRUint32 timeout;
/* check timeout situation */
const long time_left = Curl_timeleft(data, NULL, TRUE);
if(time_left < 0L) {
failf(data, "timed out before SSL handshake");
- curlerr = CURLE_OPERATION_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
goto error;
}
@@ -1743,17 +1827,15 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
/* blocking direction is updated by nss_update_connecting_state() */
return CURLE_AGAIN;
else if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
- curlerr = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
else if(conn->data->set.ssl.certverifyresult!=0)
- curlerr = CURLE_SSL_CACERT;
+ result = CURLE_SSL_CACERT;
goto error;
}
- connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = nss_recv;
- conn->send[sockindex] = nss_send;
-
- display_conn_info(conn, connssl->handle);
+ result = display_conn_info(conn, connssl->handle);
+ if(result)
+ goto error;
if(data->set.str[STRING_SSL_ISSUERCERT]) {
SECStatus ret = SECFailure;
@@ -1765,8 +1847,8 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
}
if(SECFailure == ret) {
- infof(data,"SSL certificate issuer check failed\n");
- curlerr = CURLE_SSL_ISSUER_ERROR;
+ infof(data, "SSL certificate issuer check failed\n");
+ result = CURLE_SSL_ISSUER_ERROR;
goto error;
}
else {
@@ -1774,10 +1856,15 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
}
}
+ result = cmp_peer_pubkey(connssl, data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ /* status already printed */
+ goto error;
+
return CURLE_OK;
error:
- return nss_fail_connect(connssl, data, curlerr);
+ return nss_fail_connect(connssl, data, result);
}
static CURLcode nss_connect_common(struct connectdata *conn, int sockindex,
@@ -1786,26 +1873,29 @@ static CURLcode nss_connect_common(struct connectdata *conn, int sockindex,
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct SessionHandle *data = conn->data;
const bool blocking = (done == NULL);
- CURLcode rv;
+ CURLcode result;
+
+ if(connssl->state == ssl_connection_complete)
+ return CURLE_OK;
if(connssl->connecting_state == ssl_connect_1) {
- rv = nss_setup_connect(conn, sockindex);
- if(rv)
+ result = nss_setup_connect(conn, sockindex);
+ if(result)
/* we do not expect CURLE_AGAIN from nss_setup_connect() */
- return rv;
+ return result;
if(!blocking) {
/* in non-blocking mode, set NSS non-blocking mode before handshake */
- rv = nss_set_nonblock(connssl, data);
- if(rv)
- return rv;
+ result = nss_set_nonblock(connssl, data);
+ if(result)
+ return result;
}
connssl->connecting_state = ssl_connect_2;
}
- rv = nss_do_connect(conn, sockindex);
- switch(rv) {
+ result = nss_do_connect(conn, sockindex);
+ switch(result) {
case CURLE_OK:
break;
case CURLE_AGAIN:
@@ -1814,20 +1904,26 @@ static CURLcode nss_connect_common(struct connectdata *conn, int sockindex,
return CURLE_OK;
/* fall through */
default:
- return rv;
+ return result;
}
if(blocking) {
/* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */
- rv = nss_set_nonblock(connssl, data);
- if(rv)
- return rv;
+ result = nss_set_nonblock(connssl, data);
+ if(result)
+ return result;
}
else
/* signal completed SSL handshake */
*done = TRUE;
- connssl->connecting_state = ssl_connect_done;
+ connssl->state = ssl_connection_complete;
+ conn->recv[sockindex] = nss_recv;
+ conn->send[sockindex] = nss_send;
+
+ /* ssl_connect_done is never used outside, go back to the initial state */
+ connssl->connecting_state = ssl_connect_1;
+
return CURLE_OK;
}
@@ -1866,8 +1962,10 @@ static ssize_t nss_send(struct connectdata *conn, /* connection data */
? CURLE_SSL_CERTPROBLEM
: CURLE_SEND_ERROR;
}
+
return -1;
}
+
return rc; /* number of bytes */
}
@@ -1897,8 +1995,10 @@ static ssize_t nss_recv(struct connectdata * conn, /* connection data */
? CURLE_SSL_CERTPROBLEM
: CURLE_RECV_ERROR;
}
+
return -1;
}
+
return nread;
}
@@ -1907,6 +2007,7 @@ size_t Curl_nss_version(char *buffer, size_t size)
return snprintf(buffer, size, "NSS/%s", NSS_VERSION);
}
+/* data might be NULL */
int Curl_nss_seed(struct SessionHandle *data)
{
/* make sure that NSS is initialized */
@@ -1918,13 +2019,12 @@ int Curl_nss_random(struct SessionHandle *data,
unsigned char *entropy,
size_t length)
{
- if(data)
- Curl_nss_seed(data); /* Initiate the seed if not already done */
- if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) {
- /* no way to signal a failure from here, we have to abort */
- failf(data, "PK11_GenerateRandom() failed, calling abort()...");
- abort();
- }
+ Curl_nss_seed(data); /* Initiate the seed if not already done */
+
+ if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length)))
+ /* signal a failure */
+ return -1;
+
return 0;
}
@@ -1935,9 +2035,40 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */
{
PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5);
unsigned int MD5out;
+
PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen));
PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len));
PK11_DestroyContext(MD5pw, PR_TRUE);
}
+void Curl_nss_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+ PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256);
+ unsigned int SHA256out;
+
+ PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen));
+ PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len));
+ PK11_DestroyContext(SHA256pw, PR_TRUE);
+}
+
+bool Curl_nss_cert_status_request(void)
+{
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+bool Curl_nss_false_start(void) {
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
#endif /* USE_NSS */
diff --git a/lib/vtls/nssg.h b/lib/vtls/nssg.h
index 311f873..5fd7275 100644
--- a/lib/vtls/nssg.h
+++ b/lib/vtls/nssg.h
@@ -7,7 +7,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
@@ -37,10 +37,6 @@ CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn,
/* close a SSL connection */
void Curl_nss_close(struct connectdata *conn, int sockindex);
-/* tell NSS to close down all open information regarding connections (and
- thus session ID caching etc) */
-int Curl_nss_close_all(struct SessionHandle *data);
-
int Curl_nss_init(void);
void Curl_nss_cleanup(void);
@@ -60,8 +56,23 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */
unsigned char *md5sum, /* output */
size_t md5len);
-/* this backend provides these functions: */
-#define have_curlssl_md5sum 1
+void Curl_nss_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len);
+
+bool Curl_nss_cert_status_request(void);
+
+bool Curl_nss_false_start(void);
+
+/* Set the API backend definition to NSS */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_NSS
+
+/* this backend supports the CAPATH option */
+#define have_curlssl_ca_path 1
+
+/* this backend supports CURLOPT_CERTINFO */
+#define have_curlssl_certinfo 1
/* API setup for NSS */
#define curlssl_init Curl_nss_init
@@ -71,19 +82,21 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */
/* NSS has its own session ID cache */
#define curlssl_session_free(x) Curl_nop_stmt
-#define curlssl_close_all Curl_nss_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_nss_close
/* NSS has no shutdown function provided and thus always fail */
-#define curlssl_shutdown(x,y) (x=x, y=y, 1)
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_shutdown(x,y) ((void)x, (void)y, 1)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_nss_version
#define curlssl_check_cxn(x) Curl_nss_check_cxn(x)
-#define curlssl_data_pending(x,y) (x=x, y=y, 0)
+#define curlssl_data_pending(x,y) ((void)x, (void)y, 0)
#define curlssl_random(x,y,z) Curl_nss_random(x,y,z)
#define curlssl_md5sum(a,b,c,d) Curl_nss_md5sum(a,b,c,d)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_NSS
+#define curlssl_sha256sum(a,b,c,d) Curl_nss_sha256sum(a,b,c,d)
+#define curlssl_cert_status_request() Curl_nss_cert_status_request()
+#define curlssl_false_start() Curl_nss_false_start()
#endif /* USE_NSS */
#endif /* HEADER_CURL_NSSG_H */
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index da92854..90e4c2b 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.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
@@ -32,6 +32,8 @@
#include "curl_setup.h"
+#ifdef USE_OPENSSL
+
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
@@ -49,13 +51,9 @@
#include "vtls.h"
#include "rawstr.h"
#include "hostcheck.h"
+#include "curl_printf.h"
-#define _MPRINTF_REPLACE /* use the internal *printf() functions */
-#include <curl/mprintf.h>
-
-#ifdef USE_SSLEAY
-
-#ifdef USE_OPENSSL
+#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/x509v3.h>
#include <openssl/dsa.h>
@@ -63,36 +61,29 @@
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/conf.h>
-#else
-#include <rand.h>
-#include <x509v3.h>
-#include <md5.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+#ifdef HAVE_OPENSSL_PKCS12_H
+#include <openssl/pkcs12.h>
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_IS_BORINGSSL)
+#include <openssl/ocsp.h>
#endif
#include "warnless.h"
-#include "curl_memory.h"
#include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */
-/* The last #include file should be: */
+/* The last #include files should be: */
+#include "curl_memory.h"
#include "memdebug.h"
#ifndef OPENSSL_VERSION_NUMBER
#error "OPENSSL_VERSION_NUMBER not defined"
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x0090581fL
-#define HAVE_SSL_GET1_SESSION 1
-#else
-#undef HAVE_SSL_GET1_SESSION
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00904100L
-#define HAVE_USERDATA_IN_PWD_CALLBACK 1
-#else
-#undef HAVE_USERDATA_IN_PWD_CALLBACK
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x00907001L
+#if OPENSSL_VERSION_NUMBER >= 0x00907001L && !defined(OPENSSL_IS_BORINGSSL)
/* ENGINE_load_private_key() takes four arguments */
#define HAVE_ENGINE_LOAD_FOUR_ARGS
#include <openssl/ui.h>
@@ -101,18 +92,16 @@
#undef HAVE_ENGINE_LOAD_FOUR_ARGS
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H)
-/* OpenSSL has PKCS 12 support */
+#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && \
+ defined(HAVE_OPENSSL_PKCS12_H) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+/* OpenSSL has PKCS 12 support, BoringSSL does not */
#define HAVE_PKCS12_SUPPORT
#else
-/* OpenSSL/SSLEay does not have PKCS12 support */
+/* OpenSSL does not have PKCS12 support */
#undef HAVE_PKCS12_SUPPORT
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x00906001L
-#define HAVE_ERR_ERROR_STRING_N 1
-#endif
-
#if OPENSSL_VERSION_NUMBER >= 0x00909000L
#define SSL_METHOD_QUAL const
#else
@@ -126,15 +115,32 @@
#define X509_STORE_set_flags(x,y) Curl_nop_stmt
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+#ifdef OPENSSL_IS_BORINGSSL
+/* BoringSSL has no ERR_remove_state() */
+#define ERR_remove_state(x)
+#elif (OPENSSL_VERSION_NUMBER >= 0x10000000L)
#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
+#if defined(OPENSSL_IS_BORINGSSL)
+#define NO_RAND_SEED 1
+/* In BoringSSL OpenSSL_add_all_algorithms does nothing */
+#define OpenSSL_add_all_algorithms()
+/* BoringSSL does not have CONF_modules_load_file */
+#define CONF_modules_load_file(a,b,c)
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) || defined(OPENSSL_IS_BORINGSSL)
+/* not present in BoringSSL or older OpenSSL */
+#define OPENSSL_load_builtin_modules(x)
+#endif
+
/*
* Number of bytes to read from the random number seed file. This must be
* a finite value (because some entropy "files" like /dev/urandom have
@@ -143,18 +149,8 @@
*/
#define RAND_LOAD_LENGTH 1024
-#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
-static char global_passwd[64];
-#endif
-
-static int passwd_callback(char *buf, int num, int encrypting
-#ifdef HAVE_USERDATA_IN_PWD_CALLBACK
- /* This was introduced in 0.9.4, we can set this
- using SSL_CTX_set_default_passwd_cb_userdata()
- */
- , void *global_passwd
-#endif
- )
+static int passwd_callback(char *buf, int num, int encrypting,
+ void *global_passwd)
{
DEBUGASSERT(0 == encrypting);
@@ -175,6 +171,7 @@ static int passwd_callback(char *buf, int num, int encrypting
* pass in an argument that is never used.
*/
+#ifndef NO_RAND_SEED
#ifdef HAVE_RAND_STATUS
#define seed_enough(x) rand_enough()
static bool rand_enough(void)
@@ -259,7 +256,7 @@ static int ossl_seed(struct SessionHandle *data)
return nread;
}
-static int Curl_ossl_seed(struct SessionHandle *data)
+static void Curl_ossl_seed(struct SessionHandle *data)
{
/* we have the "SSL is seeded" boolean static to prevent multiple
time-consuming seedings in vain */
@@ -270,8 +267,11 @@ static int Curl_ossl_seed(struct SessionHandle *data)
ossl_seed(data);
ssl_seeded = TRUE;
}
- return 0;
}
+#else
+/* BoringSSL needs no seeding */
+#define Curl_ossl_seed(x)
+#endif
#ifndef SSL_FILETYPE_ENGINE
@@ -308,8 +308,7 @@ static int ssl_ui_reader(UI *ui, UI_STRING *uis)
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;
}
@@ -327,8 +326,8 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis)
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:
@@ -350,43 +349,29 @@ int cert_stuff(struct connectdata *conn,
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(data->set.str[STRING_KEY_PASSWD]) {
-#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
- /*
- * If password has been given, we store that in the global
- * area (*shudder*) for a while:
- */
- size_t len = strlen(data->set.str[STRING_KEY_PASSWD]);
- if(len < sizeof(global_passwd))
- memcpy(global_passwd, data->set.str[STRING_KEY_PASSWD], len+1);
- else
- global_passwd[0] = '\0';
-#else
- /*
- * We set the password in the callback userdata
- */
+ /* set the password in the callback userdata */
SSL_CTX_set_default_passwd_cb_userdata(ctx,
data->set.str[STRING_KEY_PASSWD]);
-#endif
/* Set passwd callback: */
SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
}
-#define SSL_CLIENT_CERT_ERR \
- "unable to use client certificate (no key found or wrong pass phrase?)"
-
switch(file_type) {
case SSL_FILETYPE_PEM:
/* SSL_CTX_use_certificate_chain_file() only works on PEM files */
if(SSL_CTX_use_certificate_chain_file(ctx,
cert_file) != 1) {
- failf(data, SSL_CLIENT_CERT_ERR);
+ failf(data,
+ "could not load PEM client certificate, OpenSSL error %s, "
+ "(no key found, wrong pass phrase, or wrong file format?)",
+ ERR_error_string(ERR_get_error(), NULL) );
return 0;
}
break;
@@ -398,7 +383,10 @@ int cert_stuff(struct connectdata *conn,
if(SSL_CTX_use_certificate_file(ctx,
cert_file,
file_type) != 1) {
- failf(data, SSL_CLIENT_CERT_ERR);
+ failf(data,
+ "could not load ASN1 client certificate, OpenSSL error %s, "
+ "(no key found, wrong pass phrase, or wrong file format?)",
+ ERR_error_string(ERR_get_error(), NULL) );
return 0;
}
break;
@@ -464,7 +452,7 @@ int cert_stuff(struct connectdata *conn,
STACK_OF(X509) *ca = NULL;
int i;
- f = fopen(cert_file,"rb");
+ f = fopen(cert_file, "rb");
if(!f) {
failf(data, "could not open PKCS12 file '%s'", cert_file);
return 0;
@@ -473,7 +461,7 @@ int cert_stuff(struct connectdata *conn,
fclose(f);
if(!p12) {
- failf(data, "error reading PKCS12 file '%s'", cert_file );
+ failf(data, "error reading PKCS12 file '%s'", cert_file);
return 0;
}
@@ -491,7 +479,9 @@ int cert_stuff(struct connectdata *conn,
PKCS12_free(p12);
if(SSL_CTX_use_certificate(ctx, x509) != 1) {
- failf(data, SSL_CLIENT_CERT_ERR);
+ failf(data,
+ "could not load PKCS12 client certificate, OpenSSL error %s",
+ ERR_error_string(ERR_get_error(), NULL) );
goto fail;
}
@@ -556,7 +546,7 @@ int cert_stuff(struct connectdata *conn,
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:
@@ -574,7 +564,7 @@ int cert_stuff(struct connectdata *conn,
#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;
}
@@ -585,7 +575,7 @@ int cert_stuff(struct connectdata *conn,
#endif
/* the typecast below was added to please mingw32 */
priv_key = (EVP_PKEY *)
- ENGINE_load_private_key(data->state.engine,key_file,
+ ENGINE_load_private_key(data->state.engine, key_file,
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
ui_method,
#endif
@@ -626,8 +616,8 @@ int cert_stuff(struct connectdata *conn,
}
ssl=SSL_new(ctx);
- if(NULL == ssl) {
- failf(data,"unable to create an SSL structure");
+ if(!ssl) {
+ failf(data, "unable to create an SSL structure");
return 0;
}
@@ -635,9 +625,9 @@ int cert_stuff(struct connectdata *conn,
/* 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_copy_parameters(pktmp, SSL_get_privatekey(ssl));
EVP_PKEY_free(pktmp);
}
@@ -653,10 +643,6 @@ int cert_stuff(struct connectdata *conn,
failf(data, "Private key does not match the certificate public key");
return 0;
}
-#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
- /* erase it now */
- memset(global_passwd, 0, sizeof(global_passwd));
-#endif
}
return 1;
}
@@ -691,36 +677,17 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
#endif
}
-static
-int cert_verify_callback(int ok, X509_STORE_CTX *ctx)
-{
- X509 *err_cert;
- char buf[256];
-
- err_cert=X509_STORE_CTX_get_current_cert(ctx);
- (void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
- return ok;
-}
-
/* Return error string for last OpenSSL error
*/
static char *SSL_strerror(unsigned long error, char *buf, size_t size)
{
-#ifdef HAVE_ERR_ERROR_STRING_N
/* OpenSSL 0.9.6 and later has a function named
- ERRO_error_string_n() that takes the size of the buffer as a
+ ERR_error_string_n() that takes the size of the buffer as a
third argument */
ERR_error_string_n(error, buf, size);
-#else
- (void) size;
- ERR_error_string(error, buf);
-#endif
return buf;
}
-#endif /* USE_SSLEAY */
-
-#ifdef USE_SSLEAY
/**
* Global SSL init
*
@@ -729,6 +696,8 @@ static char *SSL_strerror(unsigned long error, char *buf, size_t size)
*/
int Curl_ossl_init(void)
{
+ OPENSSL_load_builtin_modules();
+
#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
ENGINE_load_builtin_engines();
#endif
@@ -749,17 +718,19 @@ int Curl_ossl_init(void)
calls CONF_modules_load_file() and we use that instead and we ignore
its return code! */
- (void)CONF_modules_load_file(NULL, NULL,
- CONF_MFLAGS_DEFAULT_SECTION|
- CONF_MFLAGS_IGNORE_MISSING_FILE);
+ /* 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
+
+ CONF_modules_load_file(NULL, NULL,
+ CONF_MFLAGS_DEFAULT_SECTION|
+ CONF_MFLAGS_IGNORE_MISSING_FILE);
return 1;
}
-#endif /* USE_SSLEAY */
-
-#ifdef USE_SSLEAY
-
/* Global cleanup */
void Curl_ossl_cleanup(void)
{
@@ -814,7 +785,7 @@ int Curl_ossl_check_cxn(struct connectdata *conn)
*/
CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine)
{
-#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_ENGINE_H)
ENGINE *e;
#if OPENSSL_VERSION_NUMBER >= 0x00909000L
@@ -862,7 +833,7 @@ CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
#ifdef HAVE_OPENSSL_ENGINE_H
if(data->state.engine) {
if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
- infof(data,"set default crypto engine '%s'\n",
+ infof(data, "set default crypto engine '%s'\n",
ENGINE_get_id(data->state.engine));
}
else {
@@ -882,7 +853,7 @@ CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
{
struct curl_slist *list = NULL;
-#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_ENGINE_H)
struct curl_slist *beg;
ENGINE *e;
@@ -1031,7 +1002,7 @@ void Curl_ossl_session_free(void *ptr)
* 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) {
@@ -1042,7 +1013,6 @@ int Curl_ossl_close_all(struct SessionHandle *data)
#else
(void)data;
#endif
- return 0;
}
static int asn1_output(const ASN1_UTCTIME *tm,
@@ -1052,7 +1022,7 @@ static int asn1_output(const ASN1_UTCTIME *tm,
const char *asn1_string;
int gmt=FALSE;
int i;
- int year=0,month=0,day=0,hour=0,minute=0,second=0;
+ int year=0, month=0, day=0, hour=0, minute=0, second=0;
i=tm->length;
asn1_string=(const char *)tm->data;
@@ -1112,8 +1082,7 @@ 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 */
@@ -1126,7 +1095,7 @@ static CURLcode verifyhost(struct connectdata *conn,
#else
struct in_addr addr;
#endif
- CURLcode res = CURLE_OK;
+ CURLcode result = CURLE_OK;
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
@@ -1207,19 +1176,19 @@ static CURLcode verifyhost(struct connectdata *conn,
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
distinguished one to get the most significant one. */
- int j,i=-1 ;
+ int j, i=-1;
/* The following is done because of a bug in 0.9.6b */
unsigned char *nulstr = (unsigned char *)"";
unsigned char *peer_CN = nulstr;
- X509_NAME *name = X509_get_subject_name(server_cert) ;
+ X509_NAME *name = X509_get_subject_name(server_cert);
if(name)
while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0)
i=j;
@@ -1229,7 +1198,8 @@ static CURLcode verifyhost(struct connectdata *conn,
UTF8 etc. */
if(i>=0) {
- ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
+ ASN1_STRING *tmp =
+ X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
/* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
is already UTF-8 encoded. We check for this case and copy the raw
@@ -1254,7 +1224,7 @@ static CURLcode verifyhost(struct connectdata *conn,
/* 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;
}
}
}
@@ -1271,18 +1241,18 @@ static CURLcode verifyhost(struct connectdata *conn,
}
}
- 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);
@@ -1290,9 +1260,138 @@ static CURLcode verifyhost(struct connectdata *conn,
if(peer_CN)
OPENSSL_free(peer_CN);
}
- return res;
+
+ return result;
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+static CURLcode verifystatus(struct connectdata *conn,
+ struct ssl_connect_data *connssl)
+{
+ int i, ocsp_status;
+ const unsigned char *p;
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+
+ OCSP_RESPONSE *rsp = NULL;
+ OCSP_BASICRESP *br = NULL;
+ X509_STORE *st = NULL;
+ STACK_OF(X509) *ch = NULL;
+
+ long len = SSL_get_tlsext_status_ocsp_resp(connssl->handle, &p);
+
+ if(!p) {
+ failf(data, "No OCSP response received");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if(!rsp) {
+ failf(data, "Invalid OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ ocsp_status = OCSP_response_status(rsp);
+ if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ failf(data, "Invalid OCSP response status: %s (%d)",
+ OCSP_response_status_str(ocsp_status), ocsp_status);
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ br = OCSP_response_get1_basic(rsp);
+ if(!br) {
+ failf(data, "Invalid OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ ch = SSL_get_peer_cert_chain(connssl->handle);
+ st = SSL_CTX_get_cert_store(connssl->ctx);
+
+#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
+ defined(LIBRESSL_VERSION_NUMBER))
+ /* The authorized responder cert in the OCSP response MUST be signed by the
+ peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert,
+ no problem, but if it's an intermediate cert OpenSSL has a bug where it
+ expects this issuer to be present in the chain embedded in the OCSP
+ response. So we add it if necessary. */
+
+ /* First make sure the peer cert chain includes both a peer and an issuer,
+ and the OCSP response contains a responder cert. */
+ if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
+ X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);
+
+ /* Find issuer of responder cert and add it to the OCSP response chain */
+ for(i = 0; i < sk_X509_num(ch); i++) {
+ X509 *issuer = sk_X509_value(ch, i);
+ if(X509_check_issued(issuer, responder) == X509_V_OK) {
+ if(!OCSP_basic_add1_cert(br, issuer)) {
+ failf(data, "Could not add issuer cert to OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ }
+ }
+ }
+#endif
+
+ if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
+ failf(data, "OCSP response verification failed");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ for(i = 0; i < OCSP_resp_count(br); i++) {
+ int cert_status, crl_reason;
+ OCSP_SINGLERESP *single = NULL;
+
+ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+
+ if(!(single = OCSP_resp_get0(br, i)))
+ continue;
+
+ cert_status = OCSP_single_get0_status(single, &crl_reason, &rev,
+ &thisupd, &nextupd);
+
+ if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) {
+ failf(data, "OCSP response has expired");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ infof(data, "SSL certificate status: %s (%d)\n",
+ OCSP_cert_status_str(cert_status), cert_status);
+
+ switch(cert_status) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ break;
+
+ case V_OCSP_CERTSTATUS_REVOKED:
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+
+ failf(data, "SSL certificate revocation reason: %s (%d)",
+ OCSP_crl_reason_str(crl_reason), crl_reason);
+ goto end;
+
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ }
+
+end:
+ if(br) OCSP_BASICRESP_free(br);
+ OCSP_RESPONSE_free(rsp);
+
+ return result;
}
-#endif /* USE_SSLEAY */
+#endif
+
+#endif /* USE_OPENSSL */
/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
and thus this cannot be done there. */
@@ -1300,6 +1399,7 @@ static CURLcode verifyhost(struct connectdata *conn,
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:
@@ -1322,7 +1422,9 @@ static const char *ssl_msg_type(int ssl_ver, int msg)
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";
@@ -1330,8 +1432,12 @@ static const char *ssl_msg_type(int ssl_ver, int msg)
return "Client hello";
case SSL3_MT_SERVER_HELLO:
return "Server hello";
+#ifdef SSL3_MT_NEWSESSION_TICKET
+ case SSL3_MT_NEWSESSION_TICKET:
+ return "Newsession Ticket";
+#endif
case SSL3_MT_CERTIFICATE:
- return "CERT";
+ return "Certificate";
case SSL3_MT_SERVER_KEY_EXCHANGE:
return "Server key exchange";
case SSL3_MT_CLIENT_KEY_EXCHANGE:
@@ -1344,6 +1450,10 @@ static const char *ssl_msg_type(int ssl_ver, int msg)
return "CERT verify";
case SSL3_MT_FINISHED:
return "Finished";
+#ifdef SSL3_MT_CERTIFICATE_STATUS
+ case SSL3_MT_CERTIFICATE_STATUS:
+ return "Certificate Status";
+#endif
}
}
return "Unknown";
@@ -1351,12 +1461,22 @@ static const char *ssl_msg_type(int ssl_ver, int msg)
static const char *tls_rt_type(int type)
{
- return (
- type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
- type == SSL3_RT_ALERT ? "TLS alert, " :
- type == SSL3_RT_HANDSHAKE ? "TLS handshake, " :
- type == SSL3_RT_APPLICATION_DATA ? "TLS app data, " :
- "TLS Unknown, ");
+ switch(type) {
+#ifdef SSL3_RT_HEADER
+ case SSL3_RT_HEADER:
+ return "TLS header";
+#endif
+ case SSL3_RT_CHANGE_CIPHER_SPEC:
+ return "TLS change cipher";
+ case SSL3_RT_ALERT:
+ return "TLS alert";
+ case SSL3_RT_HANDSHAKE:
+ return "TLS handshake";
+ case SSL3_RT_APPLICATION_DATA:
+ return "TLS app data";
+ default:
+ return "TLS Unknown";
+ }
}
@@ -1364,38 +1484,77 @@ static const char *tls_rt_type(int type)
* Our callback from the SSL/TLS layers.
*/
static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
- const void *buf, size_t len, const SSL *ssl,
- struct connectdata *conn)
+ const void *buf, size_t len, SSL *ssl,
+ void *userp)
{
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 = NULL;
+ struct connectdata *conn = userp;
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' : '?');
- /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
- * always pass-up content-type as 0. But the interesting message-type
- * is at 'buf[0]'.
- */
- if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
- tls_rt_name = tls_rt_type(content_type);
- else
- tls_rt_name = "";
+ switch(ssl_ver) {
+#ifdef SSL2_VERSION /* removed in recent versions */
+ case SSL2_VERSION:
+ 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
+ case 0:
+ break;
+ default:
+ snprintf(unknown, sizeof(unknown), "(%x)", ssl_ver);
+ verstr = unknown;
+ break;
+ }
+
+ if(ssl_ver) {
+ /* the info given when the version is zero is not that useful for us */
+
+ ssl_ver >>= 8; /* check the upper 8 bits only below */
- msg_type = *(char*)buf;
- msg_name = ssl_msg_type(ssl_ver, msg_type);
+ /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
+ * always pass-up content-type as 0. But the interesting message-type
+ * is at 'buf[0]'.
+ */
+ if(ssl_ver == SSL3_VERSION_MAJOR && content_type)
+ tls_rt_name = tls_rt_type(content_type);
+ else
+ tls_rt_name = "";
- txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
- ver, tls_rt_name, msg_name, msg_type);
- Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
+ msg_type = *(char*)buf;
+ msg_name = ssl_msg_type(ssl_ver, msg_type);
+
+ txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n",
+ verstr, direction?"OUT":"IN",
+ 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 :
CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
@@ -1403,7 +1562,7 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
}
#endif
-#ifdef USE_SSLEAY
+#ifdef USE_OPENSSL
/* ====================================================== */
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -1412,26 +1571,44 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
# define use_sni(x) Curl_nop_stmt
#endif
-#ifdef USE_NGHTTP2
-
+/* Check for OpenSSL 1.0.2 which has ALPN support. */
#undef HAS_ALPN
-#if defined(HAVE_SSL_CTX_SET_ALPN_PROTOS) && \
- defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
+ && !defined(OPENSSL_NO_TLSEXT)
# define HAS_ALPN 1
#endif
-#if !defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || \
- defined(OPENSSL_NO_NEXTPROTONEG)
-# if !defined(HAS_ALPN)
-# error http2 builds require OpenSSL with NPN or ALPN support
-# 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_protocol(unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ const char *key, unsigned int keylen)
+{
+ unsigned int i;
+ for(i = 0; i + keylen <= inlen; i += in[i] + 1) {
+ if(memcmp(&in[i + 1], key, keylen) == 0) {
+ *out = (unsigned char *) &in[i + 1];
+ *outlen = in[i];
+ return 0;
+ }
+ }
+ return -1;
+}
+
static int
select_next_proto_cb(SSL *ssl,
unsigned char **out, unsigned char *outlen,
@@ -1439,37 +1616,43 @@ select_next_proto_cb(SSL *ssl,
void *arg)
{
struct connectdata *conn = (struct connectdata*) arg;
- int retval = nghttp2_select_next_protocol(out, outlen, in, inlen);
+
(void)ssl;
- if(retval == 1) {
+#ifdef USE_NGHTTP2
+ if(conn->data->set.httpversion == CURL_HTTP_VERSION_2_0 &&
+ !select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN)) {
infof(conn->data, "NPN, negotiated HTTP2 (%s)\n",
NGHTTP2_PROTO_VERSION_ID);
- conn->negnpn = NPN_HTTP2;
+ conn->negnpn = CURL_HTTP_VERSION_2_0;
+ return SSL_TLSEXT_ERR_OK;
}
- else if(retval == 0) {
+#endif
+
+ if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1,
+ ALPN_HTTP_1_1_LENGTH)) {
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;
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+ return SSL_TLSEXT_ERR_OK;
}
+ infof(conn->data, "NPN, no overlap, use HTTP1.1\n");
+ *out = (unsigned char *)ALPN_HTTP_1_1;
+ *outlen = ALPN_HTTP_1_1_LENGTH;
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+
return SSL_TLSEXT_ERR_OK;
}
-#endif
+#endif /* HAS_NPN */
static const char *
-get_ssl_version_txt(SSL_SESSION *session)
+get_ssl_version_txt(SSL *ssl)
{
- if(NULL == session)
+ if(!ssl)
return "";
- switch(session->ssl_version) {
+ switch(SSL_version(ssl)) {
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
case TLS1_2_VERSION:
return "TLSv1.2";
@@ -1486,17 +1669,14 @@ get_ssl_version_txt(SSL_SESSION *session)
return "unknown";
}
-
-static CURLcode
-ossl_connect_step1(struct connectdata *conn,
- int sockindex)
+static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
{
- CURLcode retcode = CURLE_OK;
+ 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;
@@ -1508,9 +1688,6 @@ ossl_connect_step1(struct connectdata *conn,
struct in_addr addr;
#endif
#endif
-#ifdef HAS_ALPN
- unsigned char protocols[128];
-#endif
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
@@ -1529,7 +1706,12 @@ ossl_connect_step1(struct connectdata *conn,
case CURL_SSLVERSION_TLSv1_1:
case CURL_SSLVERSION_TLSv1_2:
/* it will be handled later with the context options */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+ req_method = TLS_client_method();
+#else
req_method = SSLv23_client_method();
+#endif
use_sni(TRUE);
break;
case CURL_SSLVERSION_SSLv2:
@@ -1546,6 +1728,10 @@ ossl_connect_step1(struct connectdata *conn,
break;
#endif
case CURL_SSLVERSION_SSLv3:
+#ifdef OPENSSL_NO_SSL3_METHOD
+ failf(data, "OpenSSL was built without SSLv3 support");
+ return CURLE_NOT_BUILT_IN;
+#else
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
return CURLE_SSL_CONNECT_ERROR;
@@ -1553,6 +1739,7 @@ ossl_connect_step1(struct connectdata *conn,
req_method = SSLv3_client_method();
use_sni(FALSE);
break;
+#endif
}
if(connssl->ctx)
@@ -1571,16 +1758,9 @@ ossl_connect_step1(struct connectdata *conn,
#ifdef SSL_CTRL_SET_MSG_CALLBACK
if(data->set.fdebug && data->set.verbose) {
- /* the SSL trace callback is only used for verbose logging so we only
- inform about failures of setting it */
- if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
- (void (*)(void))ssl_tls_trace)) {
- infof(data, "SSL: couldn't set callback!\n");
- }
- else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
- conn)) {
- infof(data, "SSL: couldn't set callback argument!\n");
- }
+ /* the SSL trace callback is only used for verbose logging */
+ SSL_CTX_set_msg_callback(connssl->ctx, ssl_tls_trace);
+ SSL_CTX_set_msg_callback_arg(connssl->ctx, conn);
}
#endif
@@ -1593,7 +1773,7 @@ ossl_connect_step1(struct connectdata *conn,
The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
disable "rfc4507bis session ticket support". rfc4507bis was later turned
- into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077
+ into the proper RFC5077 it seems: https://tools.ietf.org/html/rfc5077
The enabled extension concerns the session management. I wonder how often
libcurl stops a connection and then resumes a TLS session. also, sending
@@ -1613,7 +1793,7 @@ ossl_connect_step1(struct connectdata *conn,
this option regardless of OpenSSL version and SSL_OP_ALL definition.
OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
- (http://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
+ (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
SSL_OP_ALL that _disables_ that work-around despite the fact that
SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
@@ -1643,17 +1823,12 @@ ossl_connect_step1(struct connectdata *conn,
#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
- break;
-
- case CURL_SSLVERSION_SSLv3:
ctx_options |= SSL_OP_NO_SSLv2;
ctx_options |= SSL_OP_NO_TLSv1;
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
@@ -1662,6 +1837,7 @@ ossl_connect_step1(struct connectdata *conn,
#endif
break;
+ case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
ctx_options |= SSL_OP_NO_SSLv2;
ctx_options |= SSL_OP_NO_SSLv3;
@@ -1710,33 +1886,36 @@ ossl_connect_step1(struct connectdata *conn,
SSL_CTX_set_options(connssl->ctx, ctx_options);
-#ifdef USE_NGHTTP2
- if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
- if(data->set.ssl_enable_npn) {
- SSL_CTX_set_next_proto_select_cb(connssl->ctx, select_next_proto_cb,
- conn);
- }
+#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);
+ if(data->set.ssl_enable_alpn) {
+ int cur = 0;
+ unsigned char protocols[128];
- 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);
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+ protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
- infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID,
- ALPN_HTTP_1_1);
+ memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN);
+ cur += NGHTTP2_PROTO_VERSION_ID_LEN;
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
}
#endif
+
+ protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ /* expects length prefixed preference ordered list of protocols in wire
+ * format
+ */
+ SSL_CTX_set_alpn_protos(connssl->ctx, protocols, cur);
}
#endif
@@ -1759,6 +1938,7 @@ ossl_connect_step1(struct connectdata *conn,
failf(data, "failed setting cipher list: %s", ciphers);
return CURLE_SSL_CIPHER;
}
+ infof(data, "Cipher selection: %s\n", ciphers);
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
@@ -1768,7 +1948,7 @@ ossl_connect_step1(struct connectdata *conn,
failf(data, "Unable to set SRP user name");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
- if(!SSL_CTX_set_srp_password(connssl->ctx,data->set.ssl.password)) {
+ if(!SSL_CTX_set_srp_password(connssl->ctx, data->set.ssl.password)) {
failf(data, "failed setting SRP password");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
@@ -1790,7 +1970,7 @@ ossl_connect_step1(struct connectdata *conn,
data->set.str[STRING_SSL_CAPATH])) {
if(data->set.ssl.verifypeer) {
/* Fail if we insist on successfully verifying the server. */
- failf(data,"error setting certificate verify locations:\n"
+ failf(data, "error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s",
data->set.str[STRING_SSL_CAFILE]?
data->set.str[STRING_SSL_CAFILE]: "none",
@@ -1824,9 +2004,9 @@ ossl_connect_step1(struct connectdata *conn,
lookup=X509_STORE_add_lookup(SSL_CTX_get_cert_store(connssl->ctx),
X509_LOOKUP_file());
if(!lookup ||
- (!X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE],
+ (!X509_load_crl_file(lookup, data->set.str[STRING_SSL_CRLFILE],
X509_FILETYPE_PEM)) ) {
- failf(data,"error loading CRL file: %s",
+ failf(data, "error loading CRL file: %s",
data->set.str[STRING_SSL_CRLFILE]);
return CURLE_SSL_CRL_BADFILE;
}
@@ -1841,21 +2021,35 @@ ossl_connect_step1(struct connectdata *conn,
data->set.str[STRING_SSL_CRLFILE]: "none");
}
+ /* Try building a chain using issuers in the trusted store first to avoid
+ problems with server-sent legacy intermediates.
+ Newer versions of OpenSSL do alternate chain checking by default which
+ gives us the same fix without as much of a performance hit (slight), so we
+ prefer that if available.
+ https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
+ */
+#if defined(X509_V_FLAG_TRUSTED_FIRST) && !defined(X509_V_FLAG_NO_ALT_CHAINS)
+ if(data->set.ssl.verifypeer) {
+ X509_STORE_set_flags(SSL_CTX_get_cert_store(connssl->ctx),
+ X509_V_FLAG_TRUSTED_FIRST);
+ }
+#endif
+
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
SSL_CTX_set_verify(connssl->ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
- cert_verify_callback);
+ NULL);
/* 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) {
- failf(data,"error signaled by ssl ctx callback");
- return retcode;
+ result = (*data->set.ssl.fsslctx)(data, connssl->ctx,
+ data->set.ssl.fsslctxp);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ return result;
}
}
@@ -1867,6 +2061,13 @@ ossl_connect_step1(struct connectdata *conn,
failf(data, "SSL: couldn't create a context (handle)!");
return CURLE_OUT_OF_MEMORY;
}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+ if(data->set.ssl.verifystatus)
+ SSL_set_tlsext_status_type(connssl->handle, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
SSL_set_connect_state(connssl->handle);
connssl->server_cert = 0x0;
@@ -1887,7 +2088,7 @@ ossl_connect_step1(struct connectdata *conn,
/* we got a session id, use it! */
if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
- ERR_error_string(ERR_get_error(),NULL));
+ ERR_error_string(ERR_get_error(), NULL));
return CURLE_SSL_CONNECT_ERROR;
}
/* Informational message */
@@ -1897,16 +2098,16 @@ ossl_connect_step1(struct connectdata *conn,
/* pass the raw socket into the SSL layers */
if(!SSL_set_fd(connssl->handle, (int)sockfd)) {
failf(data, "SSL: SSL_set_fd failed: %s",
- ERR_error_string(ERR_get_error(),NULL));
+ ERR_error_string(ERR_get_error(), NULL));
return CURLE_SSL_CONNECT_ERROR;
}
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;
@@ -1936,10 +2137,9 @@ ossl_connect_step2(struct connectdata *conn, int sockindex)
else {
/* untreated error */
unsigned long errdetail;
- char error_buffer[256]; /* OpenSSL documents that this must be at least
- 256 bytes long. */
- CURLcode rc;
- const char *cert_problem = NULL;
+ char error_buffer[256]=""; /* OpenSSL documents that this must be at
+ least 256 bytes long. */
+ CURLcode result;
long lerr;
connssl->connecting_state = ssl_connect_2; /* the connection failed,
@@ -1962,7 +2162,7 @@ ossl_connect_step2(struct connectdata *conn, int sockindex)
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) {
@@ -1971,12 +2171,13 @@ ossl_connect_step2(struct connectdata *conn, int sockindex)
X509_verify_cert_error_string(lerr));
}
else
- cert_problem = "SSL certificate problem, verify that the CA cert is"
- " OK.";
-
+ /* strcpy() is fine here as long as the string fits within
+ error_buffer */
+ strcpy(error_buffer,
+ "SSL certificate problem, check your CA cert");
break;
default:
- rc = CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
break;
}
@@ -1987,15 +2188,16 @@ ossl_connect_step2(struct connectdata *conn, int sockindex)
* (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 */
+ failf(data, "%s", error_buffer);
- failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
- return rc;
+ return result;
}
}
else {
@@ -2003,9 +2205,9 @@ ossl_connect_step2(struct connectdata *conn, int sockindex)
connssl->connecting_state = ssl_connect_3;
/* Informational message */
- infof (data, "SSL connection using %s / %s\n",
- get_ssl_version_txt(SSL_get_session(connssl->handle)),
- SSL_get_cipher(connssl->handle));
+ infof(data, "SSL connection using %s / %s\n",
+ get_ssl_version_txt(connssl->handle),
+ SSL_get_cipher(connssl->handle));
#ifdef HAS_ALPN
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
@@ -2018,18 +2220,20 @@ ossl_connect_step2(struct connectdata *conn, int sockindex)
if(len != 0) {
infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol);
+#ifdef USE_NGHTTP2
if(len == NGHTTP2_PROTO_VERSION_ID_LEN &&
- memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len) == 0) {
- conn->negnpn = NPN_HTTP2;
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len)) {
+ conn->negnpn = CURL_HTTP_VERSION_2_0;
}
- 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
+#endif
+ if(len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
}
}
- else {
+ else
infof(data, "ALPN, server did not agree to a protocol\n");
- }
}
#endif
@@ -2082,7 +2286,7 @@ static void pubkey_show(struct SessionHandle *data,
#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); \
@@ -2123,7 +2327,7 @@ static int X509V3_ext(struct SessionHandle *data,
X509_EXTENSION_get_critical(ext)?"(critical)":"");
if(!X509V3_EXT_print(bio_out, ext, 0, 0))
- M_ASN1_OCTET_STRING_print(bio_out, ext->value);
+ ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
BIO_get_mem_ptr(bio_out, &biomem);
@@ -2160,6 +2364,7 @@ static void X509_signature(struct SessionHandle *data,
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]);
@@ -2182,7 +2387,6 @@ static void dumpcert(struct SessionHandle *data, X509 *x, int numcert)
"Cert", biomem->data, biomem->length);
BIO_free(bio_out);
-
}
/*
@@ -2196,6 +2400,7 @@ static CURLcode get_cert_chain(struct connectdata *conn,
struct ssl_connect_data *connssl)
{
+ CURLcode result;
STACK_OF(X509) *sk;
int i;
char *bufp;
@@ -2213,9 +2418,11 @@ static CURLcode get_cert_chain(struct connectdata *conn,
}
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");
@@ -2250,28 +2457,22 @@ static CURLcode get_cert_chain(struct connectdata *conn,
Curl_ssl_push_certinfo(data, i, "Version", bufp); /* hex */
num=X509_get_serialNumber(x);
- if(num->length <= 4) {
- value = ASN1_INTEGER_get(num);
- infof(data," Serial Number: %ld (0x%lx)\n", value, value);
- snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
- }
- else {
+ {
int left = CERTBUFFERSIZE;
ptr = bufp;
- *ptr++ = 0;
- if(num->type == V_ASN1_NEG_INTEGER)
+ if(num->type == V_ASN1_NEG_INTEGER) {
*ptr++='-';
+ left--;
+ }
- for(j=0; (j<num->length) && (left>=4); j++) {
- /* TODO: length restrictions */
- snprintf(ptr, 3, "%02x%c",num->data[j],
- ((j+1 == num->length)?'\n':':'));
- ptr += 3;
- left-=4;
+ for(j=0; (j<num->length) && (left>=3); j++) {
+ snprintf(ptr, left, "%02x", num->data[j]);
+ ptr += 2;
+ left -= 2;
}
if(num->length)
- infof(data," Serial Number: %s\n", bufp);
+ infof(data, " Serial Number: %s\n", bufp);
else
bufp[0]=0;
}
@@ -2357,6 +2558,65 @@ static CURLcode get_cert_chain(struct connectdata *conn,
}
/*
+ * 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 */
+
+ /* https://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 */
+
+ /* https://www.openssl.org/docs/crypto/buffer.html */
+ buff1 = temp = OPENSSL_malloc(len1);
+ if(!buff1)
+ break; /* failed */
+
+ /* https://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. It's 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);
+
+ /* https://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!
@@ -2368,7 +2628,7 @@ static CURLcode servercert(struct connectdata *conn,
struct ssl_connect_data *connssl,
bool strict)
{
- CURLcode retcode = CURLE_OK;
+ CURLcode result = CURLE_OK;
int rc;
long lerr;
ASN1_TIME *certdate;
@@ -2376,6 +2636,7 @@ static CURLcode servercert(struct connectdata *conn,
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! */
@@ -2387,7 +2648,8 @@ static CURLcode servercert(struct connectdata *conn,
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);
@@ -2402,11 +2664,11 @@ static CURLcode servercert(struct connectdata *conn,
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;
}
}
@@ -2415,7 +2677,7 @@ static CURLcode servercert(struct connectdata *conn,
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);
@@ -2425,7 +2687,7 @@ static CURLcode servercert(struct connectdata *conn,
/* 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], FOPEN_READTEXT);
if(!fp) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
@@ -2434,7 +2696,8 @@ static CURLcode servercert(struct connectdata *conn,
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)",
@@ -2444,8 +2707,10 @@ static CURLcode servercert(struct connectdata *conn,
fclose(fp);
return CURLE_SSL_ISSUER_ERROR;
}
+
fclose(fp);
- if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
+
+ if(X509_check_issued(issuer, connssl->server_cert) != X509_V_OK) {
if(strict)
failf(data, "SSL: Certificate issuer check failed (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
@@ -2454,13 +2719,15 @@ static CURLcode servercert(struct connectdata *conn,
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
@@ -2468,7 +2735,7 @@ static CURLcode servercert(struct connectdata *conn,
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),"
@@ -2479,46 +2746,52 @@ static CURLcode servercert(struct connectdata *conn,
infof(data, "\t SSL certificate verify ok.\n");
}
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+ if(data->set.ssl.verifystatus) {
+ result = verifystatus(conn, connssl);
+ if(result) {
+ X509_free(connssl->server_cert);
+ connssl->server_cert = NULL;
+ return result;
+ }
+ }
+#endif
+
+ if(!strict)
+ /* when not strict, we don't bother about the verify cert problems */
+ result = CURLE_OK;
+
+ 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);
-#ifdef HAVE_SSL_GET1_SESSION
our_ssl_sessionid = SSL_get1_session(connssl->handle);
- /* SSL_get1_session() will increment the reference
- count and the session will stay in memory until explicitly freed with
- SSL_SESSION_free(3), regardless of its state.
- This function was introduced in openssl 0.9.5a. */
-#else
- our_ssl_sessionid = SSL_get_session(connssl->handle);
-
- /* if SSL_get1_session() is unavailable, use SSL_get_session().
- This is an inferior option because the session can be flushed
- at any time by openssl. It is included only so curl compiles
- under versions of openssl < 0.9.5a.
-
- WARNING: How curl behaves if it's session is flushed is
- untested.
- */
-#endif
+ /* SSL_get1_session() will increment the reference count and the session
+ will stay in memory until explicitly freed with SSL_SESSION_free(3),
+ regardless of its state. */
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
if(incache) {
@@ -2528,15 +2801,15 @@ ossl_connect_step3(struct connectdata *conn,
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
else {
/* Session was incache, so refcount already incremented earlier.
* Avoid further increments with each SSL_get1_session() call.
@@ -2544,7 +2817,6 @@ ossl_connect_step3(struct connectdata *conn,
*/
SSL_SESSION_free(our_ssl_sessionid);
}
-#endif
/*
* We check certificates to authenticate the server; otherwise we risk
@@ -2553,26 +2825,24 @@ ossl_connect_step3(struct connectdata *conn,
* operations.
*/
- if(!data->set.ssl.verifypeer && !data->set.ssl.verifyhost)
- (void)servercert(conn, connssl, FALSE);
- else
- retcode = servercert(conn, connssl, TRUE);
+ result = servercert(conn, connssl,
+ (data->set.ssl.verifypeer || data->set.ssl.verifyhost));
- 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];
@@ -2585,7 +2855,7 @@ ossl_connect_common(struct connectdata *conn,
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);
@@ -2594,9 +2864,10 @@ ossl_connect_common(struct connectdata *conn,
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 ||
@@ -2613,8 +2884,8 @@ ossl_connect_common(struct connectdata *conn,
}
/* 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;
@@ -2647,23 +2918,22 @@ ossl_connect_common(struct connectdata *conn,
* 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;
@@ -2678,32 +2948,28 @@ ossl_connect_common(struct connectdata *conn,
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 */
@@ -2798,7 +3064,7 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
default:
/* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
value/errno" */
- /* http://www.openssl.org/docs/crypto/ERR_get_error.html */
+ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */
sslerror = ERR_get_error();
if((nread < 0) || sslerror) {
/* If the return code was negative or there actually is an error in the
@@ -2821,8 +3087,11 @@ size_t Curl_ossl_version(char *buffer, size_t size)
to OpenSSL in all other aspects */
return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
#else /* YASSL_VERSION */
+#ifdef OPENSSL_IS_BORINGSSL
+ return snprintf(buffer, size, "BoringSSL");
+#else /* OPENSSL_IS_BORINGSSL */
-#if(SSLEAY_VERSION_NUMBER >= 0x905000)
+#if(OPENSSL_VERSION_NUMBER >= 0x905000)
{
char sub[3];
unsigned long ssleay_value;
@@ -2850,47 +3119,44 @@ size_t Curl_ossl_version(char *buffer, size_t size)
}
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);
}
-#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
+#else /* OPENSSL_VERSION_NUMBER is less than 0.9.5 */
-#if(SSLEAY_VERSION_NUMBER >= 0x900000)
+#if(OPENSSL_VERSION_NUMBER >= 0x900000)
return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx",
- (SSLEAY_VERSION_NUMBER>>28)&0xff,
- (SSLEAY_VERSION_NUMBER>>20)&0xff,
- (SSLEAY_VERSION_NUMBER>>12)&0xf);
+ (OPENSSL_VERSION_NUMBER>>28)&0xff,
+ (OPENSSL_VERSION_NUMBER>>20)&0xff,
+ (OPENSSL_VERSION_NUMBER>>12)&0xf);
-#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
+#else /* (OPENSSL_VERSION_NUMBER >= 0x900000) */
{
char sub[2];
sub[1]='\0';
- if(SSLEAY_VERSION_NUMBER&0x0f) {
- sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
+ if(OPENSSL_VERSION_NUMBER&0x0f) {
+ sub[0]=(OPENSSL_VERSION_NUMBER&0x0f) + 'a' -1;
}
else
sub[0]='\0';
return snprintf(buffer, size, "SSL/%x.%x.%x%s",
- (SSLEAY_VERSION_NUMBER>>12)&0xff,
- (SSLEAY_VERSION_NUMBER>>8)&0xf,
- (SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
+ (OPENSSL_VERSION_NUMBER>>12)&0xff,
+ (OPENSSL_VERSION_NUMBER>>8)&0xf,
+ (OPENSSL_VERSION_NUMBER>>4)&0xf, sub);
}
-#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
-#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
+#endif /* (OPENSSL_VERSION_NUMBER >= 0x900000) */
+#endif /* OPENSSL_VERSION_NUMBER is less than 0.9.5 */
+#endif /* OPENSSL_IS_BORINGSSL */
#endif /* YASSL_VERSION */
}
@@ -2898,8 +3164,9 @@ size_t Curl_ossl_version(char *buffer, size_t size)
int Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
size_t length)
{
- if(data)
+ 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 */
}
@@ -2915,4 +3182,28 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
MD5_Update(&MD5pw, tmp, tmplen);
MD5_Final(md5sum, &MD5pw);
}
-#endif /* USE_SSLEAY */
+
+#ifndef OPENSSL_NO_SHA256
+void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
+{
+ SHA256_CTX SHA256pw;
+ (void)unused;
+ SHA256_Init(&SHA256pw);
+ SHA256_Update(&SHA256pw, tmp, tmplen);
+ SHA256_Final(sha256sum, &SHA256pw);
+}
+#endif
+
+bool Curl_ossl_cert_status_request(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+#endif /* USE_OPENSSL */
diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h
index 1a55ffc..a1f347a 100644
--- a/lib/vtls/openssl.h
+++ b/lib/vtls/openssl.h
@@ -7,7 +7,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
@@ -24,7 +24,7 @@
#include "curl_setup.h"
-#ifdef USE_SSLEAY
+#ifdef USE_OPENSSL
/*
* This header should only be needed to get included by vtls.c and openssl.c
*/
@@ -41,7 +41,7 @@ void Curl_ossl_close(struct connectdata *conn, int sockindex);
/* tell OpenSSL to close down all open information regarding connections (and
thus session ID caching etc) */
-int Curl_ossl_close_all(struct SessionHandle *data);
+void Curl_ossl_close_all(struct SessionHandle *data);
/* Sets an OpenSSL engine */
CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine);
@@ -72,9 +72,24 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *md5sum /* output */,
size_t unused);
+void Curl_ossl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused);
+
+bool Curl_ossl_cert_status_request(void);
+
+/* Set the API backend definition to OpenSSL */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_OPENSSL
+
+/* this backend supports the CAPATH option */
+#define have_curlssl_ca_path 1
-/* this backend provides these functions: */
-#define have_curlssl_md5sum 1
+/* this backend supports CURLOPT_CERTINFO */
+#define have_curlssl_certinfo 1
+
+/* this backend suppots CURLOPT_SSL_CTX_* */
+#define have_curlssl_ssl_ctx 1
/* API setup for OpenSSL */
#define curlssl_init Curl_ossl_init
@@ -93,9 +108,13 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
#define curlssl_data_pending(x,y) Curl_ossl_data_pending(x,y)
#define curlssl_random(x,y,z) Curl_ossl_random(x,y,z)
#define curlssl_md5sum(a,b,c,d) Curl_ossl_md5sum(a,b,c,d)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_OPENSSL
+#ifndef OPENSSL_NO_SHA256
+#define curlssl_sha256sum(a,b,c,d) Curl_ossl_sha256sum(a,b,c,d)
+#endif
+#define curlssl_cert_status_request() Curl_ossl_cert_status_request()
-#define DEFAULT_CIPHER_SELECTION "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4"
+#define DEFAULT_CIPHER_SELECTION \
+ "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH"
-#endif /* USE_SSLEAY */
+#endif /* USE_OPENSSL */
#endif /* HEADER_CURL_SSLUSE_H */
diff --git a/lib/vtls/polarssl.c b/lib/vtls/polarssl.c
index 5332b92..066c055 100644
--- a/lib/vtls/polarssl.c
+++ b/lib/vtls/polarssl.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
- * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 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
@@ -55,9 +55,7 @@
#include "select.h"
#include "rawstr.h"
#include "polarssl_threadlock.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -120,11 +118,8 @@ static void polarssl_debug(void *context, int level, const char *line)
#endif
/* ALPN for http2? */
-#ifdef USE_NGHTTP2
-# undef HAS_ALPN
-# ifdef POLARSSL_SSL_ALPN
-# define HAS_ALPN
-# endif
+#ifdef POLARSSL_SSL_ALPN
+# define HAS_ALPN
#endif
static Curl_recv polarssl_recv;
@@ -287,24 +282,38 @@ polarssl_connect_step1(struct connectdata *conn,
}
switch(data->set.ssl.version) {
+ default:
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
+ SSL_MINOR_VERSION_1);
+ break;
case CURL_SSLVERSION_SSLv3:
ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
SSL_MINOR_VERSION_0);
+ ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
+ SSL_MINOR_VERSION_0);
infof(data, "PolarSSL: Forced min. SSL Version to be SSLv3\n");
break;
case CURL_SSLVERSION_TLSv1_0:
ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
SSL_MINOR_VERSION_1);
+ ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
+ SSL_MINOR_VERSION_1);
infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.0\n");
break;
case CURL_SSLVERSION_TLSv1_1:
ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
SSL_MINOR_VERSION_2);
+ ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
+ SSL_MINOR_VERSION_2);
infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.1\n");
break;
case CURL_SSLVERSION_TLSv1_2:
ssl_set_min_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
SSL_MINOR_VERSION_3);
+ ssl_set_max_version(&connssl->ssl, SSL_MAJOR_VERSION_3,
+ SSL_MINOR_VERSION_3);
infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.2\n");
break;
}
@@ -345,15 +354,23 @@ polarssl_connect_step1(struct connectdata *conn,
}
#ifdef HAS_ALPN
- if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
- if(data->set.ssl_enable_alpn) {
- static const char* protocols[] = {
- NGHTTP2_PROTO_VERSION_ID, ALPN_HTTP_1_1, NULL
- };
- ssl_set_alpn_protocols(&connssl->ssl, protocols);
- infof(data, "ALPN, offering %s, %s\n", protocols[0],
- protocols[1]);
+ if(data->set.ssl_enable_alpn) {
+ static const char* protocols[3];
+ int cur = 0;
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+ protocols[cur++] = NGHTTP2_PROTO_VERSION_ID;
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
}
+#endif
+
+ protocols[cur++] = ALPN_HTTP_1_1;
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ protocols[cur] = NULL;
+
+ ssl_set_alpn_protocols(&connssl->ssl, protocols);
}
#endif
@@ -375,47 +392,37 @@ polarssl_connect_step2(struct connectdata *conn,
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
char buffer[1024];
-#ifdef HAS_ALPN
- const char* next_protocol;
-#endif
-
char errorbuf[128];
errorbuf[0] = 0;
conn->recv[sockindex] = polarssl_recv;
conn->send[sockindex] = polarssl_send;
- for(;;) {
- if(!(ret = ssl_handshake(&connssl->ssl)))
- break;
- else if(ret != POLARSSL_ERR_NET_WANT_READ &&
- ret != POLARSSL_ERR_NET_WANT_WRITE) {
-#ifdef POLARSSL_ERROR_C
- error_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* POLARSSL_ERROR_C */
- failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s",
- -ret, errorbuf);
+ ret = ssl_handshake(&connssl->ssl);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else {
- if(ret == POLARSSL_ERR_NET_WANT_READ) {
- connssl->connecting_state = ssl_connect_2_reading;
- return CURLE_OK;
- }
- if(ret == POLARSSL_ERR_NET_WANT_WRITE) {
- connssl->connecting_state = ssl_connect_2_writing;
- return CURLE_OK;
- }
- failf(data, "SSL_connect failed with error %d.", ret);
- return CURLE_SSL_CONNECT_ERROR;
+ switch(ret) {
+ case 0:
+ break;
- }
+ case POLARSSL_ERR_NET_WANT_READ:
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+
+ case POLARSSL_ERR_NET_WANT_WRITE:
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+
+ default:
+#ifdef POLARSSL_ERROR_C
+ error_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* POLARSSL_ERROR_C */
+ failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_SSL_CONNECT_ERROR;
}
infof(data, "PolarSSL: Handshake complete, cipher is %s\n",
- ssl_get_ciphersuite(&conn->ssl[sockindex].ssl)
- );
+ ssl_get_ciphersuite(&conn->ssl[sockindex].ssl) );
ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl);
@@ -448,22 +455,24 @@ polarssl_connect_step2(struct connectdata *conn,
#ifdef HAS_ALPN
if(data->set.ssl_enable_alpn) {
- next_protocol = ssl_get_alpn_protocol(&connssl->ssl);
+ const char *next_protocol = ssl_get_alpn_protocol(&connssl->ssl);
if(next_protocol != NULL) {
infof(data, "ALPN, server accepted to use %s\n", next_protocol);
- if(strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
+#ifdef USE_NGHTTP2
+ if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
NGHTTP2_PROTO_VERSION_ID_LEN)) {
- conn->negnpn = NPN_HTTP2;
+ conn->negnpn = CURL_HTTP_VERSION_2_0;
}
- else if(strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) {
- conn->negnpn = NPN_HTTP1_1;
+ else
+#endif
+ if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
}
}
- else {
+ else
infof(data, "ALPN, server did not agree to a protocol\n");
- }
}
#endif
@@ -477,12 +486,12 @@ static CURLcode
polarssl_connect_step3(struct connectdata *conn,
int sockindex)
{
- CURLcode retcode = CURLE_OK;
+ CURLcode result = CURLE_OK;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct SessionHandle *data = conn->data;
void *old_ssl_sessionid = NULL;
- ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ;
- int incache;
+ ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn;
+ bool incache;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -495,23 +504,21 @@ polarssl_connect_step3(struct connectdata *conn,
incache = FALSE;
}
}
+
if(!incache) {
void *new_session = malloc(sizeof(ssl_session));
if(new_session) {
- memcpy(new_session, our_ssl_sessionid,
- sizeof(ssl_session));
+ memcpy(new_session, our_ssl_sessionid, sizeof(ssl_session));
- retcode = Curl_ssl_addsessionid(conn, new_session,
- sizeof(ssl_session));
- }
- else {
- retcode = CURLE_OUT_OF_MEMORY;
+ result = Curl_ssl_addsessionid(conn, new_session, sizeof(ssl_session));
}
+ else
+ result = CURLE_OUT_OF_MEMORY;
- if(retcode) {
+ if(result) {
failf(data, "failed to store ssl session");
- return retcode;
+ return result;
}
}
@@ -540,11 +547,6 @@ static ssize_t polarssl_send(struct connectdata *conn,
return ret;
}
-void Curl_polarssl_close_all(struct SessionHandle *data)
-{
- (void)data;
-}
-
void Curl_polarssl_close(struct connectdata *conn, int sockindex)
{
rsa_free(&conn->ssl[sockindex].rsa);
@@ -585,11 +587,15 @@ void Curl_polarssl_session_free(void *ptr)
free(ptr);
}
+/* 1.3.10 was the first rebranded version. All new releases (in 1.3 branch and
+ higher) will be mbed TLS branded.. */
+
size_t Curl_polarssl_version(char *buffer, size_t size)
{
unsigned int version = version_get_number();
- return snprintf(buffer, size, "PolarSSL/%d.%d.%d", version>>24,
- (version>>16)&0xff, (version>>8)&0xff);
+ return snprintf(buffer, size, "%s/%d.%d.%d",
+ version >= 0x01030A00?"mbedTLS":"PolarSSL",
+ version>>24, (version>>16)&0xff, (version>>8)&0xff);
}
static CURLcode
@@ -598,7 +604,7 @@ polarssl_connect_common(struct connectdata *conn,
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];
@@ -611,7 +617,7 @@ polarssl_connect_common(struct connectdata *conn,
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);
@@ -620,9 +626,10 @@ polarssl_connect_common(struct connectdata *conn,
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
- retcode = polarssl_connect_step1(conn, sockindex);
- if(retcode)
- return retcode;
+
+ result = polarssl_connect_step1(conn, sockindex);
+ if(result)
+ return result;
}
while(ssl_connect_2 == connssl->connecting_state ||
@@ -639,8 +646,8 @@ polarssl_connect_common(struct connectdata *conn,
}
/* 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;
@@ -674,22 +681,22 @@ polarssl_connect_common(struct connectdata *conn,
* ensuring that a client using select() or epoll() will always
* have a valid fdset to wait on.
*/
- retcode = polarssl_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 = polarssl_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 = polarssl_connect_step3(conn, sockindex);
- if(retcode)
- return retcode;
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = polarssl_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] = polarssl_recv;
conn->send[sockindex] = polarssl_send;
@@ -717,12 +724,12 @@ CURLcode
Curl_polarssl_connect(struct connectdata *conn,
int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = polarssl_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = polarssl_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
diff --git a/lib/vtls/polarssl.h b/lib/vtls/polarssl.h
index 9ab7e47..f980dbb 100644
--- a/lib/vtls/polarssl.h
+++ b/lib/vtls/polarssl.h
@@ -8,6 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) 2012 - 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
@@ -36,10 +37,6 @@ CURLcode Curl_polarssl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done);
-/* tell PolarSSL to close down all open information regarding connections (and
- thus session ID caching etc) */
-void Curl_polarssl_close_all(struct SessionHandle *data);
-
/* close a SSL connection */
void Curl_polarssl_close(struct connectdata *conn, int sockindex);
@@ -47,27 +44,32 @@ void Curl_polarssl_session_free(void *ptr);
size_t Curl_polarssl_version(char *buffer, size_t size);
int Curl_polarssl_shutdown(struct connectdata *conn, int sockindex);
+/* Set the API backend definition to PolarSSL */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_POLARSSL
+
+/* this backend supports the CAPATH option */
+#define have_curlssl_ca_path 1
+
/* API setup for PolarSSL */
#define curlssl_init() polarssl_init()
#define curlssl_cleanup() polarssl_cleanup()
#define curlssl_connect Curl_polarssl_connect
#define curlssl_connect_nonblocking Curl_polarssl_connect_nonblocking
#define curlssl_session_free(x) Curl_polarssl_session_free(x)
-#define curlssl_close_all Curl_polarssl_close_all
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_polarssl_close
#define curlssl_shutdown(x,y) 0
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_polarssl_version
-#define curlssl_check_cxn(x) (x=x, -1)
-#define curlssl_data_pending(x,y) (x=x, y=y, 0)
-#define CURL_SSL_BACKEND CURLSSLBACKEND_POLARSSL
+#define curlssl_check_cxn(x) ((void)x, -1)
+#define curlssl_data_pending(x,y) ((void)x, (void)y, 0)
/* This might cause libcurl to use a weeker random!
TODO: implement proper use of Polarssl's CTR-DRBG or HMAC-DRBG and use that
*/
-#define curlssl_random(x,y,z) (x=x, y=y, z=z, CURLE_NOT_BUILT_IN)
+#define curlssl_random(x,y,z) ((void)x, (void)y, (void)z, CURLE_NOT_BUILT_IN)
#endif /* USE_POLARSSL */
#endif /* HEADER_CURL_POLARSSL_H */
diff --git a/lib/vtls/polarssl_threadlock.c b/lib/vtls/polarssl_threadlock.c
index ad18715..62abf43 100644
--- a/lib/vtls/polarssl_threadlock.c
+++ b/lib/vtls/polarssl_threadlock.c
@@ -36,10 +36,7 @@
#endif
#include "polarssl_threadlock.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
diff --git a/lib/vtls/qssl.c b/lib/vtls/qssl.c
deleted file mode 100644
index 4c32053..0000000
--- a/lib/vtls/qssl.c
+++ /dev/null
@@ -1,527 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2013, 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
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_QSOSSL
-
-#include <qsossl.h>
-
-#ifdef HAVE_LIMITS_H
-# include <limits.h>
-#endif
-
-#include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "qssl.h"
-#include "vtls.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "x509asn1.h"
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-
-int Curl_qsossl_init(void)
-
-{
- /* Nothing to do here. We must have connection data to initialize ssl, so
- * defer.
- */
-
- return 1;
-}
-
-
-void Curl_qsossl_cleanup(void)
-
-{
- /* Nothing to do. */
-}
-
-
-static CURLcode Curl_qsossl_init_session(struct SessionHandle * data)
-
-{
- int rc;
- char * certname;
- SSLInit initstr;
- SSLInitApp initappstr;
-
- /* Initialize the job for SSL according to the current parameters.
- * QsoSSL offers two ways to do it: SSL_Init_Application() that uses an
- * application identifier to select certificates in the main certificate
- * store, and SSL_Init() that uses named keyring files and a password.
- * It is not possible to have different keyrings for the CAs and the
- * local certificate. We thus use the certificate name to identify the
- * keyring if given, else the CA file name.
- * If the key file name is given, it is taken as the password for the
- * keyring in certificate file.
- * We first try to SSL_Init_Application(), then SSL_Init() if it failed.
- */
-
- certname = data->set.str[STRING_CERT];
-
- if(!certname) {
- certname = data->set.str[STRING_SSL_CAFILE];
-
- if(!certname)
- return CURLE_OK; /* Use previous setup. */
- }
-
- memset((char *) &initappstr, 0, sizeof initappstr);
- initappstr.applicationID = certname;
- initappstr.applicationIDLen = strlen(certname);
- initappstr.protocol = SSL_VERSION_CURRENT; /* TLSV1 compat. SSLV[23]. */
- initappstr.sessionType = SSL_REGISTERED_AS_CLIENT;
- rc = SSL_Init_Application(&initappstr);
-
- if(rc == SSL_ERROR_NOT_REGISTERED) {
- initstr.keyringFileName = certname;
- initstr.keyringPassword = data->set.str[STRING_KEY];
- initstr.cipherSuiteList = NULL; /* Use default. */
- initstr.cipherSuiteListLen = 0;
- rc = SSL_Init(&initstr);
- }
-
- switch (rc) {
-
- case 0: /* No error. */
- break;
-
- case SSL_ERROR_IO:
- failf(data, "SSL_Init() I/O error: %s", strerror(errno));
- return CURLE_SSL_CONNECT_ERROR;
-
- case SSL_ERROR_BAD_CIPHER_SUITE:
- return CURLE_SSL_CIPHER;
-
- case SSL_ERROR_KEYPASSWORD_EXPIRED:
- case SSL_ERROR_NOT_REGISTERED:
- return CURLE_SSL_CONNECT_ERROR;
-
- case SSL_ERROR_NO_KEYRING:
- return CURLE_SSL_CACERT;
-
- case SSL_ERROR_CERT_EXPIRED:
- return CURLE_SSL_CERTPROBLEM;
-
- default:
- failf(data, "SSL_Init(): %s", SSL_Strerror(rc, NULL));
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- return CURLE_OK;
-}
-
-
-static CURLcode Curl_qsossl_create(struct connectdata * conn, int sockindex)
-
-{
- SSLHandle * h;
- struct ssl_connect_data * connssl = &conn->ssl[sockindex];
-
- h = SSL_Create(conn->sock[sockindex], SSL_ENCRYPT);
-
- if(!h) {
- failf(conn->data, "SSL_Create() I/O error: %s", strerror(errno));
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- connssl->handle = h;
- return CURLE_OK;
-}
-
-
-static int Curl_qsossl_trap_cert(SSLHandle * h)
-
-{
- return 1; /* Accept certificate. */
-}
-
-
-static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex)
-
-{
- int rc;
- struct SessionHandle * data = conn->data;
- struct ssl_connect_data * connssl = &conn->ssl[sockindex];
- SSLHandle * h = connssl->handle;
- long timeout_ms;
-
- h->exitPgm = data->set.ssl.verifypeer? NULL: Curl_qsossl_trap_cert;
-
- /* figure out how long time we should wait at maximum */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* time-out, bail out, go home */
- failf(data, "Connection time-out");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* SSL_Handshake() timeout resolution is second, so round up. */
- h->timeout = (timeout_ms + 1000 - 1) / 1000;
-
- /* Set-up protocol. */
-
- switch (data->set.ssl.version) {
-
- default:
- case CURL_SSLVERSION_DEFAULT:
- h->protocol = SSL_VERSION_CURRENT; /* TLSV1 compat. SSLV[23]. */
- break;
-
- case CURL_SSLVERSION_TLSv1:
- h->protocol = TLS_VERSION_1;
- break;
-
- case CURL_SSLVERSION_SSLv2:
- h->protocol = SSL_VERSION_2;
- break;
-
- case CURL_SSLVERSION_SSLv3:
- h->protocol = SSL_VERSION_3;
- break;
-
- case CURL_SSLVERSION_TLSv1_0:
- case CURL_SSLVERSION_TLSv1_1:
- case CURL_SSLVERSION_TLSv1_2:
- failf(data, "TLS minor version cannot be set");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- h->peerCert = NULL;
- h->peerCertLen = 0;
- rc = SSL_Handshake(h, SSL_HANDSHAKE_AS_CLIENT);
-
- switch (rc) {
-
- case 0: /* No error. */
- break;
-
- case SSL_ERROR_BAD_CERTIFICATE:
- case SSL_ERROR_BAD_CERT_SIG:
- case SSL_ERROR_NOT_TRUSTED_ROOT:
- return CURLE_PEER_FAILED_VERIFICATION;
-
- case SSL_ERROR_BAD_CIPHER_SUITE:
- case SSL_ERROR_NO_CIPHERS:
- return CURLE_SSL_CIPHER;
-
- case SSL_ERROR_CERTIFICATE_REJECTED:
- case SSL_ERROR_CERT_EXPIRED:
- case SSL_ERROR_NO_CERTIFICATE:
- return CURLE_SSL_CERTPROBLEM;
-
- case SSL_ERROR_IO:
- failf(data, "SSL_Handshake() I/O error: %s", strerror(errno));
- return CURLE_SSL_CONNECT_ERROR;
-
- default:
- failf(data, "SSL_Handshake(): %s", SSL_Strerror(rc, NULL));
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- /* Verify host. */
- rc = Curl_verifyhost(conn, h->peerCert, h->peerCert + h->peerCertLen);
- if(rc != CURLE_OK)
- return rc;
-
- /* Gather certificate info. */
- if(data->set.ssl.certinfo) {
- if(Curl_ssl_init_certinfo(data, 1))
- return CURLE_OUT_OF_MEMORY;
- if(h->peerCert) {
- rc = Curl_extract_certinfo(conn, 0, h->peerCert,
- h->peerCert + h->peerCertLen);
- if(rc != CURLE_OK)
- return rc;
- }
- }
-
- return CURLE_OK;
-}
-
-
-static Curl_recv qsossl_recv;
-static Curl_send qsossl_send;
-
-CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex)
-
-{
- struct SessionHandle * data = conn->data;
- struct ssl_connect_data * connssl = &conn->ssl[sockindex];
- int rc;
-
- rc = Curl_qsossl_init_session(data);
-
- if(rc == CURLE_OK) {
- rc = Curl_qsossl_create(conn, sockindex);
-
- if(rc == CURLE_OK) {
- rc = Curl_qsossl_handshake(conn, sockindex);
- if(rc != CURLE_OK)
- SSL_Destroy(connssl->handle);
- }
- }
-
- if(rc == CURLE_OK) {
- conn->recv[sockindex] = qsossl_recv;
- conn->send[sockindex] = qsossl_send;
- connssl->state = ssl_connection_complete;
- }
- else {
- connssl->handle = NULL;
- connssl->use = FALSE;
- connssl->state = ssl_connection_none;
- }
-
- return rc;
-}
-
-
-static int Curl_qsossl_close_one(struct ssl_connect_data * conn,
- struct SessionHandle * data)
-
-{
- int rc;
-
- if(!conn->handle)
- return 0;
-
- rc = SSL_Destroy(conn->handle);
-
- if(rc) {
- if(rc == SSL_ERROR_IO) {
- failf(data, "SSL_Destroy() I/O error: %s", strerror(errno));
- return -1;
- }
-
- /* An SSL error. */
- failf(data, "SSL_Destroy() returned error %s", SSL_Strerror(rc, NULL));
- return -1;
- }
-
- conn->handle = NULL;
- return 0;
-}
-
-
-void Curl_qsossl_close(struct connectdata *conn, int sockindex)
-
-{
- struct SessionHandle *data = conn->data;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
- if(connssl->use)
- (void) Curl_qsossl_close_one(connssl, data);
-}
-
-
-int Curl_qsossl_close_all(struct SessionHandle * data)
-
-{
- /* Unimplemented. */
- (void) data;
- return 0;
-}
-
-
-int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex)
-
-{
- struct ssl_connect_data * connssl = &conn->ssl[sockindex];
- struct SessionHandle *data = conn->data;
- ssize_t nread;
- int what;
- int rc;
- char buf[120];
-
- if(!connssl->handle)
- return 0;
-
- if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
- return 0;
-
- if(Curl_qsossl_close_one(connssl, data))
- return -1;
-
- rc = 0;
-
- what = Curl_socket_ready(conn->sock[sockindex],
- CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
-
- for(;;) {
- if(what < 0) {
- /* anything that gets here is fatally bad */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- rc = -1;
- break;
- }
-
- if(!what) { /* timeout */
- failf(data, "SSL shutdown timeout");
- break;
- }
-
- /* Something to read, let's do it and hope that it is the close
- notify alert from the server. No way to SSL_Read now, so use read(). */
-
- nread = read(conn->sock[sockindex], buf, sizeof(buf));
-
- if(nread < 0) {
- failf(data, "read: %s", strerror(errno));
- rc = -1;
- }
-
- if(nread <= 0)
- break;
-
- what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0);
- }
-
- return rc;
-}
-
-
-static ssize_t qsossl_send(struct connectdata * conn, int sockindex,
- const void * mem, size_t len, CURLcode * curlcode)
-
-{
- /* SSL_Write() is said to return 'int' while write() and send() returns
- 'size_t' */
- int rc;
-
- rc = SSL_Write(conn->ssl[sockindex].handle, (void *) mem, (int) len);
-
- if(rc < 0) {
- switch(rc) {
-
- case SSL_ERROR_BAD_STATE:
- /* The operation did not complete; the same SSL I/O function
- should be called again later. This is basically an EWOULDBLOCK
- equivalent. */
- *curlcode = CURLE_AGAIN;
- return -1;
-
- case SSL_ERROR_IO:
- switch (errno) {
- case EWOULDBLOCK:
- case EINTR:
- *curlcode = CURLE_AGAIN;
- return -1;
- }
-
- failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno));
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- /* An SSL error. */
- failf(conn->data, "SSL_Write() returned error %s",
- SSL_Strerror(rc, NULL));
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- return (ssize_t) rc; /* number of bytes */
-}
-
-
-static ssize_t qsossl_recv(struct connectdata * conn, int num, char * buf,
- size_t buffersize, CURLcode * curlcode)
-
-{
- char error_buffer[120]; /* OpenSSL documents that this must be at
- least 120 bytes long. */
- unsigned long sslerror;
- int buffsize;
- int nread;
-
- buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- nread = SSL_Read(conn->ssl[num].handle, buf, buffsize);
-
- if(nread < 0) {
- /* failed SSL_read */
-
- switch (nread) {
-
- case SSL_ERROR_BAD_STATE:
- /* there's data pending, re-invoke SSL_Read(). */
- *curlcode = CURLE_AGAIN;
- return -1;
-
- case SSL_ERROR_IO:
- switch (errno) {
- case EWOULDBLOCK:
- *curlcode = CURLE_AGAIN;
- return -1;
- }
-
- failf(conn->data, "SSL_Read() I/O error: %s", strerror(errno));
- *curlcode = CURLE_RECV_ERROR;
- return -1;
-
- default:
- failf(conn->data, "SSL read error: %s", SSL_Strerror(nread, NULL));
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- }
- }
- return (ssize_t) nread;
-}
-
-
-size_t Curl_qsossl_version(char * buffer, size_t size)
-
-{
- strncpy(buffer, "IBM OS/400 SSL", size);
- return strlen(buffer);
-}
-
-
-int Curl_qsossl_check_cxn(struct connectdata * cxn)
-
-{
- int err;
- int errlen;
-
- /* The only thing that can be tested here is at the socket level. */
-
- if(!cxn->ssl[FIRSTSOCKET].handle)
- return 0; /* connection has been closed */
-
- err = 0;
- errlen = sizeof err;
-
- if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR,
- (unsigned char *) &err, &errlen) ||
- errlen != sizeof err || err)
- return 0; /* connection has been closed */
-
- return -1; /* connection status unknown */
-}
-
-#endif /* USE_QSOSSL */
diff --git a/lib/vtls/qssl.h b/lib/vtls/qssl.h
deleted file mode 100644
index 9764eec..0000000
--- a/lib/vtls/qssl.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef HEADER_CURL_QSSL_H
-#define HEADER_CURL_QSSL_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * 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
- * are also available at http://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-/*
- * This header should only be needed to get included by vtls.c and qssl.c
- */
-
-#include "urldata.h"
-
-#ifdef USE_QSOSSL
-int Curl_qsossl_init(void);
-void Curl_qsossl_cleanup(void);
-CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex);
-void Curl_qsossl_close(struct connectdata *conn, int sockindex);
-int Curl_qsossl_close_all(struct SessionHandle * data);
-int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex);
-
-size_t Curl_qsossl_version(char * buffer, size_t size);
-int Curl_qsossl_check_cxn(struct connectdata * cxn);
-
-/* API setup for QsoSSL */
-#define curlssl_init Curl_qsossl_init
-#define curlssl_cleanup Curl_qsossl_cleanup
-#define curlssl_connect Curl_qsossl_connect
-
-/* No session handling for QsoSSL */
-#define curlssl_session_free(x) Curl_nop_stmt
-#define curlssl_close_all Curl_qsossl_close_all
-#define curlssl_close Curl_qsossl_close
-#define curlssl_shutdown(x,y) Curl_qsossl_shutdown(x,y)
-#define curlssl_set_engine(x,y) CURLE_NOT_BUILT_IN
-#define curlssl_set_engine_default(x) CURLE_NOT_BUILT_IN
-#define curlssl_engines_list(x) NULL
-#define curlssl_version Curl_qsossl_version
-#define curlssl_check_cxn(x) Curl_qsossl_check_cxn(x)
-#define curlssl_data_pending(x,y) 0
-#define CURL_SSL_BACKEND CURLSSLBACKEND_QSOSSL
-#endif /* USE_QSOSSL */
-
-#endif /* HEADER_CURL_QSSL_H */
diff --git a/lib/vtls/curl_schannel.c b/lib/vtls/schannel.c
index e4e595e..2174e21 100644
--- a/lib/vtls/curl_schannel.c
+++ b/lib/vtls/schannel.c
@@ -5,9 +5,9 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012 - 2015, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 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
@@ -38,19 +38,6 @@
* Thanks for code and inspiration!
*/
-/*
- * TODO list for TLS/SSL implementation:
- * - implement client certificate authentication
- * - implement custom server certificate validation
- * - implement cipher/algorithm option
- *
- * Related articles on MSDN:
- * - Getting a Certificate for Schannel
- * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx
- * - Specifying Schannel Ciphers and Cipher Strengths
- * http://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx
- */
-
#include "curl_setup.h"
#ifdef USE_SCHANNEL
@@ -60,7 +47,7 @@
#endif
#include "curl_sspi.h"
-#include "curl_schannel.h"
+#include "schannel.h"
#include "vtls.h"
#include "sendf.h"
#include "connect.h" /* for the connect timeout */
@@ -69,10 +56,7 @@
#include "inet_pton.h" /* for IP addr SNI check */
#include "curl_multibyte.h"
#include "warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -121,13 +105,13 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
struct in6_addr addr6;
#endif
TCHAR *host_name;
- CURLcode code;
+ CURLcode result;
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
conn->host.name, conn->remote_port);
/* check for an existing re-usable credential handle */
- if(!Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)) {
+ if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
connssl->cred = old_cred;
infof(data, "schannel: re-using existing credential handle\n");
}
@@ -141,60 +125,64 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
/* certificate validation on CE doesn't seem to work right; we'll
do it following a more manual process. */
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
- SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
- SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+ SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
#else
- schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
- SCH_CRED_REVOCATION_CHECK_CHAIN;
+ schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
+ if(data->set.ssl_no_revoke)
+ schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+ else
+ schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
#endif
- infof(data, "schannel: checking server certificate revocation\n");
+ if(data->set.ssl_no_revoke)
+ infof(data, "schannel: disabled server certificate revocation "
+ "checks\n");
+ else
+ infof(data, "schannel: checking server certificate revocation\n");
}
else {
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
- SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
- SCH_CRED_IGNORE_REVOCATION_OFFLINE;
- infof(data, "schannel: disable server certificate revocation checks\n");
+ SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+ infof(data, "schannel: disabled server certificate revocation checks\n");
}
if(!data->set.ssl.verifyhost) {
schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
infof(data, "schannel: verifyhost setting prevents Schannel from "
- "comparing the supplied target name with the subject "
- "names in server certificates. Also disables SNI.\n");
+ "comparing the supplied target name with the subject "
+ "names in server certificates. Also disables SNI.\n");
}
switch(data->set.ssl.version) {
- case CURL_SSLVERSION_TLSv1:
- schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
- SP_PROT_TLS1_1_CLIENT |
- SP_PROT_TLS1_2_CLIENT;
- break;
- case CURL_SSLVERSION_TLSv1_0:
- schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
- break;
- case CURL_SSLVERSION_TLSv1_1:
- schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
- break;
- case CURL_SSLVERSION_TLSv1_2:
- schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
- break;
- case CURL_SSLVERSION_SSLv3:
- schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
- break;
- case CURL_SSLVERSION_SSLv2:
- schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
- break;
- default:
- schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
- SP_PROT_TLS1_1_CLIENT |
- SP_PROT_TLS1_2_CLIENT |
- SP_PROT_SSL3_CLIENT;
- break;
+ default:
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
+ SP_PROT_TLS1_1_CLIENT |
+ SP_PROT_TLS1_2_CLIENT;
+ break;
+ case CURL_SSLVERSION_TLSv1_0:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
+ break;
+ case CURL_SSLVERSION_SSLv3:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
+ break;
}
/* allocate memory for the re-usable credential handle */
connssl->cred = (struct curl_schannel_cred *)
- malloc(sizeof(struct curl_schannel_cred));
+ malloc(sizeof(struct curl_schannel_cred));
if(!connssl->cred) {
failf(data, "schannel: unable to allocate memory");
return CURLE_OUT_OF_MEMORY;
@@ -202,9 +190,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
- sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
- SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
- &connssl->cred->cred_handle, &connssl->cred->time_stamp);
+ sspi_status =
+ s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
+ SECPKG_CRED_OUTBOUND, NULL,
+ &schannel_cred, NULL, NULL,
+ &connssl->cred->cred_handle,
+ &connssl->cred->time_stamp);
if(sspi_status != SEC_E_OK) {
if(sspi_status == SEC_E_WRONG_PRINCIPAL)
@@ -233,12 +224,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
/* setup request flags */
connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
- ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
- ISC_REQ_STREAM;
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
/* allocate memory for the security context handle */
connssl->ctxt = (struct curl_schannel_ctxt *)
- malloc(sizeof(struct curl_schannel_ctxt));
+ malloc(sizeof(struct curl_schannel_ctxt));
if(!connssl->ctxt) {
failf(data, "schannel: unable to allocate memory");
return CURLE_OUT_OF_MEMORY;
@@ -273,10 +264,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
"sending %lu bytes...\n", outbuf.cbBuffer);
/* send initial handshake data which is now stored in output buffer */
- code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
- outbuf.cbBuffer, &written);
+ result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
+ outbuf.cbBuffer, &written);
s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
- if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
+ if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
failf(data, "schannel: failed to send initial handshake data: "
"sent %zd of %lu bytes", written, outbuf.cbBuffer);
return CURLE_SSL_CONNECT_ERROR;
@@ -285,6 +276,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "schannel: sent initial handshake data: "
"sent %zd bytes\n", written);
+ connssl->recv_unrecoverable_err = CURLE_OK;
+ connssl->recv_sspi_close_notify = false;
+ connssl->recv_connection_closed = false;
+
/* continue to second handshake step */
connssl->connecting_state = ssl_connect_2;
@@ -298,13 +293,15 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
ssize_t nread = -1, written = -1;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- SecBuffer outbuf[2];
+ unsigned char *reallocated_buffer;
+ size_t reallocated_length;
+ SecBuffer outbuf[3];
SecBufferDesc outbuf_desc;
SecBuffer inbuf[2];
SecBufferDesc inbuf_desc;
SECURITY_STATUS sspi_status = SEC_E_OK;
TCHAR *host_name;
- CURLcode code;
+ CURLcode result;
bool doread;
doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
@@ -315,6 +312,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(!connssl->cred || !connssl->ctxt)
return CURLE_SSL_CONNECT_ERROR;
+ /* buffer to store previously received and decrypted data */
+ if(connssl->decdata_buffer == NULL) {
+ connssl->decdata_offset = 0;
+ connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+ connssl->decdata_buffer = malloc(connssl->decdata_length);
+ if(connssl->decdata_buffer == NULL) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
/* buffer to store previously received and encrypted data */
if(connssl->encdata_buffer == NULL) {
connssl->encdata_offset = 0;
@@ -330,31 +338,38 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(connssl->encdata_length - connssl->encdata_offset <
CURL_SCHANNEL_BUFFER_FREE_SIZE) {
/* increase internal encrypted data buffer */
- connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
- connssl->encdata_buffer = realloc(connssl->encdata_buffer,
- connssl->encdata_length);
+ reallocated_length = connssl->encdata_offset +
+ CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ reallocated_buffer = realloc(connssl->encdata_buffer,
+ reallocated_length);
- if(connssl->encdata_buffer == NULL) {
+ if(reallocated_buffer == NULL) {
failf(data, "schannel: unable to re-allocate memory");
return CURLE_OUT_OF_MEMORY;
}
+ else {
+ connssl->encdata_buffer = reallocated_buffer;
+ connssl->encdata_length = reallocated_length;
+ }
}
for(;;) {
if(doread) {
/* read encrypted handshake data from socket */
- code = Curl_read_plain(conn->sock[sockindex],
- (char *) (connssl->encdata_buffer + connssl->encdata_offset),
- connssl->encdata_length - connssl->encdata_offset,
- &nread);
- if(code == CURLE_AGAIN) {
+ result = Curl_read_plain(conn->sock[sockindex],
+ (char *) (connssl->encdata_buffer +
+ connssl->encdata_offset),
+ connssl->encdata_length -
+ connssl->encdata_offset,
+ &nread);
+ if(result == CURLE_AGAIN) {
if(connssl->connecting_state != ssl_connect_2_writing)
connssl->connecting_state = ssl_connect_2_reading;
infof(data, "schannel: failed to receive handshake, "
"need more data\n");
return CURLE_OK;
}
- else if((code != CURLE_OK) || (nread == 0)) {
+ else if((result != CURLE_OK) || (nread == 0)) {
failf(data, "schannel: failed to receive handshake, "
"SSL/TLS connection failed");
return CURLE_SSL_CONNECT_ERROR;
@@ -365,7 +380,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
}
infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
- connssl->encdata_offset, connssl->encdata_length);
+ connssl->encdata_offset, connssl->encdata_length);
/* setup input buffers */
InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
@@ -376,7 +391,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
/* setup output buffers */
InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
- InitSecBufferDesc(&outbuf_desc, outbuf, 2);
+ InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&outbuf_desc, outbuf, 3);
if(inbuf[0].pvBuffer == NULL) {
failf(data, "schannel: unable to allocate memory");
@@ -410,19 +426,31 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
return CURLE_OK;
}
+ /* If the server has requested a client certificate, attempt to continue
+ the handshake without one. This will allow connections to servers which
+ request a client certificate but do not require it. */
+ if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
+ !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
+ connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+ connssl->connecting_state = ssl_connect_2_writing;
+ infof(data, "schannel: a client certificate has been requested\n");
+ return CURLE_OK;
+ }
+
/* check if the handshake needs to be continued */
if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
- for(i = 0; i < 2; i++) {
+ for(i = 0; i < 3; i++) {
/* search for handshake tokens that need to be send */
if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
infof(data, "schannel: sending next handshake data: "
"sending %lu bytes...\n", outbuf[i].cbBuffer);
/* send handshake token to server */
- code = Curl_write_plain(conn, conn->sock[sockindex],
- outbuf[i].pvBuffer, outbuf[i].cbBuffer,
- &written);
- if((code != CURLE_OK) || (outbuf[i].cbBuffer != (size_t)written)) {
+ result = Curl_write_plain(conn, conn->sock[sockindex],
+ outbuf[i].pvBuffer, outbuf[i].cbBuffer,
+ &written);
+ if((result != CURLE_OK) ||
+ (outbuf[i].cbBuffer != (size_t) written)) {
failf(data, "schannel: failed to send next handshake data: "
"sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
return CURLE_SSL_CONNECT_ERROR;
@@ -449,21 +477,21 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
/*
- There are two cases where we could be getting extra data here:
- 1) If we're renegotiating a connection and the handshake is already
- complete (from the server perspective), it can encrypted app data
- (not handshake data) in an extra buffer at this point.
- 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
- connection and this extra data is part of the handshake.
- We should process the data immediately; waiting for the socket to
- be ready may fail since the server is done sending handshake data.
- */
+ There are two cases where we could be getting extra data here:
+ 1) If we're renegotiating a connection and the handshake is already
+ complete (from the server perspective), it can encrypted app data
+ (not handshake data) in an extra buffer at this point.
+ 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
+ connection and this extra data is part of the handshake.
+ We should process the data immediately; waiting for the socket to
+ be ready may fail since the server is done sending handshake data.
+ */
/* check if the remaining data is less than the total amount
and therefore begins after the already processed data */
if(connssl->encdata_offset > inbuf[1].cbBuffer) {
memmove(connssl->encdata_buffer,
(connssl->encdata_buffer + connssl->encdata_offset) -
- inbuf[1].cbBuffer, inbuf[1].cbBuffer);
+ inbuf[1].cbBuffer, inbuf[1].cbBuffer);
connssl->encdata_offset = inbuf[1].cbBuffer;
if(sspi_status == SEC_I_CONTINUE_NEEDED) {
doread = FALSE;
@@ -502,11 +530,11 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
static CURLcode
schannel_connect_step3(struct connectdata *conn, int sockindex)
{
- CURLcode retcode = CURLE_OK;
+ CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct curl_schannel_cred *old_cred = NULL;
- int incache;
+ bool incache;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -539,20 +567,21 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
}
/* save the current session data for possible re-use */
- incache = !(Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL));
+ incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
if(incache) {
if(old_cred != connssl->cred) {
infof(data, "schannel: old credential handle is stale, removing\n");
- Curl_ssl_delsessionid(conn, (void*)old_cred);
+ Curl_ssl_delsessionid(conn, (void *)old_cred);
incache = FALSE;
}
}
+
if(!incache) {
- retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
- sizeof(struct curl_schannel_cred));
- if(retcode) {
+ result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
+ sizeof(struct curl_schannel_cred));
+ if(result) {
failf(data, "schannel: failed to store credential handle");
- return retcode;
+ return result;
}
else {
connssl->cred->cached = TRUE;
@@ -569,7 +598,7 @@ static CURLcode
schannel_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];
@@ -592,9 +621,9 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
return CURLE_OPERATION_TIMEDOUT;
}
- retcode = schannel_connect_step1(conn, sockindex);
- if(retcode)
- return retcode;
+ result = schannel_connect_step1(conn, sockindex);
+ if(result)
+ return result;
}
while(ssl_connect_2 == connssl->connecting_state ||
@@ -646,19 +675,19 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
* ensuring that a client using select() or epoll() will always
* have a valid fdset to wait on.
*/
- retcode = schannel_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 = schannel_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 = schannel_connect_step3(conn, sockindex);
- if(retcode)
- return retcode;
+ result = schannel_connect_step3(conn, sockindex);
+ if(result)
+ return result;
}
if(ssl_connect_done == connssl->connecting_state) {
@@ -687,14 +716,14 @@ schannel_send(struct connectdata *conn, int sockindex,
SecBuffer outbuf[4];
SecBufferDesc outbuf_desc;
SECURITY_STATUS sspi_status = SEC_E_OK;
- CURLcode code;
+ CURLcode result;
/* check if the maximum stream sizes were queried */
if(connssl->stream_sizes.cbMaximumMessage == 0) {
sspi_status = s_pSecFn->QueryContextAttributes(
- &connssl->ctxt->ctxt_handle,
- SECPKG_ATTR_STREAM_SIZES,
- &connssl->stream_sizes);
+ &connssl->ctxt->ctxt_handle,
+ SECPKG_ATTR_STREAM_SIZES,
+ &connssl->stream_sizes);
if(sspi_status != SEC_E_OK) {
*err = CURLE_SEND_ERROR;
return -1;
@@ -709,8 +738,8 @@ schannel_send(struct connectdata *conn, int sockindex,
/* calculate the complete message length and allocate a buffer for it */
data_len = connssl->stream_sizes.cbHeader + len +
- connssl->stream_sizes.cbTrailer;
- data = (unsigned char*) malloc(data_len);
+ connssl->stream_sizes.cbTrailer;
+ data = (unsigned char *) malloc(data_len);
if(data == NULL) {
*err = CURLE_OUT_OF_MEMORY;
return -1;
@@ -742,19 +771,19 @@ schannel_send(struct connectdata *conn, int sockindex,
len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
/*
- It's important to send the full message which includes the header,
- encrypted payload, and trailer. Until the client receives all the
- data a coherent message has not been delivered and the client
- can't read any of it.
-
- If we wanted to buffer the unwritten encrypted bytes, we would
- tell the client that all data it has requested to be sent has been
- sent. The unwritten encrypted bytes would be the first bytes to
- send on the next invocation.
- Here's the catch with this - if we tell the client that all the
- bytes have been sent, will the client call this method again to
- send the buffered data? Looking at who calls this function, it
- seems the answer is NO.
+ It's important to send the full message which includes the header,
+ encrypted payload, and trailer. Until the client receives all the
+ data a coherent message has not been delivered and the client
+ can't read any of it.
+
+ If we wanted to buffer the unwritten encrypted bytes, we would
+ tell the client that all data it has requested to be sent has been
+ sent. The unwritten encrypted bytes would be the first bytes to
+ send on the next invocation.
+ Here's the catch with this - if we tell the client that all the
+ bytes have been sent, will the client call this method again to
+ send the buffered data? Looking at who calls this function, it
+ seems the answer is NO.
*/
/* send entire message or fail */
@@ -793,12 +822,12 @@ schannel_send(struct connectdata *conn, int sockindex,
}
/* socket is writable */
- code = Curl_write_plain(conn, conn->sock[sockindex], data + written,
- len - written, &this_write);
- if(code == CURLE_AGAIN)
+ result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
+ len - written, &this_write);
+ if(result == CURLE_AGAIN)
continue;
- else if(code != CURLE_OK) {
- *err = code;
+ else if(result != CURLE_OK) {
+ *err = result;
written = -1;
break;
}
@@ -828,71 +857,112 @@ schannel_recv(struct connectdata *conn, int sockindex,
char *buf, size_t len, CURLcode *err)
{
size_t size = 0;
- ssize_t nread = 0, ret = -1;
- CURLcode retcode;
+ ssize_t nread = -1;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ unsigned char *reallocated_buffer;
+ size_t reallocated_length;
bool done = FALSE;
SecBuffer inbuf[4];
SecBufferDesc inbuf_desc;
SECURITY_STATUS sspi_status = SEC_E_OK;
+ /* we want the length of the encrypted buffer to be at least large enough
+ that it can hold all the bytes requested and some TLS record overhead. */
+ size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
+
+ /****************************************************************************
+ * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
+ * The pattern for return error is set *err, optional infof, goto cleanup.
+ *
+ * Our priority is to always return as much decrypted data to the caller as
+ * possible, even if an error occurs. The state of the decrypted buffer must
+ * always be valid. Transfer of decrypted data to the caller's buffer is
+ * handled in the cleanup.
+ */
infof(data, "schannel: client wants to read %zu bytes\n", len);
*err = CURLE_OK;
- /* buffer to store previously received and decrypted data */
- if(connssl->decdata_buffer == NULL) {
- connssl->decdata_offset = 0;
- connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
- connssl->decdata_buffer = malloc(connssl->decdata_length);
- if(connssl->decdata_buffer == NULL) {
- failf(data, "schannel: unable to allocate memory");
- *err = CURLE_OUT_OF_MEMORY;
- return -1;
- }
+ if(len && len <= connssl->decdata_offset) {
+ infof(data, "schannel: enough decrypted data is already available\n");
+ goto cleanup;
}
+ else if(connssl->recv_unrecoverable_err) {
+ *err = connssl->recv_unrecoverable_err;
+ infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
+ goto cleanup;
+ }
+ else if(connssl->recv_sspi_close_notify) {
+ /* once a server has indicated shutdown there is no more encrypted data */
+ infof(data, "schannel: server indicated shutdown in a prior call\n");
+ goto cleanup;
+ }
+ else if(!len) {
+ /* It's debatable what to return when !len. Regardless we can't return
+ immediately because there may be data to decrypt (in the case we want to
+ decrypt all encrypted cached data) so handle !len later in cleanup.
+ */
+ ; /* do nothing */
+ }
+ else if(!connssl->recv_connection_closed) {
+ /* increase enc buffer in order to fit the requested amount of data */
+ size = connssl->encdata_length - connssl->encdata_offset;
+ if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
+ connssl->encdata_length < min_encdata_length) {
+ reallocated_length = connssl->encdata_offset +
+ CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ if(reallocated_length < min_encdata_length) {
+ reallocated_length = min_encdata_length;
+ }
+ reallocated_buffer = realloc(connssl->encdata_buffer,
+ reallocated_length);
+ if(reallocated_buffer == NULL) {
+ *err = CURLE_OUT_OF_MEMORY;
+ failf(data, "schannel: unable to re-allocate memory");
+ goto cleanup;
+ }
- /* increase buffer in order to fit the requested amount of data */
- while(connssl->encdata_length - connssl->encdata_offset <
- CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) {
- /* increase internal encrypted data buffer */
- connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
- connssl->encdata_buffer = realloc(connssl->encdata_buffer,
- connssl->encdata_length);
-
- if(connssl->encdata_buffer == NULL) {
- failf(data, "schannel: unable to re-allocate memory");
- *err = CURLE_OUT_OF_MEMORY;
- return -1;
+ connssl->encdata_buffer = reallocated_buffer;
+ connssl->encdata_length = reallocated_length;
+ size = connssl->encdata_length - connssl->encdata_offset;
+ infof(data, "schannel: encdata_buffer resized %zu\n",
+ connssl->encdata_length);
}
- }
- /* read encrypted data from socket */
- infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
- connssl->encdata_offset, connssl->encdata_length);
- size = connssl->encdata_length - connssl->encdata_offset;
- if(size > 0) {
+ infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
+ connssl->encdata_offset, connssl->encdata_length);
+
+ /* read encrypted data from socket */
*err = Curl_read_plain(conn->sock[sockindex],
- (char *) (connssl->encdata_buffer + connssl->encdata_offset),
+ (char *)(connssl->encdata_buffer +
+ connssl->encdata_offset),
size, &nread);
- /* check for received data */
- if(*err != CURLE_OK)
- ret = -1;
- else {
- if(nread > 0)
- /* increase encrypted data buffer offset */
- connssl->encdata_offset += nread;
- ret = nread;
+ if(*err) {
+ nread = -1;
+ if(*err == CURLE_AGAIN)
+ infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
+ else if(*err == CURLE_RECV_ERROR)
+ infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
+ else
+ infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
+ }
+ else if(nread == 0) {
+ connssl->recv_connection_closed = true;
+ infof(data, "schannel: server closed the connection\n");
+ }
+ else if(nread > 0) {
+ connssl->encdata_offset += (size_t)nread;
+ infof(data, "schannel: encrypted data got %zd\n", nread);
}
- infof(data, "schannel: encrypted data got %zd\n", ret);
}
infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
connssl->encdata_offset, connssl->encdata_length);
- /* check if we still have some data in our buffers */
+ /* decrypt loop */
while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
- connssl->decdata_offset < len) {
+ (!len || connssl->decdata_offset < len ||
+ connssl->recv_connection_closed)) {
/* prepare data buffer for DecryptMessage call */
InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
curlx_uztoul(connssl->encdata_offset));
@@ -901,25 +971,18 @@ schannel_recv(struct connectdata *conn, int sockindex,
InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
-
InitSecBufferDesc(&inbuf_desc, inbuf, 4);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
&inbuf_desc, 0, NULL);
- /* check if we need more data */
- if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
- infof(data, "schannel: failed to decrypt data, need more data\n");
- *err = CURLE_AGAIN;
- return -1;
- }
-
/* check if everything went fine (server may want to renegotiate
- context) */
+ or shutdown the connection context) */
if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
- sspi_status == SEC_I_CONTEXT_EXPIRED) {
- /* check for successfully decrypted data */
+ sspi_status == SEC_I_CONTEXT_EXPIRED) {
+ /* check for successfully decrypted data, even before actual
+ renegotiation or shutdown of the connection context */
if(inbuf[1].BufferType == SECBUFFER_DATA) {
infof(data, "schannel: decrypted data length: %lu\n",
inbuf[1].cbBuffer);
@@ -927,23 +990,28 @@ schannel_recv(struct connectdata *conn, int sockindex,
/* increase buffer in order to fit the received amount of data */
size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
- while(connssl->decdata_length - connssl->decdata_offset < size ||
- connssl->decdata_length < len) {
+ if(connssl->decdata_length - connssl->decdata_offset < size ||
+ connssl->decdata_length < len) {
/* increase internal decrypted data buffer */
- connssl->decdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
- connssl->decdata_buffer = realloc(connssl->decdata_buffer,
- connssl->decdata_length);
-
- if(connssl->decdata_buffer == NULL) {
- failf(data, "schannel: unable to re-allocate memory");
+ reallocated_length = connssl->decdata_offset + size;
+ /* make sure that the requested amount of data fits */
+ if(reallocated_length < len) {
+ reallocated_length = len;
+ }
+ reallocated_buffer = realloc(connssl->decdata_buffer,
+ reallocated_length);
+ if(reallocated_buffer == NULL) {
*err = CURLE_OUT_OF_MEMORY;
- return -1;
+ failf(data, "schannel: unable to re-allocate memory");
+ goto cleanup;
}
+ connssl->decdata_buffer = reallocated_buffer;
+ connssl->decdata_length = reallocated_length;
}
/* copy decrypted data to internal buffer */
size = inbuf[1].cbBuffer;
- if(size > 0) {
+ if(size) {
memcpy(connssl->decdata_buffer + connssl->decdata_offset,
inbuf[1].pvBuffer, size);
connssl->decdata_offset += size;
@@ -961,81 +1029,151 @@ schannel_recv(struct connectdata *conn, int sockindex,
/* check if the remaining data is less than the total amount
* and therefore begins after the already processed data
- */
+ */
if(connssl->encdata_offset > inbuf[3].cbBuffer) {
/* move remaining encrypted data forward to the beginning of
buffer */
memmove(connssl->encdata_buffer,
(connssl->encdata_buffer + connssl->encdata_offset) -
- inbuf[3].cbBuffer, inbuf[3].cbBuffer);
+ inbuf[3].cbBuffer, inbuf[3].cbBuffer);
connssl->encdata_offset = inbuf[3].cbBuffer;
}
infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
connssl->encdata_offset, connssl->encdata_length);
}
- else{
+ else {
/* reset encrypted buffer offset, because there is no data remaining */
connssl->encdata_offset = 0;
}
- }
- /* check if server wants to renegotiate the connection context */
- if(sspi_status == SEC_I_RENEGOTIATE) {
- infof(data, "schannel: remote party requests SSL/TLS renegotiation\n");
-
- /* begin renegotiation */
- infof(data, "schannel: renegotiating SSL/TLS connection\n");
- connssl->state = ssl_connection_negotiating;
- connssl->connecting_state = ssl_connect_2_writing;
- retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- *err = retcode;
- else {
- infof(data, "schannel: SSL/TLS connection renegotiated\n");
+ /* check if server wants to renegotiate the connection context */
+ if(sspi_status == SEC_I_RENEGOTIATE) {
+ infof(data, "schannel: remote party requests renegotiation\n");
+ if(*err && *err != CURLE_AGAIN) {
+ infof(data, "schannel: can't renogotiate, an error is pending\n");
+ goto cleanup;
+ }
+ if(connssl->encdata_offset) {
+ *err = CURLE_RECV_ERROR;
+ infof(data, "schannel: can't renogotiate, "
+ "encrypted data available\n");
+ goto cleanup;
+ }
+ /* begin renegotiation */
+ infof(data, "schannel: renegotiating SSL/TLS connection\n");
+ connssl->state = ssl_connection_negotiating;
+ connssl->connecting_state = ssl_connect_2_writing;
+ *err = schannel_connect_common(conn, sockindex, FALSE, &done);
+ if(*err) {
+ infof(data, "schannel: renegotiation failed\n");
+ goto cleanup;
+ }
/* now retry receiving data */
- return schannel_recv(conn, sockindex, buf, len, err);
+ sspi_status = SEC_E_OK;
+ infof(data, "schannel: SSL/TLS connection renegotiated\n");
+ continue;
}
+ /* check if the server closed the connection */
+ else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
+ /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
+ returned so we have to work around that in cleanup. */
+ connssl->recv_sspi_close_notify = true;
+ if(!connssl->recv_connection_closed) {
+ connssl->recv_connection_closed = true;
+ infof(data, "schannel: server closed the connection\n");
+ }
+ goto cleanup;
+ }
+ }
+ else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+ if(!*err)
+ *err = CURLE_AGAIN;
+ infof(data, "schannel: failed to decrypt data, need more data\n");
+ goto cleanup;
+ }
+ else {
+ *err = CURLE_RECV_ERROR;
+ infof(data, "schannel: failed to read data from server: %s\n",
+ Curl_sspi_strerror(conn, sspi_status));
+ goto cleanup;
}
}
+ infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
+ connssl->encdata_offset, connssl->encdata_length);
+
infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
connssl->decdata_offset, connssl->decdata_length);
- /* copy requested decrypted data to supplied buffer */
+cleanup:
+ /* Warning- there is no guarantee the encdata state is valid at this point */
+ infof(data, "schannel: schannel_recv cleanup\n");
+
+ /* Error if the connection has closed without a close_notify.
+ Behavior here is a matter of debate. We don't want to be vulnerable to a
+ truncation attack however there's some browser precedent for ignoring the
+ close_notify for compatibility reasons.
+ Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
+ return close_notify. In that case if the connection was closed we assume it
+ was graceful (close_notify) since there doesn't seem to be a way to tell.
+ */
+ if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
+ !connssl->recv_sspi_close_notify) {
+ BOOL isWin2k;
+ ULONGLONG cm;
+ OSVERSIONINFOEX osver;
+
+ memset(&osver, 0, sizeof(osver));
+ osver.dwOSVersionInfoSize = sizeof(osver);
+ osver.dwMajorVersion = 5;
+
+ cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
+ cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_EQUAL);
+ cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+ cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
+
+ isWin2k = VerifyVersionInfo(&osver,
+ (VER_MAJORVERSION | VER_MINORVERSION |
+ VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
+ cm);
+
+ if(isWin2k && sspi_status == SEC_E_OK)
+ connssl->recv_sspi_close_notify = true;
+ else {
+ *err = CURLE_RECV_ERROR;
+ infof(data, "schannel: server closed abruptly (missing close_notify)\n");
+ }
+ }
+
+ /* Any error other than CURLE_AGAIN is an unrecoverable error. */
+ if(*err && *err != CURLE_AGAIN)
+ connssl->recv_unrecoverable_err = *err;
+
size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
- if(size > 0) {
+ if(size) {
memcpy(buf, connssl->decdata_buffer, size);
- ret = size;
-
- /* move remaining decrypted data forward to the beginning of buffer */
memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
connssl->decdata_offset - size);
connssl->decdata_offset -= size;
- infof(data, "schannel: decrypted data returned %zd\n", size);
+ infof(data, "schannel: decrypted data returned %zu\n", size);
infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
connssl->decdata_offset, connssl->decdata_length);
- }
-
- /* check if the server closed the connection */
- if(ret <= 0 && ( /* special check for Windows 2000 Professional */
- sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK &&
- connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) {
- infof(data, "schannel: server closed the connection\n");
*err = CURLE_OK;
- return 0;
+ return (ssize_t)size;
}
- /* check if something went wrong and we need to return an error */
- if(ret < 0 && sspi_status != SEC_E_OK) {
- infof(data, "schannel: failed to read data from server: %s\n",
- Curl_sspi_strerror(conn, sspi_status));
- *err = CURLE_RECV_ERROR;
- return -1;
- }
+ if(!*err && !connssl->recv_connection_closed)
+ *err = CURLE_AGAIN;
+
+ /* It's debatable what to return when !len. We could return whatever error we
+ got from decryption but instead we override here so the return is consistent.
+ */
+ if(!len)
+ *err = CURLE_OK;
- return ret;
+ return *err ? -1 : 0;
}
CURLcode
@@ -1048,12 +1186,12 @@ Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
CURLcode
Curl_schannel_connect(struct connectdata *conn, int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = schannel_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
@@ -1095,7 +1233,7 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
SECURITY_STATUS sspi_status;
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
- CURLcode code;
+ CURLcode result;
TCHAR *host_name;
DWORD dwshut = SCHANNEL_SHUTDOWN;
@@ -1118,56 +1256,56 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
sspi_status = s_pSecFn->InitializeSecurityContext(
- &connssl->cred->cred_handle,
- &connssl->ctxt->ctxt_handle,
- host_name,
- connssl->req_flags,
- 0,
- 0,
- NULL,
- 0,
- &connssl->ctxt->ctxt_handle,
- &outbuf_desc,
- &connssl->ret_flags,
- &connssl->ctxt->time_stamp);
+ &connssl->cred->cred_handle,
+ &connssl->ctxt->ctxt_handle,
+ host_name,
+ connssl->req_flags,
+ 0,
+ 0,
+ NULL,
+ 0,
+ &connssl->ctxt->ctxt_handle,
+ &outbuf_desc,
+ &connssl->ret_flags,
+ &connssl->ctxt->time_stamp);
Curl_unicodefree(host_name);
if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
/* send close message which is in output buffer */
ssize_t written;
- code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
- outbuf.cbBuffer, &written);
+ result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
+ outbuf.cbBuffer, &written);
s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
- if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
+ if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
infof(data, "schannel: failed to send close msg: %s"
- " (bytes written: %zd)\n", curl_easy_strerror(code), written);
+ " (bytes written: %zd)\n", curl_easy_strerror(result), written);
}
}
+ }
- /* free SSPI Schannel API security context handle */
- if(connssl->ctxt) {
- infof(data, "schannel: clear security context handle\n");
- s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
- Curl_safefree(connssl->ctxt);
- }
+ /* free SSPI Schannel API security context handle */
+ if(connssl->ctxt) {
+ infof(data, "schannel: clear security context handle\n");
+ s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
+ Curl_safefree(connssl->ctxt);
+ }
- /* free SSPI Schannel API credential handle */
- if(connssl->cred) {
- /* decrement the reference counter of the credential/session handle */
- if(connssl->cred->refcount > 0) {
- connssl->cred->refcount--;
- infof(data, "schannel: decremented credential handle refcount = %d\n",
- connssl->cred->refcount);
- }
+ /* free SSPI Schannel API credential handle */
+ if(connssl->cred) {
+ /* decrement the reference counter of the credential/session handle */
+ if(connssl->cred->refcount > 0) {
+ connssl->cred->refcount--;
+ infof(data, "schannel: decremented credential handle refcount = %d\n",
+ connssl->cred->refcount);
+ }
- /* if the handle was not cached and the refcount is zero */
- if(!connssl->cred->cached && connssl->cred->refcount == 0) {
- infof(data, "schannel: clear credential handle\n");
- s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
- Curl_safefree(connssl->cred);
- }
+ /* if the handle was not cached and the refcount is zero */
+ if(!connssl->cred->cached && connssl->cred->refcount == 0) {
+ infof(data, "schannel: clear credential handle\n");
+ s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
+ Curl_safefree(connssl->cred);
}
}
@@ -1192,9 +1330,14 @@ void Curl_schannel_session_free(void *ptr)
{
struct curl_schannel_cred *cred = ptr;
- if(cred && cred->cached && cred->refcount == 0) {
- s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
- Curl_safefree(cred);
+ if(cred && cred->cached) {
+ if(cred->refcount == 0) {
+ s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ Curl_safefree(cred);
+ }
+ else {
+ cred->cached = FALSE;
+ }
}
}
@@ -1262,7 +1405,8 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
NULL,
pCertContextServer->hCertStore,
&ChainPara,
- 0,
+ (data->set.ssl_no_revoke ? 0 :
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN),
NULL,
&pChainContext)) {
failf(data, "schannel: CertGetCertificateChain failed: %s",
@@ -1273,21 +1417,24 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
if(result == CURLE_OK) {
CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
- DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED|
- CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
+ DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
if(dwTrustErrorMask) {
- if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
+ if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_REVOKED");
+ else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
failf(data, "schannel: CertGetCertificateChain trust error"
- " CERT_TRUST_IS_PARTIAL_CHAIN");
- if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
+ " CERT_TRUST_IS_PARTIAL_CHAIN");
+ else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
failf(data, "schannel: CertGetCertificateChain trust error"
- " CERT_TRUST_IS_UNTRUSTED_ROOT");
- if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
+ " CERT_TRUST_IS_UNTRUSTED_ROOT");
+ else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
failf(data, "schannel: CertGetCertificateChain trust error"
- " CERT_TRUST_IS_NOT_TIME_VALID");
- failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
- dwTrustErrorMask);
+ " CERT_TRUST_IS_NOT_TIME_VALID");
+ else
+ failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+ dwTrustErrorMask);
result = CURLE_PEER_FAILED_VERIFICATION;
}
}
@@ -1303,6 +1450,14 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
cert_hostname.const_tchar_ptr = cert_hostname_buff;
hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
+ /* TODO: Fix this for certificates with multiple alternative names.
+ Right now we're only asking for the first preferred alternative name.
+ Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
+ (if WinCE supports that?) and run this section in a loop for each.
+ https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
+ curl: (51) schannel: CertGetNameString() certificate hostname
+ (.google.com) did not match connection (google.com)
+ */
len = CertGetNameString(pCertContextServer,
CERT_NAME_DNS_TYPE,
0,
diff --git a/lib/vtls/curl_schannel.h b/lib/vtls/schannel.h
index 700516b..5329584 100644
--- a/lib/vtls/curl_schannel.h
+++ b/lib/vtls/schannel.h
@@ -8,7 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
- * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 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
@@ -72,30 +72,9 @@
#define SECBUFFER_ALERT 17
#endif
-#ifndef ISC_RET_REPLAY_DETECT
-#define ISC_RET_REPLAY_DETECT 0x00000004
-#endif
-
-#ifndef ISC_RET_SEQUENCE_DETECT
-#define ISC_RET_SEQUENCE_DETECT 0x00000008
-#endif
-
-#ifndef ISC_RET_CONFIDENTIALITY
-#define ISC_RET_CONFIDENTIALITY 0x00000010
-#endif
-
-#ifndef ISC_RET_ALLOCATED_MEMORY
-#define ISC_RET_ALLOCATED_MEMORY 0x00000100
-#endif
-
-#ifndef ISC_RET_STREAM
-#define ISC_RET_STREAM 0x00008000
-#endif
-
-
+/* Both schannel buffer sizes must be > 0 */
#define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096
#define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024
-#define CURL_SCHANNEL_BUFFER_STEP_FACTOR 2
CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
@@ -115,22 +94,24 @@ size_t Curl_schannel_version(char *buffer, size_t size);
int Curl_schannel_random(unsigned char *entropy, size_t length);
+/* Set the API backend definition to Schannel */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_SCHANNEL
+
/* API setup for Schannel */
#define curlssl_init Curl_schannel_init
#define curlssl_cleanup Curl_schannel_cleanup
#define curlssl_connect Curl_schannel_connect
#define curlssl_connect_nonblocking Curl_schannel_connect_nonblocking
#define curlssl_session_free Curl_schannel_session_free
-#define curlssl_close_all(x) (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_close_all(x) ((void)x)
#define curlssl_close Curl_schannel_close
#define curlssl_shutdown Curl_schannel_shutdown
-#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
-#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
-#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_set_engine(x,y) ((void)x, (void)y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) ((void)x, (struct curl_slist *)NULL)
#define curlssl_version Curl_schannel_version
-#define curlssl_check_cxn(x) (x=x, -1)
+#define curlssl_check_cxn(x) ((void)x, -1)
#define curlssl_data_pending Curl_schannel_data_pending
-#define CURL_SSL_BACKEND CURLSSLBACKEND_SCHANNEL
#define curlssl_random(x,y,z) ((void)x, Curl_schannel_random(y,z))
#endif /* USE_SCHANNEL */
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index 88511b8..01bbc61 100644
--- a/lib/vtls/vtls.c
+++ b/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 */
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index e21fdef..2349e5b 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -7,7 +7,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
@@ -23,10 +23,28 @@
***************************************************************************/
#include "curl_setup.h"
+#include "openssl.h" /* OpenSSL versions */
+#include "gtls.h" /* GnuTLS versions */
+#include "nssg.h" /* NSS versions */
+#include "gskit.h" /* Global Secure ToolKit versions */
+#include "polarssl.h" /* PolarSSL versions */
+#include "axtls.h" /* axTLS versions */
+#include "cyassl.h" /* CyaSSL versions */
+#include "schannel.h" /* Schannel SSPI version */
+#include "darwinssl.h" /* SecureTransport (Darwin) version */
+
+#ifndef MAX_PINNED_PUBKEY_SIZE
+#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */
+#endif
+
#ifndef MD5_DIGEST_LENGTH
#define MD5_DIGEST_LENGTH 16 /* fixed size */
#endif
+#ifndef SHA256_DIGEST_LENGTH
+#define SHA256_DIGEST_LENGTH 32 /* fixed size */
+#endif
+
/* see http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 */
#define ALPN_HTTP_1_1_LENGTH 8
#define ALPN_HTTP_1_1 "http/1.1"
@@ -68,7 +86,7 @@ int Curl_ssl_check_cxn(struct connectdata *conn);
/* Certificate information list handling. */
void Curl_ssl_free_certinfo(struct SessionHandle *data);
-int Curl_ssl_init_certinfo(struct SessionHandle * data, int num);
+CURLcode Curl_ssl_init_certinfo(struct SessionHandle * data, int num);
CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle * data, int certnum,
const char * label, const char * value,
size_t valuelen);
@@ -78,9 +96,9 @@ CURLcode Curl_ssl_push_certinfo(struct SessionHandle * data, int certnum,
/* Functions to be used by SSL library adaptation functions */
/* extract a session ID */
-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 */;
/* add a new session ID */
CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
void *ssl_sessionid,
@@ -94,18 +112,24 @@ void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid);
in */
int Curl_ssl_random(struct SessionHandle *data, unsigned char *buffer,
size_t length);
-void Curl_ssl_md5sum(unsigned char *tmp, /* input */
- size_t tmplen,
- unsigned char *md5sum, /* output */
- size_t md5len);
+CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len);
+/* Check pinned public key. */
+CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
+ const unsigned char *pubkey, size_t pubkeylen);
-#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
+bool Curl_ssl_cert_status_request(void);
-#ifdef have_curlssl_md5sum
-#define HAVE_CURL_SSL_MD5SUM
-#endif
+bool Curl_ssl_false_start(void);
+
+#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
#else
+/* Set the API backend definition to none */
+#define CURL_SSL_BACKEND CURLSSLBACKEND_NONE
+
/* When SSL support is not present, just define away these function calls */
#define Curl_ssl_init() 1
#define Curl_ssl_cleanup() Curl_nop_stmt
@@ -125,8 +149,9 @@ void Curl_ssl_md5sum(unsigned char *tmp, /* input */
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
#define Curl_ssl_connect_nonblocking(x,y,z) CURLE_NOT_BUILT_IN
#define Curl_ssl_kill_session(x) Curl_nop_stmt
-#define Curl_ssl_random(x,y,z) CURLE_NOT_BUILT_IN
-#define CURL_SSL_BACKEND CURLSSLBACKEND_NONE
+#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
+#define Curl_ssl_cert_status_request() FALSE
+#define Curl_ssl_false_start() FALSE
#endif
#endif /* HEADER_CURL_VTLS_H */