aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/nss.c50
-rw-r--r--lib/transfer.c5
-rw-r--r--lib/urldata.h3
3 files changed, 55 insertions, 3 deletions
diff --git a/lib/nss.c b/lib/nss.c
index 5c1340fa9..893ca2edc 100644
--- 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;
}
diff --git a/lib/transfer.c b/lib/transfer.c
index e875ce309..86213f90d 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -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
diff --git a/lib/urldata.h b/lib/urldata.h
index 014cb98f8..d3101c03a 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -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;
};