From 10b6d81c648f5ee49405a031e6a609802c1609d6 Mon Sep 17 00:00:00 2001 From: Eric Hu Date: Wed, 12 Jun 2013 10:36:31 +0200 Subject: axtls: now done non-blocking --- lib/axtls.c | 252 +++++++++++++++++++++++++++++++++++++++------------------- lib/axtls.h | 5 ++ lib/urldata.h | 1 + 3 files changed, 177 insertions(+), 81 deletions(-) (limited to 'lib') diff --git a/lib/axtls.c b/lib/axtls.c index 21806403b..44e6b9303 100644 --- a/lib/axtls.c +++ b/lib/axtls.c @@ -41,26 +41,12 @@ #define _MPRINTF_REPLACE /* use our functions only */ #include #include "curl_memory.h" +#include /* 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; } diff --git a/lib/axtls.h b/lib/axtls.h index 8d33b1c7a..db9136597 100644 --- a/lib/axtls.h +++ b/lib/axtls.h @@ -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 diff --git a/lib/urldata.h b/lib/urldata.h index 8d6c42063..0c07cab70 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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; -- cgit v1.2.3