mirror of
https://github.com/curl/curl.git
synced 2025-04-12 16:20:35 +08:00
lib: TLS session ticket caching reworked
Described in detail in internal doc TLS-SESSIONS.md Main points: - use a new `ssl_peer_key` for cache lookups by connection filters - recognize differences between TLSv1.3 and other tickets * TLSv1.3 tickets are single-use, cache can hold several of them for a peer * TLSv1.2 are reused, keep only a single one per peer - differentiate between ticket BLOB to store (that could be persisted) and object instances - use put/take/return pattern for cache access - remember TLS version, ALPN protocol, time received and lifetime of ticket - auto-expire tickets after their lifetime Closes #15774
This commit is contained in:
parent
e5e2e09a75
commit
fa0ccd9f1f
@ -57,6 +57,7 @@ INTERNALDOCS = \
|
||||
internals/README.md \
|
||||
internals/SPLAY.md \
|
||||
internals/STRPARSE.md \
|
||||
internals/TLS-SESSIONS.md \
|
||||
internals/WEBSOCKET.md
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -132,3 +132,14 @@ CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t len);
|
||||
Sets the new shorter length of the buffer in number of bytes. Keeps the
|
||||
leftmost set number of bytes, discards the rest. To instead keep the tail part
|
||||
of the buffer, see `Curl_dyn_tail()`.
|
||||
|
||||
## `Curl_dyn_take`
|
||||
|
||||
```c
|
||||
char *Curl_dyn_take(struct dynbuf *s, size_t *plen);
|
||||
```
|
||||
|
||||
Transfers ownership of the internal buffer to the caller. The dynbuf
|
||||
resets to its initial state. The returned pointer may be `NULL` if the
|
||||
dynbuf never allocated memory. The returned length is the amount of
|
||||
data written to the buffer. The actual allocated memory might be larger.
|
||||
|
@ -64,7 +64,13 @@ See also `Curl_llist_insert_next`.
|
||||
|
||||
## Remove a node
|
||||
|
||||
Remove a node again from a list by calling `Curl_llist_remove()`.
|
||||
Remove a node again from a list by calling `Curl_llist_remove()`. This
|
||||
will destroy the node's `elem` (e.g. calling a registered free function).
|
||||
|
||||
To remove a node without destroying it's `elem`, use
|
||||
`Curl_node_take_elem()` which returns the `elem` pointer and
|
||||
removes the node from the list. The caller then owns this pointer
|
||||
and has to take care of it.
|
||||
|
||||
## Iterate
|
||||
|
||||
|
168
docs/internals/TLS-SESSIONS.md
Normal file
168
docs/internals/TLS-SESSIONS.md
Normal file
@ -0,0 +1,168 @@
|
||||
<!--
|
||||
Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
|
||||
SPDX-License-Identifier: curl
|
||||
-->
|
||||
|
||||
# TLS Sessions and Tickets
|
||||
|
||||
The TLS protocol offers methods of "resuming" a previous "session". A
|
||||
TLS "session" is a negotiated security context across a connection
|
||||
(which may be via TCP or UDP or other transports.)
|
||||
|
||||
By "resuming", the TLS protocol means that the security context from
|
||||
before can be fully or partially resurrected when the TLS client presents
|
||||
the proper crypto stuff to the server. This saves on the amount of
|
||||
TLS packets that need to be sent back and forth, reducing amount
|
||||
of data and even latency. In the case of QUIC, resumption may send
|
||||
application data without having seen any reply from the server, hence
|
||||
this is named 0-RTT data.
|
||||
|
||||
The exact mechanism of session tickets in TLSv1.2 (and earlier) and
|
||||
TLSv1.3 differs. TLSv1.2 tickets have several weaknesses (that can
|
||||
be exploited by attackers) which TLSv1.3 then fixed. See
|
||||
[Session Tickets in the real world](https://words.filippo.io/we-need-to-talk-about-session-tickets/)
|
||||
for an insight into this topic.
|
||||
|
||||
These difference between TLS protocol versions are reflected in curl's
|
||||
handling of session tickets. More below.
|
||||
|
||||
## Curl's `ssl_peer_key`
|
||||
|
||||
In order to find a ticket from a previous TLS session, curl
|
||||
needs a name for TLS sessions that uniquely identifies the peer
|
||||
it talks to.
|
||||
|
||||
This name has to reflect also the various TLS parameters that can
|
||||
be configured in curl for a connection. We do not want to use
|
||||
a ticket from an different configuration. Example: when setting
|
||||
the maximum TLS version to 1.2, we do not want to reuse a ticket
|
||||
we got from a TLSv1.3 session, although we are talking to the
|
||||
same host.
|
||||
|
||||
Internally, we call this name a `ssl_peer_key`. It is a printable
|
||||
string that carries hostname and port and any non-default TLS
|
||||
parameters involved in the connection.
|
||||
|
||||
Examples:
|
||||
- `curl.se:443:CA-/etc/ssl/cert.pem:IMPL-GnuTLS/3.8.7` is a peer key for
|
||||
a connection to `curl.se:443` using `/etc/ssl/cert.pem` as CA
|
||||
trust anchors and GnuTLS/3.8.7 as TLS backend.
|
||||
- `curl.se:443:TLSVER-6-6:CA-/etc/ssl/cert.pem:IMPL-GnuTLS/3.8.7` is the
|
||||
same as the previous, except it is configured to use TLSv1.2 as
|
||||
min and max versions.
|
||||
|
||||
Different configurations produce different keys which is just what
|
||||
curl needs when handling SSL session tickets.
|
||||
|
||||
One important thing: peer keys do not contain confidential
|
||||
information. If you configure a client certificate or SRP authentication
|
||||
with username/password, these will not be part of the peer key.
|
||||
|
||||
However, peer keys carry the hostnames you use curl for. The *do*
|
||||
leak the privacy of your communication. We recommend to *not* persist
|
||||
peer keys for this reason.
|
||||
|
||||
**Caveat**: The key may contain file names or paths. It does not
|
||||
reflect the *contents* in the filesystem. If you change `/etc/ssl/cert.pem`
|
||||
and reuse a previous ticket, curl might trust a server which no
|
||||
longer has a root certificate in the file.
|
||||
|
||||
|
||||
## Session Cache Access
|
||||
|
||||
#### Lookups
|
||||
|
||||
When a new connection is being established, each SSL connection filter creates
|
||||
its own peer_key and calls into the cache. The cache then looks for a ticket
|
||||
with exactly this peer_key. Peer keys between proxy SSL filters and SSL
|
||||
filters talking through a tunnel will differ, as they talk to different
|
||||
peers.
|
||||
|
||||
If the connection filter wants to use a client certificate or SRP
|
||||
authentication, the cache will check those as well. If the cache peer
|
||||
carries client cert or SRP auth, the connection filter must have
|
||||
those with the same values (and vice versa).
|
||||
|
||||
On a match, the connection filter gets the session ticket and feeds that
|
||||
to the TLS implementation which, on accepting it, will try to resume it
|
||||
for a shorter handshake. In addition, the filter gets the ALPN used
|
||||
before and the amount of 0-RTT data that the server announced to be
|
||||
willing to accept. The filter can then decide if it wants to attempt
|
||||
0-RTT or not. (The ALPN is needed to know if the server speaks the
|
||||
protocol you want to send in 0-RTT. It makes no sense to send HTTP/2
|
||||
requests to a server that only knows HTTP/1.1.)
|
||||
|
||||
#### Updates
|
||||
|
||||
When a new TLS session ticket is received by a filter, it adds it to the
|
||||
cache using its peer_key and SSL configuration. The cache looks for
|
||||
a matching entry and, should it find one, adds the ticket for this
|
||||
peer.
|
||||
|
||||
### Put, Take and Return
|
||||
|
||||
when a filter accesses the session cache, it *takes*
|
||||
a ticket from the cache, meaning a returned ticket is removed. The filter
|
||||
then configures its TLS backend and *returns* the ticket to the cache.
|
||||
|
||||
The cache needs to treat tickets from TLSv1.2 and 1.3 differently.
|
||||
1.2 tickets should be reused, but 1.3 tickets SHOULD NOT (RFC 8446).
|
||||
The session cache will simply drop 1.3 tickets when they are returned
|
||||
after use, but keep a 1.2 ticket.
|
||||
|
||||
When a ticket is *put* into the cache, there is also a difference. There
|
||||
can be several 1.3 tickets at the same time, but only a single 1.2 ticket.
|
||||
TLSv1.2 tickets replace any other. 1.3 tickets accumulate up to a max
|
||||
amount.
|
||||
|
||||
By having a "put/take/return" we reflect the 1.3 use case nicely. Two
|
||||
concurrent connections will not reuse the same ticket.
|
||||
|
||||
## Session Ticket Persistence
|
||||
|
||||
#### Privacy and Security
|
||||
|
||||
As mentioned above, ssl peer keys are not intended for storage in a
|
||||
file system. They'll clearly show which hosts the user talked to. This
|
||||
maybe "just" privacy relevant, but has security implications as an
|
||||
attacker might find worthy targets among your peer keys.
|
||||
|
||||
Also, we do not recommend to persist TLSv1.2 tickets.
|
||||
|
||||
### Salted Hashes
|
||||
|
||||
The TLS session cache offers an alternative to storing peer keys:
|
||||
it provides a salted SHA256 hash of the peer key for import and export.
|
||||
|
||||
#### Export
|
||||
|
||||
The salt is generated randomly for each peer key on export. The
|
||||
SHA256 makes sure that the peer key cannot be reversed and that
|
||||
a slightly different key still produces a very different result.
|
||||
|
||||
This means an attacker cannot just "grep" a session file for a
|
||||
particular entry, e.g. if they want to know if you accessed a
|
||||
specific host. They *can* however compute the SHA256 hashes for
|
||||
all salts in the file and find a specific entry. But they *cannot*
|
||||
find a hostname they do not know. They'd have to brute force by
|
||||
guessing.
|
||||
|
||||
#### Import
|
||||
|
||||
When session tickets are imported from a file, curl only gets the
|
||||
salted hashes. The tickets imported will belong to an *unknown*
|
||||
peer key.
|
||||
|
||||
When a connection filter tries to *take* a session ticket, it will
|
||||
pass its peer key. This peer key will initially not match any
|
||||
tickets in the cache. The cache then checks all entries with
|
||||
unknown peer keys if the passed key matches their salted hash. If
|
||||
it does, the peer key is recovered and remembered at the cache
|
||||
entry.
|
||||
|
||||
This is a performance penalty in the order of "unknown" peer keys
|
||||
which will diminish over time when keys are rediscovered. Note that
|
||||
this also works for putting a new ticket into the cache: when no
|
||||
present entry matches, a new one with peer key is created. This
|
||||
peer key will then no longer bear the cost of hash computes.
|
@ -56,6 +56,7 @@ LIB_VTLS_CFILES = \
|
||||
vtls/schannel_verify.c \
|
||||
vtls/sectransp.c \
|
||||
vtls/vtls.c \
|
||||
vtls/vtls_scache.c \
|
||||
vtls/wolfssl.c \
|
||||
vtls/x509asn1.c
|
||||
|
||||
@ -74,6 +75,7 @@ LIB_VTLS_HFILES = \
|
||||
vtls/sectransp.h \
|
||||
vtls/vtls.h \
|
||||
vtls/vtls_int.h \
|
||||
vtls/vtls_scache.h \
|
||||
vtls/wolfssl.h \
|
||||
vtls/x509asn1.h
|
||||
|
||||
|
12
lib/dynbuf.c
12
lib/dynbuf.c
@ -244,6 +244,18 @@ char *Curl_dyn_ptr(const struct dynbuf *s)
|
||||
return s->bufr;
|
||||
}
|
||||
|
||||
char *Curl_dyn_take(struct dynbuf *s, size_t *plen)
|
||||
{
|
||||
char *ptr = s->bufr;
|
||||
DEBUGASSERT(s);
|
||||
DEBUGASSERT(s->init == DYNINIT);
|
||||
*plen = s->leng;
|
||||
s->bufr = NULL;
|
||||
s->leng = 0;
|
||||
s->allc = 0;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an unsigned pointer to the buffer.
|
||||
*/
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define Curl_dyn_uptr(a) curlx_dyn_uptr(a)
|
||||
#define Curl_dyn_len(a) curlx_dyn_len(a)
|
||||
#define Curl_dyn_reset(a) curlx_dyn_reset(a)
|
||||
#define Curl_dyn_take(a,b) curlx_dyn_take(a,b)
|
||||
#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b)
|
||||
#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b)
|
||||
#define curlx_dynbuf dynbuf /* for the struct name */
|
||||
@ -75,6 +76,10 @@ size_t Curl_dyn_len(const struct dynbuf *s);
|
||||
/* The implementation of this function exists in mprintf.c */
|
||||
int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
|
||||
|
||||
/* Take the buffer out of the dynbuf. Caller has ownership and
|
||||
* dynbuf resets to initial state. */
|
||||
char *Curl_dyn_take(struct dynbuf *s, size_t *plen);
|
||||
|
||||
/* Dynamic buffer max sizes */
|
||||
#define DYN_DOH_RESPONSE 3000
|
||||
#define DYN_DOH_CNAME 256
|
||||
|
25
lib/llist.c
25
lib/llist.c
@ -134,16 +134,12 @@ Curl_llist_append(struct Curl_llist *list, const void *p,
|
||||
Curl_llist_insert_next(list, list->_tail, p, ne);
|
||||
}
|
||||
|
||||
/*
|
||||
* @unittest: 1300
|
||||
*/
|
||||
void
|
||||
Curl_node_uremove(struct Curl_llist_node *e, void *user)
|
||||
void *Curl_node_take_elem(struct Curl_llist_node *e)
|
||||
{
|
||||
void *ptr;
|
||||
struct Curl_llist *list;
|
||||
if(!e)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
list = e->_list;
|
||||
DEBUGASSERT(list);
|
||||
@ -179,8 +175,23 @@ Curl_node_uremove(struct Curl_llist_node *e, void *user)
|
||||
#endif
|
||||
|
||||
--list->_size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* call the dtor() last for when it actually frees the 'e' memory itself */
|
||||
/*
|
||||
* @unittest: 1300
|
||||
*/
|
||||
void
|
||||
Curl_node_uremove(struct Curl_llist_node *e, void *user)
|
||||
{
|
||||
struct Curl_llist *list;
|
||||
void *ptr;
|
||||
if(!e)
|
||||
return;
|
||||
|
||||
list = e->_list;
|
||||
DEBUGASSERT(list);
|
||||
ptr = Curl_node_take_elem(e);
|
||||
if(list->_dtor)
|
||||
list->_dtor(user, ptr);
|
||||
}
|
||||
|
@ -75,6 +75,10 @@ size_t Curl_llist_count(struct Curl_llist *list);
|
||||
/* Curl_node_elem() returns the custom data from a Curl_llist_node */
|
||||
void *Curl_node_elem(struct Curl_llist_node *n);
|
||||
|
||||
/* Remove the node from the list and return the custom data
|
||||
* from a Curl_llist_node. Will NOT incoke a registered `dtor`. */
|
||||
void *Curl_node_take_elem(struct Curl_llist_node *);
|
||||
|
||||
/* Curl_node_next() returns the next element in a list from a given
|
||||
Curl_llist_node */
|
||||
struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n);
|
||||
|
10
lib/setopt.c
10
lib/setopt.c
@ -1588,8 +1588,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
|
||||
data->hsts = NULL;
|
||||
#endif
|
||||
#ifdef USE_SSL
|
||||
if(data->share->sslsession == data->state.session)
|
||||
data->state.session = NULL;
|
||||
if(data->share->ssl_scache == data->state.ssl_scache)
|
||||
data->state.ssl_scache = NULL;
|
||||
#endif
|
||||
#ifdef USE_LIBPSL
|
||||
if(data->psl == &data->share->psl)
|
||||
@ -1632,10 +1632,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SSL
|
||||
if(data->share->sslsession) {
|
||||
data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
|
||||
data->state.session = data->share->sslsession;
|
||||
}
|
||||
if(data->share->ssl_scache)
|
||||
data->state.ssl_scache = data->share->ssl_scache;
|
||||
#endif
|
||||
#ifdef USE_LIBPSL
|
||||
if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
|
||||
|
22
lib/share.c
22
lib/share.c
@ -30,6 +30,7 @@
|
||||
#include "share.h"
|
||||
#include "psl.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_scache.h"
|
||||
#include "hsts.h"
|
||||
#include "url.h"
|
||||
|
||||
@ -108,12 +109,8 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
||||
|
||||
case CURL_LOCK_DATA_SSL_SESSION:
|
||||
#ifdef USE_SSL
|
||||
if(!share->sslsession) {
|
||||
share->max_ssl_sessions = 8;
|
||||
share->sslsession = calloc(share->max_ssl_sessions,
|
||||
sizeof(struct Curl_ssl_session));
|
||||
share->sessionage = 0;
|
||||
if(!share->sslsession)
|
||||
if(!share->ssl_scache) {
|
||||
if(Curl_ssl_scache_create(8, 2, &share->ssl_scache))
|
||||
res = CURLSHE_NOMEM;
|
||||
}
|
||||
#else
|
||||
@ -174,7 +171,10 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
||||
|
||||
case CURL_LOCK_DATA_SSL_SESSION:
|
||||
#ifdef USE_SSL
|
||||
Curl_safefree(share->sslsession);
|
||||
if(share->ssl_scache) {
|
||||
Curl_ssl_scache_destroy(share->ssl_scache);
|
||||
share->ssl_scache = NULL;
|
||||
}
|
||||
#else
|
||||
res = CURLSHE_NOT_BUILT_IN;
|
||||
#endif
|
||||
@ -245,11 +245,9 @@ curl_share_cleanup(CURLSH *sh)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SSL
|
||||
if(share->sslsession) {
|
||||
size_t i;
|
||||
for(i = 0; i < share->max_ssl_sessions; i++)
|
||||
Curl_ssl_kill_session(&(share->sslsession[i]));
|
||||
free(share->sslsession);
|
||||
if(share->ssl_scache) {
|
||||
Curl_ssl_scache_destroy(share->ssl_scache);
|
||||
share->ssl_scache = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
11
lib/share.h
11
lib/share.h
@ -31,6 +31,8 @@
|
||||
#include "urldata.h"
|
||||
#include "conncache.h"
|
||||
|
||||
struct Curl_ssl_scache;
|
||||
|
||||
#define CURL_GOOD_SHARE 0x7e117a1e
|
||||
#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
|
||||
|
||||
@ -58,9 +60,7 @@ struct Curl_share {
|
||||
struct hsts *hsts;
|
||||
#endif
|
||||
#ifdef USE_SSL
|
||||
struct Curl_ssl_session *sslsession;
|
||||
size_t max_ssl_sessions;
|
||||
long sessionage;
|
||||
struct Curl_ssl_scache *ssl_scache;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -68,4 +68,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
|
||||
curl_lock_access);
|
||||
CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data);
|
||||
|
||||
/* convenience macro to check if this handle is using a shared SSL spool */
|
||||
#define CURL_SHARE_ssl_scache(data) (data->share && \
|
||||
(data->share->specifier & \
|
||||
(1<<CURL_LOCK_DATA_SSL_SESSION)))
|
||||
|
||||
#endif /* HEADER_CURL_SHARE_H */
|
||||
|
@ -72,6 +72,7 @@
|
||||
#include "url.h"
|
||||
#include "getinfo.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_scache.h"
|
||||
#include "vquic/vquic.h"
|
||||
#include "select.h"
|
||||
#include "multiif.h"
|
||||
@ -538,7 +539,7 @@ void Curl_init_CONNECT(struct Curl_easy *data)
|
||||
*/
|
||||
CURLcode Curl_pretransfer(struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!data->state.url && !data->set.uh) {
|
||||
/* we cannot do anything without URL */
|
||||
@ -577,12 +578,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
|
||||
data->state.httpreq = data->set.method;
|
||||
data->state.url = data->set.str[STRING_SET_URL];
|
||||
|
||||
/* Init the SSL session ID cache here. We do it here since we want to do it
|
||||
after the *_setopt() calls (that could specify the size of the cache) but
|
||||
before any transfer takes place. */
|
||||
result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions);
|
||||
if(result)
|
||||
return result;
|
||||
#ifdef USE_SSL
|
||||
if(!data->state.ssl_scache) {
|
||||
result = Curl_ssl_scache_create(data->set.general_ssl.max_ssl_sessions,
|
||||
2, &data->state.ssl_scache);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
data->state.requests = 0;
|
||||
data->state.followlocation = 0; /* reset the location-follow counter */
|
||||
|
@ -271,21 +271,7 @@ enum protection_level {
|
||||
|
||||
/* SSL backend-specific data; declared differently by each SSL backend */
|
||||
struct ssl_backend_data;
|
||||
|
||||
typedef enum {
|
||||
CURL_SSL_PEER_DNS,
|
||||
CURL_SSL_PEER_IPV4,
|
||||
CURL_SSL_PEER_IPV6
|
||||
} ssl_peer_type;
|
||||
|
||||
struct ssl_peer {
|
||||
char *hostname; /* hostname for verification */
|
||||
char *dispname; /* display version of hostname */
|
||||
char *sni; /* SNI version of hostname or NULL if not usable */
|
||||
ssl_peer_type type; /* type of the peer information */
|
||||
int port; /* port we are talking to */
|
||||
int transport; /* one of TRNSPRT_* defines */
|
||||
};
|
||||
struct Curl_ssl_scache_entry;
|
||||
|
||||
struct ssl_primary_config {
|
||||
char *CApath; /* certificate dir (does not work on Windows) */
|
||||
@ -341,24 +327,6 @@ 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; /* hostname for which this ID was used */
|
||||
char *conn_to_host; /* hostname for the connection (may be NULL) */
|
||||
const char *scheme; /* protocol scheme used */
|
||||
char *alpn; /* APLN TLS negotiated protocol string */
|
||||
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) */
|
||||
int transport; /* TCP or QUIC */
|
||||
struct ssl_primary_config ssl_config; /* setup for this session */
|
||||
};
|
||||
|
||||
#ifdef USE_WINDOWS_SSPI
|
||||
#include "curl_sspi.h"
|
||||
#endif
|
||||
@ -1232,8 +1200,7 @@ struct UrlState {
|
||||
curl_prot_t first_remote_protocol;
|
||||
|
||||
int retrycount; /* number of retries on a new connection */
|
||||
struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
|
||||
long sessionage; /* number of the most recent session */
|
||||
struct Curl_ssl_scache *ssl_scache; /* TLS session pool */
|
||||
int os_errno; /* filled in with errno whenever an error occurs */
|
||||
long followlocation; /* redirect counter */
|
||||
int requests; /* request counter: redirects + authentication retakes */
|
||||
|
@ -2131,7 +2131,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) {
|
||||
Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid);
|
||||
Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid,
|
||||
SSL_version(ssl), "h3");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -2158,7 +2159,8 @@ static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
|
||||
}
|
||||
switch(htype) {
|
||||
case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
|
||||
(void)Curl_gtls_update_session_id(cf, data, session, &ctx->peer, "h3");
|
||||
(void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key,
|
||||
session, -1, "h3");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -2181,7 +2183,8 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
|
||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||
DEBUGASSERT(data);
|
||||
if(data && ctx) {
|
||||
(void)wssl_cache_session(cf, data, &ctx->peer, session);
|
||||
(void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key,
|
||||
session, wolfSSL_version(ssl), "h3");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -2258,10 +2261,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
|
||||
int qfd;
|
||||
|
||||
DEBUGASSERT(ctx->initialized);
|
||||
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
#define H3_ALPN "\x2h3\x5h3-29"
|
||||
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
|
||||
H3_ALPN, sizeof(H3_ALPN) - 1,
|
||||
|
@ -1162,9 +1162,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
|
||||
BIO_ADDR *baddr = NULL;
|
||||
|
||||
DEBUGASSERT(ctx->initialized);
|
||||
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
#define H3_ALPN "\x2h3"
|
||||
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
|
||||
|
@ -1278,10 +1278,6 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
|
||||
if(!ctx->cfg) {
|
||||
failf(data, "cannot create quiche config");
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "multiif.h"
|
||||
#include "vtls/keylog.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_scache.h"
|
||||
#include "vquic-tls.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
@ -221,7 +222,7 @@ static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx,
|
||||
}
|
||||
|
||||
if(ssl_config->primary.cache_session) {
|
||||
(void)wssl_setup_session(cf, data, &ctx->wssl, peer);
|
||||
(void)Curl_wssl_setup_session(cf, data, &ctx->wssl, peer->scache_key);
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
@ -236,11 +237,26 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
|
||||
Curl_vquic_tls_ctx_setup *cb_setup,
|
||||
void *cb_user_data, void *ssl_user_data)
|
||||
{
|
||||
char tls_id[80];
|
||||
CURLcode result;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
Curl_ossl_version(tls_id, sizeof(tls_id));
|
||||
#elif defined(USE_GNUTLS)
|
||||
Curl_gtls_version(tls_id, sizeof(tls_id));
|
||||
#elif defined(USE_WOLFSSL)
|
||||
Curl_wssl_version(tls_id, sizeof(tls_id));
|
||||
#else
|
||||
#error "no TLS lib in used, should not happen"
|
||||
return CURLE_FAILED_INIT;
|
||||
#endif
|
||||
result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
(void)result;
|
||||
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC,
|
||||
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer,
|
||||
(const unsigned char *)alpn, alpn_len,
|
||||
cb_setup, cb_user_data, NULL, ssl_user_data);
|
||||
#elif defined(USE_GNUTLS)
|
||||
@ -346,6 +362,9 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
|
||||
|
||||
}
|
||||
#endif
|
||||
/* on error, remove any session we might have in the pool */
|
||||
if(result)
|
||||
Curl_ssl_scache_remove_all(cf, data, peer->scache_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "curl_setup.h"
|
||||
#include "bufq.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/openssl.h"
|
||||
|
||||
#if defined(USE_HTTP3) && \
|
||||
@ -33,6 +34,8 @@
|
||||
|
||||
#include "vtls/wolfssl.h"
|
||||
|
||||
struct ssl_peer;
|
||||
|
||||
struct curl_tls_ctx {
|
||||
#ifdef USE_OPENSSL
|
||||
struct ossl_ctx ossl;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "inet_pton.h"
|
||||
#include "vtls.h"
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "connect.h"
|
||||
#include "select.h"
|
||||
#include "multiif.h"
|
||||
@ -609,20 +610,19 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
|
||||
br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
|
||||
|
||||
if(ssl_config->primary.cache_session) {
|
||||
void *sdata;
|
||||
size_t slen;
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
const br_ssl_session_parameters *session;
|
||||
|
||||
CURL_TRC_CF(data, cf, "connect_step1, check session cache");
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) &&
|
||||
slen == sizeof(*session)) {
|
||||
session = sdata;
|
||||
ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
|
||||
&sc_session);
|
||||
if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
|
||||
session = (br_ssl_session_parameters *)(void *)sc_session->sdata;
|
||||
br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
|
||||
session_set = 1;
|
||||
infof(data, "BearSSL: reusing session ID");
|
||||
/* single use of sessions */
|
||||
Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
|
||||
if(connssl->alpn) {
|
||||
@ -804,12 +804,6 @@ 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)
|
||||
{
|
||||
@ -832,17 +826,22 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
if(ssl_config->primary.cache_session) {
|
||||
struct Curl_ssl_session *sc_session;
|
||||
br_ssl_session_parameters *session;
|
||||
|
||||
session = malloc(sizeof(*session));
|
||||
if(!session)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
|
||||
session, sizeof(*session),
|
||||
bearssl_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session),
|
||||
(int)session->version,
|
||||
connssl->negotiated.alpn,
|
||||
0, -1, &sc_session);
|
||||
if(!ret) {
|
||||
ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
|
||||
sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
}
|
||||
if(ret)
|
||||
return ret;
|
||||
}
|
||||
|
156
lib/vtls/gtls.c
156
lib/vtls/gtls.c
@ -47,6 +47,7 @@
|
||||
#include "gtls.h"
|
||||
#include "vtls.h"
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "vauth/vauth.h"
|
||||
#include "parsedate.h"
|
||||
#include "connect.h" /* for the connect timeout */
|
||||
@ -714,21 +715,17 @@ 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);
|
||||
}
|
||||
|
||||
CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
gnutls_session_t session,
|
||||
struct ssl_peer *peer,
|
||||
const char *alpn)
|
||||
CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
gnutls_session_t session,
|
||||
int lifetime_secs,
|
||||
const char *alpn)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
void *connect_sessionid;
|
||||
size_t connect_idsize = 0;
|
||||
struct Curl_ssl_session *sc_session;
|
||||
unsigned char *sdata;
|
||||
size_t sdata_len = 0;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!ssl_config->primary.cache_session)
|
||||
@ -740,35 +737,57 @@ CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
|
||||
detect that. */
|
||||
|
||||
/* get the session ID data size */
|
||||
gnutls_session_get_data(session, NULL, &connect_idsize);
|
||||
if(!connect_idsize) /* gnutls does this for some version combinations */
|
||||
gnutls_session_get_data(session, NULL, &sdata_len);
|
||||
if(!sdata_len) /* gnutls does this for some version combinations */
|
||||
return CURLE_OK;
|
||||
|
||||
connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
|
||||
if(!connect_sessionid)
|
||||
sdata = malloc(sdata_len); /* get a buffer for it */
|
||||
if(!sdata)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* extract session ID to the allocated buffer */
|
||||
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
|
||||
gnutls_session_get_data(session, sdata, &sdata_len);
|
||||
|
||||
CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache",
|
||||
connect_idsize, alpn ? alpn : "-");
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
/* store this session id, takes ownership */
|
||||
result = Curl_ssl_set_sessionid(cf, data, peer, alpn,
|
||||
connect_sessionid, connect_idsize,
|
||||
gtls_sessionid_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
sdata_len, alpn ? alpn : "-");
|
||||
result = Curl_ssl_session_create(sdata, sdata_len,
|
||||
Curl_glts_get_ietf_proto(session),
|
||||
alpn, 0, lifetime_secs,
|
||||
&sc_session);
|
||||
/* call took ownership of `sdata`*/
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int Curl_glts_get_ietf_proto(gnutls_session_t session)
|
||||
{
|
||||
switch(gnutls_protocol_get_version(session)) {
|
||||
case GNUTLS_SSL3:
|
||||
return CURL_IETF_PROTO_SSL3;
|
||||
case GNUTLS_TLS1_0:
|
||||
return CURL_IETF_PROTO_TLS1;
|
||||
case GNUTLS_TLS1_1:
|
||||
return CURL_IETF_PROTO_TLS1_1;
|
||||
case GNUTLS_TLS1_2:
|
||||
return CURL_IETF_PROTO_TLS1_2;
|
||||
case GNUTLS_TLS1_3:
|
||||
return CURL_IETF_PROTO_TLS1_3;
|
||||
default:
|
||||
return CURL_IETF_PROTO_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
gnutls_session_t session)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
return Curl_gtls_update_session_id(cf, data, session, &connssl->peer,
|
||||
connssl->alpn_negotiated);
|
||||
return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
|
||||
session, -1,
|
||||
connssl->negotiated.alpn);
|
||||
}
|
||||
|
||||
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
|
||||
@ -1058,9 +1077,11 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||
{
|
||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
gnutls_datum_t gtls_alpns[5];
|
||||
size_t gtls_alpns_count = 0;
|
||||
CURLcode result;
|
||||
int rc;
|
||||
|
||||
DEBUGASSERT(gctx);
|
||||
|
||||
@ -1085,29 +1106,23 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||
/* This might be a reconnect, so we check for a session ID in the cache
|
||||
to speed up things */
|
||||
if(conn_config->cache_session) {
|
||||
void *ssl_sessionid;
|
||||
size_t ssl_idsize;
|
||||
char *session_alpn;
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, peer,
|
||||
&ssl_sessionid, &ssl_idsize, &session_alpn)) {
|
||||
/* we got a session id, use it! */
|
||||
int rc;
|
||||
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
|
||||
if(rc < 0)
|
||||
infof(data, "SSL failed to set session ID");
|
||||
if(scs && scs->sdata && scs->sdata_len) {
|
||||
/* we got a cached session, use it! */
|
||||
rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len);
|
||||
if(rc < 0) {
|
||||
infof(data, "SSL session not accepted by GnuTLS, continuing without");
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL reusing session ID (size=%zu, alpn=%s)",
|
||||
ssl_idsize, session_alpn ? session_alpn : "-");
|
||||
#ifdef DEBUGBUILD
|
||||
if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) &&
|
||||
#else
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
if(ssl_config->earlydata &&
|
||||
#endif
|
||||
!cf->conn->connect_only && connssl &&
|
||||
(gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) &&
|
||||
Curl_alpn_contains_proto(connssl->alpn, session_alpn)) {
|
||||
Curl_alpn_contains_proto(connssl->alpn, scs->alpn)) {
|
||||
connssl->earlydata_max =
|
||||
gnutls_record_get_max_early_data_size(gctx->session);
|
||||
if((!connssl->earlydata_max ||
|
||||
@ -1118,24 +1133,32 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, "
|
||||
"reusing ALPN '%s'",
|
||||
connssl->earlydata_max, session_alpn);
|
||||
connssl->earlydata_max, scs->alpn);
|
||||
connssl->earlydata_state = ssl_earlydata_use;
|
||||
connssl->state = ssl_connection_deferred;
|
||||
result = Curl_alpn_set_negotiated(cf, data, connssl,
|
||||
(const unsigned char *)session_alpn,
|
||||
session_alpn ? strlen(session_alpn) : 0);
|
||||
(const unsigned char *)scs->alpn,
|
||||
scs->alpn ? strlen(scs->alpn) : 0);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
/* We only try the ALPN protocol the session used before,
|
||||
* otherwise we might send early data for the wrong protocol */
|
||||
gtls_alpns[0].data = (unsigned char *)session_alpn;
|
||||
gtls_alpns[0].size = (unsigned)strlen(session_alpn);
|
||||
gtls_alpns_count = 1;
|
||||
gtls_alpns[0].data = (unsigned char *)scs->alpn;
|
||||
gtls_alpns[0].size = (unsigned)strlen(scs->alpn);
|
||||
if(gnutls_alpn_set_protocols(gctx->session,
|
||||
gtls_alpns, 1,
|
||||
GNUTLS_ALPN_MANDATORY)) {
|
||||
failf(data, "failed setting ALPN");
|
||||
result = CURLE_SSL_CONNECT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
/* don't set again below */
|
||||
gtls_alpns_count = 0;
|
||||
alpn = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
|
||||
/* convert the ALPN string from our arguments to a list of strings that
|
||||
@ -1143,19 +1166,21 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||
* to the server. nice. */
|
||||
if(!gtls_alpns_count && alpn && alpn_len) {
|
||||
size_t i, alen = alpn_len;
|
||||
unsigned char *s = (unsigned char *)alpn;
|
||||
unsigned char *salpn = (unsigned char *)alpn;
|
||||
unsigned char slen;
|
||||
for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) {
|
||||
slen = s[0];
|
||||
slen = salpn[0];
|
||||
if(slen >= alen)
|
||||
return CURLE_FAILED_INIT;
|
||||
gtls_alpns[i].data = s + 1;
|
||||
gtls_alpns[i].data = salpn + 1;
|
||||
gtls_alpns[i].size = slen;
|
||||
s += slen + 1;
|
||||
salpn += slen + 1;
|
||||
alen -= (size_t)slen + 1;
|
||||
}
|
||||
if(alen) /* not all alpn chars used, wrong format or too many */
|
||||
return CURLE_FAILED_INIT;
|
||||
if(alen) { /* not all alpn chars used, wrong format or too many */
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
gtls_alpns_count = i;
|
||||
}
|
||||
|
||||
@ -1164,10 +1189,12 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||
gtls_alpns, (unsigned int)gtls_alpns_count,
|
||||
GNUTLS_ALPN_MANDATORY)) {
|
||||
failf(data, "failed setting ALPN");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
result = CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
out:
|
||||
Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode
|
||||
@ -1197,7 +1224,8 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
}
|
||||
|
||||
result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
|
||||
proto.data, proto.len, connssl, NULL, NULL, cf);
|
||||
proto.data, proto.len,
|
||||
connssl, NULL, NULL, cf);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@ -2213,7 +2241,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t gtls_version(char *buffer, size_t size)
|
||||
size_t Curl_gtls_version(char *buffer, size_t size)
|
||||
{
|
||||
return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
|
||||
}
|
||||
@ -2268,7 +2296,7 @@ const struct Curl_ssl Curl_ssl_gnutls = {
|
||||
|
||||
gtls_init, /* init */
|
||||
gtls_cleanup, /* cleanup */
|
||||
gtls_version, /* version */
|
||||
Curl_gtls_version, /* version */
|
||||
gtls_shutdown, /* shutdown */
|
||||
gtls_data_pending, /* data_pending */
|
||||
gtls_random, /* random */
|
||||
|
@ -47,6 +47,8 @@ struct ssl_config_data;
|
||||
struct ssl_peer;
|
||||
struct ssl_connect_data;
|
||||
|
||||
int Curl_glts_get_ietf_proto(gnutls_session_t session);
|
||||
|
||||
struct gtls_shared_creds {
|
||||
gnutls_certificate_credentials_t creds;
|
||||
char *CAfile; /* CAfile path used to generate X509 store */
|
||||
@ -70,6 +72,8 @@ struct gtls_ctx {
|
||||
BIT(sent_shutdown);
|
||||
};
|
||||
|
||||
size_t Curl_gtls_version(char *buffer, size_t size);
|
||||
|
||||
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
void *user_data);
|
||||
@ -96,11 +100,12 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data,
|
||||
const char *pinned_key);
|
||||
|
||||
/* Extract TLS session and place in cache, if configured. */
|
||||
CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
gnutls_session_t session,
|
||||
struct ssl_peer *peer,
|
||||
const char *alpn);
|
||||
CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
gnutls_session_t session,
|
||||
int lifetime_secs,
|
||||
const char *alpn);
|
||||
|
||||
extern const struct Curl_ssl Curl_ssl_gnutls;
|
||||
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "mbedtls.h"
|
||||
#include "vtls.h"
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "x509asn1.h"
|
||||
#include "parsedate.h"
|
||||
#include "connect.h" /* for the connect timeout */
|
||||
@ -875,29 +876,30 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
|
||||
/* Check if there is a cached ID we can/should use here! */
|
||||
if(ssl_config->primary.cache_session) {
|
||||
void *sdata = NULL;
|
||||
size_t slen = 0;
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
CURLcode result;
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
|
||||
&sdata, &slen, NULL) && slen) {
|
||||
result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
|
||||
&sc_session);
|
||||
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
|
||||
mbedtls_ssl_session session;
|
||||
|
||||
mbedtls_ssl_session_init(&session);
|
||||
ret = mbedtls_ssl_session_load(&session, sdata, slen);
|
||||
ret = mbedtls_ssl_session_load(&session, sc_session->sdata,
|
||||
sc_session->sdata_len);
|
||||
if(ret) {
|
||||
failf(data, "error loading cached session: -0x%x", -ret);
|
||||
failf(data, "SSL session error loading: -0x%x", -ret);
|
||||
}
|
||||
else {
|
||||
ret = mbedtls_ssl_set_session(&backend->ssl, &session);
|
||||
if(ret)
|
||||
failf(data, "error setting session: -0x%x", -ret);
|
||||
failf(data, "SSL session error setting: -0x%x", -ret);
|
||||
else
|
||||
infof(data, "SSL reusing session ID");
|
||||
}
|
||||
mbedtls_ssl_session_free(&session);
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_ca_chain(&backend->config,
|
||||
@ -1115,12 +1117,6 @@ pinnedpubkey_error:
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void mbedtls_session_free(void *session, size_t slen)
|
||||
{
|
||||
(void)slen;
|
||||
free(session);
|
||||
}
|
||||
|
||||
static CURLcode
|
||||
mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
@ -1128,48 +1124,64 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
struct mbed_ssl_backend_data *backend =
|
||||
(struct mbed_ssl_backend_data *)connssl->backend;
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
mbedtls_ssl_session session;
|
||||
bool msession_alloced = FALSE;
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
unsigned char *sdata = NULL;
|
||||
size_t slen = 0;
|
||||
int ietf_tls_id;
|
||||
CURLcode result = CURLE_OK;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(backend);
|
||||
if(ssl_config->primary.cache_session) {
|
||||
int ret;
|
||||
mbedtls_ssl_session session;
|
||||
unsigned char *sdata = NULL;
|
||||
size_t slen = 0;
|
||||
if(!ssl_config->primary.cache_session)
|
||||
return CURLE_OK;
|
||||
|
||||
mbedtls_ssl_session_init(&session);
|
||||
ret = mbedtls_ssl_get_session(&backend->ssl, &session);
|
||||
if(ret) {
|
||||
if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
|
||||
mbedtls_ssl_session_free(&session);
|
||||
failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
|
||||
mbedtls_ssl_session_save(&session, NULL, 0, &slen);
|
||||
if(!slen) {
|
||||
failf(data, "failed to serialize session: length is 0");
|
||||
}
|
||||
else {
|
||||
sdata = malloc(slen);
|
||||
if(sdata) {
|
||||
ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
|
||||
if(ret) {
|
||||
failf(data, "failed to serialize session: -0x%x", -ret);
|
||||
}
|
||||
else {
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
|
||||
sdata, slen, mbedtls_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
if(!result)
|
||||
sdata = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
mbedtls_ssl_session_free(&session);
|
||||
free(sdata);
|
||||
mbedtls_ssl_session_init(&session);
|
||||
ret = mbedtls_ssl_get_session(&backend->ssl, &session);
|
||||
msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED);
|
||||
if(ret) {
|
||||
failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
|
||||
result = CURLE_SSL_CONNECT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mbedtls_ssl_session_save(&session, NULL, 0, &slen);
|
||||
if(!slen) {
|
||||
failf(data, "failed to serialize session: length is 0");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdata = malloc(slen);
|
||||
if(!sdata) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
|
||||
if(ret) {
|
||||
failf(data, "failed to serialize session: -0x%x", -ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if MBEDTLS_VERSION_NUMBER >= 0x03020000
|
||||
ietf_tls_id = mbedtls_ssl_get_version_number(&backend->ssl);
|
||||
#else
|
||||
ietf_tls_id = CURL_IETF_PROTO_UNKNOWN;
|
||||
#endif
|
||||
result = Curl_ssl_session_create(sdata, slen,
|
||||
ietf_tls_id,
|
||||
connssl->negotiated.alpn, 0, -1,
|
||||
&sc_session);
|
||||
sdata = NULL; /* call took ownership */
|
||||
if(!result)
|
||||
result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
|
||||
sc_session);
|
||||
|
||||
out:
|
||||
if(msession_alloced)
|
||||
mbedtls_ssl_session_free(&session);
|
||||
free(sdata);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "select.h"
|
||||
#include "vtls.h"
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "vauth/vauth.h"
|
||||
#include "keylog.h"
|
||||
#include "strcase.h"
|
||||
@ -960,8 +961,6 @@ static const char *SSL_ERROR_to_str(int err)
|
||||
}
|
||||
}
|
||||
|
||||
static size_t ossl_version(char *buffer, size_t size);
|
||||
|
||||
/* Return error string for last OpenSSL error
|
||||
*/
|
||||
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
|
||||
@ -970,7 +969,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
|
||||
DEBUGASSERT(size);
|
||||
*buf = '\0';
|
||||
|
||||
len = ossl_version(buf, size);
|
||||
len = Curl_ossl_version(buf, size);
|
||||
DEBUGASSERT(len < (size - 2));
|
||||
if(len < (size - 2)) {
|
||||
buf += len;
|
||||
@ -2013,13 +2012,6 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void ossl_session_free(void *sessionid, size_t idsize)
|
||||
{
|
||||
/* free the ID */
|
||||
(void)idsize;
|
||||
free(sessionid);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when the 'data' struct is going away. Close
|
||||
* down everything and free all resources!
|
||||
@ -2873,20 +2865,23 @@ 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 *session)
|
||||
const char *ssl_peer_key,
|
||||
SSL_SESSION *session,
|
||||
int ietf_tls_id,
|
||||
const char *alpn)
|
||||
{
|
||||
const struct ssl_config_data *config;
|
||||
unsigned char *der_session_buf = NULL;
|
||||
CURLcode result = CURLE_OK;
|
||||
size_t der_session_size;
|
||||
unsigned char *der_session_buf;
|
||||
unsigned char *der_session_ptr;
|
||||
|
||||
if(!cf || !data)
|
||||
goto out;
|
||||
|
||||
config = Curl_ssl_cf_get_config(cf, data);
|
||||
if(config->primary.cache_session) {
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
size_t der_session_size;
|
||||
unsigned char *der_session_ptr;
|
||||
|
||||
der_session_size = i2d_SSL_SESSION(session, NULL);
|
||||
if(der_session_size == 0) {
|
||||
@ -2903,17 +2898,22 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
|
||||
if(der_session_size == 0) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
free(der_session_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf,
|
||||
der_session_size, ossl_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
result = Curl_ssl_session_create(der_session_buf, der_session_size,
|
||||
ietf_tls_id, alpn, 0,
|
||||
SSL_SESSION_get_timeout(session),
|
||||
&sc_session);
|
||||
der_session_buf = NULL; /* took ownership of sdata */
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(der_session_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2929,7 +2929,9 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
|
||||
cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
|
||||
connssl = cf ? cf->ctx : NULL;
|
||||
data = connssl ? CF_DATA_CURRENT(cf) : NULL;
|
||||
Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
|
||||
if(data && connssl)
|
||||
Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
|
||||
SSL_version(ssl), connssl->negotiated.alpn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3468,7 +3470,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
int transport, /* TCP or QUIC */
|
||||
const unsigned char *alpn, size_t alpn_len,
|
||||
Curl_ossl_ctx_setup_cb *cb_setup,
|
||||
void *cb_user_data,
|
||||
@ -3479,9 +3480,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
const char *ciphers;
|
||||
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
|
||||
ctx_option_t ctx_options = 0;
|
||||
SSL_SESSION *ssl_session = NULL;
|
||||
const unsigned char *der_sessionid = NULL;
|
||||
size_t der_sessionid_size = 0;
|
||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
const long int ssl_version_min = conn_config->version;
|
||||
@ -3498,7 +3496,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
|
||||
ssl_config->certverifyresult = !X509_V_OK;
|
||||
|
||||
switch(transport) {
|
||||
switch(peer->transport) {
|
||||
case TRNSPRT_TCP:
|
||||
/* check to see if we have been told to use an explicit SSL/TLS version */
|
||||
switch(ssl_version_min) {
|
||||
@ -3542,7 +3540,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
failf(data, "unsupported transport %d in SSL init", transport);
|
||||
failf(data, "unsupported transport %d in SSL init", peer->transport);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
|
||||
@ -3965,32 +3963,36 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
|
||||
octx->reused_session = FALSE;
|
||||
if(ssl_config->primary.cache_session) {
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
|
||||
&der_sessionid_size, NULL)) {
|
||||
/* we got a session id, use it! */
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
|
||||
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &sc_session);
|
||||
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
|
||||
const unsigned char *der_sessionid = sc_session->sdata;
|
||||
size_t der_sessionid_size = sc_session->sdata_len;
|
||||
SSL_SESSION *ssl_session = NULL;
|
||||
|
||||
/* If OpenSSL does not accept the session from the cache, this
|
||||
* is not an error. We just continue without it. */
|
||||
ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
|
||||
(long)der_sessionid_size);
|
||||
(long)der_sessionid_size);
|
||||
if(ssl_session) {
|
||||
if(!SSL_set_session(octx->ssl, ssl_session)) {
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
SSL_SESSION_free(ssl_session);
|
||||
failf(data, "SSL: SSL_set_session failed: %s",
|
||||
infof(data, "SSL: SSL_set_session not accepted, "
|
||||
"continuing without: %s",
|
||||
ossl_strerror(ERR_get_error(), error_buffer,
|
||||
sizeof(error_buffer)));
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL reusing session");
|
||||
octx->reused_session = TRUE;
|
||||
}
|
||||
SSL_SESSION_free(ssl_session);
|
||||
/* Informational message */
|
||||
infof(data, "SSL reusing session ID");
|
||||
octx->reused_session = TRUE;
|
||||
}
|
||||
else {
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
infof(data, "SSL session not accepted by OpenSSL, continuing without");
|
||||
}
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session);
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
@ -4018,7 +4020,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
|
||||
}
|
||||
#endif
|
||||
|
||||
result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
|
||||
result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
|
||||
proto.data, proto.len, NULL, NULL,
|
||||
ossl_new_session_cb, cf);
|
||||
if(result)
|
||||
@ -4693,21 +4695,6 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
|
||||
/* do not do this after Session ID reuse */
|
||||
result = verifystatus(cf, data, octx);
|
||||
if(result) {
|
||||
/* when verifystatus failed, remove the session id from the cache again
|
||||
if present */
|
||||
if(!Curl_ssl_cf_is_proxy(cf)) {
|
||||
void *old_ssl_sessionid = NULL;
|
||||
bool incache;
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
incache = !(Curl_ssl_getsessionid(cf, data, peer,
|
||||
&old_ssl_sessionid, NULL, NULL));
|
||||
if(incache) {
|
||||
infof(data, "Remove session ID again from cache");
|
||||
Curl_ssl_delsessionid(data, old_ssl_sessionid);
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
}
|
||||
|
||||
X509_free(octx->server_cert);
|
||||
octx->server_cert = NULL;
|
||||
return result;
|
||||
@ -4757,6 +4744,9 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
|
||||
result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
|
||||
if(!result)
|
||||
connssl->connecting_state = ssl_connect_done;
|
||||
else
|
||||
/* on error, remove sessions we might have in the pool */
|
||||
Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -5172,7 +5162,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
|
||||
#endif
|
||||
}
|
||||
|
||||
static size_t ossl_version(char *buffer, size_t size)
|
||||
size_t Curl_ossl_version(char *buffer, size_t size)
|
||||
{
|
||||
#ifdef LIBRESSL_VERSION_NUMBER
|
||||
#ifdef HAVE_OPENSSL_VERSION
|
||||
@ -5336,7 +5326,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
|
||||
|
||||
ossl_init, /* init */
|
||||
ossl_cleanup, /* cleanup */
|
||||
ossl_version, /* version */
|
||||
Curl_ossl_version, /* version */
|
||||
ossl_shutdown, /* shutdown */
|
||||
ossl_data_pending, /* data_pending */
|
||||
ossl_random, /* random */
|
||||
|
@ -36,6 +36,8 @@
|
||||
|
||||
#include "urldata.h"
|
||||
|
||||
struct ssl_peer;
|
||||
|
||||
/* Struct to hold a Curl OpenSSL instance */
|
||||
struct ossl_ctx {
|
||||
/* these ones requires specific SSL-types */
|
||||
@ -53,6 +55,8 @@ struct ossl_ctx {
|
||||
BIT(reused_session); /* session-ID was reused for this */
|
||||
};
|
||||
|
||||
size_t Curl_ossl_version(char *buffer, size_t size);
|
||||
|
||||
typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
void *user_data);
|
||||
@ -63,7 +67,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
int transport, /* TCP or QUIC */
|
||||
const unsigned char *alpn, size_t alpn_len,
|
||||
Curl_ossl_ctx_setup_cb *cb_setup,
|
||||
void *cb_user_data,
|
||||
@ -94,8 +97,10 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
|
||||
*/
|
||||
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
SSL_SESSION *ssl_sessionid);
|
||||
const char *ssl_peer_key,
|
||||
SSL_SESSION *ssl_sessionid,
|
||||
int ietf_tls_id,
|
||||
const char *alpn);
|
||||
|
||||
/*
|
||||
* Get the server cert, verify it and show it, etc., only call failf() if
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "schannel_int.h"
|
||||
#include "vtls.h"
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "strcase.h"
|
||||
#include "sendf.h"
|
||||
#include "connect.h" /* for the connect timeout */
|
||||
@ -954,9 +955,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
|
||||
/* check for an existing reusable credential handle */
|
||||
if(ssl_config->primary.cache_session) {
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
|
||||
(void **)&old_cred, NULL, NULL)) {
|
||||
Curl_ssl_scache_lock(data);
|
||||
if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
|
||||
(void **)&old_cred)) {
|
||||
backend->cred = old_cred;
|
||||
DEBUGF(infof(data, "schannel: reusing existing credential handle"));
|
||||
|
||||
@ -966,7 +967,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
"schannel: incremented credential handle refcount = %d",
|
||||
backend->cred->refcount));
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
}
|
||||
|
||||
if(!backend->cred) {
|
||||
@ -1501,12 +1502,11 @@ 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)
|
||||
static void schannel_session_free(void *sessionid)
|
||||
{
|
||||
/* 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) {
|
||||
@ -1599,14 +1599,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
|
||||
/* save the current session data for possible reuse */
|
||||
if(ssl_config->primary.cache_session) {
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
Curl_ssl_scache_lock(data);
|
||||
/* Up ref count since call takes ownership */
|
||||
backend->cred->refcount++;
|
||||
result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
|
||||
backend->cred,
|
||||
sizeof(struct Curl_schannel_cred),
|
||||
schannel_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
|
||||
backend->cred, schannel_session_free);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
@ -2445,9 +2443,9 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
|
||||
/* free SSPI Schannel API credential handle */
|
||||
if(backend->cred) {
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
schannel_session_free(backend->cred, 0);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_lock(data);
|
||||
schannel_session_free(backend->cred);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
backend->cred = NULL;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "multiif.h"
|
||||
#include "strcase.h"
|
||||
#include "x509asn1.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "strerror.h"
|
||||
#include "cipher_suite.h"
|
||||
|
||||
@ -1020,7 +1021,7 @@ failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sectransp_session_free(void *sessionid, size_t idsize)
|
||||
static void sectransp_session_free(void *sessionid)
|
||||
{
|
||||
/* 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
|
||||
@ -1028,7 +1029,6 @@ static void sectransp_session_free(void *sessionid, size_t idsize)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1337,19 +1337,19 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
|
||||
char *ssl_sessionid;
|
||||
size_t ssl_sessionid_len;
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
|
||||
(void **)&ssl_sessionid, &ssl_sessionid_len,
|
||||
NULL)) {
|
||||
Curl_ssl_scache_lock(data);
|
||||
if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
|
||||
(void **)&ssl_sessionid)) {
|
||||
/* we got a session id, use it! */
|
||||
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid,
|
||||
strlen(ssl_sessionid));
|
||||
Curl_ssl_scache_unlock(data);
|
||||
if(err != noErr) {
|
||||
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
/* Informational message */
|
||||
infof(data, "SSL reusing session ID");
|
||||
else
|
||||
infof(data, "SSL reusing session ID");
|
||||
}
|
||||
/* If there is not one, then let's make one up! This has to be done prior
|
||||
to starting the handshake. */
|
||||
@ -1363,15 +1363,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
|
||||
|
||||
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
|
||||
if(err != noErr) {
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
|
||||
result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
|
||||
ssl_sessionid, ssl_sessionid_len,
|
||||
/* This is all a bit weird, as we have not handshaked yet.
|
||||
* I hope this backend will go away soon. */
|
||||
result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
|
||||
(void *)ssl_sessionid,
|
||||
sectransp_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
377
lib/vtls/vtls.c
377
lib/vtls/vtls.c
@ -55,6 +55,7 @@
|
||||
|
||||
#include "vtls.h" /* generic SSL protos etc */
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
|
||||
#include "openssl.h" /* OpenSSL versions */
|
||||
#include "gtls.h" /* GnuTLS versions */
|
||||
@ -74,6 +75,7 @@
|
||||
#include "multiif.h"
|
||||
#include "timeval.h"
|
||||
#include "curl_md5.h"
|
||||
#include "curl_sha256.h"
|
||||
#include "warnless.h"
|
||||
#include "curl_base64.h"
|
||||
#include "curl_printf.h"
|
||||
@ -88,11 +90,6 @@
|
||||
#include "memdebug.h"
|
||||
|
||||
|
||||
/* convenience macro to check if this handle is using a shared SSL session */
|
||||
#define SSLSESSION_SHARED(data) (data->share && \
|
||||
(data->share->specifier & \
|
||||
(1<<CURL_LOCK_DATA_SSL_SESSION)))
|
||||
|
||||
#define CLONE_STRING(var) \
|
||||
do { \
|
||||
if(source->var) { \
|
||||
@ -465,9 +462,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx->ssl_impl = Curl_ssl;
|
||||
ctx->alpn = alpn;
|
||||
Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES);
|
||||
ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
|
||||
ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data);
|
||||
if(!ctx->backend) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
@ -478,7 +476,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
|
||||
static void cf_ctx_free(struct ssl_connect_data *ctx)
|
||||
{
|
||||
if(ctx) {
|
||||
Curl_safefree(ctx->alpn_negotiated);
|
||||
Curl_safefree(ctx->negotiated.alpn);
|
||||
Curl_bufq_free(&ctx->earlydata);
|
||||
free(ctx->backend);
|
||||
free(ctx);
|
||||
@ -496,7 +494,7 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
/* mark this is being ssl-enabled from here on. */
|
||||
connssl->state = ssl_connection_negotiating;
|
||||
|
||||
result = Curl_ssl->connect_blocking(cf, data);
|
||||
result = connssl->ssl_impl->connect_blocking(cf, data);
|
||||
|
||||
if(!result) {
|
||||
DEBUGASSERT(connssl->state == ssl_connection_complete);
|
||||
@ -509,275 +507,13 @@ static CURLcode
|
||||
ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
|
||||
if(!ssl_prefs_check(data))
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
|
||||
/* mark this is being ssl requested from here on. */
|
||||
return Curl_ssl->connect_nonblocking(cf, data, done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock shared SSL session data
|
||||
*/
|
||||
void Curl_ssl_sessionid_lock(struct Curl_easy *data)
|
||||
{
|
||||
if(SSLSESSION_SHARED(data))
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock shared SSL session data
|
||||
*/
|
||||
void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
|
||||
{
|
||||
if(SSLSESSION_SHARED(data))
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there is a session ID for the given connection in the cache, and if
|
||||
* there is one suitable, it is provided. Returns TRUE when no entry matched.
|
||||
*/
|
||||
bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
void **ssl_sessionid,
|
||||
size_t *idsize, /* set 0 if unknown */
|
||||
char **palpn)
|
||||
{
|
||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct Curl_ssl_session *check;
|
||||
size_t i;
|
||||
long *general_age;
|
||||
bool no_match = TRUE;
|
||||
|
||||
*ssl_sessionid = NULL;
|
||||
if(palpn)
|
||||
*palpn = NULL;
|
||||
if(!ssl_config)
|
||||
return TRUE;
|
||||
|
||||
DEBUGASSERT(ssl_config->primary.cache_session);
|
||||
|
||||
if(!ssl_config->primary.cache_session || !data->state.session)
|
||||
/* session ID reuse is disabled or the session cache has not been
|
||||
setup */
|
||||
return TRUE;
|
||||
|
||||
/* Lock if shared */
|
||||
if(SSLSESSION_SHARED(data))
|
||||
general_age = &data->share->sessionage;
|
||||
else
|
||||
general_age = &data->state.sessionage;
|
||||
|
||||
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
|
||||
check = &data->state.session[i];
|
||||
if(!check->sessionid)
|
||||
/* not session ID means blank entry */
|
||||
continue;
|
||||
if(strcasecompare(peer->hostname, check->name) &&
|
||||
((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
|
||||
(cf->conn->bits.conn_to_host && check->conn_to_host &&
|
||||
strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
|
||||
((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
|
||||
(cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
|
||||
cf->conn->conn_to_port == check->conn_to_port)) &&
|
||||
(peer->port == check->remote_port) &&
|
||||
(peer->transport == check->transport) &&
|
||||
strcasecompare(cf->conn->handler->scheme, check->scheme) &&
|
||||
match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
|
||||
/* yes, we have a session ID! */
|
||||
(*general_age)++; /* increase general age */
|
||||
check->age = *general_age; /* set this as used in this age */
|
||||
*ssl_sessionid = check->sessionid;
|
||||
if(idsize)
|
||||
*idsize = check->idsize;
|
||||
if(palpn)
|
||||
*palpn = check->alpn;
|
||||
no_match = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d",
|
||||
no_match ? "No" : "Found",
|
||||
cf->conn->handler->scheme, peer->hostname, peer->port);
|
||||
return no_match;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kill a single session ID entry in the cache.
|
||||
*/
|
||||
void Curl_ssl_kill_session(struct Curl_ssl_session *session)
|
||||
{
|
||||
if(session->sessionid) {
|
||||
/* defensive check */
|
||||
|
||||
/* free the ID the SSL-layer specific way */
|
||||
session->sessionid_free(session->sessionid, session->idsize);
|
||||
|
||||
session->sessionid = NULL;
|
||||
session->sessionid_free = NULL;
|
||||
session->age = 0; /* fresh */
|
||||
|
||||
free_primary_ssl_config(&session->ssl_config);
|
||||
|
||||
Curl_safefree(session->name);
|
||||
Curl_safefree(session->conn_to_host);
|
||||
Curl_safefree(session->alpn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the given session ID from the cache.
|
||||
*/
|
||||
void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
|
||||
struct Curl_ssl_session *check = &data->state.session[i];
|
||||
|
||||
if(check->sessionid == ssl_sessionid) {
|
||||
Curl_ssl_kill_session(check);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
const char *alpn,
|
||||
void *ssl_sessionid,
|
||||
size_t idsize,
|
||||
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 = NULL;
|
||||
char *clone_conn_to_host = NULL;
|
||||
char *clone_alpn = NULL;
|
||||
int conn_to_port;
|
||||
long *general_age;
|
||||
void *old_sessionid;
|
||||
size_t old_size;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
DEBUGASSERT(ssl_sessionid);
|
||||
DEBUGASSERT(sessionid_free_cb);
|
||||
|
||||
if(!data->state.session) {
|
||||
sessionid_free_cb(ssl_sessionid, idsize);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) {
|
||||
if((old_size == idsize) &&
|
||||
((old_sessionid == ssl_sessionid) ||
|
||||
(idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) {
|
||||
/* the very same */
|
||||
sessionid_free_cb(ssl_sessionid, idsize);
|
||||
return CURLE_OK;
|
||||
}
|
||||
Curl_ssl_delsessionid(data, old_sessionid);
|
||||
}
|
||||
|
||||
store = &data->state.session[0];
|
||||
oldest_age = data->state.session[0].age; /* zero if unused */
|
||||
DEBUGASSERT(ssl_config->primary.cache_session);
|
||||
(void)ssl_config;
|
||||
|
||||
clone_host = strdup(peer->hostname);
|
||||
if(!clone_host)
|
||||
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)
|
||||
goto out;
|
||||
}
|
||||
|
||||
clone_alpn = alpn ? strdup(alpn) : NULL;
|
||||
if(alpn && !clone_alpn)
|
||||
goto out;
|
||||
|
||||
if(cf->conn->bits.conn_to_port)
|
||||
conn_to_port = cf->conn->conn_to_port;
|
||||
else
|
||||
conn_to_port = -1;
|
||||
|
||||
/* Now we should add the session ID and the hostname to the cache, (remove
|
||||
the oldest if necessary) */
|
||||
|
||||
/* If using shared SSL session, lock! */
|
||||
if(SSLSESSION_SHARED(data)) {
|
||||
general_age = &data->share->sessionage;
|
||||
}
|
||||
else {
|
||||
general_age = &data->state.sessionage;
|
||||
}
|
||||
|
||||
/* find an empty slot for us, or find the oldest */
|
||||
for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
|
||||
data->state.session[i].sessionid; i++) {
|
||||
if(data->state.session[i].age < oldest_age) {
|
||||
oldest_age = data->state.session[i].age;
|
||||
store = &data->state.session[i];
|
||||
}
|
||||
}
|
||||
if(i == data->set.general_ssl.max_ssl_sessions)
|
||||
/* cache is full, we must "kill" the oldest entry! */
|
||||
Curl_ssl_kill_session(store);
|
||||
else
|
||||
store = &data->state.session[i]; /* use this slot */
|
||||
|
||||
/* now init the session struct wisely */
|
||||
if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
|
||||
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 is one already present */
|
||||
free(store->name);
|
||||
free(store->conn_to_host);
|
||||
store->name = clone_host; /* clone hostname */
|
||||
clone_host = NULL;
|
||||
store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */
|
||||
clone_conn_to_host = NULL;
|
||||
store->conn_to_port = conn_to_port; /* connect to port number */
|
||||
store->alpn = clone_alpn;
|
||||
clone_alpn = NULL;
|
||||
/* port number */
|
||||
store->remote_port = peer->port;
|
||||
store->scheme = cf->conn->handler->scheme;
|
||||
store->transport = peer->transport;
|
||||
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
free(clone_host);
|
||||
free(clone_conn_to_host);
|
||||
free(clone_alpn);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
return connssl->ssl_impl->connect_nonblocking(cf, data, done);
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
|
||||
@ -791,14 +527,9 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
|
||||
void Curl_ssl_close_all(struct Curl_easy *data)
|
||||
{
|
||||
/* kill the session ID cache if not shared */
|
||||
if(data->state.session && !SSLSESSION_SHARED(data)) {
|
||||
size_t i;
|
||||
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
|
||||
/* the single-killer function handles empty table slots */
|
||||
Curl_ssl_kill_session(&data->state.session[i]);
|
||||
|
||||
/* free the cache data */
|
||||
Curl_safefree(data->state.session);
|
||||
if(data->state.ssl_scache && !CURL_SHARE_ssl_scache(data)) {
|
||||
Curl_ssl_scache_destroy(data->state.ssl_scache);
|
||||
data->state.ssl_scache = NULL;
|
||||
}
|
||||
|
||||
if(Curl_ssl->close_all)
|
||||
@ -853,29 +584,6 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets up a session ID cache to the specified size. Make sure this code
|
||||
* is agnostic to what underlying SSL technology we use.
|
||||
*/
|
||||
CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
|
||||
{
|
||||
struct Curl_ssl_session *session;
|
||||
|
||||
if(data->state.session)
|
||||
/* this is just a precaution to prevent multiple inits */
|
||||
return CURLE_OK;
|
||||
|
||||
session = calloc(amount, sizeof(struct Curl_ssl_session));
|
||||
if(!session)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* store the info in the SSL section */
|
||||
data->set.general_ssl.max_ssl_sessions = amount;
|
||||
data->state.session = session;
|
||||
data->state.sessionage = 1; /* this is brand new */
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static size_t multissl_version(char *buffer, size_t size);
|
||||
|
||||
void Curl_ssl_version(char *buffer, size_t size)
|
||||
@ -1494,11 +1202,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
|
||||
|
||||
void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
|
||||
{
|
||||
Curl_safefree(peer->sni);
|
||||
if(peer->dispname != peer->hostname)
|
||||
free(peer->dispname);
|
||||
free(peer->sni);
|
||||
free(peer->hostname);
|
||||
peer->hostname = peer->sni = peer->dispname = NULL;
|
||||
peer->dispname = NULL;
|
||||
Curl_safefree(peer->hostname);
|
||||
Curl_safefree(peer->scache_key);
|
||||
peer->type = CURL_SSL_PEER_DNS;
|
||||
}
|
||||
|
||||
@ -1506,7 +1215,7 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
if(connssl) {
|
||||
Curl_ssl->close(cf, data);
|
||||
connssl->ssl_impl->close(cf, data);
|
||||
connssl->state = ssl_connection_none;
|
||||
Curl_ssl_peer_cleanup(&connssl->peer);
|
||||
}
|
||||
@ -1532,7 +1241,9 @@ static ssl_peer_type get_peer_type(const char *hostname)
|
||||
return CURL_SSL_PEER_DNS;
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
|
||||
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
|
||||
struct Curl_cfilter *cf,
|
||||
const char *tls_id,
|
||||
int transport)
|
||||
{
|
||||
const char *ehostname, *edispname;
|
||||
@ -1594,7 +1305,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
|
||||
peer->sni[len] = 0;
|
||||
}
|
||||
}
|
||||
result = CURLE_OK;
|
||||
|
||||
result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key);
|
||||
|
||||
out:
|
||||
if(result)
|
||||
@ -1657,7 +1369,9 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
|
||||
|
||||
*done = FALSE;
|
||||
if(!connssl->peer.hostname) {
|
||||
result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
|
||||
char tls_id[80];
|
||||
connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
|
||||
result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
@ -1686,11 +1400,13 @@ out:
|
||||
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
|
||||
const struct Curl_easy *data)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
bool result;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
if(Curl_ssl->data_pending && Curl_ssl->data_pending(cf, data))
|
||||
if(connssl->ssl_impl->data_pending &&
|
||||
connssl->ssl_impl->data_pending(cf, data))
|
||||
result = TRUE;
|
||||
else
|
||||
result = cf->next->cft->has_data_pending(cf->next, data);
|
||||
@ -1702,6 +1418,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, const void *buf, size_t len,
|
||||
bool eos, CURLcode *err)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
ssize_t nwritten = 0;
|
||||
|
||||
@ -1710,7 +1427,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
|
||||
*err = CURLE_OK;
|
||||
if(len > 0) {
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
|
||||
nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
return nwritten;
|
||||
@ -1720,12 +1437,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, char *buf, size_t len,
|
||||
CURLcode *err)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
ssize_t nread;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
*err = CURLE_OK;
|
||||
nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
|
||||
nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
|
||||
if(nread > 0) {
|
||||
DEBUGASSERT((size_t)nread <= len);
|
||||
}
|
||||
@ -1743,6 +1461,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*done = TRUE;
|
||||
@ -1750,7 +1469,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
|
||||
struct cf_call_data save;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
result = Curl_ssl->shut_down(cf, data, TRUE, done);
|
||||
result = connssl->ssl_impl->shut_down(cf, data, TRUE, done);
|
||||
CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
cf->shutdown = (result || *done);
|
||||
@ -1762,10 +1481,11 @@ static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
Curl_ssl->adjust_pollset(cf, data, ps);
|
||||
connssl->ssl_impl->adjust_pollset(cf, data, ps);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
|
||||
@ -1971,9 +1691,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
|
||||
/* get first SSL filter in chain, if any is present */
|
||||
cf = get_ssl_filter(data->conn->cfilter[sockindex]);
|
||||
if(cf) {
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
result = Curl_ssl->get_internals(cf->ctx, info);
|
||||
result = connssl->ssl_impl->get_internals(cf->ctx, info);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
}
|
||||
@ -2006,7 +1727,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
|
||||
result = Curl_ssl->shut_down(cf, data, send_shutdown, done);
|
||||
result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done);
|
||||
if(result ||*done)
|
||||
goto out;
|
||||
|
||||
@ -2158,28 +1879,28 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
|
||||
#endif
|
||||
;
|
||||
|
||||
if(connssl->alpn_negotiated) {
|
||||
if(connssl->negotiated.alpn) {
|
||||
/* When we ask for a specific ALPN protocol, we need the confirmation
|
||||
* of it by the server, as we have installed protocol handler and
|
||||
* connection filter chain for exactly this protocol. */
|
||||
if(!proto_len) {
|
||||
failf(data, "ALPN: asked for '%s' from previous session, "
|
||||
"but server did not confirm it. Refusing to continue.",
|
||||
connssl->alpn_negotiated);
|
||||
connssl->negotiated.alpn);
|
||||
result = CURLE_SSL_CONNECT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
else if((strlen(connssl->alpn_negotiated) != proto_len) ||
|
||||
memcmp(connssl->alpn_negotiated, proto, proto_len)) {
|
||||
else if((strlen(connssl->negotiated.alpn) != proto_len) ||
|
||||
memcmp(connssl->negotiated.alpn, proto, proto_len)) {
|
||||
failf(data, "ALPN: asked for '%s' from previous session, but server "
|
||||
"selected '%.*s'. Refusing to continue.",
|
||||
connssl->alpn_negotiated, (int)proto_len, proto);
|
||||
connssl->negotiated.alpn, (int)proto_len, proto);
|
||||
result = CURLE_SSL_CONNECT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
/* ALPN is exactly what we asked for, done. */
|
||||
infof(data, "ALPN: server confirmed to use '%s'",
|
||||
connssl->alpn_negotiated);
|
||||
connssl->negotiated.alpn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -2190,11 +1911,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
|
||||
result = CURLE_SSL_CONNECT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
connssl->alpn_negotiated = malloc(proto_len + 1);
|
||||
if(!connssl->alpn_negotiated)
|
||||
connssl->negotiated.alpn = malloc(proto_len + 1);
|
||||
if(!connssl->negotiated.alpn)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
memcpy(connssl->alpn_negotiated, proto, proto_len);
|
||||
connssl->alpn_negotiated[proto_len] = 0;
|
||||
memcpy(connssl->negotiated.alpn, proto, proto_len);
|
||||
connssl->negotiated.alpn[proto_len] = 0;
|
||||
}
|
||||
|
||||
if(proto && proto_len) {
|
||||
|
@ -28,7 +28,9 @@
|
||||
struct connectdata;
|
||||
struct ssl_config_data;
|
||||
struct ssl_primary_config;
|
||||
struct Curl_ssl_session;
|
||||
struct Curl_cfilter;
|
||||
struct Curl_easy;
|
||||
struct dynbuf;
|
||||
|
||||
#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
|
||||
#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
|
||||
@ -63,9 +65,31 @@ struct Curl_ssl_session;
|
||||
#define VTLS_INFOF_ALPN_DEFERRED \
|
||||
"ALPN: deferred handshake for early data using '%.*s'."
|
||||
|
||||
/* Curl_multi SSL backend-specific data; declared differently by each SSL
|
||||
backend */
|
||||
struct Curl_cfilter;
|
||||
/* IETF defined version numbers used in TLS protocol negotiation */
|
||||
#define CURL_IETF_PROTO_UNKNOWN 0x0
|
||||
#define CURL_IETF_PROTO_SSL3 0x0300
|
||||
#define CURL_IETF_PROTO_TLS1 0x0301
|
||||
#define CURL_IETF_PROTO_TLS1_1 0x0302
|
||||
#define CURL_IETF_PROTO_TLS1_2 0x0303
|
||||
#define CURL_IETF_PROTO_TLS1_3 0x0304
|
||||
#define CURL_IETF_PROTO_DTLS1 0xFEFF
|
||||
#define CURL_IETF_PROTO_DTLS1_2 0xFEFD
|
||||
|
||||
typedef enum {
|
||||
CURL_SSL_PEER_DNS,
|
||||
CURL_SSL_PEER_IPV4,
|
||||
CURL_SSL_PEER_IPV6
|
||||
} ssl_peer_type;
|
||||
|
||||
struct ssl_peer {
|
||||
char *hostname; /* hostname for verification */
|
||||
char *dispname; /* display version of hostname */
|
||||
char *sni; /* SNI version of hostname or NULL if not usable */
|
||||
char *scache_key; /* for lookups in session cache */
|
||||
ssl_peer_type type; /* type of the peer information */
|
||||
int port; /* port we are talking to */
|
||||
int transport; /* one of TRNSPRT_* defines */
|
||||
};
|
||||
|
||||
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
|
||||
const curl_ssl_backend ***avail);
|
||||
@ -121,7 +145,9 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
|
||||
* Init SSL peer information for filter. Can be called repeatedly.
|
||||
*/
|
||||
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
|
||||
struct Curl_cfilter *cf, int transport);
|
||||
struct Curl_cfilter *cf,
|
||||
const char *tls_id,
|
||||
int transport);
|
||||
/**
|
||||
* Free all allocated data and reset peer information.
|
||||
*/
|
||||
@ -138,8 +164,6 @@ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine);
|
||||
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
|
||||
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
|
||||
|
||||
/* init the SSL session ID cache */
|
||||
CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
|
||||
void Curl_ssl_version(char *buffer, size_t size);
|
||||
|
||||
/* Certificate information list handling. */
|
||||
@ -155,33 +179,6 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum,
|
||||
|
||||
/* Functions to be used by SSL library adaptation functions */
|
||||
|
||||
/* Lock session cache mutex.
|
||||
* Call this before calling other Curl_ssl_*session* functions
|
||||
* Caller should unlock this mutex as soon as possible, as it may block
|
||||
* other SSL connection from making progress.
|
||||
* The purpose of explicitly locking SSL session cache data is to allow
|
||||
* individual SSL engines to manage session lifetime in their specific way.
|
||||
*/
|
||||
void Curl_ssl_sessionid_lock(struct Curl_easy *data);
|
||||
|
||||
/* Unlock session cache mutex */
|
||||
void Curl_ssl_sessionid_unlock(struct Curl_easy *data);
|
||||
|
||||
/* Kill a single session ID entry in the cache
|
||||
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
|
||||
* This will call engine-specific curlssl_session_free function, which must
|
||||
* take sessionid object ownership from sessionid cache
|
||||
* (e.g. decrement refcount).
|
||||
*/
|
||||
void Curl_ssl_kill_session(struct Curl_ssl_session *session);
|
||||
/* delete a session from the cache
|
||||
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
|
||||
* This will call engine-specific curlssl_session_free function, which must
|
||||
* take sessionid object ownership from sessionid cache
|
||||
* (e.g. decrement refcount).
|
||||
*/
|
||||
void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid);
|
||||
|
||||
/* get N random bytes into the buffer */
|
||||
CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
|
||||
size_t length);
|
||||
@ -273,9 +270,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
|
||||
#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
|
||||
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
|
||||
#define Curl_ssl_engines_list(x) NULL
|
||||
#define Curl_ssl_initsessions(x,y) CURLE_OK
|
||||
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
|
||||
#define Curl_ssl_kill_session(x) Curl_nop_stmt
|
||||
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
|
||||
#define Curl_ssl_cert_status_request() FALSE
|
||||
#define Curl_ssl_false_start() FALSE
|
||||
|
@ -26,9 +26,11 @@
|
||||
#include "curl_setup.h"
|
||||
#include "cfilters.h"
|
||||
#include "urldata.h"
|
||||
#include "vtls.h"
|
||||
|
||||
#ifdef USE_SSL
|
||||
|
||||
struct Curl_ssl;
|
||||
struct ssl_connect_data;
|
||||
|
||||
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
|
||||
@ -103,12 +105,15 @@ typedef enum {
|
||||
|
||||
/* Information in each SSL cfilter context: cf->ctx */
|
||||
struct ssl_connect_data {
|
||||
struct ssl_peer peer;
|
||||
const struct Curl_ssl *ssl_impl; /* TLS backend for this filter */
|
||||
struct ssl_peer peer; /* peer the filter talks to */
|
||||
const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
|
||||
void *backend; /* vtls backend specific props */
|
||||
struct cf_call_data call_data; /* data handle used in current call */
|
||||
struct curltime handshake_done; /* time when handshake finished */
|
||||
char *alpn_negotiated; /* negotiated ALPN value or NULL */
|
||||
struct {
|
||||
char *alpn; /* ALPN value or NULL */
|
||||
} negotiated;
|
||||
struct bufq earlydata; /* earlydata to be send to peer */
|
||||
size_t earlydata_max; /* max earlydata allowed by peer */
|
||||
size_t earlydata_skip; /* sending bytes to skip when earlydata
|
||||
@ -193,43 +198,6 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
*/
|
||||
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
|
||||
|
||||
/* extract a session ID
|
||||
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
|
||||
* Caller must make sure that the ownership of returned sessionid object
|
||||
* is properly taken (e.g. its refcount is incremented
|
||||
* under sessionid mutex).
|
||||
* @param cf the connection filter wanting to use it
|
||||
* @param data the transfer involved
|
||||
* @param peer the peer the filter wants to talk to
|
||||
* @param sessionid on return the TLS session
|
||||
* @param idsize on return the size of the TLS session data
|
||||
* @param palpn on return the ALPN string used by the session,
|
||||
* set to NULL when not interested
|
||||
*/
|
||||
bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
void **ssl_sessionid,
|
||||
size_t *idsize, /* set 0 if unknown */
|
||||
char **palpn);
|
||||
|
||||
/* Set a TLS session ID for `peer`. Replaces an existing session ID if
|
||||
* not already the same.
|
||||
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
|
||||
* Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb`
|
||||
* to deallocate it. Is called in all outcomes, either right away or
|
||||
* later when the session cache is cleaned up.
|
||||
* Caller must ensure that it has properly shared ownership of this sessionid
|
||||
* object with cache (e.g. incrementing refcount on success)
|
||||
*/
|
||||
CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const struct ssl_peer *peer,
|
||||
const char *alpn,
|
||||
void *sessionid,
|
||||
size_t sessionid_size,
|
||||
Curl_ssl_sessionid_dtor *sessionid_free_cb);
|
||||
|
||||
#endif /* USE_SSL */
|
||||
|
||||
#endif /* HEADER_CURL_VTLS_INT_H */
|
||||
|
876
lib/vtls/vtls_scache.c
Normal file
876
lib/vtls/vtls_scache.c
Normal file
@ -0,0 +1,876 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* 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
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* This file is for implementing all "generic" SSL functions that all libcurl
|
||||
internals should use. It is then responsible for calling the proper
|
||||
"backend" function.
|
||||
|
||||
SSL-functions in libcurl should call functions in this source file, and not
|
||||
to any specific SSL-layer.
|
||||
|
||||
Curl_ssl_ - prefix for generic ones
|
||||
|
||||
Note that this source code uses the functions of the configured SSL
|
||||
backend via the global Curl_ssl instance.
|
||||
|
||||
"SSL/TLS Strong Encryption: An Introduction"
|
||||
https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
|
||||
*/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_SSL
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
|
||||
#include "vtls.h" /* generic SSL protos etc */
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
|
||||
#include "strcase.h"
|
||||
#include "url.h"
|
||||
#include "llist.h"
|
||||
#include "share.h"
|
||||
#include "curl_trc.h"
|
||||
#include "curl_sha256.h"
|
||||
#include "warnless.h"
|
||||
#include "curl_printf.h"
|
||||
#include "strdup.h"
|
||||
|
||||
/* The last #include files should be: */
|
||||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
/* a peer+tls-config we cache sessions for */
|
||||
struct Curl_ssl_scache_peer {
|
||||
char *ssl_peer_key; /* id for peer + relevant TLS configuration */
|
||||
char *clientcert;
|
||||
char *srp_username;
|
||||
char *srp_password;
|
||||
struct Curl_llist sessions;
|
||||
void *sobj; /* object instance or NULL */
|
||||
Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
|
||||
unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
|
||||
unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
|
||||
size_t max_sessions;
|
||||
long age; /* just a number, the higher the more recent */
|
||||
BIT(hmac_set); /* if key_salt and key_hmac are present */
|
||||
};
|
||||
|
||||
struct Curl_ssl_scache {
|
||||
struct Curl_ssl_scache_peer *peers;
|
||||
size_t peer_count;
|
||||
int default_lifetime_secs;
|
||||
long age;
|
||||
};
|
||||
|
||||
static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
|
||||
{
|
||||
if(s->sdata) {
|
||||
free((void *)s->sdata);
|
||||
s->sdata = NULL;
|
||||
}
|
||||
s->sdata_len = 0;
|
||||
s->ietf_tls_id = 0;
|
||||
s->time_received = 0;
|
||||
s->lifetime_secs = 0;
|
||||
Curl_safefree(s->alpn);
|
||||
}
|
||||
|
||||
static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
|
||||
{
|
||||
(void)udata;
|
||||
cf_ssl_scache_clear_session(s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
CURLcode
|
||||
Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
|
||||
int ietf_tls_id, const char *alpn,
|
||||
curl_off_t time_received, long lifetime_secs,
|
||||
struct Curl_ssl_session **psession)
|
||||
{
|
||||
struct Curl_ssl_session *s;
|
||||
|
||||
if(!sdata || !sdata_len) {
|
||||
free(sdata);
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
|
||||
*psession = NULL;
|
||||
s = calloc(1, sizeof(*s));
|
||||
if(!s) {
|
||||
free(sdata);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
s->ietf_tls_id = ietf_tls_id;
|
||||
s->time_received = time_received;
|
||||
if(lifetime_secs < 0)
|
||||
lifetime_secs = -1; /* unknown */
|
||||
else if((s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) &&
|
||||
(lifetime_secs > CURL_SCACHE_MAX_13_LIFETIME_SEC))
|
||||
lifetime_secs = CURL_SCACHE_MAX_13_LIFETIME_SEC;
|
||||
else if(lifetime_secs > CURL_SCACHE_MAX_12_LIFETIME_SEC)
|
||||
lifetime_secs = CURL_SCACHE_MAX_12_LIFETIME_SEC;
|
||||
|
||||
s->lifetime_secs = (int)lifetime_secs;
|
||||
s->sdata = sdata;
|
||||
s->sdata_len = sdata_len;
|
||||
if(alpn) {
|
||||
s->alpn = strdup(alpn);
|
||||
if(!s->alpn) {
|
||||
cf_ssl_scache_sesssion_ldestroy(NULL, s);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
*psession = s;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
|
||||
{
|
||||
if(s) {
|
||||
/* if in the list, the list destructor takes care of it */
|
||||
if(Curl_node_llist(&s->list))
|
||||
Curl_node_remove(&s->list);
|
||||
else {
|
||||
cf_ssl_scache_sesssion_ldestroy(NULL, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
|
||||
{
|
||||
Curl_llist_destroy(&peer->sessions, NULL);
|
||||
if(peer->sobj) {
|
||||
DEBUGASSERT(peer->sobj_free);
|
||||
if(peer->sobj_free)
|
||||
peer->sobj_free(peer->sobj);
|
||||
peer->sobj = NULL;
|
||||
}
|
||||
peer->sobj_free = NULL;
|
||||
Curl_safefree(peer->clientcert);
|
||||
#ifdef USE_TLS_SRP
|
||||
Curl_safefree(peer->srp_username);
|
||||
Curl_safefree(peer->srp_password);
|
||||
#endif
|
||||
Curl_safefree(peer->ssl_peer_key);
|
||||
peer->age = 0;
|
||||
peer->hmac_set = FALSE;
|
||||
}
|
||||
|
||||
static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
|
||||
void *sobj,
|
||||
Curl_ssl_scache_obj_dtor *sobj_free)
|
||||
{
|
||||
DEBUGASSERT(peer);
|
||||
if(peer->sobj_free) {
|
||||
peer->sobj_free(peer->sobj);
|
||||
}
|
||||
peer->sobj = sobj;
|
||||
peer->sobj_free = sobj_free;
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
|
||||
const char *ssl_peer_key,
|
||||
const char *clientcert,
|
||||
const char *srp_username,
|
||||
const char *srp_password)
|
||||
{
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
DEBUGASSERT(!peer->ssl_peer_key);
|
||||
peer->ssl_peer_key = strdup(ssl_peer_key);
|
||||
if(!peer->ssl_peer_key)
|
||||
goto out;
|
||||
if(clientcert) {
|
||||
peer->clientcert = strdup(clientcert);
|
||||
if(!peer->clientcert)
|
||||
goto out;
|
||||
}
|
||||
if(srp_username) {
|
||||
peer->srp_username = strdup(srp_username);
|
||||
if(!peer->srp_username)
|
||||
goto out;
|
||||
}
|
||||
if(srp_password) {
|
||||
peer->srp_password = strdup(srp_password);
|
||||
if(!peer->srp_password)
|
||||
goto out;
|
||||
}
|
||||
result = CURLE_OK;
|
||||
out:
|
||||
if(result)
|
||||
cf_ssl_scache_clear_peer(peer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
|
||||
struct Curl_ssl_session *s)
|
||||
{
|
||||
(void)peer;
|
||||
DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
|
||||
Curl_ssl_session_destroy(s);
|
||||
}
|
||||
|
||||
static bool cf_scache_session_expired(struct Curl_ssl_session *s,
|
||||
curl_off_t now)
|
||||
{
|
||||
return (s->lifetime_secs > 0 &&
|
||||
(s->time_received + s->lifetime_secs) < now);
|
||||
}
|
||||
|
||||
static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
|
||||
curl_off_t now)
|
||||
{
|
||||
struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
|
||||
while(n) {
|
||||
struct Curl_ssl_session *s = Curl_node_elem(n);
|
||||
n = Curl_node_next(n);
|
||||
if(cf_scache_session_expired(s, now))
|
||||
cf_scache_session_remove(peer, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
|
||||
{
|
||||
struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
|
||||
while(n) {
|
||||
struct Curl_ssl_session *s = Curl_node_elem(n);
|
||||
n = Curl_node_next(n);
|
||||
if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
|
||||
cf_scache_session_remove(peer, s);
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_scache_create(size_t max_peers,
|
||||
size_t max_sessions_per_peer,
|
||||
struct Curl_ssl_scache **pscache)
|
||||
{
|
||||
struct Curl_ssl_scache *scache;
|
||||
struct Curl_ssl_scache_peer *peers;
|
||||
size_t i;
|
||||
|
||||
*pscache = NULL;
|
||||
peers = calloc(max_peers, sizeof(*peers));
|
||||
if(!peers)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
scache = calloc(1, sizeof(*scache));
|
||||
if(!scache) {
|
||||
free(peers);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
scache->default_lifetime_secs = (24*60*60); /* 1 day */
|
||||
scache->peer_count = max_peers;
|
||||
scache->peers = peers;
|
||||
scache->age = 1;
|
||||
for(i = 0; i < scache->peer_count; ++i) {
|
||||
scache->peers[i].max_sessions = max_sessions_per_peer;
|
||||
Curl_llist_init(&scache->peers[i].sessions,
|
||||
cf_ssl_scache_sesssion_ldestroy);
|
||||
}
|
||||
|
||||
*pscache = scache;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
|
||||
{
|
||||
if(scache) {
|
||||
size_t i;
|
||||
for(i = 0; i < scache->peer_count; ++i) {
|
||||
cf_ssl_scache_clear_peer(&scache->peers[i]);
|
||||
}
|
||||
free(scache->peers);
|
||||
free(scache);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lock shared SSL session data */
|
||||
void Curl_ssl_scache_lock(struct Curl_easy *data)
|
||||
{
|
||||
if(CURL_SHARE_ssl_scache(data))
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
|
||||
}
|
||||
|
||||
/* Unlock shared SSL session data */
|
||||
void Curl_ssl_scache_unlock(struct Curl_easy *data)
|
||||
{
|
||||
if(CURL_SHARE_ssl_scache(data))
|
||||
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
|
||||
const char *name,
|
||||
char *path)
|
||||
{
|
||||
if(path && path[0]) {
|
||||
/* We try to add absolute paths, so that the session key can stay
|
||||
* valid when used in another process with different CWD. However,
|
||||
* when a path does not exist, this does not work. Then, we add
|
||||
* the path as is. */
|
||||
#ifdef _WIN32
|
||||
char abspath[_MAX_PATH];
|
||||
if(_fullpath(abspath, path, _MAX_PATH))
|
||||
return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
|
||||
#else
|
||||
if(path[0] != '/') {
|
||||
char *abspath = realpath(path, NULL);
|
||||
if(abspath) {
|
||||
CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
|
||||
(free)(abspath); /* allocated by libc, free without memdebug */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return Curl_dyn_addf(buf, ":%s-%s", name, path);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
|
||||
const char *name,
|
||||
struct curl_blob *blob)
|
||||
{
|
||||
CURLcode r = CURLE_OK;
|
||||
if(blob && blob->len) {
|
||||
unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
|
||||
size_t i;
|
||||
|
||||
r = Curl_dyn_addf(buf, ":%s-", name);
|
||||
if(r)
|
||||
goto out;
|
||||
r = Curl_sha256it(hash, blob->data, blob->len);
|
||||
if(r)
|
||||
goto out;
|
||||
for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
|
||||
r = Curl_dyn_addf(buf, "%02x", hash[i]);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
|
||||
const struct ssl_peer *peer,
|
||||
const char *tls_id,
|
||||
char **ppeer_key)
|
||||
{
|
||||
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct dynbuf buf;
|
||||
size_t key_len;
|
||||
CURLcode r;
|
||||
|
||||
*ppeer_key = NULL;
|
||||
Curl_dyn_init(&buf, 10 * 1024);
|
||||
|
||||
r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
switch(peer->transport) {
|
||||
case TRNSPRT_TCP:
|
||||
break;
|
||||
case TRNSPRT_UDP:
|
||||
r = Curl_dyn_add(&buf, ":UDP");
|
||||
break;
|
||||
case TRNSPRT_QUIC:
|
||||
r = Curl_dyn_add(&buf, ":QUIC");
|
||||
break;
|
||||
case TRNSPRT_UNIX:
|
||||
r = Curl_dyn_add(&buf, ":UNIX");
|
||||
break;
|
||||
default:
|
||||
r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
|
||||
break;
|
||||
}
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
if(!ssl->verifypeer) {
|
||||
r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(!ssl->verifyhost) {
|
||||
r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->verifystatus) {
|
||||
r = Curl_dyn_add(&buf, ":VRFY-STATUS");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(!ssl->verifypeer || !ssl->verifyhost) {
|
||||
if(cf->conn->bits.conn_to_host) {
|
||||
r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(cf->conn->bits.conn_to_port) {
|
||||
r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(ssl->version || ssl->version_max) {
|
||||
r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
|
||||
(ssl->version_max >> 16));
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->ssl_options) {
|
||||
r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->cipher_list) {
|
||||
r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->cipher_list13) {
|
||||
r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->curves) {
|
||||
r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->verifypeer) {
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
|
||||
if(r)
|
||||
goto out;
|
||||
r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
|
||||
if(r)
|
||||
goto out;
|
||||
if(ssl->cert_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->ca_info_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
if(ssl->issuercert_blob) {
|
||||
r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if(ssl->pinned_key && ssl->pinned_key[0]) {
|
||||
r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ssl->clientcert && ssl->clientcert[0]) {
|
||||
r = Curl_dyn_add(&buf, ":CCERT");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
#ifdef USE_TLS_SRP
|
||||
if(ssl->username || ssl->password) {
|
||||
r = Curl_dyn_add(&buf, ":SRP-AUTH");
|
||||
if(r)
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!tls_id || !tls_id[0]) {
|
||||
r = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
|
||||
if(r)
|
||||
goto out;
|
||||
|
||||
*ppeer_key = Curl_dyn_take(&buf, &key_len);
|
||||
/* we just added printable char, and dynbuf always 0 terminates,
|
||||
* no need to track length */
|
||||
|
||||
|
||||
out:
|
||||
Curl_dyn_free(&buf);
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
|
||||
struct ssl_primary_config *conn_config)
|
||||
{
|
||||
if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
|
||||
return FALSE;
|
||||
#ifdef USE_TLS_SRP
|
||||
if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
|
||||
Curl_timestrcmp(peer->srp_password, conn_config->password))
|
||||
return FALSE;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_find_peer(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_ssl_scache *scache,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_scache_peer **ppeer)
|
||||
{
|
||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
size_t i, peer_key_len = 0;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*ppeer = NULL;
|
||||
if(!ssl_config || !ssl_config->primary.cache_session)
|
||||
goto out;
|
||||
|
||||
/* check for entries with known peer_key */
|
||||
for(i = 0; scache && i < scache->peer_count; i++) {
|
||||
if(scache->peers[i].ssl_peer_key &&
|
||||
strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
|
||||
cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
|
||||
/* yes, we have a cached session for this! */
|
||||
*ppeer = &scache->peers[i];
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* check for entries with HMAC set but no known peer_key */
|
||||
for(i = 0; scache && i < scache->peer_count; i++) {
|
||||
if(!scache->peers[i].ssl_peer_key &&
|
||||
scache->peers[i].hmac_set &&
|
||||
cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
|
||||
/* possible entry with unknown peer_key, check hmac */
|
||||
unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
|
||||
if(!peer_key_len) /* we are lazy */
|
||||
peer_key_len = strlen(ssl_peer_key);
|
||||
(void)Curl_hmacit(&Curl_HMAC_SHA256,
|
||||
scache->peers[i].key_salt,
|
||||
sizeof(scache->peers[i].key_salt),
|
||||
(const unsigned char *)ssl_peer_key,
|
||||
peer_key_len,
|
||||
my_hmac);
|
||||
if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
|
||||
/* remember peer_key for future lookups */
|
||||
scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
|
||||
if(!scache->peers[i].ssl_peer_key) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
*ppeer = &scache->peers[i];
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
if(result)
|
||||
CURL_TRC_CF(data, cf, "[SACHE] failure finding scache peer: %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_ssl_add_peer(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_ssl_scache *scache,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_scache_peer **ppeer)
|
||||
{
|
||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct Curl_ssl_scache_peer *peer = NULL;
|
||||
size_t i;
|
||||
CURLcode result;
|
||||
|
||||
*ppeer = NULL;
|
||||
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
|
||||
if(result || !scache->peer_count)
|
||||
return result;
|
||||
|
||||
if(peer) {
|
||||
*ppeer = peer;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* not there, find empty or oldest peer */
|
||||
for(i = 0; i < scache->peer_count; ++i) {
|
||||
/* free peer entry? */
|
||||
if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
|
||||
peer = &scache->peers[i];
|
||||
break;
|
||||
}
|
||||
/* peer without sessions and obj */
|
||||
if(!scache->peers[i].sobj &&
|
||||
!Curl_llist_count(&scache->peers[i].sessions)) {
|
||||
peer = &scache->peers[i];
|
||||
break;
|
||||
}
|
||||
/* remember "oldest" peer */
|
||||
if(!peer || (scache->peers[i].age < peer->age)) {
|
||||
peer = &scache->peers[i];
|
||||
}
|
||||
}
|
||||
DEBUGASSERT(peer);
|
||||
if(!peer)
|
||||
return CURLE_OK;
|
||||
/* clear previous peer and reinit */
|
||||
cf_ssl_scache_clear_peer(peer);
|
||||
result = cf_ssl_scache_peer_init(peer, ssl_peer_key,
|
||||
conn_config->clientcert,
|
||||
#ifdef USE_TLS_SRP
|
||||
conn_config->username,
|
||||
conn_config->password);
|
||||
#else
|
||||
NULL, NULL);
|
||||
#endif
|
||||
if(result)
|
||||
goto out;
|
||||
/* all ready */
|
||||
*ppeer = peer;
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
if(result) {
|
||||
cf_ssl_scache_clear_peer(peer);
|
||||
CURL_TRC_CF(data, cf, "[SACHE] failure adding peer: %d", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_scache_peer_add_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_ssl_scache *scache,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session *s)
|
||||
{
|
||||
struct Curl_ssl_scache_peer *peer = NULL;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
curl_off_t now = (curl_off_t)time(NULL);
|
||||
|
||||
if(!scache || !scache->peer_count) {
|
||||
Curl_ssl_session_destroy(s);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(!s->time_received)
|
||||
s->time_received = now;
|
||||
if(s->lifetime_secs < 0)
|
||||
s->lifetime_secs = scache->default_lifetime_secs;
|
||||
|
||||
if(cf_scache_session_expired(s, now)) {
|
||||
CURL_TRC_CF(data, cf, "[SCACHE] add, session already expired");
|
||||
Curl_ssl_session_destroy(s);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
|
||||
if(result || !peer) {
|
||||
CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
|
||||
Curl_ssl_session_destroy(s);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* A session not from TLSv1.3 replaces all other. */
|
||||
if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
|
||||
Curl_llist_destroy(&peer->sessions, NULL);
|
||||
Curl_llist_append(&peer->sessions, s, &s->list);
|
||||
}
|
||||
else {
|
||||
/* Expire existing, append, trim from head to obey max_sessions */
|
||||
cf_scache_peer_remove_expired(peer, now);
|
||||
cf_scache_peer_remove_non13(peer);
|
||||
Curl_llist_append(&peer->sessions, s, &s->list);
|
||||
while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
|
||||
Curl_node_remove(Curl_llist_head(&peer->sessions));
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if(result) {
|
||||
failf(data, "[SCACHE] failed to add session for %s, error=%d",
|
||||
ssl_peer_key, result);
|
||||
}
|
||||
else
|
||||
CURL_TRC_CF(data, cf, "[SCACHE] added session for %s [proto=0x%x, "
|
||||
"lifetime=%d, alpn=%s], peer has %zu sessions now",
|
||||
ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
|
||||
Curl_llist_count(&peer->sessions));
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session *s)
|
||||
{
|
||||
struct Curl_ssl_scache *scache = data->state.ssl_scache;
|
||||
CURLcode result;
|
||||
|
||||
Curl_ssl_scache_lock(data);
|
||||
result = cf_scache_peer_add_session(cf, data, scache, ssl_peer_key, s);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Curl_ssl_scache_return(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session *s)
|
||||
{
|
||||
/* See RFC 8446 C.4:
|
||||
* "Clients SHOULD NOT reuse a ticket for multiple connections." */
|
||||
if(s && s->ietf_tls_id < 0x304)
|
||||
(void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
|
||||
else
|
||||
Curl_ssl_session_destroy(s);
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session **ps)
|
||||
{
|
||||
struct Curl_ssl_scache *scache = data->state.ssl_scache;
|
||||
struct Curl_ssl_scache_peer *peer = NULL;
|
||||
struct Curl_llist_node *n;
|
||||
CURLcode result;
|
||||
|
||||
*ps = NULL;
|
||||
if(!scache)
|
||||
return CURLE_OK;
|
||||
|
||||
Curl_ssl_scache_lock(data);
|
||||
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
|
||||
if(!result && peer) {
|
||||
cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
|
||||
n = Curl_llist_head(&peer->sessions);
|
||||
if(n) {
|
||||
*ps = Curl_node_take_elem(n);
|
||||
(scache->age)++; /* increase general age */
|
||||
peer->age = scache->age; /* set this as used in this age */
|
||||
}
|
||||
}
|
||||
Curl_ssl_scache_unlock(data);
|
||||
|
||||
CURL_TRC_CF(data, cf, "[SCACHE] %s cached session for '%s'",
|
||||
*ps ? "Found" : "No", ssl_peer_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
void *sobj,
|
||||
Curl_ssl_scache_obj_dtor *sobj_free)
|
||||
{
|
||||
struct Curl_ssl_scache *scache = data->state.ssl_scache;
|
||||
struct Curl_ssl_scache_peer *peer = NULL;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(sobj);
|
||||
DEBUGASSERT(sobj_free);
|
||||
|
||||
result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
|
||||
if(result || !peer) {
|
||||
CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
|
||||
sobj = NULL; /* peer took ownership */
|
||||
|
||||
out:
|
||||
if(sobj && sobj_free)
|
||||
sobj_free(sobj);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
void **sobj)
|
||||
{
|
||||
struct Curl_ssl_scache *scache = data->state.ssl_scache;
|
||||
struct Curl_ssl_scache_peer *peer = NULL;
|
||||
CURLcode result;
|
||||
|
||||
*sobj = NULL;
|
||||
if(!scache)
|
||||
return FALSE;
|
||||
|
||||
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
|
||||
if(result)
|
||||
return FALSE;
|
||||
|
||||
if(peer)
|
||||
*sobj = peer->sobj;
|
||||
|
||||
CURL_TRC_CF(data, cf, "[SACHE] %s cached session for '%s'",
|
||||
*sobj ? "Found" : "No", ssl_peer_key);
|
||||
return !!*sobj;
|
||||
}
|
||||
|
||||
void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key)
|
||||
{
|
||||
struct Curl_ssl_scache *scache = data->state.ssl_scache;
|
||||
struct Curl_ssl_scache_peer *peer = NULL;
|
||||
CURLcode result;
|
||||
|
||||
(void)cf;
|
||||
if(!scache)
|
||||
return;
|
||||
|
||||
Curl_ssl_scache_lock(data);
|
||||
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
|
||||
if(!result && peer)
|
||||
cf_ssl_scache_clear_peer(peer);
|
||||
Curl_ssl_scache_unlock(data);
|
||||
}
|
||||
|
||||
#endif /* USE_SSL */
|
196
lib/vtls/vtls_scache.h
Normal file
196
lib/vtls/vtls_scache.h
Normal file
@ -0,0 +1,196 @@
|
||||
#ifndef HEADER_CURL_VTLS_SCACHE_H
|
||||
#define HEADER_CURL_VTLS_SCACHE_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* 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
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
#include "cfilters.h"
|
||||
#include "urldata.h"
|
||||
|
||||
#ifdef USE_SSL
|
||||
|
||||
struct Curl_cfilter;
|
||||
struct Curl_easy;
|
||||
struct Curl_ssl_scache;
|
||||
struct Curl_ssl_session;
|
||||
struct ssl_peer;
|
||||
|
||||
/* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for
|
||||
* other, less secure versions, we restrict it to a day */
|
||||
#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7)
|
||||
#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24)
|
||||
|
||||
/* Create a session cache for up to max_peers endpoints with a total
|
||||
* of up to max_sessions SSL sessions per peer */
|
||||
CURLcode Curl_ssl_scache_create(size_t max_peers,
|
||||
size_t max_sessions_per_peer,
|
||||
struct Curl_ssl_scache **pscache);
|
||||
|
||||
void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache);
|
||||
|
||||
/* Create a key from peer and TLS configuration information that is
|
||||
* unique for how the connection filter wants to establish a TLS
|
||||
* connection to the peer.
|
||||
* If the filter is a TLS proxy filter, it will use the proxy relevant
|
||||
* information.
|
||||
* @param cf the connection filter wanting to use it
|
||||
* @param peer the peer the filter wants to talk to
|
||||
* @param tls_id identifier of TLS implementation for sessions. Should
|
||||
* include full version if session data from other versions
|
||||
* is to be avoided.
|
||||
* @param ppeer_key on successful return, the key generated
|
||||
*/
|
||||
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
|
||||
const struct ssl_peer *peer,
|
||||
const char *tls_id,
|
||||
char **ppeer_key);
|
||||
|
||||
/* Lock session cache mutex.
|
||||
* Call this before calling other Curl_ssl_*session* functions
|
||||
* Caller should unlock this mutex as soon as possible, as it may block
|
||||
* other SSL connection from making progress.
|
||||
* The purpose of explicitly locking SSL session cache data is to allow
|
||||
* individual SSL engines to manage session lifetime in their specific way.
|
||||
*/
|
||||
void Curl_ssl_scache_lock(struct Curl_easy *data);
|
||||
|
||||
/* Unlock session cache mutex */
|
||||
void Curl_ssl_scache_unlock(struct Curl_easy *data);
|
||||
|
||||
/* Get TLS session object from the cache for the ssl_peer_ey.
|
||||
* scache mutex must be locked (see Curl_ssl_scache_lock).
|
||||
* Caller must make sure that the ownership of returned session object
|
||||
* is properly taken (e.g. its refcount is incremented
|
||||
* under scache mutex).
|
||||
* @param cf the connection filter wanting to use it
|
||||
* @param data the transfer involved
|
||||
* @param ssl_peer_key the key for lookup
|
||||
* @param sobj on return, the object for the peer key or NULL
|
||||
*/
|
||||
bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
void **sobj);
|
||||
|
||||
typedef void Curl_ssl_scache_obj_dtor(void *sobj);
|
||||
|
||||
/* Add a TLS session related object to the cache.
|
||||
* Replaces an existing object with the same peer_key.
|
||||
* scache mutex must be locked (see Curl_ssl_scache_lock).
|
||||
* Call takes ownership of `sobj`, using `sobj_dtor_cb`
|
||||
* to deallocate it. Is called in all outcomes, either right away or
|
||||
* later when the session cache is cleaned up.
|
||||
* Caller must ensure that it has properly shared ownership of `sobj`
|
||||
* with cache (e.g. incrementing refcount on success)
|
||||
* @param cf the connection filter wanting to use it
|
||||
* @param data the transfer involved
|
||||
* @param ssl_peer_key the key for lookup
|
||||
* @param sobj the TLS session object
|
||||
* @param sobj_free_cb callback to free the session objectt
|
||||
*/
|
||||
CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
void *sobj,
|
||||
Curl_ssl_scache_obj_dtor *sobj_dtor_cb);
|
||||
|
||||
/* All about a SSL session ticket */
|
||||
struct Curl_ssl_session {
|
||||
const unsigned char *sdata; /* session ticket data, plain bytes */
|
||||
size_t sdata_len; /* number of bytes in sdata */
|
||||
curl_off_t time_received; /* seconds since EPOCH ticket was received */
|
||||
int lifetime_secs; /* ticket lifetime (-1 unknown) */
|
||||
int ietf_tls_id; /* TLS protocol identifier negotiated */
|
||||
char *alpn; /* APLN TLS negotiated protocol string */
|
||||
struct Curl_llist_node list; /* internal storage handling */
|
||||
};
|
||||
|
||||
/* Create a `session` instance. Does NOT need locking.
|
||||
* Takes ownership of `sdata` and `sobj` regardless of return code.
|
||||
* @param sdata bytes of SSL session data or NULL (sobj then required)
|
||||
* @param sdata_len amount of session data bytes
|
||||
* @param ietf_tls_id IETF protocol version, e.g. 0x304 for TLSv1.3
|
||||
* @param alpn ALPN protocol selected or NULL
|
||||
* @param time_received seconds since EPOCH session was received, pass 0
|
||||
* to have the value set to time of call
|
||||
* @param lifetime_secs seconds of announced lifetime, <0 if unknown.
|
||||
* values longer than 1 week will be capped as
|
||||
* required by RFC 8446
|
||||
* @param psession on return the scached session instance created
|
||||
*/
|
||||
CURLcode
|
||||
Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
|
||||
int ietf_tls_id, const char *alpn,
|
||||
curl_off_t time_received, long lifetime_secs,
|
||||
struct Curl_ssl_session **psession);
|
||||
|
||||
/* Destroy a `session` instance. Can be called with NULL.
|
||||
* Does NOT need locking. */
|
||||
void Curl_ssl_session_destroy(struct Curl_ssl_session *s);
|
||||
|
||||
/* Put the scache session into the cache. Does NOT need locking.
|
||||
* Call takes ownership of `s` in all outcomes.
|
||||
* @param cf the connection filter wanting to use it
|
||||
* @param data the transfer involved
|
||||
* @param ssl_peer_key the key for lookup
|
||||
* @param s the scache session object
|
||||
*/
|
||||
CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session *s);
|
||||
|
||||
/* Take a matching scache session from the cache. Does NOT need locking.
|
||||
* @param cf the connection filter wanting to use it
|
||||
* @param data the transfer involved
|
||||
* @param ssl_peer_key the key for lookup
|
||||
* @param s on return, the scache session object or NULL
|
||||
*/
|
||||
CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session **ps);
|
||||
|
||||
/* Return a taken scache session to the cache. Does NOT need locking.
|
||||
* Depending on TLS version and other criteria, it may cache it again
|
||||
* or destroy it. Maybe called with a NULL session.
|
||||
*/
|
||||
void Curl_ssl_scache_return(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
struct Curl_ssl_session *s);
|
||||
|
||||
/* Remove all sessions and obj for the peer_key. Does NOT need locking. */
|
||||
void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key);
|
||||
|
||||
|
||||
#else /* USE_SSL */
|
||||
|
||||
#define Curl_ssl_scache_create(x,y) CURLE_OK
|
||||
#define Curl_ssl_scache_destroy(x) CURLE_OK
|
||||
|
||||
#endif /* USE_SSL (else) */
|
||||
|
||||
#endif /* HEADER_CURL_VTLS_SCACHE_H */
|
@ -60,6 +60,7 @@
|
||||
#include "inet_pton.h"
|
||||
#include "vtls.h"
|
||||
#include "vtls_int.h"
|
||||
#include "vtls_scache.h"
|
||||
#include "keylog.h"
|
||||
#include "parsedate.h"
|
||||
#include "connect.h" /* for the connect timeout */
|
||||
@ -395,57 +396,53 @@ static void wolfssl_bio_cf_free_methods(void)
|
||||
|
||||
#endif /* !USE_BIO_CHAIN */
|
||||
|
||||
static void wolfssl_session_free(void *sdata, size_t slen)
|
||||
{
|
||||
(void)slen;
|
||||
free(sdata);
|
||||
}
|
||||
|
||||
CURLcode wssl_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
WOLFSSL_SESSION *session)
|
||||
CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
WOLFSSL_SESSION *session,
|
||||
int ietf_tls_id,
|
||||
const char *alpn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
unsigned char *sdata = NULL;
|
||||
unsigned int slen;
|
||||
unsigned int sdata_len;
|
||||
|
||||
if(!session)
|
||||
goto out;
|
||||
|
||||
slen = wolfSSL_i2d_SSL_SESSION(session, NULL);
|
||||
if(slen <= 0) {
|
||||
CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen);
|
||||
sdata_len = wolfSSL_i2d_SSL_SESSION(session, NULL);
|
||||
if(sdata_len <= 0) {
|
||||
CURL_TRC_CF(data, cf, "fail to assess session length: %u", sdata_len);
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
sdata = calloc(1, slen);
|
||||
sdata = calloc(1, sdata_len);
|
||||
if(!sdata) {
|
||||
failf(data, "unable to allocate session buffer of %u bytes", slen);
|
||||
failf(data, "unable to allocate session buffer of %u bytes", sdata_len);
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
slen = wolfSSL_i2d_SSL_SESSION(session, &sdata);
|
||||
if(slen <= 0) {
|
||||
CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen);
|
||||
sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata);
|
||||
if(sdata_len <= 0) {
|
||||
CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len);
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
result = Curl_ssl_set_sessionid(cf, data, peer, NULL,
|
||||
sdata, slen, wolfssl_session_free);
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
if(result)
|
||||
failf(data, "failed to add new ssl session to cache (%d)", result);
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "added new session to cache");
|
||||
sdata = NULL;
|
||||
result = Curl_ssl_session_create(sdata, sdata_len,
|
||||
ietf_tls_id, alpn, 0,
|
||||
wolfSSL_SESSION_get_timeout(session),
|
||||
&sc_session);
|
||||
sdata = NULL; /* took ownership of sdata */
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
}
|
||||
|
||||
out:
|
||||
free(sdata);
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
|
||||
@ -460,32 +457,35 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
|
||||
DEBUGASSERT(connssl);
|
||||
DEBUGASSERT(data);
|
||||
if(connssl && data) {
|
||||
(void)wssl_cache_session(cf, data, &connssl->peer, session);
|
||||
(void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
|
||||
session, wolfSSL_version(ssl),
|
||||
connssl->negotiated.alpn);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
CURLcode wssl_setup_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct wolfssl_ctx *wss,
|
||||
struct ssl_peer *peer)
|
||||
CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct wolfssl_ctx *wss,
|
||||
const char *ssl_peer_key)
|
||||
{
|
||||
void *psdata;
|
||||
const unsigned char *sdata = NULL;
|
||||
size_t slen = 0;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
CURLcode result;
|
||||
|
||||
Curl_ssl_sessionid_lock(data);
|
||||
if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) {
|
||||
result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &sc_session);
|
||||
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
|
||||
WOLFSSL_SESSION *session;
|
||||
sdata = psdata;
|
||||
session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen);
|
||||
/* wolfSSL changes the passed pointer for whatever reasons, yikes */
|
||||
const unsigned char *sdata = sc_session->sdata;
|
||||
session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata,
|
||||
(long)sc_session->sdata_len);
|
||||
if(session) {
|
||||
int ret = wolfSSL_set_session(wss->handle, session);
|
||||
if(ret != WOLFSSL_SUCCESS) {
|
||||
Curl_ssl_delsessionid(data, psdata);
|
||||
infof(data, "previous session not accepted (%d), "
|
||||
Curl_ssl_session_destroy(sc_session);
|
||||
sc_session = NULL;
|
||||
infof(data, "cached session not accepted (%d), "
|
||||
"removing from cache", ret);
|
||||
}
|
||||
else
|
||||
@ -496,7 +496,7 @@ CURLcode wssl_setup_session(struct Curl_cfilter *cf,
|
||||
failf(data, "could not decode previous session");
|
||||
}
|
||||
}
|
||||
Curl_ssl_sessionid_unlock(data);
|
||||
Curl_ssl_scache_return(cf, data, ssl_peer_key, sc_session);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1188,7 +1188,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
/* Check if there is a cached ID we can/should use here! */
|
||||
if(ssl_config->primary.cache_session) {
|
||||
/* Set session from cache if there is one */
|
||||
(void)wssl_setup_session(cf, data, backend, &connssl->peer);
|
||||
(void)Curl_wssl_setup_session(cf, data, backend, connssl->peer.scache_key);
|
||||
/* Register to get notified when a new session is received */
|
||||
wolfSSL_set_app_data(backend->handle, cf);
|
||||
wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb);
|
||||
@ -1792,7 +1792,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
|
||||
static size_t wolfssl_version(char *buffer, size_t size)
|
||||
size_t Curl_wssl_version(char *buffer, size_t size)
|
||||
{
|
||||
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
|
||||
return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
|
||||
@ -2030,7 +2030,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
|
||||
|
||||
wolfssl_init, /* init */
|
||||
wolfssl_cleanup, /* cleanup */
|
||||
wolfssl_version, /* version */
|
||||
Curl_wssl_version, /* version */
|
||||
wolfssl_shutdown, /* shutdown */
|
||||
wolfssl_data_pending, /* data_pending */
|
||||
wolfssl_random, /* random */
|
||||
|
@ -47,19 +47,23 @@ struct wolfssl_ctx {
|
||||
BIT(shutting_down); /* TLS is being shut down */
|
||||
};
|
||||
|
||||
size_t Curl_wssl_version(char *buffer, size_t size);
|
||||
|
||||
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct wolfssl_ctx *wssl);
|
||||
|
||||
CURLcode wssl_setup_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct wolfssl_ctx *wss,
|
||||
struct ssl_peer *peer);
|
||||
CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct wolfssl_ctx *wss,
|
||||
const char *ssl_peer_key);
|
||||
|
||||
CURLcode wssl_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
WOLFSSL_SESSION *session);
|
||||
CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
WOLFSSL_SESSION *session,
|
||||
int ietf_tls_id,
|
||||
const char *alpn);
|
||||
|
||||
|
||||
#endif /* USE_WOLFSSL */
|
||||
|
@ -617,7 +617,7 @@ class TestDownload:
|
||||
earlydata = {}
|
||||
reused_session = False
|
||||
for line in r.trace_lines:
|
||||
m = re.match(r'^\[t-(\d+)] EarlyData: (\d+)', line)
|
||||
m = re.match(r'^\[t-(\d+)] EarlyData: (-?\d+)', line)
|
||||
if m:
|
||||
earlydata[int(m.group(1))] = int(m.group(2))
|
||||
continue
|
||||
|
@ -134,4 +134,4 @@ class TestAuth:
|
||||
# Depending on protocol, we might have an error sending or
|
||||
# the server might shutdown the connection and we see the error
|
||||
# on receiving
|
||||
assert r.exit_code in [55, 56], f'{self.dump_logs()}'
|
||||
assert r.exit_code in [55, 56], f'{r.dump_logs()}'
|
||||
|
Loading…
x
Reference in New Issue
Block a user