Downgrade TLS version support if it turns out the server can't cope with TLSv1.1.

This commit is contained in:
John-Mark Bell 2013-01-04 23:13:23 +00:00
parent d0d3d31e97
commit 3dff750ae2
9 changed files with 105 additions and 27 deletions

View File

@ -85,7 +85,7 @@ static bool fetch_rsrc_can_fetch(const nsurl *url)
} }
static void *fetch_rsrc_setup(struct fetch *parent_fetch, 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 struct fetch_multipart_data *post_multipart,
const char **headers) const char **headers)
{ {

View File

@ -235,7 +235,8 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer,
fetch_callback callback, fetch_callback callback,
void *p, bool only_2xx, const char *post_urlenc, void *p, bool only_2xx, const char *post_urlenc,
const struct fetch_multipart_data *post_multipart, const struct fetch_multipart_data *post_multipart,
bool verifiable, const char *headers[]) bool verifiable, bool downgrade_tls,
const char *headers[])
{ {
struct fetch *fetch; struct fetch *fetch;
scheme_fetcher *fetcher = fetchers; 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 */ /* Got a scheme fetcher, try and set up the fetch */
fetch->fetcher_handle = fetch->ops->setup_fetch(fetch, url, fetch->fetcher_handle = fetch->ops->setup_fetch(fetch, url,
only_2xx, post_urlenc, only_2xx, downgrade_tls,
post_multipart, headers); post_urlenc, post_multipart,
headers);
if (fetch->fetcher_handle == NULL) if (fetch->fetcher_handle == NULL)
goto failed; goto failed;

View File

@ -43,7 +43,8 @@ typedef enum {
FETCH_REDIRECT, FETCH_REDIRECT,
FETCH_NOTMODIFIED, FETCH_NOTMODIFIED,
FETCH_AUTH, FETCH_AUTH,
FETCH_CERT_ERR FETCH_CERT_ERR,
FETCH_SSL_ERR
} fetch_msg_type; } fetch_msg_type;
typedef struct fetch_msg { typedef struct fetch_msg {
@ -103,7 +104,7 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer,
fetch_callback callback, fetch_callback callback,
void *p, bool only_2xx, const char *post_urlenc, void *p, bool only_2xx, const char *post_urlenc,
const struct fetch_multipart_data *post_multipart, const struct fetch_multipart_data *post_multipart,
bool verifiable, bool verifiable, bool downgrade_tls,
const char *headers[]); const char *headers[]);
void fetch_abort(struct fetch *f); void fetch_abort(struct fetch *f);
void fetch_poll(void); void fetch_poll(void);
@ -123,17 +124,17 @@ struct fetch_multipart_data *fetch_multipart_data_clone(
/* API for fetchers themselves */ /* API for fetchers themselves */
typedef bool (*fetcher_initialise)(lwc_string *); typedef bool (*fetcher_initialise)(lwc_string *scheme);
typedef bool (*fetcher_can_fetch)(const nsurl *); typedef bool (*fetcher_can_fetch)(const nsurl *url);
typedef void* (*fetcher_setup_fetch)(struct fetch *, nsurl *, typedef void *(*fetcher_setup_fetch)(struct fetch *parent_fetch, nsurl *url,
bool, const char *, bool only_2xx, bool downgrade_tls, const char *post_urlenc,
const struct fetch_multipart_data *, const struct fetch_multipart_data *post_multipart,
const char **); const char **headers);
typedef bool (*fetcher_start_fetch)(void *); typedef bool (*fetcher_start_fetch)(void *fetch);
typedef void (*fetcher_abort_fetch)(void *); typedef void (*fetcher_abort_fetch)(void *fetch);
typedef void (*fetcher_free_fetch)(void *); typedef void (*fetcher_free_fetch)(void *fetch);
typedef void (*fetcher_poll_fetcher)(lwc_string *); typedef void (*fetcher_poll_fetcher)(lwc_string *scheme);
typedef void (*fetcher_finalise)(lwc_string *); typedef void (*fetcher_finalise)(lwc_string *scheme);
/** Register a fetcher for a scheme /** Register a fetcher for a scheme
* *

View File

@ -729,6 +729,7 @@ static void *
fetch_about_setup(struct fetch *fetchh, fetch_about_setup(struct fetch *fetchh,
nsurl *url, nsurl *url,
bool only_2xx, bool only_2xx,
bool downgrade_tls,
const char *post_urlenc, const char *post_urlenc,
const struct fetch_multipart_data *post_multipart, const struct fetch_multipart_data *post_multipart,
const char **headers) const char **headers)

View File

@ -77,6 +77,7 @@ struct curl_fetch_info {
bool abort; /**< Abort requested. */ bool abort; /**< Abort requested. */
bool stopped; /**< Download stopped on purpose. */ bool stopped; /**< Download stopped on purpose. */
bool only_2xx; /**< Only HTTP 2xx responses acceptable. */ bool only_2xx; /**< Only HTTP 2xx responses acceptable. */
bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */
nsurl *url; /**< URL of this fetch. */ nsurl *url; /**< URL of this fetch. */
lwc_string *host; /**< The hostname of this fetch. */ lwc_string *host; /**< The hostname of this fetch. */
struct curl_slist *headers; /**< List of request headers. */ 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 void fetch_curl_finalise(lwc_string *scheme);
static bool fetch_curl_can_fetch(const nsurl *url); static bool fetch_curl_can_fetch(const nsurl *url);
static void * fetch_curl_setup(struct fetch *parent_fetch, 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 struct fetch_multipart_data *post_multipart,
const char **headers); const char **headers);
static bool fetch_curl_start(void *vfetch); 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, 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 struct fetch_multipart_data *post_multipart,
const char **headers) const char **headers)
{ {
@ -370,6 +371,7 @@ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url,
fetch->abort = false; fetch->abort = false;
fetch->stopped = false; fetch->stopped = false;
fetch->only_2xx = only_2xx; fetch->only_2xx = only_2xx;
fetch->downgrade_tls = downgrade_tls;
fetch->headers = NULL; fetch->headers = NULL;
fetch->url = nsurl_ref(url); fetch->url = nsurl_ref(url);
fetch->host = nsurl_get_component(url, NSURL_HOST); fetch->host = nsurl_get_component(url, NSURL_HOST);
@ -665,14 +667,28 @@ fetch_curl_set_options(struct curl_fetch_info *f)
CURLcode CURLcode
fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm) fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm)
{ {
struct curl_fetch_info *f = (struct curl_fetch_info *) parm;
SSL_CTX *sslctx = _sslctx; SSL_CTX *sslctx = _sslctx;
long options = SSL_OP_ALL;
SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback); SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback);
SSL_CTX_set_cert_verify_callback(sslctx, fetch_curl_cert_verify_callback, SSL_CTX_set_cert_verify_callback(sslctx, fetch_curl_cert_verify_callback,
parm); 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 #ifdef SSL_OP_NO_TLSv1_2
/* Disable TLS1.2, as it causes some servers to stall. */ /* 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 #endif
SSL_CTX_set_options(sslctx, options);
return CURLE_OK; return CURLE_OK;
} }
@ -851,17 +867,16 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result)
else { else {
finished = true; 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 /* CURLE_WRITE_ERROR occurs when fetch_curl_data
* returns 0, which we use to abort intentionally */ * 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) { result == CURLE_SSL_CACERT) {
memcpy(certs, f->cert_data, sizeof(certs)); memcpy(certs, f->cert_data, sizeof(certs));
memset(f->cert_data, 0, sizeof(f->cert_data)); memset(f->cert_data, 0, sizeof(f->cert_data));
cert = true; cert = true;
} } else {
else {
LOG(("Unknown cURL response code %d", result)); LOG(("Unknown cURL response code %d", result));
error = true; error = true;
} }
@ -955,8 +970,12 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result)
msg.data.cert_err.num_certs = i; msg.data.cert_err.num_certs = i;
fetch_send_callback(&msg, f->fetch_handle); fetch_send_callback(&msg, f->fetch_handle);
} else if (error) { } else if (error) {
msg.type = FETCH_ERROR; if (result != CURLE_SSL_CONNECT_ERROR) {
msg.data.error = fetch_error_buffer; msg.type = FETCH_ERROR;
msg.data.error = fetch_error_buffer;
} else {
msg.type = FETCH_SSL_ERR;
}
fetch_send_callback(&msg, f->fetch_handle); fetch_send_callback(&msg, f->fetch_handle);
} }

