static CURLcode schannel_connect_step3(struct connectdata *conn, int sockindex) { CURLcode retcode = CURLE_OK; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct curl_schannel_cred *old_cred = NULL; int incache; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n", conn->host.name, conn->remote_port); /* check if the required context attributes are met */ if(connssl->ret_flags != connssl->req_flags) { if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT)) failf(data, "schannel: failed to setup sequence detection"); if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT)) failf(data, "schannel: failed to setup replay detection"); if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY)) failf(data, "schannel: failed to setup confidentiality"); if(!(connssl->ret_flags & ISC_RET_EXTENDED_ERROR)) failf(data, "schannel: failed to setup extended errors"); if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY)) failf(data, "schannel: failed to setup memory allocation"); if(!(connssl->ret_flags & ISC_RET_STREAM)) failf(data, "schannel: failed to setup stream orientation"); return CURLE_SSL_CONNECT_ERROR; } /* save the current session data for possible re-use */ 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); incache = FALSE; } } if(!incache) { retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred, sizeof(struct curl_schannel_cred)); if(retcode) { failf(data, "schannel: failed to store credential handle"); return retcode; } else { infof(data, "schannel: stored crendential handle\n"); } } connssl->connecting_state = ssl_connect_done; return CURLE_OK; }
static CURLcode polarssl_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; ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ; bool 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(ssl_session)); if(new_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; } if(retcode) { failf(data, "failed to store ssl session"); return retcode; } } connssl->connecting_state = ssl_connect_done; return CURLE_OK; }
static CURLcode mbed_connect_step3(struct connectdata *conn, int sockindex) { CURLcode retcode = CURLE_OK; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct Curl_easy *data = conn->data; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); if(SSL_SET_OPTION(primary.sessionid)) { int ret; mbedtls_ssl_session *our_ssl_sessionid; void *old_ssl_sessionid = NULL; our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); if(!our_ssl_sessionid) return CURLE_OUT_OF_MEMORY; mbedtls_ssl_session_init(our_ssl_sessionid); ret = mbedtls_ssl_get_session(&BACKEND->ssl, our_ssl_sessionid); if(ret) { free(our_ssl_sessionid); failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } /* If there's already a matching session in the cache, delete it */ Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex)) Curl_ssl_delsessionid(conn, old_ssl_sessionid); retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0, sockindex); Curl_ssl_sessionid_unlock(conn); if(retcode) { free(our_ssl_sessionid); failf(data, "failed to store ssl session"); return retcode; } } connssl->connecting_state = ssl_connect_done; return CURLE_OK; }
static CURLcode cyassl_connect_step3(struct connectdata *conn, int sockindex) { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); if(SSL_SET_OPTION(primary.sessionid)) { bool incache; SSL_SESSION *our_ssl_sessionid; void *old_ssl_sessionid = NULL; our_ssl_sessionid = SSL_get_session(BACKEND->handle); Curl_ssl_sessionid_lock(conn); incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex)); 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) { result = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0 /* unknown size */, sockindex); if(result) { Curl_ssl_sessionid_unlock(conn); failf(data, "failed to store ssl session"); return result; } } Curl_ssl_sessionid_unlock(conn); } connssl->connecting_state = ssl_connect_done; return result; }
static CURLcode polarssl_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; ssl_session *our_ssl_sessionid; int ret; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); our_ssl_sessionid = malloc(sizeof(ssl_session)); if(!our_ssl_sessionid) return CURLE_OUT_OF_MEMORY; ssl_session_init(our_ssl_sessionid); ret = ssl_get_session(&connssl->ssl, our_ssl_sessionid); if(ret) { failf(data, "ssl_get_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } /* If there's already a matching session in the cache, delete it */ if(!Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL)) Curl_ssl_delsessionid(conn, old_ssl_sessionid); retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0); if(retcode) { free(our_ssl_sessionid); failf(data, "failed to store ssl session"); return retcode; } connssl->connecting_state = ssl_connect_done; return CURLE_OK; }
static CURLcode cyassl_connect_step3(struct connectdata *conn, int sockindex) { CURLcode result = CURLE_OK; void *old_ssl_sessionid=NULL; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; bool incache; SSL_SESSION *our_ssl_sessionid; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); our_ssl_sessionid = SSL_get_session(connssl->handle); 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) { result = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0 /* unknown size */); if(result) { failf(data, "failed to store ssl session"); return result; } } connssl->connecting_state = ssl_connect_done; return result; }
static CURLcode polarssl_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; /* PolarSSL only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ havege_init(&connssl->hs); /* Load the trusted CA */ memset(&connssl->cacert, 0, sizeof(x509_cert)); if(data->set.str[STRING_SSL_CAFILE]) { ret = x509parse_crtfile(&connssl->cacert, data->set.str[STRING_SSL_CAFILE]); if(ret<0) { failf(data, "Error reading ca cert file %s: -0x%04X", data->set.str[STRING_SSL_CAFILE], ret); if(data->set.ssl.verifypeer) return CURLE_SSL_CACERT_BADFILE; } } /* Load the client certificate */ memset(&connssl->clicert, 0, sizeof(x509_cert)); if(data->set.str[STRING_CERT]) { ret = x509parse_crtfile(&connssl->clicert, data->set.str[STRING_CERT]); if(ret) { failf(data, "Error reading client cert file %s: -0x%04X", data->set.str[STRING_CERT], -ret); return CURLE_SSL_CERTPROBLEM; } } /* Load the client private key */ if(data->set.str[STRING_KEY]) { ret = x509parse_keyfile(&connssl->rsa, data->set.str[STRING_KEY], data->set.str[STRING_KEY_PASSWD]); if(ret) { failf(data, "Error reading private key %s: -0x%04X", data->set.str[STRING_KEY], -ret); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&connssl->crl, 0, sizeof(x509_crl)); if(data->set.str[STRING_SSL_CRLFILE]) { ret = x509parse_crlfile(&connssl->crl, data->set.str[STRING_SSL_CRLFILE]); if(ret) { failf(data, "Error reading CRL file %s: -0x%04X", data->set.str[STRING_SSL_CRLFILE], -ret); return CURLE_SSL_CRL_BADFILE; } } infof(data, "PolarSSL: Connecting to %s:%d\n", conn->host.name, conn->remote_port); if(ssl_init(&connssl->ssl)) { failf(data, "PolarSSL: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT); ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL); ssl_set_rng(&connssl->ssl, HAVEGE_RANDOM, &connssl->hs); ssl_set_bio(&connssl->ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); #if POLARSSL_VERSION_NUMBER<0x01000000 ssl_set_ciphers(&connssl->ssl, ssl_default_ciphers); #else ssl_set_ciphersuites(&connssl->ssl, ssl_default_ciphersuites); #endif if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) { memcpy(&connssl->ssn, old_session, old_session_size); infof(data, "PolarSSL re-using session\n"); } ssl_set_session(&connssl->ssl, 1, 600, &connssl->ssn); ssl_set_ca_chain(&connssl->ssl, &connssl->cacert, &connssl->crl, conn->host.name); ssl_set_own_cert(&connssl->ssl, &connssl->clicert, &connssl->rsa); if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) && #ifdef ENABLE_IPV6 !Curl_inet_pton(AF_INET6, conn->host.name, &addr) && #endif sni && ssl_set_hostname(&connssl->ssl, conn->host.name)) { infof(data, "WARNING: failed to configure " "server name indication (SNI) TLS extension\n"); } #ifdef POLARSSL_DEBUG ssl_set_dbg(&connssl->ssl, polarssl_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode polarssl_connect_step1(struct connectdata *conn, int sockindex) { struct Curl_easy *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; const char *capath = SSL_CONN_CONFIG(CApath); const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; int ret = -1; char errorbuf[128]; errorbuf[0]=0; /* PolarSSL only supports SSLv3 and TLSv1 */ if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } #ifdef THREADING_SUPPORT entropy_init_mutex(&entropy); if((ret = ctr_drbg_init(&BACKEND->ctr_drbg, entropy_func_mutex, &entropy, NULL, 0)) != 0) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #else entropy_init(&BACKEND->entropy); if((ret = ctr_drbg_init(&BACKEND->ctr_drbg, entropy_func, &BACKEND->entropy, NULL, 0)) != 0) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #endif /* THREADING_SUPPORT */ /* Load the trusted CA */ memset(&BACKEND->cacert, 0, sizeof(x509_crt)); if(SSL_CONN_CONFIG(CAfile)) { ret = x509_crt_parse_file(&BACKEND->cacert, SSL_CONN_CONFIG(CAfile)); if(ret<0) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading ca cert file %s - PolarSSL: (-0x%04X) %s", SSL_CONN_CONFIG(CAfile), -ret, errorbuf); if(SSL_CONN_CONFIG(verifypeer)) return CURLE_SSL_CACERT_BADFILE; } } if(capath) { ret = x509_crt_parse_path(&BACKEND->cacert, capath); if(ret<0) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading ca cert path %s - PolarSSL: (-0x%04X) %s", capath, -ret, errorbuf); if(SSL_CONN_CONFIG(verifypeer)) return CURLE_SSL_CACERT_BADFILE; } } /* Load the client certificate */ memset(&BACKEND->clicert, 0, sizeof(x509_crt)); if(SSL_SET_OPTION(cert)) { ret = x509_crt_parse_file(&BACKEND->clicert, SSL_SET_OPTION(cert)); if(ret) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading client cert file %s - PolarSSL: (-0x%04X) %s", SSL_SET_OPTION(cert), -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the client private key */ if(SSL_SET_OPTION(key)) { pk_context pk; pk_init(&pk); ret = pk_parse_keyfile(&pk, SSL_SET_OPTION(key), SSL_SET_OPTION(key_passwd)); if(ret == 0 && !pk_can_do(&pk, POLARSSL_PK_RSA)) ret = POLARSSL_ERR_PK_TYPE_MISMATCH; if(ret == 0) rsa_copy(&BACKEND->rsa, pk_rsa(pk)); else rsa_free(&BACKEND->rsa); pk_free(&pk); if(ret) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s", SSL_SET_OPTION(key), -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&BACKEND->crl, 0, sizeof(x509_crl)); if(SSL_SET_OPTION(CRLfile)) { ret = x509_crl_parse_file(&BACKEND->crl, SSL_SET_OPTION(CRLfile)); if(ret) { error_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s", SSL_SET_OPTION(CRLfile), -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } } infof(data, "PolarSSL: Connecting to %s:%d\n", hostname, port); if(ssl_init(&BACKEND->ssl)) { failf(data, "PolarSSL: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: ssl_set_min_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1); break; case CURL_SSLVERSION_SSLv3: ssl_set_min_version(&BACKEND->ssl, SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0); ssl_set_max_version(&BACKEND->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: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: { CURLcode result = set_ssl_version_min_max(conn, sockindex); if(result != CURLE_OK) return result; break; } default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } ssl_set_endpoint(&BACKEND->ssl, SSL_IS_CLIENT); ssl_set_authmode(&BACKEND->ssl, SSL_VERIFY_OPTIONAL); ssl_set_rng(&BACKEND->ssl, ctr_drbg_random, &BACKEND->ctr_drbg); ssl_set_bio(&BACKEND->ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); ssl_set_ciphersuites(&BACKEND->ssl, ssl_list_ciphersuites()); /* Check if there's a cached ID we can/should use here! */ if(SSL_SET_OPTION(primary.sessionid)) { void *old_session = NULL; Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &old_session, NULL, sockindex)) { ret = ssl_set_session(&BACKEND->ssl, old_session); if(ret) { Curl_ssl_sessionid_unlock(conn); failf(data, "ssl_set_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } infof(data, "PolarSSL re-using session\n"); } Curl_ssl_sessionid_unlock(conn); } ssl_set_ca_chain(&BACKEND->ssl, &BACKEND->cacert, &BACKEND->crl, hostname); ssl_set_own_cert_rsa(&BACKEND->ssl, &BACKEND->clicert, &BACKEND->rsa); if(ssl_set_hostname(&BACKEND->ssl, hostname)) { /* ssl_set_hostname() sets the name to use in CN/SAN checks *and* the name to set in the SNI extension. So even if curl connects to a host specified as an IP address, this function must be used. */ failf(data, "couldn't set hostname in PolarSSL"); return CURLE_SSL_CONNECT_ERROR; } #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { static const char *protocols[3]; int cur = 0; #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) { 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(&BACKEND->ssl, protocols); } #endif #ifdef POLARSSL_DEBUG ssl_set_dbg(&BACKEND->ssl, polarssl_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
/* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ CURLcode Curl_axtls_connect(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; SSL_CTX *ssl_ctx; SSL *ssl; int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0}; int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0}; int i, ssl_fcn_return; const uint8_t *ssl_sessionid; size_t ssl_idsize; const char *peer_CN; uint32_t dns_altname_index; const char *dns_altname; int8_t found_subject_alt_names = 0; int8_t found_subject_alt_name_matching_conn = 0; /* Assuming users will not compile in custom key/cert to axTLS */ uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER; if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ return CURLE_OK; /* axTLS only supports TLSv1 */ /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: break; default: failf(data, "axTLS only supports TLSv1"); return CURLE_SSL_CONNECT_ERROR; } #ifdef AXTLSDEBUG client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS; #endif /* AXTLSDEBUG */ /* Allocate an SSL_CTX struct */ ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS); if(ssl_ctx == NULL) { failf(data, "unable to create client SSL context"); return CURLE_SSL_CONNECT_ERROR; } /* Load the trusted CA cert bundle file */ if(data->set.ssl.CAfile) { if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL) != SSL_OK) { infof(data, "error reading ca cert file %s \n", data->set.ssl.CAfile); if(data->set.ssl.verifypeer) { Curl_axtls_close(conn, sockindex); return CURLE_SSL_CACERT_BADFILE; } } else infof(data, "found certificates in %s\n", data->set.ssl.CAfile); } /* curl_gtls.c tasks we're skipping for now: * 1) certificate revocation list checking * 2) dns name assignment to host * 3) set protocol priority. axTLS is TLSv1 only, so can probably ignore * 4) set certificate priority. axTLS ignores type and sends certs in * order added. can probably ignore this. */ /* Load client certificate */ if(data->set.str[STRING_CERT]) { i=0; /* Instead of trying to analyze cert type here, let axTLS try them all. */ while(cert_types[i] != 0) { ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i], data->set.str[STRING_CERT], NULL); if(ssl_fcn_return == SSL_OK) { infof(data, "successfully read cert file %s \n", data->set.str[STRING_CERT]); break; } i++; } /* Tried all cert types, none worked. */ if(cert_types[i] == 0) { failf(data, "%s is not x509 or pkcs12 format", data->set.str[STRING_CERT]); Curl_axtls_close(conn, sockindex); return CURLE_SSL_CERTPROBLEM; } } /* Load client key. If a pkcs12 file successfully loaded a cert, then there's nothing to do because the key has already been loaded. */ if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) { i=0; /* Instead of trying to analyze key type here, let axTLS try them all. */ while(key_types[i] != 0) { ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i], data->set.str[STRING_KEY], NULL); if(ssl_fcn_return == SSL_OK) { infof(data, "successfully read key file %s \n", data->set.str[STRING_KEY]); break; } i++; } /* Tried all key types, none worked. */ if(key_types[i] == 0) { failf(data, "Failure: %s is not a supported key file", data->set.str[STRING_KEY]); Curl_axtls_close(conn, sockindex); return CURLE_SSL_CONNECT_ERROR; } } /* curl_gtls.c does more here that is being left out for now * 1) set session credentials. can probably ignore since axtls puts this * info in the ssl_ctx struct * 2) setting up callbacks. these seem gnutls specific */ /* In axTLS, handshaking happens inside ssl_client_new. */ if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ infof (data, "SSL re-using session ID\n"); ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], ssl_sessionid, (uint8_t)ssl_idsize); } else ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0); /* Check to make sure handshake was ok. */ ssl_fcn_return = ssl_handshake_status(ssl); if(ssl_fcn_return != SSL_OK) { Curl_axtls_close(conn, sockindex); ssl_display_error(ssl_fcn_return); /* goes to stdout. */ return map_error_to_curl(ssl_fcn_return); } infof (data, "handshake completed successfully\n"); /* Here, curl_gtls.c gets the peer certificates and fails out depending on * settings in "data." axTLS api doesn't have get cert chain fcn, so omit? */ /* Verify server's certificate */ if(data->set.ssl.verifypeer) { if(ssl_verify_cert(ssl) != SSL_OK) { Curl_axtls_close(conn, sockindex); failf(data, "server cert verify failed"); return CURLE_SSL_CONNECT_ERROR; } } else infof(data, "\t server certificate verification SKIPPED\n"); /* Here, curl_gtls.c does issuer verification. axTLS has no straightforward * equivalent, so omitting for now.*/ /* Here, curl_gtls.c does the following * 1) x509 hostname checking per RFC2818. axTLS doesn't support this, but * it seems useful. This is now implemented, by Oscar Koeroo * 2) checks cert validity based on time. axTLS does this in ssl_verify_cert * 3) displays a bunch of cert information. axTLS doesn't support most of * this, but a couple fields are available. */ /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a risk of an inifite loop */ for(dns_altname_index = 0; ; dns_altname_index++) { dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index); if(dns_altname == NULL) { break; } found_subject_alt_names = 1; infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n", dns_altname, conn->host.name); if(Curl_cert_hostcheck(dns_altname, conn->host.name)) { found_subject_alt_name_matching_conn = 1; break; } } /* RFC2818 checks */ if(found_subject_alt_names && !found_subject_alt_name_matching_conn) { /* Break connection ! */ Curl_axtls_close(conn, sockindex); failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; } else if(found_subject_alt_names == 0) { /* Per RFC2818, when no Subject Alt Names were available, examine the peer CN as a legacy fallback */ peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); if(peer_CN == NULL) { /* Similar behaviour to the OpenSSL interface */ Curl_axtls_close(conn, sockindex); failf(data, "unable to obtain common name from peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } else { if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) { if(data->set.ssl.verifyhost) { /* Break connection ! */ Curl_axtls_close(conn, sockindex); failf(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname); } } } /* General housekeeping */ conn->ssl[sockindex].state = ssl_connection_complete; conn->ssl[sockindex].ssl = ssl; conn->ssl[sockindex].ssl_ctx = ssl_ctx; conn->recv[sockindex] = axtls_recv; conn->send[sockindex] = axtls_send; /* Put our freshly minted SSL session in cache */ ssl_idsize = ssl_get_session_id_size(ssl); ssl_sessionid = ssl_get_session_id(ssl); if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize) != CURLE_OK) infof (data, "failed to add session to cache\n"); return CURLE_OK; }
static CURLcode polarssl_connect_step2(struct connectdata *conn, int sockindex) { int ret; struct SessionHandle *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; char buffer[1024]; char errorbuf[128]; memset(errorbuf, 0, sizeof(errorbuf)); 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); 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; } } infof(data, "PolarSSL: Handshake complete, cipher is %s\n", #if POLARSSL_VERSION_NUMBER<0x01000000 ssl_get_cipher(&conn->ssl[sockindex].ssl) #elif POLARSSL_VERSION_NUMBER >= 0x01010000 ssl_get_ciphersuite(&conn->ssl[sockindex].ssl) #else ssl_get_ciphersuite_name(&conn->ssl[sockindex].ssl) #endif ); ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl); if(ret && data->set.ssl.verifypeer) { if(ret & BADCERT_EXPIRED) failf(data, "Cert verify failed: BADCERT_EXPIRED"); if(ret & BADCERT_REVOKED) { failf(data, "Cert verify failed: BADCERT_REVOKED"); return CURLE_SSL_CACERT; } if(ret & BADCERT_CN_MISMATCH) failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); if(ret & BADCERT_NOT_TRUSTED) failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); return CURLE_PEER_FAILED_VERIFICATION; } /* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's 1.1.4 version and the like */ #if POLARSSL_VERSION_NUMBER<0x01020000 if(conn->ssl[sockindex].ssl.peer_cert) { #else if(ssl_get_peer_cert(&(connssl->ssl))) { #endif /* If the session was resumed, there will be no peer certs */ memset(buffer, 0, sizeof(buffer)); /* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's 1.1.4 version and the like */ #if POLARSSL_VERSION_NUMBER<0x01020000 if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ", conn->ssl[sockindex].ssl.peer_cert) != -1) #else if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ", ssl_get_peer_cert(&(connssl->ssl))) != -1) #endif infof(data, "Dumping cert info:\n%s\n", buffer); } connssl->connecting_state = ssl_connect_3; infof(data, "SSL connected\n"); return CURLE_OK; } static CURLcode polarssl_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; 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(ssl_session)); if(new_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; } if(retcode) { failf(data, "failed to store ssl session"); return retcode; } } connssl->connecting_state = ssl_connect_done; return CURLE_OK; }
static CURLcode gtls_connect_step3(struct connectdata *conn, int sockindex) { unsigned int cert_list_size; const gnutls_datum *chainp; unsigned int verify_status; gnutls_x509_crt x509_cert,x509_issuer; gnutls_datum issuerp; char certbuf[256]; /* big enough? */ size_t size; unsigned int algo; unsigned int bits; time_t certclock; const char *ptr; struct SessionHandle *data = conn->data; gnutls_session session = conn->ssl[sockindex].session; int rc; int incache; void *ssl_sessionid; CURLcode result = CURLE_OK; /* 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 first certificate in the list is the peer's certificate, following the issuer's certificate, then the issuer's issuer etc. */ chainp = gnutls_certificate_get_peers(session, &cert_list_size); if(!chainp) { if(data->set.ssl.verifypeer || data->set.ssl.verifyhost || data->set.ssl.issuercert) { #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP && data->set.ssl.username != NULL && !data->set.ssl.verifypeer && gnutls_cipher_get(session)) { /* no peer cert, but auth is ok if we have SRP user and cipher and no peer verify */ } else { #endif failf(data, "failed to get server cert"); return CURLE_PEER_FAILED_VERIFICATION; #ifdef USE_TLS_SRP } #endif } infof(data, "\t common name: WARNING couldn't obtain\n"); } 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 more of the gnutls_certificate_status_t enumerated elements bitwise or'd. To avoid denial of service attacks some default upper limits regarding the certificate key size and chain size are set. To override them use gnutls_certificate_set_verify_limits(). */ rc = gnutls_certificate_verify_peers2(session, &verify_status); if(rc < 0) { failf(data, "server cert verify failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* verify_status is a bitmask of gnutls_certificate_status bits */ if(verify_status & GNUTLS_CERT_INVALID) { if(data->set.ssl.verifypeer) { failf(data, "server certificate verification failed. CAfile: %s " "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none", data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none"); return CURLE_SSL_CACERT; } else infof(data, "\t server certificate verification FAILED\n"); } else infof(data, "\t server certificate verification OK\n"); } else { infof(data, "\t server certificate verification SKIPPED\n"); goto after_server_cert_verification; } /* initialize an X.509 certificate structure. */ gnutls_x509_crt_init(&x509_cert); /* convert the given DER or PEM encoded Certificate to the native gnutls_x509_crt_t format */ gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); if(data->set.ssl.issuercert) { 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); unload_file(issuerp); if(rc <= 0) { failf(data, "server certificate issuer check failed (IssuerCert: %s)", data->set.ssl.issuercert?data->set.ssl.issuercert:"none"); return CURLE_SSL_ISSUER_ERROR; } infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n", data->set.ssl.issuercert?data->set.ssl.issuercert:"none"); } size=sizeof(certbuf); rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, 0, /* the first and only one */ FALSE, certbuf, &size); if(rc) { infof(data, "error fetching CN from cert:%s\n", gnutls_strerror(rc)); } /* This function will check if the given certificate's subject matches the given hostname. This is a basic implementation of the matching described in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on failure. */ rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name); if(!rc) { if(data->set.ssl.verifyhost) { failf(data, "SSL: certificate subject name (%s) does not match " "target host name '%s'", certbuf, conn->host.dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t common name: %s (does not match '%s')\n", certbuf, conn->host.dispname); } else infof(data, "\t common name: %s (matched)\n", certbuf); /* Check for time-based validity */ certclock = gnutls_x509_crt_get_expiration_time(x509_cert); if(certclock == (time_t)-1) { failf(data, "server cert expiration date verify failed"); return CURLE_SSL_CONNECT_ERROR; } if(certclock < time(NULL)) { if(data->set.ssl.verifypeer) { failf(data, "server certificate expiration date has passed."); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t server certificate expiration date FAILED\n"); } else infof(data, "\t server certificate expiration date OK\n"); certclock = gnutls_x509_crt_get_activation_time(x509_cert); if(certclock == (time_t)-1) { failf(data, "server cert activation date verify failed"); return CURLE_SSL_CONNECT_ERROR; } if(certclock > time(NULL)) { if(data->set.ssl.verifypeer) { failf(data, "server certificate not activated yet."); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t server certificate activation date FAILED\n"); } else infof(data, "\t server certificate activation date OK\n"); /* Show: - ciphers used - subject - start date - expire date - common name - issuer */ /* public key algorithm's parameters */ algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); infof(data, "\t certificate public key: %s\n", gnutls_pk_algorithm_get_name(algo)); /* version of the X.509 certificate. */ infof(data, "\t certificate version: #%d\n", gnutls_x509_crt_get_version(x509_cert)); size = sizeof(certbuf); gnutls_x509_crt_get_dn(x509_cert, certbuf, &size); infof(data, "\t subject: %s\n", certbuf); certclock = gnutls_x509_crt_get_activation_time(x509_cert); showtime(data, "start date", certclock); certclock = gnutls_x509_crt_get_expiration_time(x509_cert); showtime(data, "expire date", certclock); size = sizeof(certbuf); gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size); infof(data, "\t issuer: %s\n", certbuf); gnutls_x509_crt_deinit(x509_cert); after_server_cert_verification: /* compression algorithm (if any) */ ptr = gnutls_compression_get_name(gnutls_compression_get(session)); /* 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); conn->ssl[sockindex].state = ssl_connection_complete; conn->recv[sockindex] = gtls_recv; conn->send[sockindex] = gtls_send; { /* we always unconditionally get the session id here, as even if we already got it from the cache and asked to use it in the connection, it might've been rejected and then a new one is in use now and we need to detect that. */ void *connect_sessionid; size_t connect_idsize; /* get the session ID data size */ gnutls_session_get_data(session, NULL, &connect_idsize); connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ if(connect_sessionid) { /* extract session ID to the allocated buffer */ gnutls_session_get_data(session, connect_sessionid, &connect_idsize); incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)); if(incache) { /* there was one before in the cache, so instead of risking that the previous one was rejected, we just kill that and store the new */ Curl_ssl_delsessionid(conn, ssl_sessionid); } /* store this session id */ result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize); if(result) { free(connect_sessionid); result = CURLE_OUT_OF_MEMORY; } } else result = CURLE_OUT_OF_MEMORY; } return result; }
int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) { /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx * Shutting Down an Schannel Connection */ struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct curl_schannel_cred *cached_cred = NULL; infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n", conn->host.name, conn->remote_port); if(connssl->cred && connssl->ctxt) { SecBufferDesc BuffDesc; SecBuffer Buffer; SECURITY_STATUS sspi_status; SecBuffer outbuf; SecBufferDesc outbuf_desc; CURLcode code; TCHAR *host_name; DWORD dwshut = SCHANNEL_SHUTDOWN; InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); InitSecBufferDesc(&BuffDesc, &Buffer, 1); sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle, &BuffDesc); if(sspi_status != SEC_E_OK) failf(data, "schannel: ApplyControlToken failure: %s", Curl_sspi_strerror(conn, sspi_status)); host_name = Curl_convert_UTF8_to_tchar(conn->host.name); if(!host_name) return CURLE_OUT_OF_MEMORY; /* setup output buffer */ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); 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); 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); s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if((code != 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); } } /* 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); } /* if the handle refcount is zero, check if we have not cached it */ if(connssl->cred->refcount == 0) { if(Curl_ssl_getsessionid(conn, (void**)&cached_cred, NULL)) { cached_cred = NULL; } /* if the handle was not cached, it is stale to be freed */ if(connssl->cred != cached_cred) { infof(data, "schannel: clear credential handle\n"); s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle); Curl_safefree(connssl->cred); } } } } /* free internal buffer for received encrypted data */ if(connssl->encdata_buffer != NULL) { Curl_safefree(connssl->encdata_buffer); connssl->encdata_length = 0; connssl->encdata_offset = 0; } /* free internal buffer for received decrypted data */ if(connssl->decdata_buffer != NULL) { Curl_safefree(connssl->decdata_buffer); connssl->decdata_length = 0; connssl->decdata_offset = 0; } return CURLE_OK; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ CURLcode Curl_polarssl_connect(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; 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 buffer[1024]; if(conn->ssl[sockindex].state == ssl_connection_complete) return CURLE_OK; /* PolarSSL only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ havege_init(&conn->ssl[sockindex].hs); /* Load the trusted CA */ memset(&conn->ssl[sockindex].cacert, 0, sizeof(x509_cert)); if(data->set.str[STRING_SSL_CAFILE]) { ret = x509parse_crtfile(&conn->ssl[sockindex].cacert, data->set.str[STRING_SSL_CAFILE]); if(ret) { failf(data, "Error reading ca cert file %s: -0x%04X", data->set.str[STRING_SSL_CAFILE], -ret); if(data->set.ssl.verifypeer) return CURLE_SSL_CACERT_BADFILE; } } /* Load the client certificate */ memset(&conn->ssl[sockindex].clicert, 0, sizeof(x509_cert)); if(data->set.str[STRING_CERT]) { ret = x509parse_crtfile(&conn->ssl[sockindex].clicert, data->set.str[STRING_CERT]); if(ret) { failf(data, "Error reading client cert file %s: -0x%04X", data->set.str[STRING_CERT], -ret); return CURLE_SSL_CERTPROBLEM; } } /* Load the client private key */ if(data->set.str[STRING_KEY]) { ret = x509parse_keyfile(&conn->ssl[sockindex].rsa, data->set.str[STRING_KEY], data->set.str[STRING_KEY_PASSWD]); if(ret) { failf(data, "Error reading private key %s: -0x%04X", data->set.str[STRING_KEY], -ret); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&conn->ssl[sockindex].crl, 0, sizeof(x509_crl)); if(data->set.str[STRING_SSL_CRLFILE]) { ret = x509parse_crlfile(&conn->ssl[sockindex].crl, data->set.str[STRING_SSL_CRLFILE]); if(ret) { failf(data, "Error reading CRL file %s: -0x%04X", data->set.str[STRING_SSL_CRLFILE], -ret); return CURLE_SSL_CRL_BADFILE; } } infof(data, "PolarSSL: Connected to %s:%d\n", conn->host.name, conn->remote_port); havege_init(&conn->ssl[sockindex].hs); if(ssl_init(&conn->ssl[sockindex].ssl)) { failf(data, "PolarSSL: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } ssl_set_endpoint(&conn->ssl[sockindex].ssl, SSL_IS_CLIENT); ssl_set_authmode(&conn->ssl[sockindex].ssl, SSL_VERIFY_OPTIONAL); ssl_set_rng(&conn->ssl[sockindex].ssl, havege_rand, &conn->ssl[sockindex].hs); ssl_set_bio(&conn->ssl[sockindex].ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); ssl_set_ciphers(&conn->ssl[sockindex].ssl, ssl_default_ciphers); if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) { memcpy(&conn->ssl[sockindex].ssn, old_session, old_session_size); infof(data, "PolarSSL re-using session\n"); } ssl_set_session(&conn->ssl[sockindex].ssl, 1, 600, &conn->ssl[sockindex].ssn); ssl_set_ca_chain(&conn->ssl[sockindex].ssl, &conn->ssl[sockindex].cacert, &conn->ssl[sockindex].crl, conn->host.name); ssl_set_own_cert(&conn->ssl[sockindex].ssl, &conn->ssl[sockindex].clicert, &conn->ssl[sockindex].rsa); if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) && #ifdef ENABLE_IPV6 !Curl_inet_pton(AF_INET6, conn->host.name, &addr) && #endif sni && ssl_set_hostname(&conn->ssl[sockindex].ssl, conn->host.name)) { infof(data, "WARNING: failed to configure " "server name indication (SNI) TLS extension\n"); } infof(data, "PolarSSL: performing SSL/TLS handshake...\n"); #ifdef POLARSSL_DEBUG ssl_set_dbg(&conn->ssl[sockindex].ssl, polarssl_debug, data); #endif for(;;) { if(!(ret = ssl_handshake(&conn->ssl[sockindex].ssl))) break; else if(ret != POLARSSL_ERR_NET_TRY_AGAIN) { failf(data, "ssl_handshake returned -0x%04X", -ret); return CURLE_SSL_CONNECT_ERROR; } else { /* wait for data from server... */ long timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } switch(Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, timeout_ms)) { case 0: failf(data, "SSL handshake timeout"); return CURLE_OPERATION_TIMEDOUT; break; case CURL_CSELECT_IN: continue; break; default: return CURLE_SSL_CONNECT_ERROR; break; } } } infof(data, "PolarSSL: Handshake complete, cipher is %s\n", ssl_get_cipher(&conn->ssl[sockindex].ssl)); ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl); if(ret && data->set.ssl.verifypeer) { if(ret & BADCERT_EXPIRED) failf(data, "Cert verify failed: BADCERT_EXPIRED\n"); if(ret & BADCERT_REVOKED) failf(data, "Cert verify failed: BADCERT_REVOKED"); if(ret & BADCERT_CN_MISMATCH) failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); if(ret & BADCERT_NOT_TRUSTED) failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); return CURLE_SSL_CACERT; } if(conn->ssl[sockindex].ssl.peer_cert) { /* If the session was resumed, there will be no peer certs */ memset(buffer, 0, sizeof(buffer)); if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ", conn->ssl[sockindex].ssl.peer_cert) != -1) infof(data, "Dumping cert info:\n%s\n", buffer); } conn->ssl[sockindex].state = ssl_connection_complete; conn->recv[sockindex] = polarssl_recv; conn->send[sockindex] = polarssl_send; /* Save the current session data for possible re-use */ { void *new_session = malloc(sizeof(conn->ssl[sockindex].ssn)); if(new_session) { memcpy(new_session, &conn->ssl[sockindex].ssn, sizeof(conn->ssl[sockindex].ssn)); if(old_session) Curl_ssl_delsessionid(conn, old_session); return Curl_ssl_addsessionid(conn, new_session, sizeof(conn->ssl[sockindex].ssn)); } } return CURLE_OK; }
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; }
/* * For both blocking and non-blocking connects, this function sets up the * ssl context and state. This function is called after the TCP connect * has completed. */ static CURLcode connect_prep(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; SSL_CTX *ssl_ctx; SSL *ssl = NULL; int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0}; int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0}; int i, ssl_fcn_return; const uint8_t *ssl_sessionid; size_t ssl_idsize; /* Assuming users will not compile in custom key/cert to axTLS. * Also, even for blocking connects, use axTLS non-blocking feature. */ uint32_t client_option = SSL_NO_DEFAULT_KEY | SSL_SERVER_VERIFY_LATER | SSL_CONNECT_IN_PARTS; if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ return CURLE_OK; /* axTLS only supports TLSv1 */ /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: break; default: failf(data, "axTLS only supports TLSv1"); return CURLE_SSL_CONNECT_ERROR; } #ifdef AXTLSDEBUG client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS; #endif /* AXTLSDEBUG */ /* Allocate an SSL_CTX struct */ ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS); if(ssl_ctx == NULL) { failf(data, "unable to create client SSL context"); return CURLE_SSL_CONNECT_ERROR; } conn->ssl[sockindex].ssl_ctx = ssl_ctx; conn->ssl[sockindex].ssl = NULL; /* Load the trusted CA cert bundle file */ if(data->set.ssl.CAfile) { if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL) != SSL_OK) { infof(data, "error reading ca cert file %s \n", data->set.ssl.CAfile); if(data->set.ssl.verifypeer) { return CURLE_SSL_CACERT_BADFILE; } } else infof(data, "found certificates in %s\n", data->set.ssl.CAfile); } /* gtls.c tasks we're skipping for now: * 1) certificate revocation list checking * 2) dns name assignment to host * 3) set protocol priority. axTLS is TLSv1 only, so can probably ignore * 4) set certificate priority. axTLS ignores type and sends certs in * order added. can probably ignore this. */ /* Load client certificate */ if(data->set.str[STRING_CERT]) { i=0; /* Instead of trying to analyze cert type here, let axTLS try them all. */ while(cert_types[i] != 0) { ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i], data->set.str[STRING_CERT], NULL); if(ssl_fcn_return == SSL_OK) { infof(data, "successfully read cert file %s \n", data->set.str[STRING_CERT]); break; } i++; } /* Tried all cert types, none worked. */ if(cert_types[i] == 0) { failf(data, "%s is not x509 or pkcs12 format", data->set.str[STRING_CERT]); return CURLE_SSL_CERTPROBLEM; } } /* Load client key. If a pkcs12 file successfully loaded a cert, then there's nothing to do because the key has already been loaded. */ if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) { i=0; /* Instead of trying to analyze key type here, let axTLS try them all. */ while(key_types[i] != 0) { ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i], data->set.str[STRING_KEY], NULL); if(ssl_fcn_return == SSL_OK) { infof(data, "successfully read key file %s \n", data->set.str[STRING_KEY]); break; } i++; } /* Tried all key types, none worked. */ if(key_types[i] == 0) { failf(data, "Failure: %s is not a supported key file", data->set.str[STRING_KEY]); return CURLE_SSL_CONNECT_ERROR; } } /* gtls.c does more here that is being left out for now * 1) set session credentials. can probably ignore since axtls puts this * info in the ssl_ctx struct * 2) setting up callbacks. these seem gnutls specific */ /* In axTLS, handshaking happens inside ssl_client_new. */ if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ infof (data, "SSL re-using session ID\n"); ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], ssl_sessionid, (uint8_t)ssl_idsize); } else ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0); conn->ssl[sockindex].ssl = ssl; return CURLE_OK; }
static CURLcode gtls_connect_step1(struct connectdata *conn, int sockindex) { #ifndef USE_GNUTLS_PRIORITY_SET_DIRECT static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; #endif struct SessionHandle *data = conn->data; gnutls_session session; int rc; void *ssl_sessionid; size_t ssl_idsize; bool sni = TRUE; /* default is SNI enabled */ #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ return CURLE_OK; if(!gtls_inited) Curl_gtls_init(); /* GnuTLS only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ /* allocate a cred struct */ rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username); rc = gnutls_srp_allocate_client_credentials( &conn->ssl[sockindex].srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_allocate_client_cred() failed: %s", gnutls_strerror(rc)); return CURLE_OUT_OF_MEMORY; } rc = gnutls_srp_set_client_credentials(conn->ssl[sockindex]. srp_client_cred, data->set.ssl.username, data->set.ssl.password); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_set_client_cred() failed: %s", gnutls_strerror(rc)); return CURLE_BAD_FUNCTION_ARGUMENT; } } #endif if(data->set.ssl.CAfile) { /* set the trusted CA cert bundle file */ gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred, data->set.ssl.CAfile, 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.CAfile); } if(data->set.ssl.CRLfile) { /* set the CRL list file */ rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred, data->set.ssl.CRLfile, GNUTLS_X509_FMT_PEM); if(rc < 0) { failf(data, "error reading crl file %s (%s)", data->set.ssl.CRLfile, gnutls_strerror(rc)); return CURLE_SSL_CRL_BADFILE; } else infof(data, "found %d CRL in %s\n", rc, data->set.ssl.CRLfile); } /* Initialize TLS session as a client */ rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_init() failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* convenient assign */ session = conn->ssl[sockindex].session; if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) && #endif sni && (gnutls_server_name_set(session, GNUTLS_NAME_DNS, conn->host.name, strlen(conn->host.name)) < 0)) infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); /* Use default priorities */ rc = gnutls_set_default_priority(session); if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) { #ifndef USE_GNUTLS_PRIORITY_SET_DIRECT static const int protocol_priority[] = { GNUTLS_SSL3, 0 }; rc = gnutls_protocol_set_priority(session, protocol_priority); #else const char *err; /* the combination of the cipher ARCFOUR with SSL 3.0 and TLS 1.0 is not vulnerable to attacks such as the BEAST, why this code now explicitly asks for that */ rc = gnutls_priority_set_direct(session, "NORMAL:-VERS-TLS-ALL:+VERS-SSL3.0:" "-CIPHER-ALL:+ARCFOUR-128", &err); #endif if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; } #ifndef USE_GNUTLS_PRIORITY_SET_DIRECT /* Sets the priority on the certificate types supported by gnutls. Priority is higher for types specified before others. After specifying the types you want, you must append a 0. */ rc = gnutls_certificate_type_set_priority(session, cert_type_priority); if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; #endif if(data->set.str[STRING_CERT]) { if(gnutls_certificate_set_x509_key_file( conn->ssl[sockindex].cred, data->set.str[STRING_CERT], data->set.str[STRING_KEY] ? data->set.str[STRING_KEY] : data->set.str[STRING_CERT], do_file_type(data->set.str[STRING_CERT_TYPE]) ) != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 key or certificate file"); return CURLE_SSL_CONNECT_ERROR; } } #ifdef USE_TLS_SRP /* put the credentials to the current session */ 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) failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); } else #endif rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, conn->ssl[sockindex].cred); /* set the connection handle (file descriptor for the socket) */ gnutls_transport_set_ptr(session, GNUTLS_INT_TO_POINTER_CAST(conn->sock[sockindex])); /* register callback functions to send and receive data. */ gnutls_transport_set_push_function(session, Curl_gtls_push); gnutls_transport_set_pull_function(session, Curl_gtls_pull); /* lowat must be set to zero when using custom push and pull functions. */ gnutls_transport_set_lowat(session, 0); /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ gnutls_session_set_data(session, ssl_sessionid, ssl_idsize); /* Informational message */ infof (data, "SSL re-using session ID\n"); } return CURLE_OK; }
static CURLcode schannel_connect_step1(struct connectdata *conn, int sockindex) { ssize_t written = -1; struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SecBuffer outbuf; SecBufferDesc outbuf_desc; SCHANNEL_CRED schannel_cred; SECURITY_STATUS sspi_status = SEC_E_OK; struct curl_schannel_cred *old_cred = NULL; struct in_addr addr; #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif TCHAR *host_name; CURLcode code; 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)) { connssl->cred = old_cred; infof(data, "schannel: re-using existing credential handle\n"); } else { /* setup Schannel API options */ memset(&schannel_cred, 0, sizeof(schannel_cred)); schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; if(data->set.ssl.verifypeer) { #ifdef _WIN32_WCE /* 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; #else schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_REVOCATION_CHECK_CHAIN; #endif 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"); } if(Curl_inet_pton(AF_INET, conn->host.name, &addr) #ifdef ENABLE_IPV6 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6) #endif ) { schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; infof(data, "schannel: using IP address, SNI is being disabled by " "disabling the servername check against the " "subject names in server certificates.\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"); } 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_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 = malloc(sizeof(struct curl_schannel_cred)); if(!connssl->cred) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } 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); if(sspi_status != SEC_E_OK) { if(sspi_status == SEC_E_WRONG_PRINCIPAL) failf(data, "schannel: SNI or certificate check failed: %s", Curl_sspi_strerror(conn, sspi_status)); else failf(data, "schannel: AcquireCredentialsHandle failed: %s", Curl_sspi_strerror(conn, sspi_status)); Curl_safefree(connssl->cred); return CURLE_SSL_CONNECT_ERROR; } } /* setup output buffer */ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&outbuf_desc, &outbuf, 1); /* setup request flags */ connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; /* allocate memory for the security context handle */ connssl->ctxt = malloc(sizeof(struct curl_schannel_ctxt)); if(!connssl->ctxt) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt)); host_name = Curl_convert_UTF8_to_tchar(conn->host.name); if(!host_name) return CURLE_OUT_OF_MEMORY; /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */ sspi_status = s_pSecFn->InitializeSecurityContext( &connssl->cred->cred_handle, NULL, 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_I_CONTINUE_NEEDED) { if(sspi_status == SEC_E_WRONG_PRINCIPAL) failf(data, "schannel: SNI or certificate check failed: %s", Curl_sspi_strerror(conn, sspi_status)); else failf(data, "schannel: initial InitializeSecurityContext failed: %s", Curl_sspi_strerror(conn, sspi_status)); Curl_safefree(connssl->ctxt); return CURLE_SSL_CONNECT_ERROR; } infof(data, "schannel: sending initial handshake data: " "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); s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if((code != 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; } infof(data, "schannel: sent initial handshake data: " "sent %zd bytes\n", written); /* continue to second handshake step */ connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode mbed_connect_step1(struct connectdata *conn, int sockindex) { struct Curl_easy *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char * const ssl_capath = SSL_CONN_CONFIG(CApath); char * const ssl_cert = SSL_SET_OPTION(cert); const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile); const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; int ret = -1; char errorbuf[128]; errorbuf[0]=0; /* mbedTLS only supports SSLv3 and TLSv1 */ if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) { failf(data, "mbedTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } #ifdef THREADING_SUPPORT entropy_init_mutex(&ts_entropy); mbedtls_ctr_drbg_init(&BACKEND->ctr_drbg); ret = mbedtls_ctr_drbg_seed(&BACKEND->ctr_drbg, entropy_func_mutex, &ts_entropy, NULL, 0); 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(&BACKEND->entropy); mbedtls_ctr_drbg_init(&BACKEND->ctr_drbg); ret = mbedtls_ctr_drbg_seed(&BACKEND->ctr_drbg, mbedtls_entropy_func, &BACKEND->entropy, NULL, 0); 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 */ mbedtls_x509_crt_init(&BACKEND->cacert); if(ssl_cafile) { ret = mbedtls_x509_crt_parse_file(&BACKEND->cacert, 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", ssl_cafile, -ret, errorbuf); if(verifypeer) return CURLE_SSL_CACERT_BADFILE; } } if(ssl_capath) { ret = mbedtls_x509_crt_parse_path(&BACKEND->cacert, 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", ssl_capath, -ret, errorbuf); if(verifypeer) return CURLE_SSL_CACERT_BADFILE; } } /* Load the client certificate */ mbedtls_x509_crt_init(&BACKEND->clicert); if(ssl_cert) { ret = mbedtls_x509_crt_parse_file(&BACKEND->clicert, ssl_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", ssl_cert, -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the client private key */ mbedtls_pk_init(&BACKEND->pk); if(SSL_SET_OPTION(key)) { ret = mbedtls_pk_parse_keyfile(&BACKEND->pk, SSL_SET_OPTION(key), SSL_SET_OPTION(key_passwd)); if(ret == 0 && !mbedtls_pk_can_do(&BACKEND->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", SSL_SET_OPTION(key), -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ mbedtls_x509_crl_init(&BACKEND->crl); if(ssl_crlfile) { ret = mbedtls_x509_crl_parse_file(&BACKEND->crl, 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", ssl_crlfile, -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } } infof(data, "mbedTLS: Connecting to %s:%d\n", hostname, port); mbedtls_ssl_config_init(&BACKEND->config); mbedtls_ssl_init(&BACKEND->ssl); if(mbedtls_ssl_setup(&BACKEND->ssl, &BACKEND->config)) { failf(data, "mbedTLS: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } ret = mbedtls_ssl_config_defaults(&BACKEND->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(&BACKEND->config, &mbedtls_x509_crt_profile_fr); switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: mbedtls_ssl_conf_min_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_1); infof(data, "mbedTLS: Set min SSL version to TLS 1.0\n"); break; case CURL_SSLVERSION_SSLv3: mbedtls_ssl_conf_min_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); mbedtls_ssl_conf_max_version(&BACKEND->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); infof(data, "mbedTLS: Set SSL version to SSLv3\n"); break; case CURL_SSLVERSION_TLSv1_0: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: { CURLcode result = set_ssl_version_min_max(conn, sockindex); if(result != CURLE_OK) return result; break; } default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } mbedtls_ssl_conf_authmode(&BACKEND->config, MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_rng(&BACKEND->config, mbedtls_ctr_drbg_random, &BACKEND->ctr_drbg); mbedtls_ssl_set_bio(&BACKEND->ssl, &conn->sock[sockindex], mbedtls_net_send, mbedtls_net_recv, NULL /* rev_timeout() */); mbedtls_ssl_conf_ciphersuites(&BACKEND->config, mbedtls_ssl_list_ciphersuites()); #if defined(MBEDTLS_SSL_RENEGOTIATION) mbedtls_ssl_conf_renegotiation(&BACKEND->config, MBEDTLS_SSL_RENEGOTIATION_ENABLED); #endif #if defined(MBEDTLS_SSL_SESSION_TICKETS) mbedtls_ssl_conf_session_tickets(&BACKEND->config, MBEDTLS_SSL_SESSION_TICKETS_DISABLED); #endif /* Check if there's a cached ID we can/should use here! */ if(SSL_SET_OPTION(primary.sessionid)) { void *old_session = NULL; Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &old_session, NULL, sockindex)) { ret = mbedtls_ssl_set_session(&BACKEND->ssl, old_session); if(ret) { Curl_ssl_sessionid_unlock(conn); failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } infof(data, "mbedTLS re-using session\n"); } Curl_ssl_sessionid_unlock(conn); } mbedtls_ssl_conf_ca_chain(&BACKEND->config, &BACKEND->cacert, &BACKEND->crl); if(SSL_SET_OPTION(key)) { mbedtls_ssl_conf_own_cert(&BACKEND->config, &BACKEND->clicert, &BACKEND->pk); } if(mbedtls_ssl_set_hostname(&BACKEND->ssl, hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks *and* the name to set in the SNI extension. So even if curl connects to a host specified as an IP address, this function must be used. */ failf(data, "couldn't set hostname in mbedTLS"); return CURLE_SSL_CONNECT_ERROR; } #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { const char **p = &BACKEND->protocols[0]; #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) *p++ = NGHTTP2_PROTO_VERSION_ID; #endif *p++ = ALPN_HTTP_1_1; *p = NULL; /* this function doesn't clone the protocols array, which is why we need to keep it around */ if(mbedtls_ssl_conf_alpn_protocols(&BACKEND->config, &BACKEND->protocols[0])) { failf(data, "Failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } for(p = &BACKEND->protocols[0]; *p; ++p) infof(data, "ALPN, offering %s\n", *p); } #endif #ifdef MBEDTLS_DEBUG /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ mbedtls_ssl_conf_dbg(&BACKEND->config, mbed_debug, data); /* - 0 No debug * - 1 Error * - 2 State change * - 3 Informational * - 4 Verbose */ mbedtls_debug_set_threshold(4); #endif /* give application a chance to interfere with mbedTLS set up. */ if(data->set.ssl.fsslctx) { ret = (*data->set.ssl.fsslctx)(data, &BACKEND->config, data->set.ssl.fsslctxp); if(ret) { failf(data, "error signaled by ssl ctx callback"); return ret; } } connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode polarssl_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; /* PolarSSL only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL 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); if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func_mutex, &entropy, connssl->ssn.id, connssl->ssn.length)) != 0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #else entropy_init(&connssl->entropy); if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func, &connssl->entropy, connssl->ssn.id, connssl->ssn.length)) != 0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #endif /* THREADING_SUPPORT */ /* Load the trusted CA */ memset(&connssl->cacert, 0, sizeof(x509_crt)); if(data->set.str[STRING_SSL_CAFILE]) { ret = x509_crt_parse_file(&connssl->cacert, data->set.str[STRING_SSL_CAFILE]); if(ret<0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading ca cert file %s - PolarSSL: (-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 = x509_crt_parse_path(&connssl->cacert, data->set.str[STRING_SSL_CAPATH]); if(ret<0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading ca cert path %s - PolarSSL: (-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(x509_crt)); if(data->set.str[STRING_CERT]) { ret = x509_crt_parse_file(&connssl->clicert, data->set.str[STRING_CERT]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading client cert file %s - PolarSSL: (-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]) { pk_context pk; pk_init(&pk); ret = pk_parse_keyfile(&pk, data->set.str[STRING_KEY], data->set.str[STRING_KEY_PASSWD]); if(ret == 0 && !pk_can_do(&pk, POLARSSL_PK_RSA)) ret = POLARSSL_ERR_PK_TYPE_MISMATCH; if(ret == 0) rsa_copy(&connssl->rsa, pk_rsa(pk)); else rsa_free(&connssl->rsa); pk_free(&pk); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_KEY], -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&connssl->crl, 0, sizeof(x509_crl)); if(data->set.str[STRING_SSL_CRLFILE]) { ret = x509_crl_parse_file(&connssl->crl, data->set.str[STRING_SSL_CRLFILE]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } } infof(data, "PolarSSL: Connecting to %s:%d\n", conn->host.name, conn->remote_port); if(ssl_init(&connssl->ssl)) { failf(data, "PolarSSL: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } switch(data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: 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); 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); 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); 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); infof(data, "PolarSSL: Forced min. SSL Version to be TLS 1.2\n"); break; } ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT); ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL); ssl_set_rng(&connssl->ssl, ctr_drbg_random, &connssl->ctr_drbg); ssl_set_bio(&connssl->ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); ssl_set_ciphersuites(&connssl->ssl, ssl_list_ciphersuites()); if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) { memcpy(&connssl->ssn, old_session, old_session_size); infof(data, "PolarSSL re-using session\n"); } ssl_set_session(&connssl->ssl, &connssl->ssn); ssl_set_ca_chain(&connssl->ssl, &connssl->cacert, &connssl->crl, conn->host.name); ssl_set_own_cert_rsa(&connssl->ssl, &connssl->clicert, &connssl->rsa); if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) && #ifdef ENABLE_IPV6 !Curl_inet_pton(AF_INET6, conn->host.name, &addr) && #endif sni && 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.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]); } } #endif #ifdef POLARSSL_DEBUG ssl_set_dbg(&connssl->ssl, polarssl_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { char *ciphers; struct Curl_easy *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = 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(connssl->state == ssl_connection_complete) return CURLE_OK; if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { failf(data, "CyaSSL does not support to set maximum SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #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: #ifdef WOLFSSL_ALLOW_TLSV10 req_method = TLSv1_client_method(); use_sni(TRUE); #else failf(data, "CyaSSL does not support TLS 1.0"); return CURLE_NOT_BUILT_IN; #endif 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_TLSv1_3: #ifdef WOLFSSL_TLS13 req_method = wolfTLSv1_3_client_method(); use_sni(TRUE); break; #else failf(data, "CyaSSL: TLS 1.3 is not yet supported"); return CURLE_SSL_CONNECT_ERROR; #endif case CURL_SSLVERSION_SSLv3: #ifdef WOLFSSL_ALLOW_SSLV3 req_method = SSLv3_client_method(); use_sni(FALSE); #else failf(data, "CyaSSL does not support SSLv3"); return CURLE_NOT_BUILT_IN; #endif break; case CURL_SSLVERSION_SSLv2: failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(BACKEND->ctx) SSL_CTX_free(BACKEND->ctx); BACKEND->ctx = SSL_CTX_new(req_method); if(!BACKEND->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } switch(SSL_CONN_CONFIG(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(BACKEND->ctx, WOLFSSL_TLSV1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_2) != 1) #ifdef WOLFSSL_TLS13 && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_3) != 1) #endif ) { failf(data, "SSL: couldn't set the minimum protocol version"); return CURLE_SSL_CONNECT_ERROR; } #endif break; } ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } infof(data, "Cipher selection: %s\n", ciphers); } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(SSL_CONN_CONFIG(CAfile)) { if(1 != SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile), SSL_CONN_CONFIG(CApath))) { if(SSL_CONN_CONFIG(verifypeer)) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", SSL_CONN_CONFIG(CAfile)? SSL_CONN_CONFIG(CAfile): "none", SSL_CONN_CONFIG(CApath)? SSL_CONN_CONFIG(CApath) : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none", SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none"); } /* Load the client certificate, and private key */ if(SSL_SET_OPTION(cert) && SSL_SET_OPTION(key)) { int file_type = do_file_type(SSL_SET_OPTION(cert_type)); if(SSL_CTX_use_certificate_file(BACKEND->ctx, SSL_SET_OPTION(cert), file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(SSL_SET_OPTION(key_type)); if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key), file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #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 * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(BACKEND->ctx, SSL_CONN_CONFIG(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 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; size_t hostname_len = strlen(hostname); if((hostname_len < USHRT_MAX) && (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) && #endif (CyaSSL_CTX_UseSNI(BACKEND->ctx, CYASSL_SNI_HOST_NAME, hostname, (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, BACKEND->ctx, data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; } } #ifdef NO_FILESYSTEM else if(SSL_CONN_CONFIG(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(BACKEND->handle) SSL_free(BACKEND->handle); BACKEND->handle = SSL_new(BACKEND->ctx); if(!BACKEND->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } #ifdef HAVE_ALPN if(conn->bits.tls_enable_alpn) { char protocols[128]; *protocols = '\0'; /* wolfSSL's ALPN protocol name list format is a comma separated string of protocols in descending order of preference, eg: "h2,http/1.1" */ #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) { strcpy(protocols + strlen(protocols), NGHTTP2_PROTO_VERSION_ID ","); infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } #endif strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); if(wolfSSL_UseALPN(BACKEND->handle, protocols, (unsigned)strlen(protocols), WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* HAVE_ALPN */ /* Check if there's a cached ID we can/should use here! */ if(SSL_SET_OPTION(primary.sessionid)) { void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { char error_buffer[CYASSL_MAX_ERROR_SZ]; Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof(data, "SSL re-using session ID\n"); } Curl_ssl_sessionid_unlock(conn); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(BACKEND->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode gtls_connect_step1(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; gnutls_session_t session; int rc; void *ssl_sessionid; size_t ssl_idsize; bool sni = TRUE; /* default is SNI enabled */ #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif #ifndef USE_GNUTLS_PRIORITY_SET_DIRECT static const int cipher_priority[] = { /* These two ciphers were added to GnuTLS as late as ver. 3.0.1, but this code path is only ever used for ver. < 2.12.0. GNUTLS_CIPHER_AES_128_GCM, GNUTLS_CIPHER_AES_256_GCM, */ GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_CAMELLIA_128_CBC, GNUTLS_CIPHER_CAMELLIA_256_CBC, GNUTLS_CIPHER_3DES_CBC, }; static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; static int protocol_priority[] = { 0, 0, 0, 0 }; #else #define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509" /* If GnuTLS was compiled without support for SRP it will error out if SRP is requested in the priority string, so treat it specially */ #define GNUTLS_SRP "+SRP" const char* prioritylist; const char *err = NULL; #endif if(conn->ssl[sockindex].state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ return CURLE_OK; if(!gtls_inited) Curl_gtls_init(); /* GnuTLS only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ /* allocate a cred struct */ rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) { infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username); rc = gnutls_srp_allocate_client_credentials( &conn->ssl[sockindex].srp_client_cred); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_allocate_client_cred() failed: %s", gnutls_strerror(rc)); return CURLE_OUT_OF_MEMORY; } rc = gnutls_srp_set_client_credentials(conn->ssl[sockindex]. srp_client_cred, data->set.ssl.username, data->set.ssl.password); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_set_client_cred() failed: %s", gnutls_strerror(rc)); return CURLE_BAD_FUNCTION_ARGUMENT; } } #endif if(data->set.ssl.CAfile) { /* set the trusted CA cert bundle file */ gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred, data->set.ssl.CAfile, 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.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 #ifdef CURL_CA_FALLBACK /* use system ca certificate store as fallback */ if(data->set.ssl.verifypeer && !(data->set.ssl.CAfile || data->set.ssl.CApath)) { gnutls_certificate_set_x509_system_trust(conn->ssl[sockindex].cred); } #endif if(data->set.ssl.CRLfile) { /* set the CRL list file */ rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred, data->set.ssl.CRLfile, GNUTLS_X509_FMT_PEM); if(rc < 0) { failf(data, "error reading crl file %s (%s)", data->set.ssl.CRLfile, gnutls_strerror(rc)); return CURLE_SSL_CRL_BADFILE; } else infof(data, "found %d CRL in %s\n", rc, data->set.ssl.CRLfile); } /* Initialize TLS session as a client */ rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_init() failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* convenient assign */ session = conn->ssl[sockindex].session; if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) && #endif sni && (gnutls_server_name_set(session, GNUTLS_NAME_DNS, conn->host.name, strlen(conn->host.name)) < 0)) infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); /* Use default priorities */ rc = gnutls_set_default_priority(session); if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; #ifndef USE_GNUTLS_PRIORITY_SET_DIRECT rc = gnutls_cipher_set_priority(session, cipher_priority); if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; /* Sets the priority on the certificate types supported by gnutls. Priority is higher for types specified before others. After specifying the types you want, you must append a 0. */ rc = gnutls_certificate_type_set_priority(session, cert_type_priority); if(rc != GNUTLS_E_SUCCESS) return CURLE_SSL_CONNECT_ERROR; if(data->set.ssl.cipher_list != NULL) { failf(data, "can't pass a custom cipher list to older GnuTLS" " versions"); return CURLE_SSL_CONNECT_ERROR; } switch (data->set.ssl.version) { case CURL_SSLVERSION_SSLv3: protocol_priority[0] = GNUTLS_SSL3; break; case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: protocol_priority[0] = GNUTLS_TLS1_0; protocol_priority[1] = GNUTLS_TLS1_1; protocol_priority[2] = GNUTLS_TLS1_2; break; case CURL_SSLVERSION_TLSv1_0: protocol_priority[0] = GNUTLS_TLS1_0; break; case CURL_SSLVERSION_TLSv1_1: protocol_priority[0] = GNUTLS_TLS1_1; break; case CURL_SSLVERSION_TLSv1_2: protocol_priority[0] = GNUTLS_TLS1_2; break; case CURL_SSLVERSION_SSLv2: default: failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; break; } rc = gnutls_protocol_set_priority(session, protocol_priority); if(rc != GNUTLS_E_SUCCESS) { failf(data, "Did you pass a valid GnuTLS cipher list?"); return CURLE_SSL_CONNECT_ERROR; } #else /* Ensure +SRP comes at the *end* of all relevant strings so that it can be * removed if a run-time error indicates that SRP is not supported by this * GnuTLS version */ switch (data->set.ssl.version) { case CURL_SSLVERSION_SSLv3: prioritylist = GNUTLS_CIPHERS ":-VERS-TLS-ALL:+VERS-SSL3.0"; sni = false; break; case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:" GNUTLS_SRP; break; case CURL_SSLVERSION_TLSv1_0: prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" "+VERS-TLS1.0:" GNUTLS_SRP; break; case CURL_SSLVERSION_TLSv1_1: prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" "+VERS-TLS1.1:" GNUTLS_SRP; break; case CURL_SSLVERSION_TLSv1_2: prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" "+VERS-TLS1.2:" GNUTLS_SRP; break; case CURL_SSLVERSION_SSLv2: default: failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; break; } rc = gnutls_priority_set_direct(session, prioritylist, &err); if((rc == GNUTLS_E_INVALID_REQUEST) && err) { if(!strcmp(err, GNUTLS_SRP)) { /* This GnuTLS was probably compiled without support for SRP. * Note that fact and try again without it. */ int validprioritylen = curlx_uztosi(err - prioritylist); char *prioritycopy = strdup(prioritylist); if(!prioritycopy) return CURLE_OUT_OF_MEMORY; infof(data, "This GnuTLS does not support SRP\n"); if(validprioritylen) /* Remove the :+SRP */ prioritycopy[validprioritylen - 1] = 0; rc = gnutls_priority_set_direct(session, prioritycopy, &err); free(prioritycopy); } } if(rc != GNUTLS_E_SUCCESS) { failf(data, "Error %d setting GnuTLS cipher list starting with %s", rc, err); return CURLE_SSL_CONNECT_ERROR; } #endif #ifdef HAS_ALPN 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) { 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 if(data->set.str[STRING_CERT]) { if(data->set.str[STRING_KEY_PASSWD]) { #if HAVE_GNUTLS_CERTIFICATE_SET_X509_KEY_FILE2 const unsigned int supported_key_encryption_algorithms = GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES | GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 | GNUTLS_PKCS_USE_PBES2_AES_256; rc = gnutls_certificate_set_x509_key_file2( conn->ssl[sockindex].cred, data->set.str[STRING_CERT], data->set.str[STRING_KEY] ? data->set.str[STRING_KEY] : data->set.str[STRING_CERT], do_file_type(data->set.str[STRING_CERT_TYPE]), data->set.str[STRING_KEY_PASSWD], supported_key_encryption_algorithms); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 potentially-encrypted key file: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } #else failf(data, "gnutls lacks support for encrypted key files"); return CURLE_SSL_CONNECT_ERROR; #endif } else { rc = gnutls_certificate_set_x509_key_file( conn->ssl[sockindex].cred, data->set.str[STRING_CERT], data->set.str[STRING_KEY] ? data->set.str[STRING_KEY] : data->set.str[STRING_CERT], do_file_type(data->set.str[STRING_CERT_TYPE]) ); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 key or certificate file: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } } } #ifdef USE_TLS_SRP /* put the credentials to the current session */ 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) { 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, GNUTLS_INT_TO_POINTER_CAST(conn->sock[sockindex])); /* register callback functions to send and receive data. */ gnutls_transport_set_push_function(session, Curl_gtls_push); gnutls_transport_set_pull_function(session, Curl_gtls_pull); /* 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 */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ gnutls_session_set_data(session, ssl_sessionid, ssl_idsize); /* Informational message */ infof (data, "SSL re-using session ID\n"); } return CURLE_OK; }
static CURLcode polarssl_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]; memset(errorbuf, 0, sizeof(errorbuf)); /* PolarSSL only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ #if POLARSSL_VERSION_NUMBER<0x01010000 havege_init(&connssl->hs); #else #ifdef THREADING_SUPPORT entropy_init_mutex(&entropy); if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func_mutex, &entropy, connssl->ssn.id, connssl->ssn.length)) != 0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #else entropy_init(&connssl->entropy); if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func, &connssl->entropy, connssl->ssn.id, connssl->ssn.length)) != 0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #endif /* THREADING_SUPPORT */ #endif /* POLARSSL_VERSION_NUMBER<0x01010000 */ /* Load the trusted CA */ memset(&connssl->cacert, 0, sizeof(x509_cert)); if(data->set.str[STRING_SSL_CAFILE]) { ret = x509parse_crtfile(&connssl->cacert, data->set.str[STRING_SSL_CAFILE]); if(ret<0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading ca cert file %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_SSL_CAFILE], -ret, errorbuf); if(data->set.ssl.verifypeer) return CURLE_SSL_CACERT_BADFILE; } } /* Load the client certificate */ memset(&connssl->clicert, 0, sizeof(x509_cert)); if(data->set.str[STRING_CERT]) { ret = x509parse_crtfile(&connssl->clicert, data->set.str[STRING_CERT]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading client cert file %s - PolarSSL: (-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]) { ret = x509parse_keyfile(&connssl->rsa, data->set.str[STRING_KEY], data->set.str[STRING_KEY_PASSWD]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_KEY], -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&connssl->crl, 0, sizeof(x509_crl)); if(data->set.str[STRING_SSL_CRLFILE]) { ret = x509parse_crlfile(&connssl->crl, data->set.str[STRING_SSL_CRLFILE]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } } infof(data, "PolarSSL: Connecting to %s:%d\n", conn->host.name, conn->remote_port); if(ssl_init(&connssl->ssl)) { failf(data, "PolarSSL: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT); ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL); #if POLARSSL_VERSION_NUMBER<0x01010000 ssl_set_rng(&connssl->ssl, havege_rand, &connssl->hs); #else ssl_set_rng(&connssl->ssl, ctr_drbg_random, &connssl->ctr_drbg); #endif /* POLARSSL_VERSION_NUMBER<0x01010000 */ ssl_set_bio(&connssl->ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); #if POLARSSL_VERSION_NUMBER<0x01000000 ssl_set_ciphers(&connssl->ssl, ssl_default_ciphers); #else ssl_set_ciphersuites(&connssl->ssl, ssl_default_ciphersuites); #endif if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) { memcpy(&connssl->ssn, old_session, old_session_size); infof(data, "PolarSSL re-using session\n"); } /* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's 1.1.4 version and the like */ #if POLARSSL_VERSION_NUMBER<0x01020000 ssl_set_session(&connssl->ssl, 1, 600, &connssl->ssn); #else ssl_set_session(&connssl->ssl, &connssl->ssn); #endif ssl_set_ca_chain(&connssl->ssl, &connssl->cacert, &connssl->crl, conn->host.name); ssl_set_own_cert(&connssl->ssl, &connssl->clicert, &connssl->rsa); if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) && #ifdef ENABLE_IPV6 !Curl_inet_pton(AF_INET6, conn->host.name, &addr) && #endif sni && ssl_set_hostname(&connssl->ssl, conn->host.name)) { infof(data, "WARNING: failed to configure " "server name indication (SNI) TLS extension\n"); } #ifdef POLARSSL_DEBUG ssl_set_dbg(&connssl->ssl, polarssl_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode gtls_connect_step3(struct connectdata *conn, int sockindex) { unsigned int cert_list_size; const gnutls_datum_t *chainp; 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; unsigned int algo; unsigned int bits; time_t certclock; const char *ptr; struct SessionHandle *data = conn->data; gnutls_session_t session = conn->ssl[sockindex].session; int rc; 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 first certificate in the list is the peer's certificate, following the issuer's certificate, then the issuer's issuer etc. */ chainp = gnutls_certificate_get_peers(session, &cert_list_size); if(!chainp) { if(data->set.ssl.verifypeer || data->set.ssl.verifyhost || data->set.ssl.issuercert) { #ifdef USE_TLS_SRP if(data->set.ssl.authtype == CURL_TLSAUTH_SRP && data->set.ssl.username != NULL && !data->set.ssl.verifypeer && gnutls_cipher_get(session)) { /* no peer cert, but auth is ok if we have SRP user and cipher and no peer verify */ } else { #endif failf(data, "failed to get server cert"); return CURLE_PEER_FAILED_VERIFICATION; #ifdef USE_TLS_SRP } #endif } 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 more of the gnutls_certificate_status_t enumerated elements bitwise or'd. To avoid denial of service attacks some default upper limits regarding the certificate key size and chain size are set. To override them use gnutls_certificate_set_verify_limits(). */ rc = gnutls_certificate_verify_peers2(session, &verify_status); if(rc < 0) { failf(data, "server cert verify failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* verify_status is a bitmask of gnutls_certificate_status bits */ if(verify_status & GNUTLS_CERT_INVALID) { if(data->set.ssl.verifypeer) { failf(data, "server certificate verification failed. CAfile: %s " "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none", data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none"); return CURLE_SSL_CACERT; } else infof(data, "\t server certificate verification FAILED\n"); } else infof(data, "\t server certificate verification OK\n"); } 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); if(chainp) /* convert the given DER or PEM encoded Certificate to the native gnutls_x509_crt_t format */ gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); if(data->set.ssl.issuercert) { 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); 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", data->set.ssl.issuercert?data->set.ssl.issuercert:"none"); } size=sizeof(certbuf); rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, 0, /* the first and only one */ FALSE, certbuf, &size); if(rc) { infof(data, "error fetching CN from cert:%s\n", gnutls_strerror(rc)); } /* This function will check if the given certificate's subject matches the given hostname. This is a basic implementation of the matching described in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on failure. */ rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name); #if GNUTLS_VERSION_NUMBER < 0x030306 /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP addresses. */ if(!rc) { #ifdef ENABLE_IPV6 #define use_addr in6_addr #else #define use_addr in_addr #endif unsigned char addrbuf[sizeof(struct use_addr)]; unsigned char certaddr[sizeof(struct use_addr)]; size_t addrlen = 0, certaddrlen; int i; int ret = 0; if(Curl_inet_pton(AF_INET, conn->host.name, addrbuf) > 0) addrlen = 4; #ifdef ENABLE_IPV6 else if(Curl_inet_pton(AF_INET6, conn->host.name, addrbuf) > 0) addrlen = 16; #endif if(addrlen) { for(i=0; ; i++) { certaddrlen = sizeof(certaddr); ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr, &certaddrlen, NULL); /* If this happens, it wasn't an IP address. */ if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER) continue; if(ret < 0) break; if(ret != GNUTLS_SAN_IPADDRESS) continue; if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) { rc = 1; break; } } } } #endif if(!rc) { if(data->set.ssl.verifyhost) { failf(data, "SSL: certificate subject name (%s) does not match " "target host name '%s'", certbuf, conn->host.dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t common name: %s (does not match '%s')\n", certbuf, conn->host.dispname); } else infof(data, "\t common name: %s (matched)\n", certbuf); /* Check for time-based validity */ certclock = gnutls_x509_crt_get_expiration_time(x509_cert); 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 infof(data, "\t server certificate expiration date verify FAILED\n"); } else { 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 infof(data, "\t server certificate expiration date FAILED\n"); } else infof(data, "\t server certificate expiration date OK\n"); } certclock = gnutls_x509_crt_get_activation_time(x509_cert); 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 infof(data, "\t server certificate activation date verify FAILED\n"); } else { 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 infof(data, "\t server certificate activation date FAILED\n"); } else infof(data, "\t server certificate activation date OK\n"); } ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; if(ptr) { result = pkp_pin_peer_pubkey(data, 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: - subject - start date - expire date - common name - issuer */ /* public key algorithm's parameters */ algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); infof(data, "\t certificate public key: %s\n", gnutls_pk_algorithm_get_name(algo)); /* version of the X.509 certificate. */ infof(data, "\t certificate version: #%d\n", gnutls_x509_crt_get_version(x509_cert)); size = sizeof(certbuf); gnutls_x509_crt_get_dn(x509_cert, certbuf, &size); infof(data, "\t subject: %s\n", certbuf); certclock = gnutls_x509_crt_get_activation_time(x509_cert); showtime(data, "start date", certclock); certclock = gnutls_x509_crt_get_expiration_time(x509_cert); showtime(data, "expire date", certclock); size = sizeof(certbuf); gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size); infof(data, "\t issuer: %s\n", certbuf); gnutls_x509_crt_deinit(x509_cert); /* compression algorithm (if any) */ ptr = gnutls_compression_get_name(gnutls_compression_get(session)); /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */ infof(data, "\t compression: %s\n", ptr); #ifdef HAS_ALPN if(data->set.ssl_enable_alpn) { rc = gnutls_alpn_get_selected_protocol(session, &proto); if(rc == 0) { 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)) { conn->negnpn = CURL_HTTP_VERSION_2; } 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 infof(data, "ALPN, server did not agree to a protocol\n"); } #endif conn->ssl[sockindex].state = ssl_connection_complete; conn->recv[sockindex] = gtls_recv; conn->send[sockindex] = gtls_send; { /* we always unconditionally get the session id here, as even if we already got it from the cache and asked to use it in the connection, it might've been rejected and then a new one is in use now and we need to detect that. */ void *connect_sessionid; size_t connect_idsize = 0; /* get the session ID data size */ gnutls_session_get_data(session, NULL, &connect_idsize); connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ if(connect_sessionid) { /* extract session ID to the allocated buffer */ gnutls_session_get_data(session, connect_sessionid, &connect_idsize); incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)); if(incache) { /* there was one before in the cache, so instead of risking that the previous one was rejected, we just kill that and store the new */ Curl_ssl_delsessionid(conn, ssl_sessionid); } /* store this session id */ result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize); if(result) { free(connect_sessionid); result = CURLE_OUT_OF_MEMORY; } } else result = CURLE_OUT_OF_MEMORY; } return result; }
/* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex) { const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; struct SessionHandle *data = conn->data; gnutls_session session; int rc; unsigned int cert_list_size; const gnutls_datum *chainp; unsigned int verify_status; gnutls_x509_crt x509_cert; char certbuf[256]; /* big enough? */ size_t size; unsigned int algo; unsigned int bits; time_t clock; const char *ptr; void *ssl_sessionid; size_t ssl_idsize; /* GnuTLS only supports TLSv1 (and SSLv3?) */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "GnuTLS does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } /* allocate a cred struct */ rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred); if(rc < 0) { failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } if(data->set.ssl.CAfile) { /* set the trusted CA cert bundle file */ gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred, data->set.ssl.CAfile, 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.CAfile); } /* Initialize TLS session as a client */ rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT); if(rc) { failf(data, "gnutls_init() failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* convenient assign */ session = conn->ssl[sockindex].session; /* Use default priorities */ rc = gnutls_set_default_priority(session); if(rc < 0) return CURLE_SSL_CONNECT_ERROR; /* Sets the priority on the certificate types supported by gnutls. Priority is higher for types specified before others. After specifying the types you want, you must append a 0. */ rc = gnutls_certificate_type_set_priority(session, cert_type_priority); if(rc < 0) return CURLE_SSL_CONNECT_ERROR; if(data->set.cert) { if( gnutls_certificate_set_x509_key_file( conn->ssl[sockindex].cred, data->set.cert, data->set.key != 0 ? data->set.key : data->set.cert, do_file_type(data->set.cert_type) ) ) { failf(data, "error reading X.509 key or certificate file"); return CURLE_SSL_CONNECT_ERROR; } } /* put the credentials to the current session */ rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, conn->ssl[sockindex].cred); /* set the connection handle (file descriptor for the socket) */ gnutls_transport_set_ptr(session, (gnutls_transport_ptr)conn->sock[sockindex]); /* register callback functions to send and receive data. */ gnutls_transport_set_push_function(session, Curl_gtls_push); gnutls_transport_set_pull_function(session, Curl_gtls_pull); /* lowat must be set to zero when using custom push and pull functions. */ gnutls_transport_set_lowat(session, 0); /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) { /* we got a session id, use it! */ gnutls_session_set_data(session, ssl_sessionid, ssl_idsize); /* Informational message */ infof (data, "SSL re-using session ID\n"); } rc = handshake(conn, session, sockindex, TRUE); if(rc) /* handshake() sets its own error message with failf() */ return rc; /* 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 first certificate in the list is the peer's certificate, following the issuer's certificate, then the issuer's issuer etc. */ chainp = gnutls_certificate_get_peers(session, &cert_list_size); if(!chainp) { if(data->set.ssl.verifyhost) { failf(data, "failed to get server cert"); return CURLE_SSL_PEER_CERTIFICATE; } infof(data, "\t common name: WARNING couldn't obtain\n"); } /* 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 more of the gnutls_certificate_status_t enumerated elements bitwise or'd. To avoid denial of service attacks some default upper limits regarding the certificate key size and chain size are set. To override them use gnutls_certificate_set_verify_limits(). */ rc = gnutls_certificate_verify_peers2(session, &verify_status); if (rc < 0) { failf(data, "server cert verify failed: %d", rc); return CURLE_SSL_CONNECT_ERROR; } /* verify_status is a bitmask of gnutls_certificate_status bits */ if(verify_status & GNUTLS_CERT_INVALID) { if (data->set.ssl.verifypeer) { failf(data, "server certificate verification failed. CAfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none"); return CURLE_SSL_CACERT; } else infof(data, "\t server certificate verification FAILED\n"); } else infof(data, "\t server certificate verification OK\n"); /* initialize an X.509 certificate structure. */ gnutls_x509_crt_init(&x509_cert); /* convert the given DER or PEM encoded Certificate to the native gnutls_x509_crt_t format */ gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); size=sizeof(certbuf); rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, 0, /* the first and only one */ FALSE, certbuf, &size); if(rc) { infof(data, "error fetching CN from cert:%s\n", gnutls_strerror(rc)); } /* This function will check if the given certificate's subject matches the given hostname. This is a basic implementation of the matching described in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on failure. */ rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name); if(!rc) { if (data->set.ssl.verifyhost > 1) { failf(data, "SSL: certificate subject name (%s) does not match " "target host name '%s'", certbuf, conn->host.dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_SSL_PEER_CERTIFICATE; } else infof(data, "\t common name: %s (does not match '%s')\n", certbuf, conn->host.dispname); } else infof(data, "\t common name: %s (matched)\n", certbuf); /* Show: - ciphers used - subject - start date - expire date - common name - issuer */ /* public key algorithm's parameters */ algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); infof(data, "\t certificate public key: %s\n", gnutls_pk_algorithm_get_name(algo)); /* version of the X.509 certificate. */ infof(data, "\t certificate version: #%d\n", gnutls_x509_crt_get_version(x509_cert)); size = sizeof(certbuf); gnutls_x509_crt_get_dn(x509_cert, certbuf, &size); infof(data, "\t subject: %s\n", certbuf); clock = gnutls_x509_crt_get_activation_time(x509_cert); showtime(data, "start date", clock); clock = gnutls_x509_crt_get_expiration_time(x509_cert); showtime(data, "expire date", clock); size = sizeof(certbuf); gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size); infof(data, "\t issuer: %s\n", certbuf); gnutls_x509_crt_deinit(x509_cert); /* compression algorithm (if any) */ ptr = gnutls_compression_get_name(gnutls_compression_get(session)); /* 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); if(!ssl_sessionid) { /* this session was not previously in the cache, add it now */ /* get the session ID data size */ gnutls_session_get_data(session, NULL, &ssl_idsize); ssl_sessionid = malloc(ssl_idsize); /* get a buffer for it */ if(ssl_sessionid) { /* extract session ID to the allocated buffer */ gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize); /* store this session id */ return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize); } } return CURLE_OK; }
/* ====================================================== */ CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex) { CURLcode retcode = CURLE_OK; struct SessionHandle *data = conn->data; int err; long lerr; int what; char * str; SSL_METHOD_QUAL SSL_METHOD *req_method=NULL; void *ssl_sessionid=NULL; ASN1_TIME *certdate; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { /* Make funny stuff to get random input */ random_the_seed(data); ssl_seeded = TRUE; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: /* we try to figure out version */ req_method = SSLv23_client_method(); break; case CURL_SSLVERSION_TLSv1: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_SSLv2: req_method = SSLv2_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; } connssl->ctx = SSL_CTX_new(req_method); if(!connssl->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } #ifdef SSL_CTRL_SET_MSG_CALLBACK if (data->set.fdebug) { /* 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!"); } else if (!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, conn)) { infof(data, "SSL: couldn't set callback argument!"); } } #endif /* OpenSSL contains code to work-around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those work-arounds. The man page for this option states that SSL_OP_ALL enables ll the work-arounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken implementations is desired." */ SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL); #if 0 /* * Not sure it's needed to tell SSL_connect() that socket is * non-blocking. It doesn't seem to care, but just return with * SSL_ERROR_WANT_x. */ if (data->state.used_interface == Curl_if_multi) SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL); #endif if(data->set.cert) { if(!cert_stuff(conn, connssl->ctx, data->set.cert, data->set.cert_type, data->set.key, data->set.key_type)) { /* failf() is already done in cert_stuff() */ return CURLE_SSL_CERTPROBLEM; } } if(data->set.ssl.cipher_list) { if(!SSL_CTX_set_cipher_list(connssl->ctx, data->set.ssl.cipher_list)) { failf(data, "failed setting cipher list"); return CURLE_SSL_CIPHER; } } if (data->set.ssl.CAfile || data->set.ssl.CApath) { /* tell SSL where to find CA certificates that are used to verify the servers certificate. */ if (!SSL_CTX_load_verify_locations(connssl->ctx, data->set.ssl.CAfile, data->set.ssl.CApath)) { if (data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ failf(data,"error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); return CURLE_SSL_CACERT; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); } /* 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); /* 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; } } /* Lets make an SSL structure */ connssl->handle = SSL_new(connssl->ctx); if (!connssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } SSL_set_connect_state(connssl->handle); connssl->server_cert = 0x0; /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* 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)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layers */ if (!SSL_set_fd(connssl->handle, sockfd)) { failf(data, "SSL: SSL_set_fd failed: %s", ERR_error_string(ERR_get_error(),NULL)); return CURLE_SSL_CONNECT_ERROR; } while(1) { int writefd; int readfd; long timeout_ms; long has_passed; /* Find out if any timeout is set. If not, use 300 seconds. Otherwise, figure out the most strict timeout of the two possible one and then how much time that has elapsed to know how much time we allow for the connect call */ if(data->set.timeout || data->set.connecttimeout) { /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && (data->set.timeout>data->set.connecttimeout)) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; } else /* no particular time-out has been set */ timeout_ms= DEFAULT_CONNECT_TIMEOUT; /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* subtract the passed time */ timeout_ms -= has_passed; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEOUTED; } readfd = CURL_SOCKET_BAD; writefd = CURL_SOCKET_BAD; err = SSL_connect(connssl->handle); /* 1 is fine 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { int detail = SSL_get_error(connssl->handle, err); if(SSL_ERROR_WANT_READ == detail) readfd = sockfd; else if(SSL_ERROR_WANT_WRITE == detail) writefd = sockfd; else { /* untreated error */ unsigned long errdetail; char error_buffer[120]; /* OpenSSL documents that this must be at least 120 bytes long. */ CURLcode rc; const char *cert_problem = NULL; errdetail = ERR_get_error(); /* Gets the earliest error code from the thread's error queue and removes the entry. */ switch(errdetail) { case 0x1407E086: /* 1407E086: SSL routines: SSL2_SET_CERTIFICATE: certificate verify failed */ /* fall-through */ case 0x14090086: /* 14090086: SSL routines: SSL3_GET_SERVER_CERTIFICATE: certificate verify failed */ cert_problem = "SSL certificate problem, verify that the CA cert is" " OK. Details:\n"; rc = CURLE_SSL_CACERT; break; default: rc = CURLE_SSL_CONNECT_ERROR; break; } /* detail is already set to the SSL error above */ /* If we e.g. use SSLv2 request-method and the server doesn't like us * (RST connection etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { failf(data, "Unknown SSL protocol error in connection to %s:%d ", conn->host.name, conn->port); return rc; } /* Could be a CERT problem */ SSL_strerror(errdetail, error_buffer, sizeof(error_buffer)); failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); return rc; } } else /* we have been connected fine, get out of the connect loop */ break; while(1) { what = Curl_select(readfd, writefd, (int)timeout_ms); if(what > 0) /* reabable or writable, go loop in the outer loop */ break; else if(0 == what) { /* timeout */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } else { /* anything that gets here is fatally bad */ failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); return CURLE_SSL_CONNECT_ERROR; } } /* while()-loop for the select() */ } /* while()-loop for the SSL_connect() */ /* Informational message */ infof (data, "SSL connection using %s\n", SSL_get_cipher(connssl->handle)); if(!ssl_sessionid) { /* Since this is not a cached session ID, then we want to stach this one in the cache! */ SSL_SESSION *ssl_sessionid; #ifdef HAVE_SSL_GET1_SESSION 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 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 retcode = Curl_ssl_addsessionid(conn, ssl_sessionid, 0 /* unknown size */); if(retcode) { failf(data, "failed to store ssl session"); return retcode; } } /* Get server's certificate (note: beware of dynamic allocation) - opt */ /* major serious hack alert -- we should check certificates * to authenticate the server; otherwise we risk man-in-the-middle * attack */ connssl->server_cert = SSL_get_peer_certificate(connssl->handle); if(!connssl->server_cert) { failf(data, "SSL: couldn't get peer certificate!"); return CURLE_SSL_PEER_CERTIFICATE; } infof (data, "Server certificate:\n"); str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert), NULL, 0); if(!str) { failf(data, "SSL: couldn't get X509-subject!"); X509_free(connssl->server_cert); connssl->server_cert = NULL; return CURLE_SSL_CONNECT_ERROR; } infof(data, "\t subject: %s\n", str); CRYPTO_free(str); certdate = X509_get_notBefore(connssl->server_cert); Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate); certdate = X509_get_notAfter(connssl->server_cert); Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate); if(data->set.ssl.verifyhost) { retcode = verifyhost(conn, connssl->server_cert); if(retcode) { X509_free(connssl->server_cert); connssl->server_cert = NULL; return retcode; } } str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert), NULL, 0); if(!str) { failf(data, "SSL: couldn't get X509-issuer name!"); retcode = CURLE_SSL_CONNECT_ERROR; } else { infof(data, "\t issuer: %s\n", str); CRYPTO_free(str); /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ 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 and we return earlyer if verifypeer is set? */ failf(data, "SSL certificate verify result: %s (%ld)", X509_verify_cert_error_string(lerr), lerr); retcode = CURLE_SSL_PEER_CERTIFICATE; } else infof(data, "SSL certificate verify result: %s (%ld)," " continuing anyway.\n", X509_verify_cert_error_string(lerr), lerr); } else infof(data, "SSL certificate verify ok.\n"); } X509_free(connssl->server_cert); connssl->server_cert = NULL; return retcode; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { 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]; 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) { default: case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: infof(data, "CyaSSL cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_TLSv1_0: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(conssl->ctx) SSL_CTX_free(conssl->ctx); conssl->ctx = SSL_CTX_new(req_method); if(!conssl->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } #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(data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ 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", data->set.str[STRING_SSL_CAPATH]? data->set.str[STRING_SSL_CAPATH] : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]: "none"); } /* Load the client certificate, and private key */ if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) { int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]); if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT], file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(data->set.str[STRING_KEY_TYPE]); if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY], file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #else if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) { return CURLE_SSL_CONNECT_ERROR; } #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 * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(conssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, NULL); /* Let's make an SSL structure */ if(conssl->handle) SSL_free(conssl->handle); conssl->handle = SSL_new(conssl->ctx); if(!conssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* 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)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(conssl->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } conssl->connecting_state = ssl_connect_2; return CURLE_OK; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ 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]; if(conssl->state == ssl_connection_complete) return CURLE_OK; /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #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 break; case CURL_SSLVERSION_TLSv1_0: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; case CURL_SSLVERSION_SSLv2: failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(conssl->ctx) SSL_CTX_free(conssl->ctx); conssl->ctx = SSL_CTX_new(req_method); if(!conssl->ctx) { failf(data, "SSL: couldn't create a context!"); 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(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" " CAfile: %s\n CApath: %s", data->set.str[STRING_SSL_CAFILE]? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH]? data->set.str[STRING_SSL_CAPATH] : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]: "none"); } /* Load the client certificate, and private key */ if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) { int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]); if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT], file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(data->set.str[STRING_KEY_TYPE]); if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY], file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #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 * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(conssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, NULL); /* 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); conssl->handle = SSL_new(conssl->ctx); if(!conssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* 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), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(conssl->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } conssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode mbed_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; 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, NULL, 0); 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, NULL, 0); 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 */ mbedtls_x509_crt_init(&connssl->cacert); 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 */ mbedtls_x509_crt_init(&connssl->clicert); 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 */ mbedtls_pk_init(&connssl->pk); if(data->set.str[STRING_KEY]) { 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 */ mbedtls_x509_crl_init(&connssl->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_DEFAULT: case CURL_SSLVERSION_TLSv1: mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_1); infof(data, "mbedTLS: Set min SSL version to TLS 1.0\n"); break; case CURL_SSLVERSION_SSLv3: mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); infof(data, "mbedTLS: Set SSL version to 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); mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_1); infof(data, "mbedTLS: Set SSL version to 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); mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2); infof(data, "mbedTLS: Set SSL version to 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); mbedtls_ssl_conf_max_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); infof(data, "mbedTLS: Set SSL version to TLS 1.2\n"); break; default: failf(data, "mbedTLS: Unsupported SSL protocol version"); return CURLE_SSL_CONNECT_ERROR; } 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, NULL)) { ret = mbedtls_ssl_set_session(&connssl->ssl, old_session); if(ret) { failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } infof(data, "mbedTLS re-using session\n"); } 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(mbedtls_ssl_set_hostname(&connssl->ssl, conn->host.name)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks *and* the name to set in the SNI extension. So even if curl connects to a host specified as an IP address, this function must be used. */ failf(data, "couldn't set hostname in mbedTLS"); return CURLE_SSL_CONNECT_ERROR; } #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { const char **p = &connssl->protocols[0]; #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) *p++ = NGHTTP2_PROTO_VERSION_ID; #endif *p++ = ALPN_HTTP_1_1; *p = NULL; /* this function doesn't clone the protocols array, which is why we need to keep it around */ if(mbedtls_ssl_conf_alpn_protocols(&connssl->config, &connssl->protocols[0])) { failf(data, "Failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } for(p = &connssl->protocols[0]; *p; ++p) infof(data, "ALPN, offering %s\n", *p); } #endif #ifdef MBEDTLS_DEBUG mbedtls_ssl_conf_dbg(&connssl->config, mbedtls_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static CURLcode polarssl_connect_step1(struct connectdata *conn, int sockindex) { struct Curl_easy *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 char errorbuf[128]; errorbuf[0]=0; /* PolarSSL only supports SSLv3 and TLSv1 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "PolarSSL 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); if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func_mutex, &entropy, NULL, 0)) != 0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #else entropy_init(&connssl->entropy); if((ret = ctr_drbg_init(&connssl->ctr_drbg, entropy_func, &connssl->entropy, NULL, 0)) != 0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Failed - PolarSSL: ctr_drbg_init returned (-0x%04X) %s\n", -ret, errorbuf); } #endif /* THREADING_SUPPORT */ /* Load the trusted CA */ memset(&connssl->cacert, 0, sizeof(x509_crt)); if(data->set.str[STRING_SSL_CAFILE]) { ret = x509_crt_parse_file(&connssl->cacert, data->set.str[STRING_SSL_CAFILE]); if(ret<0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading ca cert file %s - PolarSSL: (-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 = x509_crt_parse_path(&connssl->cacert, data->set.str[STRING_SSL_CAPATH]); if(ret<0) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading ca cert path %s - PolarSSL: (-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(x509_crt)); if(data->set.str[STRING_CERT]) { ret = x509_crt_parse_file(&connssl->clicert, data->set.str[STRING_CERT]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading client cert file %s - PolarSSL: (-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]) { pk_context pk; pk_init(&pk); ret = pk_parse_keyfile(&pk, data->set.str[STRING_KEY], data->set.str[STRING_KEY_PASSWD]); if(ret == 0 && !pk_can_do(&pk, POLARSSL_PK_RSA)) ret = POLARSSL_ERR_PK_TYPE_MISMATCH; if(ret == 0) rsa_copy(&connssl->rsa, pk_rsa(pk)); else rsa_free(&connssl->rsa); pk_free(&pk); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_KEY], -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } /* Load the CRL */ memset(&connssl->crl, 0, sizeof(x509_crl)); if(data->set.str[STRING_SSL_CRLFILE]) { ret = x509_crl_parse_file(&connssl->crl, data->set.str[STRING_SSL_CRLFILE]); if(ret) { #ifdef POLARSSL_ERROR_C error_strerror(ret, errorbuf, sizeof(errorbuf)); #endif /* POLARSSL_ERROR_C */ failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s", data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } } infof(data, "PolarSSL: Connecting to %s:%d\n", conn->host.name, conn->remote_port); if(ssl_init(&connssl->ssl)) { failf(data, "PolarSSL: ssl_init failed"); return CURLE_SSL_CONNECT_ERROR; } 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; } ssl_set_endpoint(&connssl->ssl, SSL_IS_CLIENT); ssl_set_authmode(&connssl->ssl, SSL_VERIFY_OPTIONAL); ssl_set_rng(&connssl->ssl, ctr_drbg_random, &connssl->ctr_drbg); ssl_set_bio(&connssl->ssl, net_recv, &conn->sock[sockindex], net_send, &conn->sock[sockindex]); ssl_set_ciphersuites(&connssl->ssl, ssl_list_ciphersuites()); /* Check if there's a cached ID we can/should use here! */ if(conn->ssl_config.sessionid) { void *old_session = NULL; Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &old_session, NULL)) { ret = ssl_set_session(&connssl->ssl, old_session); Curl_ssl_sessionid_unlock(conn); if(ret) { failf(data, "ssl_set_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } infof(data, "PolarSSL re-using session\n"); } } ssl_set_ca_chain(&connssl->ssl, &connssl->cacert, &connssl->crl, conn->host.name); ssl_set_own_cert_rsa(&connssl->ssl, &connssl->clicert, &connssl->rsa); if(ssl_set_hostname(&connssl->ssl, conn->host.name)) { /* ssl_set_hostname() sets the name to use in CN/SAN checks *and* the name to set in the SNI extension. So even if curl connects to a host specified as an IP address, this function must be used. */ failf(data, "couldn't set hostname in PolarSSL"); return CURLE_SSL_CONNECT_ERROR; } #ifdef HAS_ALPN if(conn->bits.tls_enable_alpn) { static const char* protocols[3]; int cur = 0; #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) { 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 #ifdef POLARSSL_DEBUG ssl_set_dbg(&connssl->ssl, polarssl_debug, data); #endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; }