axtls: now done non-blocking
This commit is contained in:
parent
8026bd7abd
commit
10b6d81c64
252
lib/axtls.c
252
lib/axtls.c
@ -41,26 +41,12 @@
|
||||
#define _MPRINTF_REPLACE /* use our functions only */
|
||||
#include <curl/mprintf.h>
|
||||
#include "curl_memory.h"
|
||||
#include <unistd.h>
|
||||
/* The last #include file should be: */
|
||||
#include "memdebug.h"
|
||||
#include "hostcheck.h"
|
||||
|
||||
|
||||
/* SSL_read is opied from axTLS compat layer */
|
||||
static int SSL_read(SSL *ssl, void *buf, int num)
|
||||
{
|
||||
uint8_t *read_buf;
|
||||
int ret;
|
||||
|
||||
while((ret = ssl_read(ssl, &read_buf)) == SSL_OK);
|
||||
|
||||
if(ret > SSL_OK) {
|
||||
memcpy(buf, read_buf, ret > num ? num : ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Global axTLS init, called from Curl_ssl_init() */
|
||||
int Curl_axtls_init(void)
|
||||
{
|
||||
@ -131,24 +117,24 @@ static CURLcode map_error_to_curl(int axtls_err)
|
||||
static Curl_recv axtls_recv;
|
||||
static Curl_send axtls_send;
|
||||
|
||||
static void free_ssl_structs(SSL_CTX *ssl_ctx, SSL *ssl)
|
||||
static void free_ssl_structs(struct ssl_connect_data *connssl)
|
||||
{
|
||||
if(ssl) {
|
||||
ssl_free (ssl);
|
||||
if(connssl->ssl) {
|
||||
ssl_free (connssl->ssl);
|
||||
connssl->ssl = NULL;
|
||||
}
|
||||
if(ssl_ctx) {
|
||||
ssl_ctx_free(ssl_ctx);
|
||||
if(connssl->ssl_ctx) {
|
||||
ssl_ctx_free(connssl->ssl_ctx);
|
||||
connssl->ssl_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called after the TCP connect has completed. Setup the TLS
|
||||
* layer and do all necessary magic.
|
||||
* 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.
|
||||
*/
|
||||
CURLcode
|
||||
Curl_axtls_connect(struct connectdata *conn,
|
||||
int sockindex)
|
||||
|
||||
static CURLcode connect_prep(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
SSL_CTX *ssl_ctx;
|
||||
@ -158,14 +144,13 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
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;
|
||||
/* 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
|
||||
@ -194,6 +179,9 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
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)
|
||||
@ -201,8 +189,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
infof(data, "error reading ca cert file %s \n",
|
||||
data->set.ssl.CAfile);
|
||||
if(data->set.ssl.verifypeer) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
}
|
||||
@ -236,8 +222,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
if(cert_types[i] == 0) {
|
||||
failf(data, "%s is not x509 or pkcs12 format",
|
||||
data->set.str[STRING_CERT]);
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
return CURLE_SSL_CERTPROBLEM;
|
||||
}
|
||||
}
|
||||
@ -262,8 +246,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
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);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
@ -284,15 +266,25 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
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);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
ssl_display_error(ssl_fcn_return); /* goes to stdout. */
|
||||
return map_error_to_curl(ssl_fcn_return);
|
||||
}
|
||||
infof (data, "handshake completed successfully\n");
|
||||
conn->ssl[sockindex].ssl = ssl;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* For both blocking and non-blocking connects, this function finalizes the
|
||||
* SSL connection.
|
||||
*/
|
||||
static CURLcode connect_finish(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
SSL *ssl = conn->ssl[sockindex].ssl;
|
||||
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;
|
||||
|
||||
/* Here, 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?
|
||||
@ -302,9 +294,8 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
if(data->set.ssl.verifypeer) {
|
||||
if(ssl_verify_cert(ssl) != SSL_OK) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
failf(data, "server cert verify failed");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
return CURLE_PEER_FAILED_VERIFICATION;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -321,7 +312,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
* 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++) {
|
||||
@ -344,13 +334,13 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
if(data->set.ssl.verifyhost) {
|
||||
/* Break connection ! */
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
failf(data, "\tsubjectAltName(s) do not match %s\n",
|
||||
conn->host.dispname);
|
||||
return CURLE_PEER_FAILED_VERIFICATION;
|
||||
}
|
||||
else
|
||||
infof(data, "\tsubjectAltName(s) do not match %s\n",
|
||||
conn->host.dispname);
|
||||
}
|
||||
else if(found_subject_alt_names == 0) {
|
||||
/* Per RFC2818, when no Subject Alt Names were available, examine the peer
|
||||
@ -359,7 +349,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
if(peer_CN == NULL) {
|
||||
if(data->set.ssl.verifyhost) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
failf(data, "unable to obtain common name from peer certificate");
|
||||
return CURLE_PEER_FAILED_VERIFICATION;
|
||||
}
|
||||
@ -371,7 +360,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
if(data->set.ssl.verifyhost) {
|
||||
/* Break connection ! */
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
free_ssl_structs(ssl_ctx, ssl);
|
||||
failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
|
||||
peer_CN, conn->host.dispname);
|
||||
return CURLE_PEER_FAILED_VERIFICATION;
|
||||
@ -385,8 +373,6 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
|
||||
/* 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;
|
||||
|
||||
@ -400,6 +386,107 @@ Curl_axtls_connect(struct connectdata *conn,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use axTLS's non-blocking connection feature to open an SSL connection.
|
||||
* This is called after a TCP connection is already established.
|
||||
*/
|
||||
CURLcode Curl_axtls_connect_nonblocking(
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
bool *done)
|
||||
{
|
||||
CURLcode conn_step;
|
||||
int ssl_fcn_return;
|
||||
|
||||
*done = FALSE;
|
||||
/* connectdata is calloc'd and connecting_state is only changed in this
|
||||
function, so this is safe, as the state is effectively initialized. */
|
||||
if(conn->ssl[sockindex].connecting_state == ssl_connect_1) {
|
||||
conn_step = connect_prep(conn, sockindex);
|
||||
if(conn_step != CURLE_OK) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
return conn_step;
|
||||
}
|
||||
conn->ssl[sockindex].connecting_state = ssl_connect_2;
|
||||
}
|
||||
|
||||
if(conn->ssl[sockindex].connecting_state == ssl_connect_2) {
|
||||
/* Check to make sure handshake was ok. */
|
||||
if(ssl_handshake_status(conn->ssl[sockindex].ssl) != SSL_OK) {
|
||||
ssl_fcn_return = ssl_read(conn->ssl[sockindex].ssl, NULL);
|
||||
if(ssl_fcn_return < 0) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
ssl_display_error(ssl_fcn_return); /* goes to stdout. */
|
||||
return map_error_to_curl(ssl_fcn_return);
|
||||
}
|
||||
else {
|
||||
return CURLE_OK; /* Return control to caller for retries */
|
||||
}
|
||||
}
|
||||
infof (conn->data, "handshake completed successfully\n");
|
||||
conn->ssl[sockindex].connecting_state = ssl_connect_3;
|
||||
}
|
||||
|
||||
if(conn->ssl[sockindex].connecting_state == ssl_connect_3) {
|
||||
conn_step = connect_finish(conn, sockindex);
|
||||
if(conn_step != CURLE_OK) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
return conn_step;
|
||||
}
|
||||
|
||||
/* Reset connect state */
|
||||
conn->ssl[sockindex].connecting_state = ssl_connect_1;
|
||||
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Unrecognized state. Things are very bad. */
|
||||
conn->ssl[sockindex].state = ssl_connection_none;
|
||||
conn->ssl[sockindex].connecting_state = ssl_connect_1;
|
||||
/* Return value perhaps not strictly correct, but distinguishes the issue.*/
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function is called after the TCP connect has completed. Setup the TLS
|
||||
* layer and do all necessary magic for a blocking connect.
|
||||
*/
|
||||
CURLcode
|
||||
Curl_axtls_connect(struct connectdata *conn,
|
||||
int sockindex)
|
||||
|
||||
{
|
||||
CURLcode conn_step = connect_prep(conn, sockindex);
|
||||
int ssl_fcn_return;
|
||||
SSL *ssl = conn->ssl[sockindex].ssl;
|
||||
|
||||
if(conn_step != CURLE_OK) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
return conn_step;
|
||||
}
|
||||
|
||||
/* Check to make sure handshake was ok. */
|
||||
while(ssl_handshake_status(ssl) != SSL_OK) {
|
||||
ssl_fcn_return = ssl_read(ssl, NULL);
|
||||
if(ssl_fcn_return < 0) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
ssl_display_error(ssl_fcn_return); /* goes to stdout. */
|
||||
return map_error_to_curl(ssl_fcn_return);
|
||||
}
|
||||
usleep(10000);
|
||||
}
|
||||
infof (conn->data, "handshake completed successfully\n");
|
||||
|
||||
conn_step = connect_finish(conn, sockindex);
|
||||
if(conn_step != CURLE_OK) {
|
||||
Curl_axtls_close(conn, sockindex);
|
||||
return conn_step;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* return number of sent (non-SSL) bytes */
|
||||
static ssize_t axtls_send(struct connectdata *conn,
|
||||
@ -433,7 +520,7 @@ void Curl_axtls_close(struct connectdata *conn, int sockindex)
|
||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||
|
||||
infof(conn->data, " Curl_axtls_close\n");
|
||||
if(connssl->ssl) {
|
||||
|
||||
/* line from ssluse.c: (void)SSL_shutdown(connssl->ssl);
|
||||
axTLS compat layer does nothing for SSL_shutdown */
|
||||
|
||||
@ -441,13 +528,7 @@ void Curl_axtls_close(struct connectdata *conn, int sockindex)
|
||||
equivalent. ssl_free and ssl_ctx_free close things.
|
||||
SSL_set_connect_state(connssl->handle); */
|
||||
|
||||
ssl_free (connssl->ssl);
|
||||
connssl->ssl = NULL;
|
||||
}
|
||||
if(connssl->ssl_ctx) {
|
||||
ssl_ctx_free (connssl->ssl_ctx);
|
||||
connssl->ssl_ctx = NULL;
|
||||
}
|
||||
free_ssl_structs(connssl);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -462,8 +543,7 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
|
||||
int retval = 0;
|
||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||
struct SessionHandle *data = conn->data;
|
||||
char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
|
||||
to be at least 120 bytes long. */
|
||||
uint8_t *buf;
|
||||
ssize_t nread;
|
||||
|
||||
infof(conn->data, " Curl_axtls_shutdown\n");
|
||||
@ -483,9 +563,10 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
|
||||
CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
|
||||
if(what > 0) {
|
||||
/* Something to read, let's do it and hope that it is the close
|
||||
notify alert from the server */
|
||||
nread = (ssize_t)SSL_read(conn->ssl[sockindex].ssl, buf,
|
||||
sizeof(buf));
|
||||
notify alert from the server. buf is managed internally by
|
||||
axTLS and will be released upon calling ssl_free via
|
||||
free_ssl_structs. */
|
||||
nread = (ssize_t)ssl_read(connssl->ssl, &buf);
|
||||
|
||||
if(nread < SSL_OK) {
|
||||
failf(data, "close notify alert not received during shutdown");
|
||||
@ -502,8 +583,7 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
ssl_free (connssl->ssl);
|
||||
connssl->ssl = NULL;
|
||||
free_ssl_structs(connssl);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@ -516,26 +596,36 @@ static ssize_t axtls_recv(struct connectdata *conn, /* connection data */
|
||||
{
|
||||
struct ssl_connect_data *connssl = &conn->ssl[num];
|
||||
ssize_t ret = 0;
|
||||
uint8_t *read_buf;
|
||||
|
||||
infof(conn->data, " axtls_recv\n");
|
||||
|
||||
*err = CURLE_OK;
|
||||
if(connssl) {
|
||||
ret = (ssize_t)SSL_read(conn->ssl[num].ssl, buf, (int)buffersize);
|
||||
|
||||
/* axTLS isn't terribly generous about error reporting */
|
||||
/* With patched axTLS, SSL_CLOSE_NOTIFY=-3. Hard-coding until axTLS
|
||||
team approves proposed fix. */
|
||||
if(ret == -3 ) {
|
||||
ret = ssl_read(connssl->ssl, &read_buf);
|
||||
if(ret > SSL_OK) {
|
||||
/* ssl_read returns SSL_OK if there is more data to read, so if it is
|
||||
larger, then all data has been read already. */
|
||||
memcpy(buf, read_buf,
|
||||
(size_t)ret > buffersize ? buffersize : (size_t)ret);
|
||||
}
|
||||
else if(ret == SSL_OK) {
|
||||
/* more data to be read, signal caller to call again */
|
||||
*err = CURLE_AGAIN;
|
||||
ret = -1;
|
||||
}
|
||||
else if(ret == -3) {
|
||||
/* With patched axTLS, SSL_CLOSE_NOTIFY=-3. Hard-coding until axTLS
|
||||
team approves proposed fix. */
|
||||
Curl_axtls_close(conn, num);
|
||||
}
|
||||
else if(ret < 0) {
|
||||
failf(conn->data, "axTLS recv error (%d)", (int)ret);
|
||||
else {
|
||||
failf(conn->data, "axTLS recv error (%d)", ret);
|
||||
*err = map_error_to_curl(ret);
|
||||
return -1;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
*err = CURLE_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,10 @@
|
||||
int Curl_axtls_init(void);
|
||||
int Curl_axtls_cleanup(void);
|
||||
CURLcode Curl_axtls_connect(struct connectdata *conn, int sockindex);
|
||||
CURLcode Curl_axtls_connect_nonblocking(
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
bool *done);
|
||||
|
||||
/* tell axTLS to close down all open information regarding connections (and
|
||||
thus session ID caching etc) */
|
||||
@ -47,6 +51,7 @@ int Curl_axtls_check_cxn(struct connectdata *conn);
|
||||
#define curlssl_init Curl_axtls_init
|
||||
#define curlssl_cleanup Curl_axtls_cleanup
|
||||
#define curlssl_connect Curl_axtls_connect
|
||||
#define curlssl_connect_nonblocking Curl_axtls_connect_nonblocking
|
||||
#define curlssl_session_free(x) Curl_axtls_session_free(x)
|
||||
#define curlssl_close_all Curl_axtls_close_all
|
||||
#define curlssl_close Curl_axtls_close
|
||||
|
@ -325,6 +325,7 @@ struct ssl_connect_data {
|
||||
#ifdef USE_AXTLS
|
||||
SSL_CTX* ssl_ctx;
|
||||
SSL* ssl;
|
||||
ssl_connect_state connecting_state;
|
||||
#endif /* USE_AXTLS */
|
||||
#ifdef USE_SCHANNEL
|
||||
struct curl_schannel_cred *cred;
|
||||
|
Loading…
x
Reference in New Issue
Block a user