mirror of
https://github.com/curl/curl.git
synced 2024-12-03 06:20:31 +08:00
d58ba66eec
- Switch from verifying a pinned public key in a callback during the certificate verification to inline after the certificate verification. The callback method had three problems: 1. If a pinned public key didn't match, CURLE_SSL_PINNEDPUBKEYNOTMATCH was not returned. 2. If peer certificate verification was disabled the pinned key verification did not take place as it should. 3. (related to #2) If there was no certificate of depth 0 the callback would not have checked the pinned public key. Though all those problems could have been fixed it would have made the code more complex. Instead we now verify inline after the certificate verification in mbedtls_connect_step2. Ref: http://curl.haxx.se/mail/lib-2016-01/0047.html Ref: https://github.com/bagder/curl/pull/601
846 lines
24 KiB
C
846 lines
24 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
|
|
* Copyright (C) 2012 - 2016, 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.
|
|
*
|
|
***************************************************************************/
|
|
|
|
/*
|
|
* Source file for all mbedTSL-specific code for the TLS/SSL layer. No code
|
|
* but vtls.c should ever call or use these functions.
|
|
*
|
|
*/
|
|
|
|
#include "curl_setup.h"
|
|
|
|
#ifdef USE_MBEDTLS
|
|
|
|
#include <mbedtls/net.h>
|
|
#include <mbedtls/ssl.h>
|
|
#include <mbedtls/certs.h>
|
|
#include <mbedtls/x509.h>
|
|
#include <mbedtls/version.h>
|
|
|
|
#include <mbedtls/error.h>
|
|
#include <mbedtls/entropy.h>
|
|
#include <mbedtls/ctr_drbg.h>
|
|
|
|
#include "urldata.h"
|
|
#include "sendf.h"
|
|
#include "inet_pton.h"
|
|
#include "mbedtls.h"
|
|
#include "vtls.h"
|
|
#include "parsedate.h"
|
|
#include "connect.h" /* for the connect timeout */
|
|
#include "select.h"
|
|
#include "rawstr.h"
|
|
#include "polarssl_threadlock.h"
|
|
|
|
#define _MPRINTF_REPLACE /* use our functions only */
|
|
#include <curl/mprintf.h>
|
|
#include "curl_memory.h"
|
|
/* The last #include file should be: */
|
|
#include "memdebug.h"
|
|
|
|
/* apply threading? */
|
|
#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
|
|
#define THREADING_SUPPORT
|
|
#endif
|
|
|
|
#if defined(THREADING_SUPPORT)
|
|
static mbedtls_entropy_context entropy;
|
|
|
|
static int entropy_init_initialized = 0;
|
|
|
|
/* start of entropy_init_mutex() */
|
|
static void entropy_init_mutex(mbedtls_entropy_context *ctx)
|
|
{
|
|
/* lock 0 = entropy_init_mutex() */
|
|
Curl_polarsslthreadlock_lock_function(0);
|
|
if(entropy_init_initialized == 0) {
|
|
mbedtls_entropy_init(ctx);
|
|
entropy_init_initialized = 1;
|
|
}
|
|
Curl_polarsslthreadlock_unlock_function(0);
|
|
}
|
|
/* end of entropy_init_mutex() */
|
|
|
|
/* start of entropy_func_mutex() */
|
|
static int entropy_func_mutex(void *data, unsigned char *output, size_t len)
|
|
{
|
|
int ret;
|
|
/* lock 1 = entropy_func_mutex() */
|
|
Curl_polarsslthreadlock_lock_function(1);
|
|
ret = mbedtls_entropy_func(data, output, len);
|
|
Curl_polarsslthreadlock_unlock_function(1);
|
|
|
|
return ret;
|
|
}
|
|
/* end of entropy_func_mutex() */
|
|
|
|
#endif /* THREADING_SUPPORT */
|
|
|
|
/* Define this to enable lots of debugging for mbedTLS */
|
|
#undef MBEDTLS_DEBUG
|
|
|
|
#ifdef MBEDTLS_DEBUG
|
|
static void mbedtls_debug(void *context, int level, const char *line)
|
|
{
|
|
struct SessionHandle *data = NULL;
|
|
|
|
if(!context)
|
|
return;
|
|
|
|
data = (struct SessionHandle *)context;
|
|
|
|
infof(data, "%s", line);
|
|
(void) level;
|
|
}
|
|
#else
|
|
#endif
|
|
|
|
/* ALPN for http2? */
|
|
#ifdef USE_NGHTTP2
|
|
# undef HAS_ALPN
|
|
# ifdef MBEDTLS_SSL_ALPN
|
|
# define HAS_ALPN
|
|
# endif
|
|
#endif
|
|
|
|
|
|
/*
|
|
* profile
|
|
*/
|
|
const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr =
|
|
{
|
|
/* Hashes from SHA-1 and above */
|
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) |
|
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_RIPEMD160 ) |
|
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) |
|
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
|
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) |
|
|
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ),
|
|
0xFFFFFFF, /* Any PK alg */
|
|
0xFFFFFFF, /* Any curve */
|
|
1024, /* RSA min key len */
|
|
};
|
|
|
|
/* See https://tls.mbed.org/discussions/generic/
|
|
howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der
|
|
*/
|
|
#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE)
|
|
#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES)
|
|
|
|
#define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \
|
|
RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES)
|
|
|
|
static Curl_recv mbedtls_recv;
|
|
static Curl_send mbedtls_send;
|
|
|
|
static CURLcode
|
|
mbedtls_connect_step1(struct connectdata *conn,
|
|
int sockindex)
|
|
{
|
|
struct SessionHandle *data = conn->data;
|
|
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
|
|
|
bool sni = TRUE; /* default is SNI enabled */
|
|
int ret = -1;
|
|
#ifdef ENABLE_IPV6
|
|
struct in6_addr addr;
|
|
#else
|
|
struct in_addr addr;
|
|
#endif
|
|
void *old_session = NULL;
|
|
size_t old_session_size = 0;
|
|
char errorbuf[128];
|
|
errorbuf[0]=0;
|
|
|
|
/* mbedTLS only supports SSLv3 and TLSv1 */
|
|
if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
|
|
failf(data, "mbedTLS does not support SSLv2");
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
}
|
|
else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
|
|
sni = FALSE; /* SSLv3 has no SNI */
|
|
|
|
#ifdef THREADING_SUPPORT
|
|
entropy_init_mutex(&entropy);
|
|
mbedtls_ctr_drbg_init(&connssl->ctr_drbg);
|
|
|
|
ret = mbedtls_ctr_drbg_seed(&connssl->ctr_drbg, entropy_func_mutex,
|
|
&entropy, connssl->ssn.id,
|
|
connssl->ssn.id_len);
|
|
if(ret) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
|
|
-ret, errorbuf);
|
|
}
|
|
#else
|
|
mbedtls_entropy_init(&connssl->entropy);
|
|
mbedtls_ctr_drbg_init(&connssl->ctr_drbg);
|
|
|
|
ret = mbedtls_ctr_drbg_seed(&connssl->ctr_drbg, mbedtls_entropy_func,
|
|
&connssl->entropy, connssl->ssn.id,
|
|
connssl->ssn.id_len);
|
|
if(ret) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
|
|
-ret, errorbuf);
|
|
}
|
|
#endif /* THREADING_SUPPORT */
|
|
|
|
/* Load the trusted CA */
|
|
memset(&connssl->cacert, 0, sizeof(mbedtls_x509_crt));
|
|
|
|
if(data->set.str[STRING_SSL_CAFILE]) {
|
|
ret = mbedtls_x509_crt_parse_file(&connssl->cacert,
|
|
data->set.str[STRING_SSL_CAFILE]);
|
|
|
|
if(ret<0) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s",
|
|
data->set.str[STRING_SSL_CAFILE], -ret, errorbuf);
|
|
|
|
if(data->set.ssl.verifypeer)
|
|
return CURLE_SSL_CACERT_BADFILE;
|
|
}
|
|
}
|
|
|
|
if(data->set.str[STRING_SSL_CAPATH]) {
|
|
ret = mbedtls_x509_crt_parse_path(&connssl->cacert,
|
|
data->set.str[STRING_SSL_CAPATH]);
|
|
|
|
if(ret<0) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s",
|
|
data->set.str[STRING_SSL_CAPATH], -ret, errorbuf);
|
|
|
|
if(data->set.ssl.verifypeer)
|
|
return CURLE_SSL_CACERT_BADFILE;
|
|
}
|
|
}
|
|
|
|
/* Load the client certificate */
|
|
memset(&connssl->clicert, 0, sizeof(mbedtls_x509_crt));
|
|
|
|
if(data->set.str[STRING_CERT]) {
|
|
ret = mbedtls_x509_crt_parse_file(&connssl->clicert,
|
|
data->set.str[STRING_CERT]);
|
|
|
|
if(ret) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s",
|
|
data->set.str[STRING_CERT], -ret, errorbuf);
|
|
|
|
return CURLE_SSL_CERTPROBLEM;
|
|
}
|
|
}
|
|
|
|
/* Load the client private key */
|
|
if(data->set.str[STRING_KEY]) {
|
|
mbedtls_pk_init(&connssl->pk);
|
|
ret = mbedtls_pk_parse_keyfile(&connssl->pk, data->set.str[STRING_KEY],
|
|
data->set.str[STRING_KEY_PASSWD]);
|
|
if(ret == 0 && !mbedtls_pk_can_do(&connssl->pk, MBEDTLS_PK_RSA))
|
|
ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
|
|
|
|
if(ret) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
|
|
data->set.str[STRING_KEY], -ret, errorbuf);
|
|
|
|
return CURLE_SSL_CERTPROBLEM;
|
|
}
|
|
}
|
|
|
|
/* Load the CRL */
|
|
memset(&connssl->crl, 0, sizeof(mbedtls_x509_crl));
|
|
|
|
if(data->set.str[STRING_SSL_CRLFILE]) {
|
|
ret = mbedtls_x509_crl_parse_file(&connssl->crl,
|
|
data->set.str[STRING_SSL_CRLFILE]);
|
|
|
|
if(ret) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s",
|
|
data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf);
|
|
|
|
return CURLE_SSL_CRL_BADFILE;
|
|
}
|
|
}
|
|
|
|
infof(data, "mbedTLS: Connecting to %s:%d\n",
|
|
conn->host.name, conn->remote_port);
|
|
|
|
mbedtls_ssl_config_init(&connssl->config);
|
|
|
|
mbedtls_ssl_init(&connssl->ssl);
|
|
if(mbedtls_ssl_setup(&connssl->ssl, &connssl->config)) {
|
|
failf(data, "mbedTLS: ssl_init failed");
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
}
|
|
ret = mbedtls_ssl_config_defaults(&connssl->config,
|
|
MBEDTLS_SSL_IS_CLIENT,
|
|
MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
MBEDTLS_SSL_PRESET_DEFAULT);
|
|
if(ret) {
|
|
failf(data, "mbedTLS: ssl_config failed");
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
}
|
|
|
|
/* new profile with RSA min key len = 1024 ... */
|
|
mbedtls_ssl_conf_cert_profile( &connssl->config,
|
|
&mbedtls_x509_crt_profile_fr);
|
|
|
|
switch(data->set.ssl.version) {
|
|
case CURL_SSLVERSION_SSLv3:
|
|
mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
|
|
MBEDTLS_SSL_MINOR_VERSION_0);
|
|
infof(data, "mbedTLS: Forced min. SSL Version to be SSLv3\n");
|
|
break;
|
|
case CURL_SSLVERSION_TLSv1_0:
|
|
mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
|
|
MBEDTLS_SSL_MINOR_VERSION_1);
|
|
infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.0\n");
|
|
break;
|
|
case CURL_SSLVERSION_TLSv1_1:
|
|
mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
|
|
MBEDTLS_SSL_MINOR_VERSION_2);
|
|
infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.1\n");
|
|
break;
|
|
case CURL_SSLVERSION_TLSv1_2:
|
|
mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
|
|
MBEDTLS_SSL_MINOR_VERSION_3);
|
|
infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.2\n");
|
|
break;
|
|
}
|
|
|
|
mbedtls_ssl_conf_authmode(&connssl->config, MBEDTLS_SSL_VERIFY_OPTIONAL);
|
|
|
|
mbedtls_ssl_conf_rng(&connssl->config, mbedtls_ctr_drbg_random,
|
|
&connssl->ctr_drbg);
|
|
mbedtls_ssl_set_bio(&connssl->ssl, &conn->sock[sockindex],
|
|
mbedtls_net_send,
|
|
mbedtls_net_recv,
|
|
NULL /* rev_timeout() */);
|
|
|
|
mbedtls_ssl_conf_ciphersuites(&connssl->config,
|
|
mbedtls_ssl_list_ciphersuites());
|
|
if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) {
|
|
memcpy(&connssl->ssn, old_session, old_session_size);
|
|
infof(data, "mbedTLS re-using session\n");
|
|
}
|
|
|
|
mbedtls_ssl_set_session(&connssl->ssl,
|
|
&connssl->ssn);
|
|
|
|
mbedtls_ssl_conf_ca_chain(&connssl->config,
|
|
&connssl->cacert,
|
|
&connssl->crl);
|
|
|
|
if(data->set.str[STRING_KEY]) {
|
|
mbedtls_ssl_conf_own_cert(&connssl->config,
|
|
&connssl->clicert, &connssl->pk);
|
|
}
|
|
if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) &&
|
|
#ifdef ENABLE_IPV6
|
|
!Curl_inet_pton(AF_INET6, conn->host.name, &addr) &&
|
|
#endif
|
|
sni && mbedtls_ssl_set_hostname(&connssl->ssl, conn->host.name)) {
|
|
infof(data, "WARNING: failed to configure "
|
|
"server name indication (SNI) TLS extension\n");
|
|
}
|
|
|
|
#ifdef HAS_ALPN
|
|
if(data->set.ssl_enable_alpn) {
|
|
const char *protocols[3];
|
|
const char **p = protocols;
|
|
#ifdef USE_NGHTTP2
|
|
if(data->set.httpversion >= CURL_HTTP_VERSION_2)
|
|
*p++ = NGHTTP2_PROTO_VERSION_ID;
|
|
#endif
|
|
*p++ = ALPN_HTTP_1_1;
|
|
*p = NULL;
|
|
if(mbedtls_ssl_conf_alpn_protocols(&connssl->config, protocols)) {
|
|
failf(data, "Failed setting ALPN protocols");
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
}
|
|
for(p = protocols; *p; ++p)
|
|
infof(data, "ALPN, offering %s\n", *p);
|
|
}
|
|
#endif
|
|
|
|
#ifdef MBEDTLS_DEBUG
|
|
mbedtls_ssl_conf_dbg(&connssl->ssl, mbedtls_debug, data);
|
|
#endif
|
|
|
|
connssl->connecting_state = ssl_connect_2;
|
|
|
|
return CURLE_OK;
|
|
}
|
|
|
|
static CURLcode
|
|
mbedtls_connect_step2(struct connectdata *conn,
|
|
int sockindex)
|
|
{
|
|
int ret;
|
|
struct SessionHandle *data = conn->data;
|
|
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
|
const mbedtls_x509_crt *peercert;
|
|
|
|
#ifdef HAS_ALPN
|
|
const char* next_protocol;
|
|
#endif
|
|
|
|
char errorbuf[128];
|
|
errorbuf[0] = 0;
|
|
|
|
conn->recv[sockindex] = mbedtls_recv;
|
|
conn->send[sockindex] = mbedtls_send;
|
|
|
|
ret = mbedtls_ssl_handshake(&connssl->ssl);
|
|
|
|
if(ret == MBEDTLS_ERR_SSL_WANT_READ) {
|
|
connssl->connecting_state = ssl_connect_2_reading;
|
|
return CURLE_OK;
|
|
}
|
|
else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
|
connssl->connecting_state = ssl_connect_2_writing;
|
|
return CURLE_OK;
|
|
}
|
|
else if(ret) {
|
|
#ifdef MBEDTLS_ERROR_C
|
|
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
|
|
#endif /* MBEDTLS_ERROR_C */
|
|
failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s",
|
|
-ret, errorbuf);
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
}
|
|
|
|
infof(data, "mbedTLS: Handshake complete, cipher is %s\n",
|
|
mbedtls_ssl_get_ciphersuite(&conn->ssl[sockindex].ssl)
|
|
);
|
|
|
|
ret = mbedtls_ssl_get_verify_result(&conn->ssl[sockindex].ssl);
|
|
|
|
if(ret && data->set.ssl.verifypeer) {
|
|
if(ret & MBEDTLS_X509_BADCERT_EXPIRED)
|
|
failf(data, "Cert verify failed: BADCERT_EXPIRED");
|
|
|
|
if(ret & MBEDTLS_X509_BADCERT_REVOKED) {
|
|
failf(data, "Cert verify failed: BADCERT_REVOKED");
|
|
return CURLE_SSL_CACERT;
|
|
}
|
|
|
|
if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
|
|
failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");
|
|
|
|
if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
|
|
failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
|
|
|
|
return CURLE_PEER_FAILED_VERIFICATION;
|
|
}
|
|
|
|
peercert = mbedtls_ssl_get_peer_cert(&connssl->ssl);
|
|
|
|
if(peercert && data->set.verbose) {
|
|
const size_t bufsize = 16384;
|
|
char *buffer = malloc(bufsize);
|
|
|
|
if(!buffer)
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0)
|
|
infof(data, "Dumping cert info:\n%s\n", buffer);
|
|
else
|
|
infof(data, "Unable to dump certificate information.\n");
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
|
|
int size;
|
|
CURLcode result;
|
|
mbedtls_x509_crt *p;
|
|
unsigned char pubkey[PUB_DER_MAX_BYTES];
|
|
|
|
if(!peercert || !peercert->raw.p || !peercert->raw.len) {
|
|
failf(data, "Failed due to missing peer certificate");
|
|
return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
|
|
}
|
|
|
|
p = calloc(1, sizeof(*p));
|
|
|
|
if(!p)
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
mbedtls_x509_crt_init(p);
|
|
|
|
/* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der
|
|
needs a non-const key, for now.
|
|
https://github.com/ARMmbed/mbedtls/issues/396 */
|
|
if(mbedtls_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) {
|
|
failf(data, "Failed copying peer certificate");
|
|
mbedtls_x509_crt_free(p);
|
|
free(p);
|
|
return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
|
|
}
|
|
|
|
size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES);
|
|
|
|
if(size <= 0) {
|
|
failf(data, "Failed copying public key from peer certificate");
|
|
mbedtls_x509_crt_free(p);
|
|
free(p);
|
|
return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
|
|
}
|
|
|
|
/* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */
|
|
result = Curl_pin_peer_pubkey(data,
|
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
|
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
|
if(result) {
|
|
mbedtls_x509_crt_free(p);
|
|
free(p);
|
|
return result;
|
|
}
|
|
|
|
mbedtls_x509_crt_free(p);
|
|
free(p);
|
|
}
|
|
|
|
#ifdef HAS_ALPN
|
|
if(data->set.ssl_enable_alpn) {
|
|
next_protocol = mbedtls_ssl_get_alpn_protocol(&connssl->ssl);
|
|
|
|
if(next_protocol) {
|
|
infof(data, "ALPN, server accepted to use %s\n", next_protocol);
|
|
#ifdef USE_NGHTTP2
|
|
if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
|
|
NGHTTP2_PROTO_VERSION_ID_LEN) &&
|
|
!next_protocol[NGHTTP2_PROTO_VERSION_ID_LEN]) {
|
|
conn->negnpn = CURL_HTTP_VERSION_2;
|
|
}
|
|
else
|
|
#endif
|
|
if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) &&
|
|
!next_protocol[ALPN_HTTP_1_1_LENGTH]) {
|
|
conn->negnpn = CURL_HTTP_VERSION_1_1;
|
|
}
|
|
}
|
|
else {
|
|
infof(data, "ALPN, server did not agree to a protocol\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
connssl->connecting_state = ssl_connect_3;
|
|
infof(data, "SSL connected\n");
|
|
|
|
return CURLE_OK;
|
|
}
|
|
|
|
static CURLcode
|
|
mbedtls_connect_step3(struct connectdata *conn,
|
|
int sockindex)
|
|
{
|
|
CURLcode retcode = CURLE_OK;
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
|
struct SessionHandle *data = conn->data;
|
|
void *old_ssl_sessionid = NULL;
|
|
mbedtls_ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn;
|
|
int incache;
|
|
|
|
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
|
|
|
|
/* Save the current session data for possible re-use */
|
|
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
|
|
if(incache) {
|
|
if(old_ssl_sessionid != our_ssl_sessionid) {
|
|
infof(data, "old SSL session ID is stale, removing\n");
|
|
Curl_ssl_delsessionid(conn, old_ssl_sessionid);
|
|
incache = FALSE;
|
|
}
|
|
}
|
|
if(!incache) {
|
|
void *new_session = malloc(sizeof(mbedtls_ssl_session));
|
|
|
|
if(new_session) {
|
|
memcpy(new_session, our_ssl_sessionid,
|
|
sizeof(mbedtls_ssl_session));
|
|
|
|
retcode = Curl_ssl_addsessionid(conn, new_session,
|
|
sizeof(mbedtls_ssl_session));
|
|
}
|
|
else {
|
|
retcode = CURLE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if(retcode) {
|
|
failf(data, "failed to store ssl session");
|
|
return retcode;
|
|
}
|
|
}
|
|
|
|
connssl->connecting_state = ssl_connect_done;
|
|
|
|
return CURLE_OK;
|
|
}
|
|
|
|
static ssize_t mbedtls_send(struct connectdata *conn,
|
|
int sockindex,
|
|
const void *mem,
|
|
size_t len,
|
|
CURLcode *curlcode)
|
|
{
|
|
int ret = -1;
|
|
|
|
ret = mbedtls_ssl_write(&conn->ssl[sockindex].ssl,
|
|
(unsigned char *)mem, len);
|
|
|
|
if(ret < 0) {
|
|
*curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ?
|
|
CURLE_AGAIN : CURLE_SEND_ERROR;
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void Curl_mbedtls_close_all(struct SessionHandle *data)
|
|
{
|
|
(void)data;
|
|
}
|
|
|
|
void Curl_mbedtls_close(struct connectdata *conn, int sockindex)
|
|
{
|
|
/* mbedtls_rsa_free(&conn->ssl[sockindex].rsa); */
|
|
mbedtls_x509_crt_free(&conn->ssl[sockindex].clicert);
|
|
mbedtls_x509_crt_free(&conn->ssl[sockindex].cacert);
|
|
mbedtls_x509_crl_free(&conn->ssl[sockindex].crl);
|
|
mbedtls_ssl_free(&conn->ssl[sockindex].ssl);
|
|
}
|
|
|
|
static ssize_t mbedtls_recv(struct connectdata *conn,
|
|
int num,
|
|
char *buf,
|
|
size_t buffersize,
|
|
CURLcode *curlcode)
|
|
{
|
|
int ret = -1;
|
|
ssize_t len = -1;
|
|
|
|
memset(buf, 0, buffersize);
|
|
ret = mbedtls_ssl_read(&conn->ssl[num].ssl, (unsigned char *)buf,
|
|
buffersize);
|
|
|
|
if(ret <= 0) {
|
|
if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
|
|
return 0;
|
|
|
|
*curlcode = (ret == MBEDTLS_ERR_SSL_WANT_READ) ?
|
|
CURLE_AGAIN : CURLE_RECV_ERROR;
|
|
return -1;
|
|
}
|
|
|
|
len = ret;
|
|
|
|
return len;
|
|
}
|
|
|
|
void Curl_mbedtls_session_free(void *ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
size_t Curl_mbedtls_version(char *buffer, size_t size)
|
|
{
|
|
unsigned int version = mbedtls_version_get_number();
|
|
return snprintf(buffer, size, "mbedTLS/%d.%d.%d", version>>24,
|
|
(version>>16)&0xff, (version>>8)&0xff);
|
|
}
|
|
|
|
static CURLcode
|
|
mbedtls_connect_common(struct connectdata *conn,
|
|
int sockindex,
|
|
bool nonblocking,
|
|
bool *done)
|
|
{
|
|
CURLcode retcode;
|
|
struct SessionHandle *data = conn->data;
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
|
curl_socket_t sockfd = conn->sock[sockindex];
|
|
long timeout_ms;
|
|
int what;
|
|
|
|
/* check if the connection has already been established */
|
|
if(ssl_connection_complete == connssl->state) {
|
|
*done = TRUE;
|
|
return CURLE_OK;
|
|
}
|
|
|
|
if(ssl_connect_1==connssl->connecting_state) {
|
|
/* Find out how much more time we're allowed */
|
|
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;
|
|
}
|
|
retcode = mbedtls_connect_step1(conn, sockindex);
|
|
if(retcode)
|
|
return retcode;
|
|
}
|
|
|
|
while(ssl_connect_2 == connssl->connecting_state ||
|
|
ssl_connect_2_reading == connssl->connecting_state ||
|
|
ssl_connect_2_writing == connssl->connecting_state) {
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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) {
|
|
|
|
curl_socket_t writefd = ssl_connect_2_writing==
|
|
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
|
|
curl_socket_t readfd = ssl_connect_2_reading==
|
|
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
|
|
|
|
what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
|
|
if(what < 0) {
|
|
/* fatal error */
|
|
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
}
|
|
else if(0 == what) {
|
|
if(nonblocking) {
|
|
*done = FALSE;
|
|
return CURLE_OK;
|
|
}
|
|
else {
|
|
/* timeout */
|
|
failf(data, "SSL connection timeout");
|
|
return CURLE_OPERATION_TIMEDOUT;
|
|
}
|
|
}
|
|
/* socket is readable or writable */
|
|
}
|
|
|
|
/* Run transaction, and return to the caller if it failed or if
|
|
* this connection is part of a multi handle and this loop would
|
|
* execute again. This permits the owner of a multi handle to
|
|
* abort a connection attempt before step2 has completed while
|
|
* ensuring that a client using select() or epoll() will always
|
|
* have a valid fdset to wait on.
|
|
*/
|
|
retcode = mbedtls_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;
|
|
|
|
} /* repeat step2 until all transactions are done. */
|
|
|
|
if(ssl_connect_3==connssl->connecting_state) {
|
|
retcode = mbedtls_connect_step3(conn, sockindex);
|
|
if(retcode)
|
|
return retcode;
|
|
}
|
|
|
|
if(ssl_connect_done==connssl->connecting_state) {
|
|
connssl->state = ssl_connection_complete;
|
|
conn->recv[sockindex] = mbedtls_recv;
|
|
conn->send[sockindex] = mbedtls_send;
|
|
*done = TRUE;
|
|
}
|
|
else
|
|
*done = FALSE;
|
|
|
|
/* Reset our connect state machine */
|
|
connssl->connecting_state = ssl_connect_1;
|
|
|
|
return CURLE_OK;
|
|
}
|
|
|
|
CURLcode
|
|
Curl_mbedtls_connect_nonblocking(struct connectdata *conn,
|
|
int sockindex,
|
|
bool *done)
|
|
{
|
|
return mbedtls_connect_common(conn, sockindex, TRUE, done);
|
|
}
|
|
|
|
|
|
CURLcode
|
|
Curl_mbedtls_connect(struct connectdata *conn,
|
|
int sockindex)
|
|
{
|
|
CURLcode retcode;
|
|
bool done = FALSE;
|
|
|
|
retcode = mbedtls_connect_common(conn, sockindex, FALSE, &done);
|
|
if(retcode)
|
|
return retcode;
|
|
|
|
DEBUGASSERT(done);
|
|
|
|
return CURLE_OK;
|
|
}
|
|
|
|
/*
|
|
* return 0 error initializing SSL
|
|
* return 1 SSL initialized successfully
|
|
*/
|
|
int mbedtls_init(void)
|
|
{
|
|
return Curl_polarsslthreadlock_thread_setup();
|
|
}
|
|
|
|
void mbedtls_cleanup(void)
|
|
{
|
|
(void)Curl_polarsslthreadlock_thread_cleanup();
|
|
}
|
|
|
|
#endif /* USE_MBEDTLS */
|