diff --git a/CHANGES b/CHANGES index 0fd2fe2a5..267ae2f24 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,17 @@ +Daniel (22 October 2005) +- Dima Barsky reported a problem with GnuTLS-enabled libcurl in bug report + #1334338 (http://curl.haxx.se/bug/view.cgi?id=1334338). When reading an SSL + stream from a server and the server requests a "rehandshake", the current + code simply returns this as an error. I have no good way to test this, but + I've added a crude attempt of dealing with this situation slightly better - + it makes a blocking handshake if this happens. Done like this because fixing + this the "proper" way (that would handshake asynchronously) will require + quite some work and I really need a good way to test this to do such a + change. + Daniel (21 October 2005) - "Ofer" reported a problem when libcurl re-used a connection and failed to do it, it could then accidentally actually crash. Presumably, this concerns FTP diff --git a/lib/gtls.c b/lib/gtls.c index dbe3d1f77..aa9d98dfa 100644 --- a/lib/gtls.c +++ b/lib/gtls.c @@ -110,6 +110,72 @@ static void showtime(struct SessionHandle *data, infof(data, "%s", data->state.buffer); } +/* this function does a BLOCKING SSL/TLS (re-)handshake */ +static CURLcode handshake(struct connectdata *conn, + gnutls_session session, + int sockindex, + bool duringconnect) +{ + struct SessionHandle *data = conn->data; + int rc; + + do { + rc = gnutls_handshake(session); + + if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { + long timeout_ms = DEFAULT_CONNECT_TIMEOUT; + long has_passed; + + if(duringconnect && data->set.connecttimeout) + timeout_ms = data->set.connecttimeout*1000; + + if(data->set.timeout) { + /* get the strictest timeout of the ones converted to milliseconds */ + if((data->set.timeout*1000) < timeout_ms) + timeout_ms = data->set.timeout*1000; + } + + /* 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; + } + + rc = Curl_select(conn->sock[sockindex], + conn->sock[sockindex], (int)timeout_ms); + if(rc > 0) + /* reabable or writable, go loop*/ + continue; + else if(0 == rc) { + /* 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; + } + } + else + break; + } while(1); + + if (rc < 0) { + failf(data, "gnutls_handshake() failed: %d", rc); + /* gnutls_perror(ret); */ + return CURLE_SSL_CONNECT_ERROR; + } + + return CURLE_OK; +} + /* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. @@ -206,61 +272,10 @@ Curl_gtls_connect(struct connectdata *conn, infof (data, "SSL re-using session ID\n"); } - do { - rc = gnutls_handshake(session); - - if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { - long timeout_ms; - long has_passed; - - 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 - 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; - } - - rc = Curl_select(conn->sock[sockindex], - conn->sock[sockindex], (int)timeout_ms); - if(rc > 0) - /* reabable or writable, go loop*/ - continue; - else if(0 == rc) { - /* 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; - } - } - else - break; - } while(1); - - if (rc < 0) { - failf(data, "gnutls_handshake() failed: %d", rc); - /* gnutls_perror(ret); */ - return CURLE_SSL_CONNECT_ERROR; - } + 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 @@ -467,6 +482,17 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ return -1; } + if(ret == GNUTLS_E_REHANDSHAKE) { + /* BLOCKING call, this is bad but a work-around for now. Fixing this "the + proper way" takes a whole lot of work. */ + CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE); + if(rc) + /* handshake() writes error message on its own */ + return rc; + *wouldblock = TRUE; /* then return as if this was a wouldblock */ + return -1; + } + *wouldblock = FALSE; if (!ret) { failf(conn->data, "Peer closed the TLS connection");