diff --git a/beos/fetch_rsrc.cpp b/beos/fetch_rsrc.cpp index f7c99d7c4..6f78aafa5 100644 --- a/beos/fetch_rsrc.cpp +++ b/beos/fetch_rsrc.cpp @@ -85,7 +85,7 @@ static bool fetch_rsrc_can_fetch(const nsurl *url) } static void *fetch_rsrc_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { diff --git a/content/fetch.c b/content/fetch.c index 028fe9ee4..bfe4458cf 100644 --- a/content/fetch.c +++ b/content/fetch.c @@ -235,7 +235,8 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, - bool verifiable, const char *headers[]) + bool verifiable, bool downgrade_tls, + const char *headers[]) { struct fetch *fetch; scheme_fetcher *fetcher = fetchers; @@ -321,8 +322,9 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, /* Got a scheme fetcher, try and set up the fetch */ fetch->fetcher_handle = fetch->ops->setup_fetch(fetch, url, - only_2xx, post_urlenc, - post_multipart, headers); + only_2xx, downgrade_tls, + post_urlenc, post_multipart, + headers); if (fetch->fetcher_handle == NULL) goto failed; diff --git a/content/fetch.h b/content/fetch.h index d7cdced1b..d23b3cd4b 100644 --- a/content/fetch.h +++ b/content/fetch.h @@ -43,7 +43,8 @@ typedef enum { FETCH_REDIRECT, FETCH_NOTMODIFIED, FETCH_AUTH, - FETCH_CERT_ERR + FETCH_CERT_ERR, + FETCH_SSL_ERR } fetch_msg_type; typedef struct fetch_msg { @@ -103,7 +104,7 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, - bool verifiable, + bool verifiable, bool downgrade_tls, const char *headers[]); void fetch_abort(struct fetch *f); void fetch_poll(void); @@ -123,17 +124,17 @@ struct fetch_multipart_data *fetch_multipart_data_clone( /* API for fetchers themselves */ -typedef bool (*fetcher_initialise)(lwc_string *); -typedef bool (*fetcher_can_fetch)(const nsurl *); -typedef void* (*fetcher_setup_fetch)(struct fetch *, nsurl *, - bool, const char *, - const struct fetch_multipart_data *, - const char **); -typedef bool (*fetcher_start_fetch)(void *); -typedef void (*fetcher_abort_fetch)(void *); -typedef void (*fetcher_free_fetch)(void *); -typedef void (*fetcher_poll_fetcher)(lwc_string *); -typedef void (*fetcher_finalise)(lwc_string *); +typedef bool (*fetcher_initialise)(lwc_string *scheme); +typedef bool (*fetcher_can_fetch)(const nsurl *url); +typedef void *(*fetcher_setup_fetch)(struct fetch *parent_fetch, nsurl *url, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, + const struct fetch_multipart_data *post_multipart, + const char **headers); +typedef bool (*fetcher_start_fetch)(void *fetch); +typedef void (*fetcher_abort_fetch)(void *fetch); +typedef void (*fetcher_free_fetch)(void *fetch); +typedef void (*fetcher_poll_fetcher)(lwc_string *scheme); +typedef void (*fetcher_finalise)(lwc_string *scheme); /** Register a fetcher for a scheme * diff --git a/content/fetchers/about.c b/content/fetchers/about.c index 78f22df01..387e32a98 100644 --- a/content/fetchers/about.c +++ b/content/fetchers/about.c @@ -729,6 +729,7 @@ static void * fetch_about_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c index ff7ccbe30..668e05a46 100644 --- a/content/fetchers/curl.c +++ b/content/fetchers/curl.c @@ -77,6 +77,7 @@ struct curl_fetch_info { bool abort; /**< Abort requested. */ bool stopped; /**< Download stopped on purpose. */ bool only_2xx; /**< Only HTTP 2xx responses acceptable. */ + bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */ nsurl *url; /**< URL of this fetch. */ lwc_string *host; /**< The hostname of this fetch. */ struct curl_slist *headers; /**< List of request headers. */ @@ -114,7 +115,7 @@ static bool fetch_curl_initialise(lwc_string *scheme); static void fetch_curl_finalise(lwc_string *scheme); static bool fetch_curl_can_fetch(const nsurl *url); static void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers); static bool fetch_curl_start(void *vfetch); @@ -348,7 +349,7 @@ bool fetch_curl_can_fetch(const nsurl *url) */ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { @@ -370,6 +371,7 @@ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, fetch->abort = false; fetch->stopped = false; fetch->only_2xx = only_2xx; + fetch->downgrade_tls = downgrade_tls; fetch->headers = NULL; fetch->url = nsurl_ref(url); fetch->host = nsurl_get_component(url, NSURL_HOST); @@ -665,14 +667,28 @@ fetch_curl_set_options(struct curl_fetch_info *f) CURLcode fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm) { + struct curl_fetch_info *f = (struct curl_fetch_info *) parm; SSL_CTX *sslctx = _sslctx; + long options = SSL_OP_ALL; + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback); SSL_CTX_set_cert_verify_callback(sslctx, fetch_curl_cert_verify_callback, parm); + + if (f->downgrade_tls) { +#ifdef SSL_OP_NO_TLSv1_1 + /* Disable TLS1.1, if the server can't cope with it */ + options |= SSL_OP_NO_TLSv1_1; +#endif + } + #ifdef SSL_OP_NO_TLSv1_2 /* Disable TLS1.2, as it causes some servers to stall. */ - SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); + options |= SSL_OP_NO_TLSv1_2; #endif + + SSL_CTX_set_options(sslctx, options); + return CURLE_OK; } @@ -851,17 +867,16 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result) else { finished = true; } - } else if (result == CURLE_WRITE_ERROR && f->stopped) + } else if (result == CURLE_WRITE_ERROR && f->stopped) { /* CURLE_WRITE_ERROR occurs when fetch_curl_data * returns 0, which we use to abort intentionally */ ; - else if (result == CURLE_SSL_PEER_CERTIFICATE || + } else if (result == CURLE_SSL_PEER_CERTIFICATE || result == CURLE_SSL_CACERT) { memcpy(certs, f->cert_data, sizeof(certs)); memset(f->cert_data, 0, sizeof(f->cert_data)); cert = true; - } - else { + } else { LOG(("Unknown cURL response code %d", result)); error = true; } @@ -955,8 +970,12 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result) msg.data.cert_err.num_certs = i; fetch_send_callback(&msg, f->fetch_handle); } else if (error) { - msg.type = FETCH_ERROR; - msg.data.error = fetch_error_buffer; + if (result != CURLE_SSL_CONNECT_ERROR) { + msg.type = FETCH_ERROR; + msg.data.error = fetch_error_buffer; + } else { + msg.type = FETCH_SSL_ERR; + } fetch_send_callback(&msg, f->fetch_handle); } diff --git a/content/fetchers/data.c b/content/fetchers/data.c index 3f8989e8c..77d3c9f9c 100644 --- a/content/fetchers/data.c +++ b/content/fetchers/data.c @@ -81,7 +81,7 @@ static bool fetch_data_can_fetch(const nsurl *url) } static void *fetch_data_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { diff --git a/content/fetchers/file.c b/content/fetchers/file.c index 4c02c0c60..b7b831d7b 100644 --- a/content/fetchers/file.c +++ b/content/fetchers/file.c @@ -128,6 +128,7 @@ static void * fetch_file_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/fetchers/resource.c b/content/fetchers/resource.c index 86d5498d5..1fc33f882 100644 --- a/content/fetchers/resource.c +++ b/content/fetchers/resource.c @@ -231,6 +231,7 @@ static void * fetch_resource_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/llcache.c b/content/llcache.c index ed5cc6eda..4bdebdbc2 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -84,6 +84,8 @@ typedef struct { bool tried_with_auth; /**< Whether we've tried with auth */ + bool tried_with_tls_downgrade; /**< Whether we've tried TLS <= 1.0 */ + bool outstanding_query; /**< Waiting for a query response */ } llcache_fetch_ctx; @@ -711,6 +713,7 @@ static nserror llcache_object_refetch(llcache_object *object) object->fetch.flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES, urlenc, multipart, object->fetch.flags & LLCACHE_RETRIEVE_VERIFIABLE, + object->fetch.tried_with_tls_downgrade, (const char **) headers); /* Clean up cache-control headers */ @@ -1543,6 +1546,45 @@ static nserror llcache_fetch_cert_error(llcache_object *object, return error; } +/** + * Handle a TLS connection setup failure + * + * \param object Object being fetched + * \return NSERROR_OK on success, appropriate error otherwise + */ +static nserror llcache_fetch_ssl_error(llcache_object *object) +{ + nserror error = NSERROR_OK; + + /* Fetch has been stopped, and destroyed. Invalidate object's pointer */ + object->fetch.fetch = NULL; + + /* Invalidate cache-control data */ + llcache_invalidate_cache_control_data(object); + + if (object->fetch.tried_with_tls_downgrade == true) { + /* Have already tried to downgrade, so give up */ + llcache_event event; + + /* Mark object complete */ + object->fetch.state = LLCACHE_FETCH_COMPLETE; + + /* Inform client(s) that object fetch failed */ + event.type = LLCACHE_EVENT_ERROR; + /** \todo More appropriate error message */ + event.data.msg = messages_get("FetchFailed"); + + error = llcache_send_event_to_users(object, &event); + } else { + /* Flag that we've tried to downgrade, so that if the + * fetch fails again, we give up */ + object->fetch.tried_with_tls_downgrade = true; + error = llcache_object_refetch(object); + } + + return error; +} + /** * Handler for fetch events * @@ -1705,6 +1747,17 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) msg->data.cert_err.certs, msg->data.cert_err.num_certs); break; + case FETCH_SSL_ERR: + /* TLS connection setup failed */ + + /* Release candidate, if any */ + if (object->candidate != NULL) { + object->candidate->candidate_count--; + object->candidate = NULL; + } + + error = llcache_fetch_ssl_error(object); + break; } /* Deal with any errors reported by event handlers */