openssl: associate/detach the transfer from connection

CVE-2021-22901

Bug: https://curl.se/docs/CVE-2021-22901.html
This commit is contained in:
Harry Sintonen 2021-05-05 13:42:26 +02:00 committed by Daniel Stenberg
parent 39ce47f219
commit 7f4a9a9b2a
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 172 additions and 50 deletions

View File

@ -878,8 +878,10 @@ bool Curl_multiplex_wanted(const struct Curl_multi *multi)
void Curl_detach_connnection(struct Curl_easy *data) void Curl_detach_connnection(struct Curl_easy *data)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
if(conn) if(conn) {
Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
Curl_ssl_detach_conn(data, conn);
}
data->conn = NULL; data->conn = NULL;
} }
@ -898,6 +900,7 @@ void Curl_attach_connnection(struct Curl_easy *data,
&data->conn_queue); &data->conn_queue);
if(conn->handler->attach) if(conn->handler->attach)
conn->handler->attach(data, conn); conn->handler->attach(data, conn);
Curl_ssl_associate_conn(data, conn);
} }
static int waitconnect_getsock(struct connectdata *conn, static int waitconnect_getsock(struct connectdata *conn,

View File

@ -1304,7 +1304,9 @@ const struct Curl_ssl Curl_ssl_gskit = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
NULL /* sha256sum */ NULL, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif /* USE_GSKIT */ #endif /* USE_GSKIT */

View File

@ -1656,7 +1656,9 @@ const struct Curl_ssl Curl_ssl_gnutls = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
gtls_sha256sum /* sha256sum */ gtls_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif /* USE_GNUTLS */ #endif /* USE_GNUTLS */

View File

@ -1093,7 +1093,9 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
mbedtls_sha256sum /* sha256sum */ mbedtls_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif /* USE_MBEDTLS */ #endif /* USE_MBEDTLS */

View File

@ -666,7 +666,9 @@ const struct Curl_ssl Curl_ssl_mesalink = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
NULL /* sha256sum */ NULL, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif #endif

View File

@ -2465,7 +2465,9 @@ const struct Curl_ssl Curl_ssl_nss = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
nss_false_start, /* false_start */ nss_false_start, /* false_start */
nss_sha256sum /* sha256sum */ nss_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif /* USE_NSS */ #endif /* USE_NSS */

View File

@ -240,6 +240,10 @@ struct ssl_backend_data {
#endif #endif
}; };
static void ossl_associate_connection(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
/* /*
* Number of bytes to read from the random number seed file. This must be * Number of bytes to read from the random number seed file. This must be
* a finite value (because some entropy "files" like /dev/urandom have * a finite value (because some entropy "files" like /dev/urandom have
@ -2581,6 +2585,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
curl_socket_t sockfd = conn->sock[sockindex]; curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex];
ctx_option_t ctx_options = 0; ctx_option_t ctx_options = 0;
void *ssl_sessionid = NULL;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
bool sni; bool sni;
@ -3225,46 +3230,23 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
} }
#endif #endif
/* Check if there's a cached ID we can/should use here! */ ossl_associate_connection(data, conn, sockindex);
if(SSL_SET_OPTION(primary.sessionid)) {
void *ssl_sessionid = NULL;
int data_idx = ossl_get_ssl_data_index();
int connectdata_idx = ossl_get_ssl_conn_index();
int sockindex_idx = ossl_get_ssl_sockindex_index();
int proxy_idx = ossl_get_proxy_index();
if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
proxy_idx >= 0) {
/* Store the data needed for the "new session" callback.
* The sockindex is stored as a pointer to an array element. */
SSL_set_ex_data(backend->handle, data_idx, data);
SSL_set_ex_data(backend->handle, connectdata_idx, conn);
SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
#ifndef CURL_DISABLE_PROXY
SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
NULL);
#else
SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
&ssl_sessionid, NULL, sockindex)) {
/* we got a session id, use it! */
if(!SSL_set_session(backend->handle, ssl_sessionid)) {
Curl_ssl_sessionid_unlock(data);
failf(data, "SSL: SSL_set_session failed: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
return CURLE_SSL_CONNECT_ERROR;
} }
/* Informational message */
Curl_ssl_sessionid_lock(data); infof(data, "SSL re-using session ID\n");
if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
&ssl_sessionid, NULL, sockindex)) {
/* we got a session id, use it! */
if(!SSL_set_session(backend->handle, ssl_sessionid)) {
Curl_ssl_sessionid_unlock(data);
failf(data, "SSL: SSL_set_session failed: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
/* Informational message */
infof(data, "SSL re-using session ID\n");
}
Curl_ssl_sessionid_unlock(data);
} }
Curl_ssl_sessionid_unlock(data);
#ifndef CURL_DISABLE_PROXY #ifndef CURL_DISABLE_PROXY
if(conn->proxy_ssl[sockindex].use) { if(conn->proxy_ssl[sockindex].use) {
@ -4498,6 +4480,90 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
(void *)backend->ctx : (void *)backend->handle; (void *)backend->ctx : (void *)backend->handle;
} }
static void ossl_associate_connection(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
/* If we don't have SSL context, do nothing. */
if(!backend->handle)
return;
if(SSL_SET_OPTION(primary.sessionid)) {
int data_idx = ossl_get_ssl_data_index();
int connectdata_idx = ossl_get_ssl_conn_index();
int sockindex_idx = ossl_get_ssl_sockindex_index();
int proxy_idx = ossl_get_proxy_index();
if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
proxy_idx >= 0) {
/* Store the data needed for the "new session" callback.
* The sockindex is stored as a pointer to an array element. */
SSL_set_ex_data(backend->handle, data_idx, data);
SSL_set_ex_data(backend->handle, connectdata_idx, conn);
SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
#ifndef CURL_DISABLE_PROXY
SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
NULL);
#else
SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif
}
}
}
/*
* Starting with TLS 1.3, the ossl_new_session_cb callback gets called after
* the handshake. If the transfer that sets up the callback gets killed before
* this callback arrives, we must make sure to properly clear the data to
* avoid UAF problems. A future optimization could be to instead store another
* transfer that might still be using the same connection.
*/
static void ossl_disassociate_connection(struct Curl_easy *data,
int sockindex)
{
struct connectdata *conn = data->conn;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
/* If we don't have SSL context, do nothing. */
if(!backend->handle)
return;
if(SSL_SET_OPTION(primary.sessionid)) {
bool isproxy = FALSE;
bool incache;
void *old_ssl_sessionid = NULL;
int data_idx = ossl_get_ssl_data_index();
int connectdata_idx = ossl_get_ssl_conn_index();
int sockindex_idx = ossl_get_ssl_sockindex_index();
int proxy_idx = ossl_get_proxy_index();
if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
proxy_idx >= 0) {
/* Invalidate the session cache entry, if any */
isproxy = SSL_get_ex_data(backend->handle, proxy_idx) ? TRUE : FALSE;
/* Disable references to data in "new session" callback to avoid
* accessing a stale pointer. */
SSL_set_ex_data(backend->handle, data_idx, NULL);
SSL_set_ex_data(backend->handle, connectdata_idx, NULL);
SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
SSL_set_ex_data(backend->handle, proxy_idx, NULL);
}
Curl_ssl_sessionid_lock(data);
incache = !(Curl_ssl_getsessionid(data, conn, isproxy,
&old_ssl_sessionid, NULL, sockindex));
if(incache)
Curl_ssl_delsessionid(data, old_ssl_sessionid);
Curl_ssl_sessionid_unlock(data);
}
}
const struct Curl_ssl Curl_ssl_openssl = { const struct Curl_ssl Curl_ssl_openssl = {
{ CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */
@ -4533,10 +4599,12 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_engines_list, /* engines_list */ ossl_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
ossl_sha256sum /* sha256sum */ ossl_sha256sum, /* sha256sum */
#else #else
NULL /* sha256sum */ NULL, /* sha256sum */
#endif #endif
ossl_associate_connection, /* associate_connection */
ossl_disassociate_connection /* disassociate_connection */
}; };
#endif /* USE_OPENSSL */ #endif /* USE_OPENSSL */

View File

@ -604,7 +604,9 @@ const struct Curl_ssl Curl_ssl_rustls = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
NULL /* sha256sum */ NULL, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif /* USE_RUSTLS */ #endif /* USE_RUSTLS */

View File

@ -329,7 +329,7 @@ get_alg_id_by_name(char *name)
static CURLcode static CURLcode
set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
int *algIds) ALG_ID *algIds)
{ {
char *startCur = ciphers; char *startCur = ciphers;
int algCount = 0; int algCount = 0;
@ -2433,7 +2433,9 @@ const struct Curl_ssl Curl_ssl_schannel = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
schannel_sha256sum /* sha256sum */ schannel_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif /* USE_SCHANNEL */ #endif /* USE_SCHANNEL */

View File

@ -3453,6 +3453,8 @@ const struct Curl_ssl Curl_ssl_sectransp = {
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
sectransp_false_start, /* false_start */ sectransp_false_start, /* false_start */
sectransp_sha256sum /* sha256sum */ sectransp_sha256sum /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#ifdef __clang__ #ifdef __clang__

View File

@ -586,6 +586,25 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
return CURLE_OK; return CURLE_OK;
} }
void Curl_ssl_associate_conn(struct Curl_easy *data,
struct connectdata *conn)
{
if(Curl_ssl->associate_connection) {
Curl_ssl->associate_connection(data, conn, FIRSTSOCKET);
if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET);
}
}
void Curl_ssl_detach_conn(struct Curl_easy *data,
struct connectdata *conn)
{
if(Curl_ssl->disassociate_connection) {
Curl_ssl->disassociate_connection(data, FIRSTSOCKET);
if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
Curl_ssl->disassociate_connection(data, SECONDARYSOCKET);
}
}
void Curl_ssl_close_all(struct Curl_easy *data) void Curl_ssl_close_all(struct Curl_easy *data)
{ {
@ -1214,7 +1233,9 @@ static const struct Curl_ssl Curl_ssl_multi = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
NULL /* sha256sum */ NULL, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
const struct Curl_ssl *Curl_ssl = const struct Curl_ssl *Curl_ssl =

View File

@ -84,6 +84,11 @@ struct Curl_ssl {
bool (*false_start)(void); bool (*false_start)(void);
CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
unsigned char *sha256sum, size_t sha256sumlen); unsigned char *sha256sum, size_t sha256sumlen);
void (*associate_connection)(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
void (*disassociate_connection)(struct Curl_easy *data, int sockindex);
}; };
#ifdef USE_SSL #ifdef USE_SSL
@ -283,6 +288,11 @@ bool Curl_ssl_cert_status_request(void);
bool Curl_ssl_false_start(void); bool Curl_ssl_false_start(void);
void Curl_ssl_associate_conn(struct Curl_easy *data,
struct connectdata *conn);
void Curl_ssl_detach_conn(struct Curl_easy *data,
struct connectdata *conn);
#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
#else /* if not USE_SSL */ #else /* if not USE_SSL */
@ -309,6 +319,8 @@ bool Curl_ssl_false_start(void);
#define Curl_ssl_cert_status_request() FALSE #define Curl_ssl_cert_status_request() FALSE
#define Curl_ssl_false_start() FALSE #define Curl_ssl_false_start() FALSE
#define Curl_ssl_tls13_ciphersuites() FALSE #define Curl_ssl_tls13_ciphersuites() FALSE
#define Curl_ssl_associate_conn(a,b) Curl_nop_stmt
#define Curl_ssl_detach_conn(a,b) Curl_nop_stmt
#endif #endif
#endif /* HEADER_CURL_VTLS_H */ #endif /* HEADER_CURL_VTLS_H */

View File

@ -1125,7 +1125,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
Curl_none_set_engine_default, /* set_engine_default */ Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */ Curl_none_engines_list, /* engines_list */
Curl_none_false_start, /* false_start */ Curl_none_false_start, /* false_start */
wolfssl_sha256sum /* sha256sum */ wolfssl_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL /* disassociate_connection */
}; };
#endif #endif