View File

@ -81,7 +81,7 @@ static bool fetch_data_can_fetch(const nsurl *url)
} }
static void *fetch_data_setup(struct fetch *parent_fetch, 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 struct fetch_multipart_data *post_multipart,
const char **headers) const char **headers)
{ {

View File

@ -128,6 +128,7 @@ static void *
fetch_file_setup(struct fetch *fetchh, fetch_file_setup(struct fetch *fetchh,
nsurl *url, nsurl *url,
bool only_2xx, bool only_2xx,
bool downgrade_tls,
const char *post_urlenc, const char *post_urlenc,
const struct fetch_multipart_data *post_multipart, const struct fetch_multipart_data *post_multipart,
const char **headers) const char **headers)

View File

@ -231,6 +231,7 @@ static void *
fetch_resource_setup(struct fetch *fetchh, fetch_resource_setup(struct fetch *fetchh,
nsurl *url, nsurl *url,
bool only_2xx, bool only_2xx,
bool downgrade_tls,
const char *post_urlenc, const char *post_urlenc,
const struct fetch_multipart_data *post_multipart, const struct fetch_multipart_data *post_multipart,
const char **headers) const char **headers)

View File

@ -84,6 +84,8 @@ typedef struct {
bool tried_with_auth; /**< Whether we've tried with auth */ 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 */ bool outstanding_query; /**< Waiting for a query response */
} llcache_fetch_ctx; } llcache_fetch_ctx;
@ -711,6 +713,7 @@ static nserror llcache_object_refetch(llcache_object *object)
object->fetch.flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES, object->fetch.flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES,
urlenc, multipart, urlenc, multipart,
object->fetch.flags & LLCACHE_RETRIEVE_VERIFIABLE, object->fetch.flags & LLCACHE_RETRIEVE_VERIFIABLE,
object->fetch.tried_with_tls_downgrade,
(const char **) headers); (const char **) headers);
/* Clean up cache-control headers */ /* Clean up cache-control headers */
@ -1543,6 +1546,45 @@ static nserror llcache_fetch_cert_error(llcache_object *object,
return error; 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 * 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.certs,
msg->data.cert_err.num_certs); msg->data.cert_err.num_certs);
break; 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 */ /* Deal with any errors reported by event handlers */