* [PATCH 1/6] time: Add time_realtime_now
@ 2022-10-26 13:15 Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 2/6] cert: Add l_cert_get_valid_times Andrew Zaborowski
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Andrew Zaborowski @ 2022-10-26 13:15 UTC (permalink / raw)
To: ell
---
ell/time-private.h | 1 +
ell/time.c | 8 ++++++++
2 files changed, 9 insertions(+)
diff --git a/ell/time-private.h b/ell/time-private.h
index 83c23dd..e107503 100644
--- a/ell/time-private.h
+++ b/ell/time-private.h
@@ -24,3 +24,4 @@ uint64_t _time_pick_interval_secs(uint32_t min_secs, uint32_t max_secs);
uint64_t _time_fuzz_msecs(uint64_t ms);
uint64_t _time_fuzz_secs(uint32_t secs, uint32_t max_offset);
uint64_t _time_realtime_to_boottime(const struct timeval *ts);
+uint64_t time_realtime_now(void);
diff --git a/ell/time.c b/ell/time.c
index 41e5725..2eac6c4 100644
--- a/ell/time.c
+++ b/ell/time.c
@@ -58,6 +58,14 @@ LIB_EXPORT uint64_t l_time_now(void)
return _time_from_timespec(&now);
}
+uint64_t time_realtime_now(void)
+{
+ struct timespec now;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ return _time_from_timespec(&now);
+}
+
/**
* l_time_after
*
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/6] cert: Add l_cert_get_valid_times
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
@ 2022-10-26 13:15 ` Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 3/6] tls: Fix an RFC reference Andrew Zaborowski
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Andrew Zaborowski @ 2022-10-26 13:15 UTC (permalink / raw)
To: ell
Add utility to extract the Not Before and Not After timestamps from
certificates.
---
ell/asn1-private.h | 2 +
ell/cert.c | 185 +++++++++++++++++++++++++++++++++++++++++++++
ell/cert.h | 2 +
ell/ell.sym | 1 +
4 files changed, 190 insertions(+)
diff --git a/ell/asn1-private.h b/ell/asn1-private.h
index d794515..36d8de3 100644
--- a/ell/asn1-private.h
+++ b/ell/asn1-private.h
@@ -34,6 +34,8 @@
#define ASN1_ID_UTF8STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x0c)
#define ASN1_ID_PRINTABLESTRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x13)
#define ASN1_ID_IA5STRING ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x16)
+#define ASN1_ID_UTCTIME ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x17)
+#define ASN1_ID_GENERALIZEDTIME ASN1_ID(ASN1_CLASS_UNIVERSAL, 0, 0x18)
struct asn1_oid {
uint8_t asn1_len;
diff --git a/ell/cert.c b/ell/cert.c
index a158142..b4f5df7 100644
--- a/ell/cert.c
+++ b/ell/cert.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
+#include <time.h>
#include "private.h"
#include "useful.h"
@@ -33,6 +34,8 @@
#include "asn1-private.h"
#include "cipher.h"
#include "pem-private.h"
+#include "time.h"
+#include "utf8.h"
#include "cert.h"
#include "cert-private.h"
#include "tls.h"
@@ -186,6 +189,188 @@ LIB_EXPORT const uint8_t *l_cert_get_dn(struct l_cert *cert, size_t *out_len)
-1);
}
+static uint64_t cert_parse_asn1_time(const uint8_t *data, size_t len,
+ uint8_t tag)
+{
+ struct tm tm = {};
+ int tz_hours;
+ int tz_mins;
+ int century;
+ int msecs = 0;
+ time_t tt;
+ unsigned int i;
+
+ for (i = 0; i < len && i < 15; i++)
+ if (unlikely(!l_ascii_isdigit(data[i])))
+ break;
+
+ if (tag == ASN1_ID_UTCTIME) {
+ if (unlikely(!L_IN_SET(i, 10, 12)))
+ return L_TIME_INVALID;
+
+ century = 19;
+ } else if (tag == ASN1_ID_GENERALIZEDTIME) {
+ if (unlikely(!L_IN_SET(i, 10, 12, 14)))
+ return L_TIME_INVALID;
+
+ century = (data[0] - '0') * 10 + (data[1] - '0');
+ if (century < 19)
+ return L_TIME_INVALID;
+
+ if (len >= i + 4 && data[i] == '.') {
+ if (unlikely(!l_ascii_isdigit(data[i + 1]) ||
+ !l_ascii_isdigit(data[i + 2]) ||
+ !l_ascii_isdigit(data[i + 3])))
+ return L_TIME_INVALID;
+
+ i++;
+ msecs += (data[i++] - '0') * 100;
+ msecs += (data[i++] - '0') * 10;
+ msecs += (data[i++] - '0');
+ }
+
+ data += 2;
+ len -= 2;
+ i -= 2;
+ } else
+ return L_TIME_INVALID;
+
+ if (unlikely((len != i + 1 || data[i] != 'Z') &&
+ (len != i + 5 || data[i] != '+' || data[i] != '-')))
+ return L_TIME_INVALID;
+
+ tm.tm_year = (data[0] - '0') * 10 + (data[1] - '0');
+ tm.tm_mon = (data[2] - '0') * 10 + (data[3] - '0');
+ tm.tm_mday = (data[4] - '0') * 10 + (data[5] - '0');
+ tm.tm_hour = (data[6] - '0') * 10 + (data[7] - '0');
+
+ if (unlikely(tm.tm_mon < 1 || tm.tm_mon > 12 ||
+ tm.tm_mday < 1 || tm.tm_mday > 31 ||
+ tm.tm_hour > 23))
+ return L_TIME_INVALID;
+
+ if (i >= 10) {
+ tm.tm_min = (data[8] - '0') * 10 + (data[9] - '0');
+ if (unlikely(tm.tm_min > 59))
+ return L_TIME_INVALID;
+ }
+
+ if (i >= 12) {
+ tm.tm_sec = (data[10] - '0') * 10 + (data[11] - '0');
+ if (unlikely(tm.tm_sec > 59))
+ return L_TIME_INVALID;
+ }
+
+ /* RFC5280 Section 4.1.2.5.1 */
+ if (tag == ASN1_ID_UTCTIME && tm.tm_year < 50)
+ century = 20;
+
+ tm.tm_year += (century - 19) * 100;
+
+ /* Month number is 1-based in UTCTime and 0-based in struct tm */
+ tm.tm_mon -= 1;
+
+ tt = timegm(&tm);
+ if (unlikely(tt == (time_t) -1))
+ return L_TIME_INVALID;
+
+ if (len == i + 5) {
+ data += i;
+
+ for (i = 1; i < 5; i++)
+ if (unlikely(!l_ascii_isdigit(data[i])))
+ return L_TIME_INVALID;
+
+ tz_hours = (data[1] - '0') * 10 + (data[2] - '0');
+ tz_mins = (data[3] - '0') * 10 + (data[4] - '0');
+
+ if (unlikely(tz_hours > 14 || tz_mins > 59))
+ return L_TIME_INVALID;
+
+ /* The sign converts UTC to local so invert it */
+ if (data[0] == '+')
+ tt -= tz_hours * 3600 + tz_mins * 60;
+ else
+ tt += tz_hours * 3600 + tz_mins * 60;
+ }
+
+ return (uint64_t) tt * L_USEC_PER_SEC + msecs * L_USEC_PER_MSEC;
+}
+
+LIB_EXPORT bool l_cert_get_valid_times(struct l_cert *cert,
+ uint64_t *out_not_before_time,
+ uint64_t *out_not_after_time)
+{
+ const uint8_t *validity;
+ const uint8_t *not_before;
+ const uint8_t *not_after;
+ size_t seq_size;
+ size_t not_before_size;
+ size_t not_after_size;
+ uint8_t not_before_tag;
+ uint8_t not_after_tag;
+ uint64_t not_before_time = 0;
+ uint64_t not_after_time = 0;
+
+ if (unlikely(!cert))
+ return false;
+
+ validity = asn1_der_find_elem_by_path(cert->asn1, cert->asn1_len,
+ ASN1_ID_SEQUENCE, &seq_size,
+ X509_CERTIFICATE_POS,
+ X509_TBSCERTIFICATE_POS,
+ X509_TBSCERT_VALIDITY_POS,
+ -1);
+ if (unlikely(!validity))
+ return false;
+
+ not_before = asn1_der_find_elem(validity, seq_size, 0, ¬_before_tag,
+ ¬_before_size);
+ if (!not_before)
+ return false;
+
+ seq_size -= not_before_size + (not_before - validity);
+ validity = not_before + not_before_size;
+ not_after = asn1_der_find_elem(validity, seq_size, 0, ¬_after_tag,
+ ¬_after_size);
+ if (!not_after)
+ return false;
+
+ if (out_not_before_time) {
+ not_before_time = cert_parse_asn1_time(not_before,
+ not_before_size,
+ not_before_tag);
+ if (not_before_time == L_TIME_INVALID)
+ return false;
+ }
+
+ if (out_not_after_time) {
+ /*
+ * RFC5280 Section 4.1.2.5: "To indicate that a certificate
+ * has no well-defined expiration date, the notAfter SHOULD
+ * be assigned the GeneralizedTime value of 99991231235959Z."
+ */
+ if (not_after_size == 15 &&
+ !memcmp(not_after, "99991231235959Z", 15))
+ not_after_time = 0;
+ else {
+ not_after_time = cert_parse_asn1_time(not_after,
+ not_after_size,
+ not_after_tag);
+ if (not_after_time == L_TIME_INVALID)
+ return false;
+ }
+ }
+
+ if (out_not_before_time)
+ *out_not_before_time = not_before_time;
+
+ if (out_not_after_time)
+ *out_not_after_time = not_after_time;
+
+ return true;
+}
+
const uint8_t *cert_get_extension(struct l_cert *cert,
const struct asn1_oid *ext_id,
bool *out_critical, size_t *out_len)
diff --git a/ell/cert.h b/ell/cert.h
index f637588..db116e0 100644
--- a/ell/cert.h
+++ b/ell/cert.h
@@ -48,6 +48,8 @@ DEFINE_CLEANUP_FUNC(l_cert_free);
const uint8_t *l_cert_get_der_data(struct l_cert *cert, size_t *out_len);
const uint8_t *l_cert_get_dn(struct l_cert *cert, size_t *out_len);
+bool l_cert_get_valid_times(struct l_cert *cert, uint64_t *out_not_before_time,
+ uint64_t *out_not_after_time);
enum l_cert_key_type l_cert_get_pubkey_type(struct l_cert *cert);
struct l_key *l_cert_get_pubkey(struct l_cert *cert);
diff --git a/ell/ell.sym b/ell/ell.sym
index 6df9024..5fe8d6e 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -553,6 +553,7 @@ global:
l_cert_free;
l_cert_get_der_data;
l_cert_get_dn;
+ l_cert_get_valid_times;
l_cert_get_pubkey_type;
l_cert_get_pubkey;
l_certchain_free;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/6] tls: Fix an RFC reference
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 2/6] cert: Add l_cert_get_valid_times Andrew Zaborowski
@ 2022-10-26 13:15 ` Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 4/6] tls: Add support for caching client session states Andrew Zaborowski
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Andrew Zaborowski @ 2022-10-26 13:15 UTC (permalink / raw)
To: ell
---
ell/tls-extensions.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ell/tls-extensions.c b/ell/tls-extensions.c
index 6154df0..7796825 100644
--- a/ell/tls-extensions.c
+++ b/ell/tls-extensions.c
@@ -785,7 +785,7 @@ ssize_t tls_parse_signature_algorithms(struct l_tls *tls,
return ptr + len - buf;
}
-/* RFC 5462, Section 7.4.1.4.1 */
+/* RFC 5246, Section 7.4.1.4.1 */
static ssize_t tls_signature_algorithms_client_write(struct l_tls *tls,
uint8_t *buf, size_t len)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/6] tls: Add support for caching client session states
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 2/6] cert: Add l_cert_get_valid_times Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 3/6] tls: Fix an RFC reference Andrew Zaborowski
@ 2022-10-26 13:15 ` Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 5/6] tls: Client session resumption Andrew Zaborowski
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Andrew Zaborowski @ 2022-10-26 13:15 UTC (permalink / raw)
To: ell
If a session cache is configured using l_tls_set_session_cache(), save
session states to a set of 7 key/value pairs in a given l_settings
object, in a given group. This only implements the client side as the
semantics on the server side will be different.
---
ell/ell.sym | 1 +
ell/tls-private.h | 10 +++
ell/tls.c | 192 +++++++++++++++++++++++++++++++++++++++++++++-
ell/tls.h | 7 ++
4 files changed, 208 insertions(+), 2 deletions(-)
diff --git a/ell/ell.sym b/ell/ell.sym
index 5fe8d6e..6c836e1 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -518,6 +518,7 @@ global:
l_tls_set_auth_data;
l_tls_set_version_range;
l_tls_set_domain_mask;
+ l_tls_set_session_cache;
l_tls_alert_to_str;
l_tls_set_debug;
l_tls_set_cert_dump_path;
diff --git a/ell/tls-private.h b/ell/tls-private.h
index 8941e90..6f09f6a 100644
--- a/ell/tls-private.h
+++ b/ell/tls-private.h
@@ -218,6 +218,12 @@ struct l_tls {
struct tls_cipher_suite **cipher_suite_pref_list;
+ struct l_settings *session_settings;
+ char *session_group;
+ uint64_t session_lifetime;
+ l_tls_session_update_cb_t session_update_cb;
+ void *session_update_user_data;
+
bool in_callback;
bool pending_destroy;
@@ -251,6 +257,10 @@ struct l_tls {
const struct tls_named_group *negotiated_curve;
const struct tls_named_group *negotiated_ff_group;
+ uint8_t session_id[32];
+ size_t session_id_size;
+ bool session_id_new;
+
/* SecurityParameters current and pending */
struct {
diff --git a/ell/tls.c b/ell/tls.c
index fa3df5d..ff4fa5b 100644
--- a/ell/tls.c
+++ b/ell/tls.c
@@ -46,6 +46,9 @@
#include "strv.h"
#include "missing.h"
#include "string.h"
+#include "settings.h"
+#include "time.h"
+#include "time-private.h"
bool tls10_prf(const void *secret, size_t secret_len,
const char *label,
@@ -204,6 +207,9 @@ static void tls_reset_handshake(struct l_tls *tls)
TLS_SET_STATE(TLS_HANDSHAKE_WAIT_START);
tls->cert_requested = 0;
tls->cert_sent = 0;
+
+ tls->session_id_size = 0;
+ tls->session_id_new = false;
}
static void tls_cleanup_handshake(struct l_tls *tls)
@@ -820,6 +826,31 @@ parse_error:
return false;
}
+static void tls_forget_cached_client_session(struct l_tls *tls)
+{
+ /* Note: might want to l_settings_remove_group() instead. */
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionID");
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionMasterSecret");
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionVersion");
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionCipherSuite");
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionCompressionMethod");
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionExpiryTime");
+ l_settings_remove_key(tls->session_settings, tls->session_group,
+ "TLSSessionPeerIdentity");
+
+ if (tls->session_update_cb) {
+ tls->in_callback = true;
+ tls->session_update_cb(tls->session_update_user_data);
+ tls->in_callback = false;
+ }
+}
+
#define SWITCH_ENUM_TO_STR(val) \
case (val): \
return L_STRINGIFY(val);
@@ -868,6 +899,28 @@ static void tls_send_alert(struct l_tls *tls, bool fatal,
void tls_disconnect(struct l_tls *tls, enum l_tls_alert_desc desc,
enum l_tls_alert_desc local_desc)
{
+ bool forget_session = false;
+
+ if (!tls->server && (desc || local_desc) &&
+ tls->session_settings && tls->session_id_size &&
+ !tls->session_id_new)
+ /*
+ * RFC5246 Section 7.2: "Alert messages with a level of fatal
+ * result in the immediate termination of the connection. In
+ * this case, other connections corresponding to the session
+ * may continue, but the session identifier MUST be
+ * invalidated, preventing the failed session from being used
+ * to establish new connections."
+ *
+ * and 7.2.1: "Note that as of TLS 1.1, failure to properly
+ * close a connection no longer requires that a session not
+ * be resumed."
+ *
+ * I.e. we need to remove the session from the cache here but
+ * not on l_tls_close().
+ */
+ forget_session = true;
+
tls_send_alert(tls, true, desc);
tls_reset_handshake(tls);
@@ -879,6 +932,13 @@ void tls_disconnect(struct l_tls *tls, enum l_tls_alert_desc desc,
tls->negotiated_version = 0;
tls->ready = false;
+ if (forget_session) {
+ tls_forget_cached_client_session(tls);
+
+ if (tls->pending_destroy)
+ return;
+ }
+
tls->disconnected(local_desc ?: desc, local_desc && !desc,
tls->user_data);
}
@@ -1814,6 +1874,15 @@ static void tls_handle_server_hello(struct l_tls *tls,
compression_method_id = buf[35 + session_id_size + 2];
len -= session_id_size + 2 + 1;
+ if (session_id_size > 32)
+ goto decode_error;
+
+ if (session_id_size && tls->session_settings) {
+ tls->session_id_new = true;
+ tls->session_id_size = session_id_size;
+ memcpy(tls->session_id, buf + 35, session_id_size);
+ }
+
extensions_seen = l_queue_new();
result = tls_handle_hello_extensions(tls, buf + 38 + session_id_size,
len, extensions_seen);
@@ -2350,7 +2419,9 @@ error:
static void tls_finished(struct l_tls *tls)
{
- char *peer_identity = NULL;
+ _auto_(l_free) char *peer_identity = NULL;
+ uint64_t peer_cert_expiry;
+ bool session_update = false;
if (tls->peer_authenticated) {
peer_identity = tls_get_peer_identity_str(tls->peer_cert);
@@ -2359,6 +2430,65 @@ static void tls_finished(struct l_tls *tls)
"tls_get_peer_identity_str failed");
return;
}
+
+ if (tls->session_id_new &&
+ !l_cert_get_valid_times(tls->peer_cert, NULL,
+ &peer_cert_expiry)) {
+ TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
+ "l_cert_get_valid_times failed");
+ return;
+ }
+ }
+
+ if (!tls->server && tls->session_settings && tls->session_id_new) {
+ _auto_(l_free) char *session_id_str =
+ l_util_hexstring(tls->session_id, tls->session_id_size);
+ uint64_t expiry = tls->session_lifetime ?
+ time_realtime_now() + tls->session_lifetime : 0;
+
+ if (tls->peer_authenticated &&
+ (!expiry || peer_cert_expiry < expiry))
+ expiry = peer_cert_expiry;
+
+ l_settings_set_bytes(tls->session_settings, tls->session_group,
+ "TLSSessionID", tls->session_id,
+ tls->session_id_size);
+ l_settings_set_bytes(tls->session_settings, tls->session_group,
+ "TLSSessionMasterSecret",
+ tls->pending.master_secret, 48);
+ l_settings_set_int(tls->session_settings, tls->session_group,
+ "TLSSessionVersion",
+ tls->negotiated_version);
+ l_settings_set_bytes(tls->session_settings, tls->session_group,
+ "TLSSessionCipherSuite",
+ tls->pending.cipher_suite->id, 2);
+ l_settings_set_uint(tls->session_settings, tls->session_group,
+ "TLSSessionCompressionMethod",
+ tls->pending.compression_method->id);
+
+ if (expiry)
+ l_settings_set_uint64(tls->session_settings,
+ tls->session_group,
+ "TLSSessionExpiryTime", expiry);
+ else
+ /* We may be overwriting an older session's data */
+ l_settings_remove_key(tls->session_settings,
+ tls->session_group,
+ "TLSSessionExpiryTime");
+
+ if (tls->peer_authenticated)
+ l_settings_set_string(tls->session_settings,
+ tls->session_group,
+ "TLSSessionPeerIdentity",
+ peer_identity);
+ else
+ /* We may be overwriting an older session's data */
+ l_settings_remove_key(tls->session_settings,
+ tls->session_group,
+ "TLSSessionPeerIdentity");
+
+ TLS_DEBUG("Saving new session %s to cache", session_id_str);
+ session_update = true;
}
/* Free up the resources used in the handshake */
@@ -2367,10 +2497,18 @@ static void tls_finished(struct l_tls *tls)
TLS_SET_STATE(TLS_HANDSHAKE_DONE);
tls->ready = true;
+ if (session_update && tls->session_update_cb) {
+ tls->in_callback = true;
+ tls->session_update_cb(tls->session_update_user_data);
+ tls->in_callback = false;
+
+ if (tls->pending_destroy)
+ return;
+ }
+
tls->in_callback = true;
tls->ready_handle(peer_identity, tls->user_data);
tls->in_callback = false;
- l_free(peer_identity);
tls_cleanup_handshake(tls);
}
@@ -2643,6 +2781,7 @@ LIB_EXPORT struct l_tls *l_tls_new(bool server,
tls->cipher_suite_pref_list = tls_cipher_suite_pref;
tls->min_version = TLS_MIN_VERSION;
tls->max_version = TLS_MAX_VERSION;
+ tls->session_lifetime = 24 * 3600 * L_USEC_PER_SEC;
/* If we're the server wait for the Client Hello already */
if (tls->server)
@@ -2669,6 +2808,7 @@ LIB_EXPORT void l_tls_free(struct l_tls *tls)
l_tls_set_auth_data(tls, NULL, NULL);
l_tls_set_domain_mask(tls, NULL);
l_tls_set_cert_dump_path(tls, NULL);
+ l_tls_set_session_cache(tls, NULL, NULL, 0, NULL, NULL);
tls_reset_handshake(tls);
tls_cleanup_handshake(tls);
@@ -3042,6 +3182,54 @@ LIB_EXPORT void l_tls_set_domain_mask(struct l_tls *tls, char **mask)
tls->subject_mask = l_strv_copy(mask);
}
+/**
+ * l_tls_set_session_cache:
+ * @tls: TLS object being configured
+ * @settings: l_settings object to read and write session data from/to or
+ * NULL to disable caching session states. The object must remain valid
+ * until this method is called with a different value.
+ * @group: group name inside @settings
+ * @lifetime: a CLOCK_REALTIME-based microsecond resolution lifetime for
+ * cached sessions. The RFC recommends 24 hours.
+ * @update_cb: a callback to be invoked whenever the settings in @settings
+ * have been updated and may need to be written to persistent storage if
+ * desired, or NULL.
+ * @user_data: user data pointer to pass to @update_cb.
+ *
+ * Enables caching and resuming session states as described in RFC 5246 for
+ * faster setup. l_tls will maintain the required session state data in
+ * @settings including removing expired or erroneous sessions.
+ *
+ * A client's cache contains at most one session state since the client
+ * must request one specific Session ID from the server when resuming a
+ * session. The resumption will only work while the state is cached by
+ * both the server and the client so clients should keep separate @settings
+ * objects or use separate groups inside one object for every discrete
+ * server they may want to connect to.
+ *
+ * Multiple l_tls clients connecting to the same server can share one cache
+ * to allow reusing an established session that is still active (actual
+ * concurrency is not supported as there's no locking.)
+ */
+LIB_EXPORT void l_tls_set_session_cache(struct l_tls *tls,
+ struct l_settings *settings,
+ const char *group_name,
+ uint64_t lifetime,
+ l_tls_session_update_cb_t update_cb,
+ void *user_data)
+{
+ if (unlikely(!tls))
+ return;
+
+ tls->session_settings = settings;
+ tls->session_lifetime = lifetime;
+ tls->session_update_cb = update_cb;
+ tls->session_update_user_data = user_data;
+
+ l_free(tls->session_group);
+ tls->session_group = l_strdup(group_name);
+}
+
LIB_EXPORT const char *l_tls_alert_to_str(enum l_tls_alert_desc desc)
{
switch (desc) {
diff --git a/ell/tls.h b/ell/tls.h
index a4fd414..92d8b9e 100644
--- a/ell/tls.h
+++ b/ell/tls.h
@@ -36,6 +36,7 @@ struct l_tls;
struct l_key;
struct l_certchain;
struct l_queue;
+struct l_settings;
enum l_tls_alert_desc {
TLS_ALERT_CLOSE_NOTIFY = 0,
@@ -72,6 +73,7 @@ typedef void (*l_tls_disconnect_cb_t)(enum l_tls_alert_desc reason,
bool remote, void *user_data);
typedef void (*l_tls_debug_cb_t)(const char *str, void *user_data);
typedef void (*l_tls_destroy_cb_t)(void *user_data);
+typedef void (*l_tls_session_update_cb_t)(void *user_data);
/*
* app_data_handler gets called with newly received decrypted data.
@@ -127,6 +129,11 @@ void l_tls_set_version_range(struct l_tls *tls,
void l_tls_set_domain_mask(struct l_tls *tls, char **mask);
+void l_tls_set_session_cache(struct l_tls *tls, struct l_settings *settings,
+ const char *group_name, uint64_t lifetime,
+ l_tls_session_update_cb_t update_cb,
+ void *user_data);
+
const char *l_tls_alert_to_str(enum l_tls_alert_desc desc);
enum l_checksum_type;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 5/6] tls: Client session resumption
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
` (2 preceding siblings ...)
2022-10-26 13:15 ` [PATCH 4/6] tls: Add support for caching client session states Andrew Zaborowski
@ 2022-10-26 13:15 ` Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 6/6] examples: Cache sessions in https-client-test Andrew Zaborowski
2022-10-28 18:27 ` [PATCH 1/6] time: Add time_realtime_now Denis Kenzior
5 siblings, 0 replies; 7+ messages in thread
From: Andrew Zaborowski @ 2022-10-26 13:15 UTC (permalink / raw)
To: ell
In client mode load a stored session state from the cache in l_tls_start
and attempt to resume that session.
---
ell/tls-private.h | 3 +
ell/tls-suites.c | 4 +-
ell/tls.c | 316 +++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 286 insertions(+), 37 deletions(-)
diff --git a/ell/tls-private.h b/ell/tls-private.h
index 6f09f6a..7156666 100644
--- a/ell/tls-private.h
+++ b/ell/tls-private.h
@@ -260,6 +260,9 @@ struct l_tls {
uint8_t session_id[32];
size_t session_id_size;
bool session_id_new;
+ uint8_t session_cipher_suite_id[2];
+ uint8_t session_compression_method_id;
+ char *session_peer_identity;
/* SecurityParameters current and pending */
diff --git a/ell/tls-suites.c b/ell/tls-suites.c
index ee4e7ee..8cbcb63 100644
--- a/ell/tls-suites.c
+++ b/ell/tls-suites.c
@@ -352,8 +352,8 @@ static bool tls_send_rsa_client_key_xchg(struct l_tls *tls)
}
/* Must match the version in tls_send_client_hello */
- pre_master_secret[0] = (uint8_t) (tls->max_version >> 8);
- pre_master_secret[1] = (uint8_t) (tls->max_version >> 0);
+ pre_master_secret[0] = (uint8_t) (tls->client_version >> 8);
+ pre_master_secret[1] = (uint8_t) (tls->client_version >> 0);
l_getrandom(pre_master_secret + 2, 46);
diff --git a/ell/tls.c b/ell/tls.c
index ff4fa5b..88ac3d7 100644
--- a/ell/tls.c
+++ b/ell/tls.c
@@ -210,6 +210,7 @@ static void tls_reset_handshake(struct l_tls *tls)
tls->session_id_size = 0;
tls->session_id_new = false;
+ l_free(l_steal_ptr(tls->session_peer_identity));
}
static void tls_cleanup_handshake(struct l_tls *tls)
@@ -851,6 +852,155 @@ static void tls_forget_cached_client_session(struct l_tls *tls)
}
}
+static bool tls_load_cached_client_session(struct l_tls *tls)
+{
+ /*
+ * The following settings are required:
+ * TLSSessionID,
+ * TLSSessionMasterSecret,
+ * TLSSessionVersion,
+ * TLSSessionCipherSuite,
+ * TLSSessionCompressionMethod,
+ * and these two are optional:
+ * TLSSessionExpiryTime,
+ * TLSSessionPeerIdentity.
+ */
+ _auto_(l_free) uint8_t *session_id = NULL;
+ size_t session_id_size;
+ _auto_(l_free) char *session_id_str = NULL;
+ _auto_(l_free) uint8_t *master_secret = NULL;
+ int version;
+ _auto_(l_free) uint8_t *cipher_suite_id = NULL;
+ struct tls_cipher_suite *cipher_suite;
+ unsigned int compression_method_id;
+ _auto_(l_free) char *peer_identity = NULL;
+ size_t size;
+ const char *error;
+
+ tls->session_id_size = 0;
+ tls->session_id_new = false;
+
+ if (!tls->session_settings ||
+ !l_settings_has_key(tls->session_settings,
+ tls->session_group,
+ "TLSSessionID"))
+ /* No session cached, no error */
+ return false;
+
+ session_id = l_settings_get_bytes(tls->session_settings,
+ tls->session_group,
+ "TLSSessionID",
+ &session_id_size);
+ if (unlikely(!session_id ||
+ session_id_size < 1 || session_id_size > 32))
+ goto warn_corrupt;
+
+ session_id_str =
+ l_util_hexstring(tls->session_id, tls->session_id_size);
+
+ if (l_settings_has_key(tls->session_settings, tls->session_group,
+ "TLSSessionExpiryTime")) {
+ uint64_t expiry_time;
+
+ if (unlikely(!l_settings_get_uint64(tls->session_settings,
+ tls->session_group,
+ "TLSSessionExpiryTime",
+ &expiry_time)))
+ goto warn_corrupt;
+
+ if (time_realtime_now() > expiry_time) {
+ TLS_DEBUG("Cached session %s is expired, removing it, "
+ "will start a new session",
+ session_id_str);
+ goto forget;
+ }
+ }
+
+ if (unlikely(!l_settings_get_int(tls->session_settings,
+ tls->session_group,
+ "TLSSessionVersion",
+ &version) ||
+ version < TLS_MIN_VERSION || version > TLS_MAX_VERSION))
+ goto warn_corrupt;
+
+ master_secret = l_settings_get_bytes(tls->session_settings,
+ tls->session_group,
+ "TLSSessionMasterSecret",
+ &size);
+ if (unlikely(!master_secret || size != 48))
+ goto warn_corrupt;
+
+ cipher_suite_id = l_settings_get_bytes(tls->session_settings,
+ tls->session_group,
+ "TLSSessionCipherSuite",
+ &size);
+ if (unlikely(!cipher_suite_id || size != 2 ||
+ !(cipher_suite =
+ tls_find_cipher_suite(cipher_suite_id))))
+ goto warn_corrupt;
+
+ /*
+ * While we could attempt to resume a session even though we're now
+ * configured with, say, a different certificate type than what we
+ * had when we cached that session, that is too questionable of a
+ * scenario to support it. We don't specifically check that all of
+ * the authentication data is the same, e.g. we don't save the
+ * certificate serial number or path, but ensure the cached cipher
+ * suite is compatible with current authentication data.
+ *
+ * We filter the cipher suites in our Client Hello to only offer the
+ * ones compatible with current configuration so if we also include
+ * a Session ID from a session who's cipher suite is not one of those
+ * listed in that same Client Hello, the server is likely to notice
+ * and either start a new session or send a fatal Alert.
+ *
+ * It is up to the user to keep multiple cache instances if it needs
+ * to save multiple sessions.
+ */
+ if (unlikely(!tls_cipher_suite_is_compatible(tls, cipher_suite,
+ &error))) {
+ TLS_DEBUG("Cached session %s cipher suite not compatible: %s",
+ session_id_str, error);
+ goto forget;
+ }
+
+ if (unlikely(!l_settings_get_uint(tls->session_settings,
+ tls->session_group,
+ "TLSSessionCompressionMethod",
+ &compression_method_id) ||
+ !tls_find_compression_method(compression_method_id)))
+ goto warn_corrupt;
+
+ if (l_settings_has_key(tls->session_settings, tls->session_group,
+ "TLSSessionPeerIdentity")) {
+ peer_identity = l_settings_get_string(tls->session_settings,
+ tls->session_group,
+ "TLSSessionPeerIdentity");
+ if (unlikely(!peer_identity || !cipher_suite->signature))
+ goto warn_corrupt;
+ }
+
+ tls->session_id_size = session_id_size;
+ memcpy(tls->session_id, session_id, session_id_size);
+ tls->session_id_new = false;
+ tls->client_version = version;
+ memcpy(tls->pending.master_secret, master_secret, 48);
+ memcpy(tls->session_cipher_suite_id, cipher_suite_id, 2);
+ tls->session_compression_method_id = compression_method_id;
+ l_free(tls->session_peer_identity);
+ tls->session_peer_identity = l_steal_ptr(peer_identity);
+ return true;
+
+warn_corrupt:
+ TLS_DEBUG("Cached session %s data is corrupt or has unsupported "
+ "parameters, removing it, will start a new session",
+ session_id_str ?: "<unknonwn>");
+
+forget:
+ tls_forget_cached_client_session(tls);
+ return false;
+}
+
#define SWITCH_ENUM_TO_STR(val) \
case (val): \
return L_STRINGIFY(val);
@@ -1063,14 +1213,19 @@ static bool tls_send_client_hello(struct l_tls *tls)
/* Fill in the Client Hello body */
- *ptr++ = (uint8_t) (tls->max_version >> 8);
- *ptr++ = (uint8_t) (tls->max_version >> 0);
+ *ptr++ = (uint8_t) (tls->client_version >> 8);
+ *ptr++ = (uint8_t) (tls->client_version >> 0);
tls_write_random(tls->pending.client_random);
memcpy(ptr, tls->pending.client_random, 32);
ptr += 32;
- *ptr++ = 0; /* No SessionID */
+ if (tls->session_id_size) {
+ *ptr++ = tls->session_id_size;
+ memcpy(ptr, tls->session_id, tls->session_id_size);
+ ptr += tls->session_id_size;
+ } else
+ *ptr++ = 0;
len_ptr = ptr;
ptr += 2;
@@ -1317,22 +1472,10 @@ static void tls_send_server_hello_done(struct l_tls *tls)
TLS_HANDSHAKE_HEADER_SIZE);
}
-void tls_generate_master_secret(struct l_tls *tls,
- const uint8_t *pre_master_secret,
- int pre_master_secret_len)
+static void tls_update_key_block(struct l_tls *tls)
{
uint8_t seed[64];
- int key_block_size;
-
- memcpy(seed + 0, tls->pending.client_random, 32);
- memcpy(seed + 32, tls->pending.server_random, 32);
-
- tls_prf_get_bytes(tls, pre_master_secret, pre_master_secret_len,
- "master secret", seed, 64,
- tls->pending.master_secret, 48);
-
- /* Directly generate the key block while we're at it */
- key_block_size = 0;
+ int key_block_size = 0;
if (tls->pending.cipher_suite->encryption)
key_block_size += 2 *
@@ -1360,8 +1503,25 @@ void tls_generate_master_secret(struct l_tls *tls,
tls_prf_get_bytes(tls, tls->pending.master_secret, 48,
"key expansion", seed, 64,
tls->pending.key_block, key_block_size);
+ explicit_bzero(seed, 64);
+}
+void tls_generate_master_secret(struct l_tls *tls,
+ const uint8_t *pre_master_secret,
+ int pre_master_secret_len)
+{
+ uint8_t seed[64];
+
+ memcpy(seed + 0, tls->pending.client_random, 32);
+ memcpy(seed + 32, tls->pending.server_random, 32);
+
+ tls_prf_get_bytes(tls, pre_master_secret, pre_master_secret_len,
+ "master secret", seed, 64,
+ tls->pending.master_secret, 48);
explicit_bzero(seed, 64);
+
+ /* Directly generate the key block while we're at it */
+ tls_update_key_block(tls);
}
static void tls_get_handshake_hash(struct l_tls *tls,
@@ -1856,11 +2016,14 @@ static void tls_handle_server_hello(struct l_tls *tls,
int i;
struct l_queue *extensions_seen;
bool result;
+ uint16_t version;
+ bool resuming = false;
/* Do we have enough for ProtocolVersion + Random + SessionID len ? */
if (len < 2 + 32 + 1)
goto decode_error;
+ version = l_get_be16(buf);
memcpy(tls->pending.server_random, buf + 2, 32);
session_id_size = buf[34];
len -= 35;
@@ -1877,6 +2040,32 @@ static void tls_handle_server_hello(struct l_tls *tls,
if (session_id_size > 32)
goto decode_error;
+ if (tls->session_id_size) {
+ _auto_(l_free) char *session_id_str =
+ l_util_hexstring(tls->session_id, tls->session_id_size);
+
+ if (session_id_size == tls->session_id_size &&
+ !memcmp(buf + 35, tls->session_id,
+ session_id_size)) {
+ TLS_DEBUG("Negotiated resumption of cached session %s",
+ session_id_str);
+ resuming = true;
+
+ /*
+ * Skip parsing extensions as none of the ones we
+ * support are used in session resumption. We could
+ * as well signal an error if the ServerHello has any
+ * extensions, for now ignore them.
+ */
+ goto check_version;
+ }
+
+ TLS_DEBUG("Server decided not to resume cached session %s, "
+ "sent %s session ID", session_id_str,
+ session_id_size ? "a new" : "no");
+ tls->session_id_size = 0;
+ }
+
if (session_id_size && tls->session_settings) {
tls->session_id_new = true;
tls->session_id_size = session_id_size;
@@ -1891,18 +2080,17 @@ static void tls_handle_server_hello(struct l_tls *tls,
if (!result)
return;
- tls->negotiated_version = l_get_be16(buf);
-
- if (tls->negotiated_version < tls->min_version ||
- tls->negotiated_version > tls->max_version) {
- TLS_DISCONNECT(tls->negotiated_version < tls->min_version ?
+check_version:
+ if (version < tls->min_version || version > tls->max_version) {
+ TLS_DISCONNECT(version < tls->min_version ?
TLS_ALERT_PROTOCOL_VERSION :
TLS_ALERT_ILLEGAL_PARAM, 0,
- "Unsupported version %02x",
- tls->negotiated_version);
+ "Unsupported version %02x", version);
return;
}
+ tls->negotiated_version = version;
+
/* Stop maintaining handshake message hashes other than MD1 and SHA. */
if (tls->negotiated_version < L_TLS_V12)
for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++)
@@ -1958,7 +2146,30 @@ static void tls_handle_server_hello(struct l_tls *tls,
TLS_DEBUG("Negotiated %s", tls->pending.compression_method->name);
- if (tls->pending.cipher_suite->signature)
+ if (resuming) {
+ /*
+ * Now that we've validated the Server Hello parameters and
+ * know that they're supported by this version of ell and
+ * consistent with the current configuration, ensure that
+ * they're identical with the ones in the cached session
+ * being resumed. This serves as a sanity check for
+ * rare situations like a corrupt session cache file or
+ * a file written by a newer ell version.
+ */
+ if (tls->negotiated_version != tls->client_version ||
+ memcmp(cipher_suite_id,
+ tls->session_cipher_suite_id, 2) ||
+ compression_method_id !=
+ tls->session_compression_method_id) {
+ TLS_DISCONNECT(TLS_ALERT_HANDSHAKE_FAIL, 0,
+ "Session parameters don't match");
+ return;
+ }
+
+ tls_update_key_block(tls);
+
+ TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC);
+ } else if (tls->pending.cipher_suite->signature)
TLS_SET_STATE(TLS_HANDSHAKE_WAIT_CERTIFICATE);
else
TLS_SET_STATE(TLS_HANDSHAKE_WAIT_KEY_EXCHANGE);
@@ -2419,18 +2630,22 @@ error:
static void tls_finished(struct l_tls *tls)
{
- _auto_(l_free) char *peer_identity = NULL;
+ _auto_(l_free) char *peer_cert_identity = NULL;
+ char *peer_identity = NULL;
uint64_t peer_cert_expiry;
+ bool resuming = tls->session_id_size && !tls->session_id_new;
bool session_update = false;
- if (tls->peer_authenticated) {
- peer_identity = tls_get_peer_identity_str(tls->peer_cert);
- if (!peer_identity) {
+ if (tls->peer_authenticated && !resuming) {
+ peer_cert_identity = tls_get_peer_identity_str(tls->peer_cert);
+ if (!peer_cert_identity) {
TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
"tls_get_peer_identity_str failed");
return;
}
+ peer_identity = peer_cert_identity;
+
if (tls->session_id_new &&
!l_cert_get_valid_times(tls->peer_cert, NULL,
&peer_cert_expiry)) {
@@ -2438,7 +2653,8 @@ static void tls_finished(struct l_tls *tls)
"l_cert_get_valid_times failed");
return;
}
- }
+ } else if (tls->peer_authenticated && resuming)
+ peer_identity = tls->session_peer_identity;
if (!tls->server && tls->session_settings && tls->session_id_new) {
_auto_(l_free) char *session_id_str =
@@ -2516,6 +2732,8 @@ static void tls_finished(struct l_tls *tls)
static void tls_handle_handshake(struct l_tls *tls, int type,
const uint8_t *buf, size_t len)
{
+ bool resuming;
+
TLS_DEBUG("Handling a %s of %zi bytes",
tls_handshake_type_to_str(type), len);
@@ -2703,7 +2921,9 @@ static void tls_handle_handshake(struct l_tls *tls, int type,
if (!tls_verify_finished(tls, buf, len))
break;
- if (tls->server) {
+ resuming = tls->session_id_size && !tls->session_id_new;
+
+ if (tls->server || (!tls->server && resuming)) {
const char *error;
tls_send_change_cipher_spec(tls);
@@ -2717,9 +2937,9 @@ static void tls_handle_handshake(struct l_tls *tls, int type,
}
/*
- * On the client, the server's certificate is now verified
- * regardless of the key exchange method, based on the
- * following logic:
+ * When starting a new session on the client, the server's
+ * certificate is now verified regardless of the key exchange
+ * method, based on the following logic:
*
* - tls->ca_certs is non-NULL so tls_handle_certificate
* (always called on the client) must have veritifed the
@@ -2744,9 +2964,14 @@ static void tls_handle_handshake(struct l_tls *tls, int type,
* able to sign the client random together with the
* ServerKeyExchange parameters using its certified key
* pair.
+ *
+ * If we're resuming a cached session, we have authenticated
+ * this server before and the successful decryption of this
+ * message confirms the server identity hasn't changed.
*/
if (!tls->server && tls->cipher_suite[0]->signature &&
- tls->ca_certs)
+ ((!resuming && tls->ca_certs) ||
+ (resuming && tls->session_peer_identity)))
tls->peer_authenticated = true;
tls_finished(tls);
@@ -3010,6 +3235,27 @@ LIB_EXPORT bool l_tls_start(struct l_tls *tls)
if (!tls_init_handshake_hash(tls))
return false;
+ /*
+ * If we're going to try resuming a cached session, send the Client
+ * Hello with the version we think is supported.
+ *
+ * RFC5246 Appendix E.1:
+ * "Whenever a client already knows the highest protocol version known
+ * to a server (for example, when resuming a session), it SHOULD
+ * initiate the connection in that native protocol."
+ *
+ * Don't directly set tls->{min,max}_version as that would make the
+ * handshake fail if the server decides to start a new session with
+ * a new version instead of resuming, which it is allowed to do.
+ */
+ tls->client_version = tls->max_version;
+ tls_load_cached_client_session(tls);
+
+ if (tls->pending_destroy) {
+ l_tls_free(tls);
+ return false;
+ }
+
if (!tls_send_client_hello(tls))
return false;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6/6] examples: Cache sessions in https-client-test
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
` (3 preceding siblings ...)
2022-10-26 13:15 ` [PATCH 5/6] tls: Client session resumption Andrew Zaborowski
@ 2022-10-26 13:15 ` Andrew Zaborowski
2022-10-28 18:27 ` [PATCH 1/6] time: Add time_realtime_now Denis Kenzior
5 siblings, 0 replies; 7+ messages in thread
From: Andrew Zaborowski @ 2022-10-26 13:15 UTC (permalink / raw)
To: ell
If the environment variable TLS_CACHE is set, use
l_tls_set_session_cache() to enable session resumption.
---
examples/https-client-test.c | 48 ++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/examples/https-client-test.c b/examples/https-client-test.c
index b0c24b4..2c6939a 100644
--- a/examples/https-client-test.c
+++ b/examples/https-client-test.c
@@ -32,13 +32,18 @@
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <ell/ell.h>
+#include <ell/useful.h>
static struct l_io *io;
static struct l_tls *tls;
static const char *hostname;
static bool ready;
+static struct l_settings *session_cache;
+static char *session_cache_path;
static void https_io_disconnect(struct l_io *io, void *user_data)
{
@@ -127,6 +132,27 @@ static void https_tls_debug_cb(const char *str, void *user_data)
printf("%s\n", str);
}
+static void https_tls_session_cache_update_cb(void *user_data)
+{
+ size_t len;
+ char *data = l_settings_to_data(session_cache, &len);
+ _auto_(close) int fd = L_TFR(creat(session_cache_path, 0600));
+
+ if (!data) {
+ fprintf(stderr, "l_settings_to_data() failed\n");
+ return;
+ }
+
+ if (fd < 0) {
+ fprintf(stderr, "can't open %s: %s\n",
+ session_cache_path, strerror(errno));
+ return;
+ }
+
+ if (L_TFR(write(fd, data, len)) < (ssize_t) len)
+ fprintf(stderr, "short write to %s\n", session_cache_path);
+}
+
int main(int argc, char *argv[])
{
struct hostent *he;
@@ -200,6 +226,23 @@ int main(int argc, char *argv[])
l_free(str);
}
+ if (getenv("TLS_CACHE")) {
+ const char *homedir = getenv("HOME");
+
+ if (!homedir)
+ homedir = "/tmp";
+
+ session_cache_path =
+ l_strdup_printf("%s/.ell-https-client-test", homedir);
+ session_cache = l_settings_new();
+ l_settings_load_from_file(session_cache, session_cache_path);
+
+ l_tls_set_session_cache(tls, session_cache, hostname,
+ 24 * 3600 * L_USEC_PER_SEC,
+ https_tls_session_cache_update_cb,
+ NULL);
+ }
+
if (argc >= 3) {
ca_cert = l_pem_load_certificate_list(argv[2]);
if (!ca_cert) {
@@ -244,6 +287,11 @@ int main(int argc, char *argv[])
l_io_destroy(io);
l_tls_free(tls);
+ if (session_cache) {
+ l_settings_free(session_cache);
+ l_free(session_cache_path);
+ }
+
l_main_exit();
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/6] time: Add time_realtime_now
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
` (4 preceding siblings ...)
2022-10-26 13:15 ` [PATCH 6/6] examples: Cache sessions in https-client-test Andrew Zaborowski
@ 2022-10-28 18:27 ` Denis Kenzior
5 siblings, 0 replies; 7+ messages in thread
From: Denis Kenzior @ 2022-10-28 18:27 UTC (permalink / raw)
To: Andrew Zaborowski, ell
Hi Andrew,
On 10/26/22 08:15, Andrew Zaborowski wrote:
> ---
> ell/time-private.h | 1 +
> ell/time.c | 8 ++++++++
> 2 files changed, 9 insertions(+)
>
All applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-10-28 18:27 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-26 13:15 [PATCH 1/6] time: Add time_realtime_now Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 2/6] cert: Add l_cert_get_valid_times Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 3/6] tls: Fix an RFC reference Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 4/6] tls: Add support for caching client session states Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 5/6] tls: Client session resumption Andrew Zaborowski
2022-10-26 13:15 ` [PATCH 6/6] examples: Cache sessions in https-client-test Andrew Zaborowski
2022-10-28 18:27 ` [PATCH 1/6] time: Add time_realtime_now Denis Kenzior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).