From 8868a226cdad66a9a07d6e3f168884817592a1df Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 17 Apr 2014 13:27:39 +0200 Subject: [PATCH] nss: implement non-blocking SSL handshake --- RELEASE-NOTES | 1 + lib/urldata.h | 1 + lib/vtls/nss.c | 57 +++++++++++++++++++++++++++++++++++++++++-------- lib/vtls/nssg.h | 1 + 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index f1ba02221..9bf6e7585 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -16,6 +16,7 @@ This release includes the following changes: o sasl: Added DIGEST-MD5 qop-option validation in native challange handling o imap: Expanded mailbox SEARCH support to use URL query strings [7] o imap: Extended FETCH support to include PARTIAL URL specifier [7] + o nss: implement non-blocking SSL handshake o This release includes the following bugfixes: diff --git a/lib/urldata.h b/lib/urldata.h index 19c79cfe8..ec48f6525 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -318,6 +318,7 @@ struct ssl_connect_data { struct SessionHandle *data; struct curl_llist *obj_list; PK11GenericObject *obj_clicert; + ssl_connect_state connecting_state; #endif /* USE_NSS */ #ifdef USE_QSOSSL SSLHandle *handle; diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 455648cc6..8d00488d1 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -1611,7 +1611,10 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex) /* Force the handshake now */ timeout = PR_MillisecondsToInterval((PRUint32) time_left); if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { - if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) + if(PR_GetError() == PR_WOULD_BLOCK_ERROR) + /* TODO: propagate the blocking direction from the NSPR layer */ + return CURLE_AGAIN; + else if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; @@ -1649,32 +1652,68 @@ error: return nss_fail_connect(connssl, data, curlerr); } -CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) +static CURLcode nss_connect_common(struct connectdata *conn, int sockindex, + bool *done) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct SessionHandle *data = conn->data; + const bool blocking = (done == NULL); CURLcode rv; - rv = nss_setup_connect(conn, sockindex); - if(rv) - return rv; + if(connssl->connecting_state == ssl_connect_1) { + rv = nss_setup_connect(conn, sockindex); + if(rv) + /* we do not expect CURLE_AGAIN from nss_setup_connect() */ + return rv; + + if(!blocking) { + /* in non-blocking mode, set NSS non-blocking mode before handshake */ + rv = nss_set_nonblock(connssl, data); + if(rv) + return rv; + } + + connssl->connecting_state = ssl_connect_2; + } rv = nss_do_connect(conn, sockindex); switch(rv) { case CURLE_OK: break; + case CURLE_AGAIN: + if(!blocking) + /* CURLE_AGAIN in non-blocking mode is not an error */ + return CURLE_OK; + /* fall through */ default: return rv; } - /* switch the SSL socket into non-blocking mode */ - rv = nss_set_nonblock(connssl, data); - if(rv) - return rv; + if(blocking) { + /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ + rv = nss_set_nonblock(connssl, data); + if(rv) + return rv; + } + else + /* signal completed SSL handshake */ + *done = TRUE; + connssl->connecting_state = ssl_connect_done; return CURLE_OK; } +CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) +{ + return nss_connect_common(conn, sockindex, /* blocking */ NULL); +} + +CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn, + int sockindex, bool *done) +{ + return nss_connect_common(conn, sockindex, done); +} + static ssize_t nss_send(struct connectdata *conn, /* connection data */ int sockindex, /* socketindex */ const void *mem, /* send this data */ diff --git a/lib/vtls/nssg.h b/lib/vtls/nssg.h index 38181a958..21e96ce4b 100644 --- a/lib/vtls/nssg.h +++ b/lib/vtls/nssg.h @@ -68,6 +68,7 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */ #define curlssl_init Curl_nss_init #define curlssl_cleanup Curl_nss_cleanup #define curlssl_connect Curl_nss_connect +#define curlssl_connect_nonblocking Curl_nss_connect_nonblocking /* NSS has its own session ID cache */ #define curlssl_session_free(x) Curl_nop_stmt