diff options
author | Kamil Dudka <kdudka@redhat.com> | 2009-11-12 11:16:31 +0000 |
---|---|---|
committer | Kamil Dudka <kdudka@redhat.com> | 2009-11-12 11:16:31 +0000 |
commit | 571309dc3edaf1c03ff6fdfbcf551875644b5a7f (patch) | |
tree | 4d9a937fdb822578e6899d657c7d1bd324d23c8f /lib | |
parent | d547d00f2cfa69e07d6873bbebbc82f3132f1b82 (diff) |
- 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
Diffstat (limited to 'lib')
-rw-r--r-- | lib/nss.c | 50 | ||||
-rw-r--r-- | lib/transfer.c | 5 | ||||
-rw-r--r-- | lib/urldata.h | 3 |
3 files changed, 55 insertions, 3 deletions
@@ -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; }; |