]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
- libcurl-NSS now tries to reconnect with TLS disabled in case it detects
authorKamil Dudka <kdudka@redhat.com>
Thu, 12 Nov 2009 11:16:31 +0000 (11:16 +0000)
committerKamil Dudka <kdudka@redhat.com>
Thu, 12 Nov 2009 11:16:31 +0000 (11:16 +0000)
  a broken TLS server. However it does not happen if SSL version is selected
  manually. The approach was originally taken from PSM. Kaspar Brand helped me
  to complete the patch. Original bug reports:
  https://bugzilla.redhat.com/525496
  https://bugzilla.redhat.com/527771

CHANGES
lib/nss.c
lib/transfer.c
lib/urldata.h

diff --git a/CHANGES b/CHANGES
index 5bfe9312a4438ba06b2522d6b5d9ad18f2bfee1b..47ee50df7d296f9592420b97c61b7d1d8c7aff9e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,13 @@ Kamil Dudka (12 Nov 2009)
   before and always closed unresolved. More info at the RH bug:
   https://bugzilla.redhat.com/534176
 
+- libcurl-NSS now tries to reconnect with TLS disabled in case it detects
+  a broken TLS server. However it does not happen if SSL version is selected
+  manually. The approach was originally taken from PSM. Kaspar Brand helped me
+  to complete the patch. Original bug reports:
+  https://bugzilla.redhat.com/525496
+  https://bugzilla.redhat.com/527771
+
 Yang Tse (11 Nov 2009)
 - Marco Maggi reported that compilation failed when configured --with-gssapi
   and GNU GSS installed due to a missing mutual exclusion of header files in
index 5c1340fa9ee9a67a52995488909707e89ee12368..893ca2edcca43388fa31e5c091b0d03664b0aab9 100644 (file)
--- a/lib/nss.c
+++ b/lib/nss.c
@@ -844,6 +844,36 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
   return SECSuccess;
 }
 
+/* This function is supposed to decide, which error codes should be used
+ * to conclude server is TLS intolerant.
+ *
+ * taken from xulrunner - nsNSSIOLayer.cpp
+ */
+static PRBool
+isTLSIntoleranceError(PRInt32 err)
+{
+  switch (err) {
+  case SSL_ERROR_BAD_MAC_ALERT:
+  case SSL_ERROR_BAD_MAC_READ:
+  case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
+  case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
+  case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE:
+  case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
+  case SSL_ERROR_NO_CYPHER_OVERLAP:
+  case SSL_ERROR_BAD_SERVER:
+  case SSL_ERROR_BAD_BLOCK_PADDING:
+  case SSL_ERROR_UNSUPPORTED_VERSION:
+  case SSL_ERROR_PROTOCOL_VERSION_ALERT:
+  case SSL_ERROR_RX_MALFORMED_FINISHED:
+  case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE:
+  case SSL_ERROR_DECODE_ERROR_ALERT:
+  case SSL_ERROR_RX_UNKNOWN_ALERT:
+    return PR_TRUE;
+  default:
+    return PR_FALSE;
+  }
+}
+
 /**
  * Global SSL init
  *
@@ -1081,7 +1111,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
   switch (data->set.ssl.version) {
   default:
   case CURL_SSLVERSION_DEFAULT:
-    ssl3 = tlsv1 = PR_TRUE;
+    ssl3 = PR_TRUE;
+    if (data->state.ssl_connect_retry)
+      infof(data, "TLS disabled due to previous handshake failure\n");
+    else
+      tlsv1 = PR_TRUE;
     break;
   case CURL_SSLVERSION_TLSv1:
     tlsv1 = PR_TRUE;
@@ -1104,6 +1138,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
   if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess)
     goto error;
 
+  /* reset the flag to avoid an infinite loop */
+  data->state.ssl_connect_retry = FALSE;
+
   /* enable all ciphers from enable_ciphers_by_default */
   cipher_to_enable = enable_ciphers_by_default;
   while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) {
@@ -1282,10 +1319,21 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
   return CURLE_OK;
 
 error:
+  /* reset the flag to avoid an infinite loop */
+  data->state.ssl_connect_retry = FALSE;
+
   err = PR_GetError();
   infof(data, "NSS error %d\n", err);
   if(model)
     PR_Close(model);
+
+  if (ssl3 && tlsv1 && isTLSIntoleranceError(err)) {
+    /* schedule reconnect through Curl_retry_request() */
+    data->state.ssl_connect_retry = TRUE;
+    infof(data, "Error in TLS handshake, trying SSLv3...\n");
+    return CURLE_OK;
+  }
+
   return curlerr;
 }
 
index e875ce309a9e035fd31c87b1c3de70428e62caf0..86213f90dc82783e270ff17cb6cb7171a8686a29 100644 (file)
@@ -2572,10 +2572,11 @@ CURLcode Curl_retry_request(struct connectdata *conn,
   if(data->set.upload && !(conn->protocol&PROT_HTTP))
     return CURLE_OK;
 
-  if((data->req.bytecount +
+  if(/* workaround for broken TLS servers */ data->state.ssl_connect_retry ||
+      ((data->req.bytecount +
       data->req.headerbytecount == 0) &&
      conn->bits.reuse &&
-     !data->set.opt_no_body) {
+     !data->set.opt_no_body)) {
     /* We got no data, we attempted to re-use a connection and yet we want a
        "body". This might happen if the connection was left alive when we were
        done using it before, but that was closed when we wanted to read from
index 014cb98f86a6eb071f9a09ca646a6a6a69a98eb7..d3101c03acef68b4037b09a58451792a268de54c 100644 (file)
@@ -1331,6 +1331,9 @@ struct UrlState {
   } proto;
   /* current user of this SessionHandle instance, or NULL */
   struct connectdata *current_conn;
+
+  /* if true, force SSL connection retry (workaround for certain servers) */
+  bool ssl_connect_retry;
 };