mirror of
https://github.com/curl/curl.git
synced 2026-05-30 06:37:30 +03:00
vtls: TLS session storage overhaul
- add session with destructor callback - remove vtls `session_free` method - let `Curl_ssl_addsessionid()` take ownership of session object, freeing it also on failures - change tls backend use - test_17, add tests for SSL session resumption Closes #13386
This commit is contained in:
parent
2d2c27e5a3
commit
fb22459dc1
17 changed files with 470 additions and 229 deletions
|
|
@ -345,6 +345,8 @@ struct ssl_general_config {
|
|||
int ca_cache_timeout; /* Certificate store cache timeout (seconds) */
|
||||
};
|
||||
|
||||
typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize);
|
||||
|
||||
/* information stored about one single SSL session */
|
||||
struct Curl_ssl_session {
|
||||
char *name; /* host name for which this ID was used */
|
||||
|
|
@ -352,6 +354,7 @@ struct Curl_ssl_session {
|
|||
const char *scheme; /* protocol scheme used */
|
||||
void *sessionid; /* as returned from the SSL layer */
|
||||
size_t idsize; /* if known, otherwise 0 */
|
||||
Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */
|
||||
long age; /* just a number, the higher the more recent */
|
||||
int remote_port; /* remote port */
|
||||
int conn_to_port; /* remote port for the connection (may be -1) */
|
||||
|
|
|
|||
|
|
@ -1992,9 +1992,8 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
|
|||
ctx = cf? cf->ctx : NULL;
|
||||
data = cf? CF_DATA_CURRENT(cf) : NULL;
|
||||
if(cf && data && ctx) {
|
||||
CURLcode result = Curl_ossl_add_session(cf, data, &ctx->peer,
|
||||
ssl_sessionid);
|
||||
return result? 0 : 1;
|
||||
Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -830,7 +830,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
|
|||
CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result);
|
||||
if(ret == 0) {
|
||||
failf(data, "SSL: EOF without close notify");
|
||||
return CURLE_READ_ERROR;
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
if(ret <= 0) {
|
||||
return result;
|
||||
|
|
@ -873,6 +873,12 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void bearssl_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
(void)idsize;
|
||||
free(sessionid);
|
||||
}
|
||||
|
||||
static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -896,7 +902,6 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
|
|||
|
||||
if(ssl_config->primary.sessionid) {
|
||||
bool incache;
|
||||
bool added = FALSE;
|
||||
void *oldsession;
|
||||
br_ssl_session_parameters *session;
|
||||
|
||||
|
|
@ -909,13 +914,12 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
|
|||
&oldsession, NULL));
|
||||
if(incache)
|
||||
Curl_ssl_delsessionid(data, oldsession);
|
||||
ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0, &added);
|
||||
|
||||
ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0,
|
||||
bearssl_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
if(!added)
|
||||
free(session);
|
||||
if(ret) {
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
if(ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
connssl->connecting_state = ssl_connect_done;
|
||||
|
|
@ -1174,11 +1178,6 @@ static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
}
|
||||
}
|
||||
|
||||
static void bearssl_session_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static CURLcode bearssl_sha256sum(const unsigned char *input,
|
||||
size_t inputlen,
|
||||
unsigned char *sha256sum,
|
||||
|
|
@ -1211,7 +1210,6 @@ const struct Curl_ssl Curl_ssl_bearssl = {
|
|||
bearssl_get_internals, /* get_internals */
|
||||
bearssl_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
bearssl_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
147
lib/vtls/gtls.c
147
lib/vtls/gtls.c
|
|
@ -532,6 +532,88 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void gtls_sessionid_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
(void)idsize;
|
||||
free(sessionid);
|
||||
}
|
||||
|
||||
static CURLcode gtls_update_session_id(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
gnutls_session_t session)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ssl_config->primary.sessionid) {
|
||||
/* we always unconditionally get the session id here, as even if we
|
||||
already got it from the cache and asked to use it in the connection, it
|
||||
might've been rejected and then a new one is in use now and we need to
|
||||
detect that. */
|
||||
void *connect_sessionid;
|
||||
size_t connect_idsize = 0;
|
||||
|
||||
/* get the session ID data size */
|
||||
gnutls_session_get_data(session, NULL, &connect_idsize);
|
||||
connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
|
||||
if(!connect_sessionid) {
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
bool incache;
|
||||
void *ssl_sessionid;
|
||||
|
||||
/* extract session ID to the allocated buffer */
|
||||
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
|
||||
|
||||
DEBUGF(infof(data, "get session id (len=%zu) and store in cache",
|
||||
connect_idsize));
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
|
||||
&ssl_sessionid, NULL));
|
||||
if(incache) {
|
||||
/* there was one before in the cache, so instead of risking that the
|
||||
previous one was rejected, we just kill that and store the new */
|
||||
Curl_ssl_delsessionid(data, ssl_sessionid);
|
||||
}
|
||||
|
||||
/* store this session id, takes ownership */
|
||||
result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
|
||||
connect_sessionid, connect_idsize,
|
||||
gtls_sessionid_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
|
||||
unsigned when, unsigned int incoming,
|
||||
const gnutls_datum_t *msg)
|
||||
{
|
||||
struct Curl_cfilter *cf = gnutls_session_get_ptr(session);
|
||||
|
||||
(void)msg;
|
||||
(void)incoming;
|
||||
if(when) { /* after message has been processed */
|
||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||
if(data) {
|
||||
DEBUGF(infof(data, "handshake: %s message type %d",
|
||||
incoming? "incoming" : "outgoing", htype));
|
||||
switch(htype) {
|
||||
case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
|
||||
gtls_update_session_id(cf, data, session);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CURLcode gtls_client_init(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
|
|
@ -828,10 +910,13 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
|||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) {
|
||||
/* we got a session id, use it! */
|
||||
gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
|
||||
int rc;
|
||||
|
||||
/* Informational message */
|
||||
infof(data, "SSL reusing session ID");
|
||||
rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
|
||||
if(rc < 0)
|
||||
infof(data, "SSL failed to set session ID");
|
||||
else
|
||||
infof(data, "SSL reusing session ID (size=%zu)", ssl_idsize);
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
|
|
@ -869,6 +954,9 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
if(result)
|
||||
return result;
|
||||
|
||||
gnutls_handshake_set_hook_function(backend->gtls.session,
|
||||
GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST,
|
||||
gtls_handshake_cb);
|
||||
|
||||
/* register callback functions and handle to send and receive data. */
|
||||
gnutls_transport_set_ptr(backend->gtls.session, cf);
|
||||
|
|
@ -1403,49 +1491,10 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
|
|||
Curl_alpn_set_negotiated(cf, data, NULL, 0);
|
||||
}
|
||||
|
||||
if(ssl_config->primary.sessionid) {
|
||||
/* we always unconditionally get the session id here, as even if we
|
||||
already got it from the cache and asked to use it in the connection, it
|
||||
might've been rejected and then a new one is in use now and we need to
|
||||
detect that. */
|
||||
void *connect_sessionid;
|
||||
size_t connect_idsize = 0;
|
||||
|
||||
/* get the session ID data size */
|
||||
gnutls_session_get_data(session, NULL, &connect_idsize);
|
||||
connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
|
||||
|
||||
if(connect_sessionid) {
|
||||
bool incache;
|
||||
bool added = FALSE;
|
||||
void *ssl_sessionid;
|
||||
|
||||
/* extract session ID to the allocated buffer */
|
||||
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
|
||||
&ssl_sessionid, NULL));
|
||||
if(incache) {
|
||||
/* there was one before in the cache, so instead of risking that the
|
||||
previous one was rejected, we just kill that and store the new */
|
||||
Curl_ssl_delsessionid(data, ssl_sessionid);
|
||||
}
|
||||
|
||||
/* store this session id */
|
||||
result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
|
||||
connect_sessionid, connect_idsize,
|
||||
&added);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
if(!added)
|
||||
free(connect_sessionid);
|
||||
if(result) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
/* Only on TLSv1.2 or lower do we have the session id now. For
|
||||
* TLSv1.3 we get it via a SESSION_TICKET message that arrives later. */
|
||||
if(gnutls_protocol_get_version(session) < GNUTLS_TLS1_3)
|
||||
result = gtls_update_session_id(cf, data, session);
|
||||
|
||||
out:
|
||||
return result;
|
||||
|
|
@ -1734,11 +1783,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void gtls_session_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static size_t gtls_version(char *buffer, size_t size)
|
||||
{
|
||||
return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
|
||||
|
|
@ -1805,7 +1849,6 @@ const struct Curl_ssl Curl_ssl_gnutls = {
|
|||
gtls_get_internals, /* get_internals */
|
||||
gtls_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
gtls_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -1033,6 +1033,13 @@ pinnedpubkey_error:
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void mbedtls_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
(void)idsize;
|
||||
mbedtls_ssl_session_free(sessionid);
|
||||
free(sessionid);
|
||||
}
|
||||
|
||||
static CURLcode
|
||||
mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -1049,7 +1056,6 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
int ret;
|
||||
mbedtls_ssl_session *our_ssl_sessionid;
|
||||
void *old_ssl_sessionid = NULL;
|
||||
bool added = FALSE;
|
||||
|
||||
our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session));
|
||||
if(!our_ssl_sessionid)
|
||||
|
|
@ -1073,16 +1079,11 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
Curl_ssl_delsessionid(data, old_ssl_sessionid);
|
||||
|
||||
retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer,
|
||||
our_ssl_sessionid, 0, &added);
|
||||
our_ssl_sessionid, 0,
|
||||
mbedtls_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
if(!added) {
|
||||
mbedtls_ssl_session_free(our_ssl_sessionid);
|
||||
free(our_ssl_sessionid);
|
||||
}
|
||||
if(retcode) {
|
||||
failf(data, "failed to store ssl session");
|
||||
if(retcode)
|
||||
return retcode;
|
||||
}
|
||||
}
|
||||
|
||||
connssl->connecting_state = ssl_connect_done;
|
||||
|
|
@ -1176,12 +1177,6 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
return len;
|
||||
}
|
||||
|
||||
static void mbedtls_session_free(void *ptr)
|
||||
{
|
||||
mbedtls_ssl_session_free(ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static size_t mbedtls_version(char *buffer, size_t size)
|
||||
{
|
||||
#ifdef MBEDTLS_VERSION_C
|
||||
|
|
@ -1460,7 +1455,6 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
|
|||
mbedtls_get_internals, /* get_internals */
|
||||
mbedtls_close, /* close_one */
|
||||
mbedtls_close_all, /* close_all */
|
||||
mbedtls_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -2074,10 +2074,11 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void ossl_session_free(void *ptr)
|
||||
static void ossl_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
/* free the ID */
|
||||
SSL_SESSION_free(ptr);
|
||||
(void)idsize;
|
||||
SSL_SESSION_free(sessionid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2935,52 +2936,45 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
|
|||
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
SSL_SESSION *ssl_sessionid)
|
||||
SSL_SESSION *session)
|
||||
{
|
||||
const struct ssl_config_data *config;
|
||||
bool isproxy;
|
||||
CURLcode result = CURLE_WRITE_ERROR;
|
||||
bool added = FALSE;
|
||||
|
||||
if(!cf || !data)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
isproxy = Curl_ssl_cf_is_proxy(cf);
|
||||
|
||||
config = Curl_ssl_cf_get_config(cf, data);
|
||||
if(config->primary.sessionid) {
|
||||
bool incache;
|
||||
bool added = FALSE;
|
||||
void *old_ssl_sessionid = NULL;
|
||||
void *old_session = NULL;
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(isproxy)
|
||||
incache = FALSE;
|
||||
else
|
||||
incache = !(Curl_ssl_getsessionid(cf, data, peer,
|
||||
&old_ssl_sessionid, NULL));
|
||||
if(incache) {
|
||||
if(old_ssl_sessionid != ssl_sessionid) {
|
||||
infof(data, "old SSL session ID is stale, removing");
|
||||
Curl_ssl_delsessionid(data, old_ssl_sessionid);
|
||||
incache = FALSE;
|
||||
}
|
||||
&old_session, NULL));
|
||||
if(incache && (old_session != session)) {
|
||||
infof(data, "old SSL session ID is stale, removing");
|
||||
Curl_ssl_delsessionid(data, old_session);
|
||||
incache = FALSE;
|
||||
}
|
||||
|
||||
if(!incache) {
|
||||
if(!Curl_ssl_addsessionid(cf, data, peer, ssl_sessionid,
|
||||
0 /* unknown size */, &added)) {
|
||||
if(added) {
|
||||
/* the session has been put into the session cache */
|
||||
result = CURLE_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
failf(data, "failed to store ssl session");
|
||||
added = TRUE;
|
||||
Curl_ssl_addsessionid(cf, data, peer, session, 0, ossl_session_free);
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
out:
|
||||
if(!added)
|
||||
ossl_session_free(session, 0);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* The "new session" callback must return zero if the session can be removed
|
||||
|
|
@ -2991,13 +2985,12 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
|
|||
struct Curl_cfilter *cf;
|
||||
struct Curl_easy *data;
|
||||
struct ssl_connect_data *connssl;
|
||||
CURLcode result;
|
||||
|
||||
cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
|
||||
connssl = cf? cf->ctx : NULL;
|
||||
data = connssl? CF_DATA_CURRENT(cf) : NULL;
|
||||
result = Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
|
||||
return result? 0 : 1;
|
||||
Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static CURLcode load_cacert_from_memory(X509_STORE *store,
|
||||
|
|
@ -5291,7 +5284,6 @@ const struct Curl_ssl Curl_ssl_openssl = {
|
|||
ossl_get_internals, /* get_internals */
|
||||
ossl_close, /* close_one */
|
||||
ossl_close_all, /* close_all */
|
||||
ossl_session_free, /* session_free */
|
||||
ossl_set_engine, /* set_engine */
|
||||
ossl_set_engine_default, /* set_engine_default */
|
||||
ossl_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
|
|||
SSL_CTX *ssl_ctx);
|
||||
|
||||
/*
|
||||
* Add a new session to the cache.
|
||||
* Add a new session to the cache. Takes ownership of the session.
|
||||
*/
|
||||
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
|
|
|
|||
|
|
@ -742,7 +742,6 @@ const struct Curl_ssl Curl_ssl_rustls = {
|
|||
cr_get_internals, /* get_internals */
|
||||
cr_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
Curl_none_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -1675,6 +1675,28 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order,
|
|||
return args->result == CURLE_OK;
|
||||
}
|
||||
|
||||
static void schannel_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
/* this is expected to be called under sessionid lock */
|
||||
struct Curl_schannel_cred *cred = sessionid;
|
||||
|
||||
(void)idsize;
|
||||
if(cred) {
|
||||
cred->refcount--;
|
||||
if(cred->refcount == 0) {
|
||||
s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
|
||||
curlx_unicodefree(cred->sni_hostname);
|
||||
#ifdef HAS_CLIENT_CERT_PATH
|
||||
if(cred->client_cert_store) {
|
||||
CertCloseStore(cred->client_cert_store, 0);
|
||||
cred->client_cert_store = NULL;
|
||||
}
|
||||
#endif
|
||||
Curl_safefree(cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode
|
||||
schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -1752,7 +1774,6 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
/* save the current session data for possible reuse */
|
||||
if(ssl_config->primary.sessionid) {
|
||||
bool incache;
|
||||
bool added = FALSE;
|
||||
struct Curl_schannel_cred *old_cred = NULL;
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
|
|
@ -1768,20 +1789,15 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
}
|
||||
}
|
||||
if(!incache) {
|
||||
/* Up ref count since call takes ownership */
|
||||
backend->cred->refcount++;
|
||||
result = Curl_ssl_addsessionid(cf, data, &connssl->peer, backend->cred,
|
||||
sizeof(struct Curl_schannel_cred),
|
||||
&added);
|
||||
schannel_session_free);
|
||||
if(result) {
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
failf(data, "schannel: failed to store credential handle");
|
||||
return result;
|
||||
}
|
||||
else if(added) {
|
||||
/* this cred session is now also referenced by sessionid cache */
|
||||
backend->cred->refcount++;
|
||||
DEBUGF(infof(data,
|
||||
"schannel: stored credential handle in session cache"));
|
||||
}
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
|
|
@ -2456,27 +2472,6 @@ static bool schannel_data_pending(struct Curl_cfilter *cf,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void schannel_session_free(void *ptr)
|
||||
{
|
||||
/* this is expected to be called under sessionid lock */
|
||||
struct Curl_schannel_cred *cred = ptr;
|
||||
|
||||
if(cred) {
|
||||
cred->refcount--;
|
||||
if(cred->refcount == 0) {
|
||||
s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
|
||||
curlx_unicodefree(cred->sni_hostname);
|
||||
#ifdef HAS_CLIENT_CERT_PATH
|
||||
if(cred->client_cert_store) {
|
||||
CertCloseStore(cred->client_cert_store, 0);
|
||||
cred->client_cert_store = NULL;
|
||||
}
|
||||
#endif
|
||||
Curl_safefree(cred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* shut down the SSL connection and clean up related memory.
|
||||
this function can be called multiple times on the same connection including
|
||||
if the SSL connection failed (eg connection made but failed handshake). */
|
||||
|
|
@ -2560,7 +2555,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf,
|
|||
/* free SSPI Schannel API credential handle */
|
||||
if(backend->cred) {
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
schannel_session_free(backend->cred);
|
||||
schannel_session_free(backend->cred, 0);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
backend->cred = NULL;
|
||||
}
|
||||
|
|
@ -2923,7 +2918,6 @@ const struct Curl_ssl Curl_ssl_schannel = {
|
|||
schannel_get_internals, /* get_internals */
|
||||
schannel_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
schannel_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -1636,6 +1636,18 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data,
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void sectransp_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
|
||||
cached session ID inside the Security framework. There is a private
|
||||
function that does this, but I don't want to have to explain to you why I
|
||||
got your application rejected from the App Store due to the use of a
|
||||
private API, so the best we can do is free up our own char array that we
|
||||
created way back in sectransp_connect_step1... */
|
||||
(void)idsize;
|
||||
Curl_safefree(sessionid);
|
||||
}
|
||||
|
||||
static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -2078,12 +2090,11 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
|
|||
}
|
||||
|
||||
result = Curl_ssl_addsessionid(cf, data, &connssl->peer, ssl_sessionid,
|
||||
ssl_sessionid_len, NULL);
|
||||
ssl_sessionid_len,
|
||||
sectransp_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
if(result) {
|
||||
failf(data, "failed to store ssl session");
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3225,17 +3236,6 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void sectransp_session_free(void *ptr)
|
||||
{
|
||||
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
|
||||
cached session ID inside the Security framework. There is a private
|
||||
function that does this, but I don't want to have to explain to you why I
|
||||
got your application rejected from the App Store due to the use of a
|
||||
private API, so the best we can do is free up our own char array that we
|
||||
created way back in sectransp_connect_step1... */
|
||||
Curl_safefree(ptr);
|
||||
}
|
||||
|
||||
static size_t sectransp_version(char *buffer, size_t size)
|
||||
{
|
||||
return msnprintf(buffer, size, "SecureTransport");
|
||||
|
|
@ -3469,7 +3469,6 @@ const struct Curl_ssl Curl_ssl_sectransp = {
|
|||
sectransp_get_internals, /* get_internals */
|
||||
sectransp_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
sectransp_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -605,9 +605,10 @@ void Curl_ssl_kill_session(struct Curl_ssl_session *session)
|
|||
/* defensive check */
|
||||
|
||||
/* free the ID the SSL-layer specific way */
|
||||
Curl_ssl->session_free(session->sessionid);
|
||||
session->sessionid_free(session->sessionid, session->idsize);
|
||||
|
||||
session->sessionid = NULL;
|
||||
session->sessionid_free = NULL;
|
||||
session->age = 0; /* fresh */
|
||||
|
||||
Curl_free_primary_ssl_config(&session->ssl_config);
|
||||
|
|
@ -645,42 +646,41 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
|
|||
const struct ssl_peer *peer,
|
||||
void *ssl_sessionid,
|
||||
size_t idsize,
|
||||
bool *added)
|
||||
Curl_ssl_sessionid_dtor *sessionid_free_cb)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
||||
size_t i;
|
||||
struct Curl_ssl_session *store;
|
||||
long oldest_age;
|
||||
char *clone_host;
|
||||
char *clone_conn_to_host;
|
||||
char *clone_host = NULL;
|
||||
char *clone_conn_to_host = NULL;
|
||||
int conn_to_port;
|
||||
long *general_age;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(added)
|
||||
*added = FALSE;
|
||||
DEBUGASSERT(ssl_sessionid);
|
||||
DEBUGASSERT(sessionid_free_cb);
|
||||
|
||||
if(!data->state.session)
|
||||
if(!data->state.session) {
|
||||
sessionid_free_cb(ssl_sessionid, idsize);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
store = &data->state.session[0];
|
||||
oldest_age = data->state.session[0].age; /* zero if unused */
|
||||
(void)ssl_config;
|
||||
DEBUGASSERT(ssl_config->primary.sessionid);
|
||||
(void)ssl_config;
|
||||
|
||||
clone_host = strdup(peer->hostname);
|
||||
if(!clone_host)
|
||||
return CURLE_OUT_OF_MEMORY; /* bail out */
|
||||
goto out;
|
||||
|
||||
if(cf->conn->bits.conn_to_host) {
|
||||
clone_conn_to_host = strdup(cf->conn->conn_to_host.name);
|
||||
if(!clone_conn_to_host) {
|
||||
free(clone_host);
|
||||
return CURLE_OUT_OF_MEMORY; /* bail out */
|
||||
}
|
||||
if(!clone_conn_to_host)
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
clone_conn_to_host = NULL;
|
||||
|
||||
if(cf->conn->bits.conn_to_port)
|
||||
conn_to_port = cf->conn->conn_to_port;
|
||||
|
|
@ -713,34 +713,43 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
|
|||
store = &data->state.session[i]; /* use this slot */
|
||||
|
||||
/* now init the session struct wisely */
|
||||
if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
|
||||
Curl_free_primary_ssl_config(&store->ssl_config);
|
||||
store->sessionid = NULL; /* let caller free sessionid */
|
||||
goto out;
|
||||
}
|
||||
store->sessionid = ssl_sessionid;
|
||||
store->idsize = idsize;
|
||||
store->sessionid_free = sessionid_free_cb;
|
||||
store->age = *general_age; /* set current age */
|
||||
/* free it if there's one already present */
|
||||
free(store->name);
|
||||
free(store->conn_to_host);
|
||||
store->name = clone_host; /* clone host name */
|
||||
clone_host = NULL;
|
||||
store->conn_to_host = clone_conn_to_host; /* clone connect to host name */
|
||||
clone_conn_to_host = NULL;
|
||||
store->conn_to_port = conn_to_port; /* connect to port number */
|
||||
/* port number */
|
||||
store->remote_port = peer->port;
|
||||
store->scheme = cf->conn->handler->scheme;
|
||||
store->transport = peer->transport;
|
||||
|
||||
if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
|
||||
Curl_free_primary_ssl_config(&store->ssl_config);
|
||||
store->sessionid = NULL; /* let caller free sessionid */
|
||||
free(clone_host);
|
||||
free(clone_conn_to_host);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
free(clone_host);
|
||||
free(clone_conn_to_host);
|
||||
if(result) {
|
||||
failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]",
|
||||
store->scheme, store->name, store->remote_port,
|
||||
Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
|
||||
sessionid_free_cb(ssl_sessionid, idsize);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(added)
|
||||
*added = TRUE;
|
||||
|
||||
DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]",
|
||||
store->scheme, store->name, store->remote_port,
|
||||
Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"));
|
||||
CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]",
|
||||
store->scheme, store->name, store->remote_port,
|
||||
Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
@ -1323,7 +1332,6 @@ static const struct Curl_ssl Curl_ssl_multi = {
|
|||
multissl_get_internals, /* get_internals */
|
||||
multissl_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
Curl_none_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ struct Curl_ssl {
|
|||
void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info);
|
||||
void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data);
|
||||
void (*close_all)(struct Curl_easy *data);
|
||||
void (*session_free)(void *ptr);
|
||||
|
||||
CURLcode (*set_engine)(struct Curl_easy *data, const char *engine);
|
||||
CURLcode (*set_engine_default)(struct Curl_easy *data);
|
||||
|
|
@ -186,13 +185,15 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
|
|||
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
|
||||
* Caller must ensure that it has properly shared ownership of this sessionid
|
||||
* object with cache (e.g. incrementing refcount on success)
|
||||
* Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb`
|
||||
* to destroy it in case of failure or later removal.
|
||||
*/
|
||||
CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
void *ssl_sessionid,
|
||||
size_t idsize,
|
||||
bool *added);
|
||||
Curl_ssl_sessionid_dtor *sessionid_free_cb);
|
||||
|
||||
#include "openssl.h" /* OpenSSL versions */
|
||||
#include "gtls.h" /* GnuTLS versions */
|
||||
|
|
|
|||
|
|
@ -1058,6 +1058,13 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
}
|
||||
|
||||
|
||||
static void wolfssl_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
(void)idsize;
|
||||
wolfSSL_SESSION_free(sessionid);
|
||||
}
|
||||
|
||||
|
||||
static CURLcode
|
||||
wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -1071,42 +1078,27 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
DEBUGASSERT(backend);
|
||||
|
||||
if(ssl_config->primary.sessionid) {
|
||||
bool incache;
|
||||
bool added = FALSE;
|
||||
void *old_ssl_sessionid = NULL;
|
||||
/* wolfSSL_get1_session allocates memory that has to be freed. */
|
||||
WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle);
|
||||
|
||||
if(our_ssl_sessionid) {
|
||||
void *old_ssl_sessionid = NULL;
|
||||
bool incache;
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
|
||||
&old_ssl_sessionid, NULL));
|
||||
if(incache) {
|
||||
if(old_ssl_sessionid != our_ssl_sessionid) {
|
||||
infof(data, "old SSL session ID is stale, removing");
|
||||
Curl_ssl_delsessionid(data, old_ssl_sessionid);
|
||||
incache = FALSE;
|
||||
}
|
||||
Curl_ssl_delsessionid(data, old_ssl_sessionid);
|
||||
}
|
||||
|
||||
if(!incache) {
|
||||
result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
|
||||
our_ssl_sessionid, 0, NULL);
|
||||
if(result) {
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
wolfSSL_SESSION_free(our_ssl_sessionid);
|
||||
failf(data, "failed to store ssl session");
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
added = TRUE;
|
||||
}
|
||||
}
|
||||
/* call takes ownership of `our_ssl_sessionid` */
|
||||
result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
|
||||
our_ssl_sessionid, 0,
|
||||
wolfssl_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
|
||||
if(!added) {
|
||||
/* If the session info wasn't added to the cache, free our copy. */
|
||||
wolfSSL_SESSION_free(our_ssl_sessionid);
|
||||
if(result) {
|
||||
failf(data, "failed to store ssl session");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1240,12 +1232,6 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
|
|||
}
|
||||
|
||||
|
||||
static void wolfssl_session_free(void *ptr)
|
||||
{
|
||||
wolfSSL_SESSION_free(ptr);
|
||||
}
|
||||
|
||||
|
||||
static size_t wolfssl_version(char *buffer, size_t size)
|
||||
{
|
||||
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
|
||||
|
|
@ -1525,7 +1511,6 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
|
|||
wolfssl_get_internals, /* get_internals */
|
||||
wolfssl_close, /* close_one */
|
||||
Curl_none_close_all, /* close_all */
|
||||
wolfssl_session_free, /* session_free */
|
||||
Curl_none_set_engine, /* set_engine */
|
||||
Curl_none_set_engine_default, /* set_engine_default */
|
||||
Curl_none_engines_list, /* engines_list */
|
||||
|
|
|
|||
104
tests/http/test_17_ssl_use.py
Normal file
104
tests/http/test_17_ssl_use.py
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# This software is licensed as described in the file COPYING, which
|
||||
# you should have received as part of this distribution. The terms
|
||||
# are also available at https://curl.se/docs/copyright.html.
|
||||
#
|
||||
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
# copies of the Software, and permit persons to whom the Software is
|
||||
# furnished to do so, under the terms of the COPYING file.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
#
|
||||
import difflib
|
||||
import filecmp
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import timedelta
|
||||
import pytest
|
||||
|
||||
from testenv import Env, CurlClient, LocalClient, ExecResult
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestSSLUse:
|
||||
|
||||
@pytest.fixture(autouse=True, scope='class')
|
||||
def _class_scope(self, env, httpd, nghttpx):
|
||||
if env.have_h3():
|
||||
nghttpx.start_if_needed()
|
||||
httpd.clear_extra_configs()
|
||||
httpd.reload()
|
||||
|
||||
def test_17_01_sslinfo_plain(self, env: Env, httpd, nghttpx, repeat):
|
||||
proto = 'http/1.1'
|
||||
curl = CurlClient(env=env)
|
||||
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/sslinfo'
|
||||
r = curl.http_get(url=url, alpn_proto=proto)
|
||||
assert r.json['HTTPS'] == 'on', f'{r.json}'
|
||||
assert 'SSL_SESSION_ID' in r.json, f'{r.json}'
|
||||
assert 'SSL_SESSION_RESUMED' in r.json, f'{r.json}'
|
||||
assert r.json['SSL_SESSION_RESUMED'] == 'Initial', f'{r.json}'
|
||||
|
||||
@pytest.mark.parametrize("tls_max", ['1.2', '1.3'])
|
||||
def test_17_02_sslinfo_reconnect(self, env: Env, httpd, nghttpx, tls_max, repeat):
|
||||
proto = 'http/1.1'
|
||||
count = 3
|
||||
exp_resumed = 'Resumed'
|
||||
xargs = ['--sessionid', '--tls-max', tls_max, f'--tlsv{tls_max}']
|
||||
if env.curl_uses_lib('gnutls'):
|
||||
if tls_max == '1.3':
|
||||
exp_resumed = 'Initial' # 1.2 works in gnutls, but 1.3 does not, TODO
|
||||
if env.curl_uses_lib('libressl'):
|
||||
if tls_max == '1.3':
|
||||
exp_resumed = 'Initial' # 1.2 works in libressl, but 1.3 does not, TODO
|
||||
if env.curl_uses_lib('wolfssl'):
|
||||
xargs = ['--sessionid', f'--tlsv{tls_max}']
|
||||
if tls_max == '1.3':
|
||||
exp_resumed = 'Initial' # 1.2 works in wolfssl, but 1.3 does not, TODO
|
||||
if env.curl_uses_lib('rustls-ffi'):
|
||||
exp_resumed = 'Initial' # rustls does not support sessions, TODO
|
||||
if env.curl_uses_lib('bearssl') and tls_max == '1.3':
|
||||
pytest.skip('BearSSL does not support TLSv1.3')
|
||||
if env.curl_uses_lib('mbedtls') and tls_max == '1.3':
|
||||
pytest.skip('mbedtls does not support TLSv1.3')
|
||||
|
||||
curl = CurlClient(env=env)
|
||||
# tell the server to close the connection after each request
|
||||
urln = f'https://{env.authority_for(env.domain1, proto)}/curltest/sslinfo?'\
|
||||
f'id=[0-{count-1}]&close'
|
||||
r = curl.http_download(urls=[urln], alpn_proto=proto, with_stats=True,
|
||||
extra_args=xargs)
|
||||
r.check_response(count=count, http_status=200)
|
||||
# should have used one connection for each request, sessions after
|
||||
# first should have been resumed
|
||||
assert r.total_connects == count, r.dump_logs()
|
||||
for i in range(count):
|
||||
dfile = curl.download_file(i)
|
||||
assert os.path.exists(dfile)
|
||||
with open(dfile) as f:
|
||||
djson = json.load(f)
|
||||
assert djson['HTTPS'] == 'on', f'{i}: {djson}'
|
||||
if i == 0:
|
||||
assert djson['SSL_SESSION_RESUMED'] == 'Initial', f'{i}: {djson}'
|
||||
else:
|
||||
assert djson['SSL_SESSION_RESUMED'] == exp_resumed, f'{i}: {djson}'
|
||||
|
||||
|
||||
|
|
@ -415,9 +415,15 @@ class CurlClient:
|
|||
return xargs
|
||||
|
||||
def http_get(self, url: str, extra_args: Optional[List[str]] = None,
|
||||
def_tracing: bool = True, with_profile: bool = False):
|
||||
return self._raw(url, options=extra_args, with_stats=False,
|
||||
def_tracing=def_tracing, with_profile=with_profile)
|
||||
alpn_proto: Optional[str] = None,
|
||||
def_tracing: bool = True,
|
||||
with_stats: bool = False,
|
||||
with_profile: bool = False):
|
||||
return self._raw(url, options=extra_args,
|
||||
with_stats=with_stats,
|
||||
alpn_proto=alpn_proto,
|
||||
def_tracing=def_tracing,
|
||||
with_profile=with_profile)
|
||||
|
||||
def http_download(self, urls: List[str],
|
||||
alpn_proto: Optional[str] = None,
|
||||
|
|
|
|||
|
|
@ -397,6 +397,10 @@ class Httpd:
|
|||
f' Redirect 302 /curltest/echo302 /curltest/echo',
|
||||
f' Redirect 303 /curltest/echo303 /curltest/echo',
|
||||
f' Redirect 307 /curltest/echo307 /curltest/echo',
|
||||
f' <Location /curltest/sslinfo>',
|
||||
f' SSLOptions StdEnvVars',
|
||||
f' SetHandler curltest-sslinfo',
|
||||
f' </Location>',
|
||||
f' <Location /curltest/echo>',
|
||||
f' SetHandler curltest-echo',
|
||||
f' </Location>',
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ static int curltest_echo_handler(request_rec *r);
|
|||
static int curltest_put_handler(request_rec *r);
|
||||
static int curltest_tweak_handler(request_rec *r);
|
||||
static int curltest_1_1_required(request_rec *r);
|
||||
static int curltest_sslinfo_handler(request_rec *r);
|
||||
|
||||
AP_DECLARE_MODULE(curltest) = {
|
||||
STANDARD20_MODULE_STUFF,
|
||||
|
|
@ -88,6 +89,7 @@ static void curltest_hooks(apr_pool_t *pool)
|
|||
ap_hook_handler(curltest_put_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_handler(curltest_tweak_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_handler(curltest_1_1_required, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
ap_hook_handler(curltest_sslinfo_handler, NULL, NULL, APR_HOOK_MIDDLE);
|
||||
}
|
||||
|
||||
#define SECS_PER_HOUR (60*60)
|
||||
|
|
@ -628,3 +630,113 @@ cleanup:
|
|||
}
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
static int brigade_env_var(request_rec *r, apr_bucket_brigade *bb,
|
||||
const char *name)
|
||||
{
|
||||
const char *s;
|
||||
s = apr_table_get(r->subprocess_env, name);
|
||||
if(s)
|
||||
return apr_brigade_printf(bb, NULL, NULL, ",\n \"%s\": \"%s\"", name, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int curltest_sslinfo_handler(request_rec *r)
|
||||
{
|
||||
conn_rec *c = r->connection;
|
||||
apr_bucket_brigade *bb;
|
||||
apr_bucket *b;
|
||||
apr_status_t rv;
|
||||
apr_array_header_t *args = NULL;
|
||||
const char *request_id = NULL;
|
||||
int close_conn = 0;
|
||||
long l;
|
||||
int i;
|
||||
|
||||
if(strcmp(r->handler, "curltest-sslinfo")) {
|
||||
return DECLINED;
|
||||
}
|
||||
if(r->method_number != M_GET) {
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
if(r->args) {
|
||||
apr_array_header_t *args = apr_cstr_split(r->args, "&", 1, r->pool);
|
||||
for(i = 0; i < args->nelts; ++i) {
|
||||
char *s, *val, *arg = APR_ARRAY_IDX(args, i, char*);
|
||||
s = strchr(arg, '=');
|
||||
if(s) {
|
||||
*s = '\0';
|
||||
val = s + 1;
|
||||
if(!strcmp("id", arg)) {
|
||||
/* just an id for repeated requests with curl's url globbing */
|
||||
request_id = val;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(!strcmp("close", arg)) {
|
||||
/* we are asked to close the connection */
|
||||
close_conn = 1;
|
||||
continue;
|
||||
}
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "query parameter not "
|
||||
"understood: '%s' in %s",
|
||||
arg, r->args);
|
||||
ap_die(HTTP_BAD_REQUEST, r);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "sslinfo: processing");
|
||||
r->status = 200;
|
||||
r->clength = -1;
|
||||
r->chunked = 1;
|
||||
apr_table_unset(r->headers_out, "Content-Length");
|
||||
/* Discourage content-encodings */
|
||||
apr_table_unset(r->headers_out, "Content-Encoding");
|
||||
apr_table_setn(r->subprocess_env, "no-brotli", "1");
|
||||
apr_table_setn(r->subprocess_env, "no-gzip", "1");
|
||||
|
||||
ap_set_content_type(r, "application/json");
|
||||
|
||||
bb = apr_brigade_create(r->pool, c->bucket_alloc);
|
||||
|
||||
apr_brigade_puts(bb, NULL, NULL, "{\n \"Name\": \"SSL-Information\"");
|
||||
brigade_env_var(r, bb, "HTTPS");
|
||||
brigade_env_var(r, bb, "SSL_PROTOCOL");
|
||||
brigade_env_var(r, bb, "SSL_CIPHER");
|
||||
brigade_env_var(r, bb, "SSL_SESSION_ID");
|
||||
brigade_env_var(r, bb, "SSL_SESSION_RESUMED");
|
||||
brigade_env_var(r, bb, "SSL_SRP_USER");
|
||||
brigade_env_var(r, bb, "SSL_SRP_USERINFO");
|
||||
apr_brigade_puts(bb, NULL, NULL, "}\n");
|
||||
|
||||
/* flush response */
|
||||
b = apr_bucket_flush_create(c->bucket_alloc);
|
||||
APR_BRIGADE_INSERT_TAIL(bb, b);
|
||||
rv = ap_pass_brigade(r->output_filters, bb);
|
||||
if (APR_SUCCESS != rv) goto cleanup;
|
||||
|
||||
/* we are done */
|
||||
b = apr_bucket_eos_create(c->bucket_alloc);
|
||||
APR_BRIGADE_INSERT_TAIL(bb, b);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "1_1_handler: request read");
|
||||
|
||||
rv = ap_pass_brigade(r->output_filters, bb);
|
||||
|
||||
cleanup:
|
||||
if(close_conn)
|
||||
r->connection->keepalive = AP_CONN_CLOSE;
|
||||
if(rv == APR_SUCCESS
|
||||
|| r->status != HTTP_OK
|
||||
|| c->aborted) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler: done");
|
||||
return OK;
|
||||
}
|
||||
else {
|
||||
/* no way to know what type of error occurred */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, "1_1_handler failed");
|
||||
return AP_FILTER_ERROR;
|
||||
}
|
||||
return DECLINED;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue