diff options
Diffstat (limited to 'lib/ssluse.c')
-rw-r--r-- | lib/ssluse.c | 357 |
1 files changed, 238 insertions, 119 deletions
diff --git a/lib/ssluse.c b/lib/ssluse.c index 0ec216a3f..7be5a7cad 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -1116,23 +1116,21 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, #ifdef USE_SSLEAY /* ====================================================== */ -CURLcode -Curl_ossl_connect(struct connectdata *conn, + +static CURLcode +Curl_ossl_connect_step1(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]; + curlassert(ssl_connect_1 == connssl->connecting_state); + if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { /* Make funny stuff to get random input */ random_the_seed(data); @@ -1297,134 +1295,150 @@ Curl_ossl_connect(struct connectdata *conn, 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; + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); +static CURLcode +Curl_ossl_connect_step2(struct connectdata *conn, + int sockindex, long* timeout_ms) +{ + struct SessionHandle *data = conn->data; + int err; + long has_passed; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - /* subtract the passed time */ - timeout_ms -= has_passed; + curlassert(ssl_connect_2 == connssl->connecting_state + || ssl_connect_2_reading == connssl->connecting_state + || ssl_connect_2_writing == connssl->connecting_state); - if(timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEOUTED; - } + /* 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) { - readfd = CURL_SOCKET_BAD; - writefd = CURL_SOCKET_BAD; + /* 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; - err = SSL_connect(connssl->handle); + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); - /* 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); + /* subtract the passed time */ + *timeout_ms -= has_passed; - 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; - } + if(*timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } - /* detail is already set to the SSL error above */ + err = SSL_connect(connssl->handle); - /* 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 */ + /* 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); - SSL_strerror(errdetail, error_buffer, sizeof(error_buffer)); - failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); - return rc; - } + if(SSL_ERROR_WANT_READ == detail) { + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_OK; } - 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 */ + else if(SSL_ERROR_WANT_WRITE == detail) { + connssl->connecting_state = ssl_connect_2_writing; + return CURLE_OK; + } + 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; + + connssl->connecting_state = ssl_connect_2; /* the connection failed, + we're not waiting for + anything else. */ + + 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; - 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; + + /* 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; } - } /* while()-loop for the select() */ - } /* while()-loop for the SSL_connect() */ + /* 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, we're not waiting for anything else. */ + connssl->connecting_state = ssl_connect_3; + + /* Informational message */ + infof (data, "SSL connection using %s\n", + SSL_get_cipher(connssl->handle)); + + return CURLE_OK; + } +} + +static CURLcode +Curl_ossl_connect_step3(struct connectdata *conn, + int sockindex) +{ + CURLcode retcode = CURLE_OK; + char * str; + long lerr; + ASN1_TIME *certdate; + void *ssl_sessionid=NULL; + struct SessionHandle *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - /* Informational message */ - infof (data, "SSL connection using %s\n", - SSL_get_cipher(connssl->handle)); + curlassert(ssl_connect_3 == connssl->connecting_state); - if(!ssl_sessionid) { + if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* Since this is not a cached session ID, then we want to stach this one in the cache! */ SSL_SESSION *ssl_sessionid; @@ -1529,9 +1543,114 @@ Curl_ossl_connect(struct connectdata *conn, X509_free(connssl->server_cert); connssl->server_cert = NULL; + connssl->connecting_state = ssl_connect_done; return retcode; } +static CURLcode +Curl_ossl_connect_common(struct connectdata *conn, + int sockindex, + bool nonblocking, + bool *done) +{ + CURLcode retcode; + struct SessionHandle *data = conn->data; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; + long timeout_ms; + + if (ssl_connect_1==connssl->connecting_state) { + retcode = Curl_ossl_connect_step1(conn, sockindex); + if (retcode) + return retcode; + } + + timeout_ms = 0; + while (ssl_connect_2 == connssl->connecting_state || + ssl_connect_2_reading == connssl->connecting_state || + ssl_connect_2_writing == connssl->connecting_state) { + + /* if ssl is expecting something, check if it's available. */ + if (connssl->connecting_state == ssl_connect_2_reading + || connssl->connecting_state == ssl_connect_2_writing) { + + int writefd = ssl_connect_2_writing== + connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + int readfd = ssl_connect_2_reading== + connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + + while(1) { + int what = Curl_select(readfd, writefd, nonblocking?0:(int)timeout_ms); + if(what > 0) + /* reabable or writable, go loop in the outer loop */ + break; + else if(0 == what) { + if (nonblocking) { + *done = FALSE; + return CURLE_OK; + } + else { + /* 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() */ + } + + /* get the timeout from step2 to avoid computing it twice. */ + retcode = Curl_ossl_connect_step2(conn, sockindex, &timeout_ms); + if (retcode) + return retcode; + + } /* repeat step2 until all transactions are done. */ + + + if (ssl_connect_3==connssl->connecting_state) { + retcode = Curl_ossl_connect_step3(conn, sockindex); + if (retcode) + return retcode; + } + + if (ssl_connect_done==connssl->connecting_state) { + *done = TRUE; + } + else { + *done = FALSE; + } + + return CURLE_OK; +} + +CURLcode +Curl_ossl_connect_nonblocking(struct connectdata *conn, + int sockindex, + bool *done) +{ + return Curl_ossl_connect_common(conn, sockindex, TRUE, done); +} + +CURLcode +Curl_ossl_connect(struct connectdata *conn, + int sockindex) +{ + CURLcode retcode; + bool done = FALSE; + + retcode = Curl_ossl_connect_common(conn, sockindex, FALSE, &done); + if (retcode) + return retcode; + + curlassert(done); + + return CURLE_OK; +} + /* return number of sent (non-SSL) bytes */ int Curl_ossl_send(struct connectdata *conn, int sockindex, |