aboutsummaryrefslogtreecommitdiff
path: root/lib/gtls.c
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2005-10-22 21:05:07 +0000
committerDaniel Stenberg <daniel@haxx.se>2005-10-22 21:05:07 +0000
commitc890149c8c144e6eb226d4ebf1723d203e1859ff (patch)
tree3ae091fa33a02be0948835637a288d7afdb5f0fd /lib/gtls.c
parent1a1ab2e2e8abb6618721188109156e5bdda35cdd (diff)
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.
Diffstat (limited to 'lib/gtls.c')
-rw-r--r--lib/gtls.c136
1 files changed, 81 insertions, 55 deletions
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");