]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Extend TLS context cache with TLS client session cache
authorArtem Boldariev <artem@boldariev.com>
Fri, 22 Apr 2022 08:41:14 +0000 (11:41 +0300)
committerArtem Boldariev <artem@boldariev.com>
Fri, 20 May 2022 17:13:20 +0000 (20:13 +0300)
This commit extends TLS context cache with TLS client session cache so
that an associated session cache can be stored alongside the TLS
context within the context cache.

bin/dig/dighost.c
lib/dns/xfrin.c
lib/isc/include/isc/tls.h
lib/isc/tls.c
lib/ns/listenlist.c

index da6ce83de94fed6eb4440b3c535738b2ca0d905d..622c90fff69914bbb3e57cec71df64cae86f2fe0 100644 (file)
@@ -2764,7 +2764,8 @@ _cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line) {
 }
 
 static isc_tlsctx_t *
-get_create_tls_context(dig_query_t *query, const bool is_https) {
+get_create_tls_context(dig_query_t *query, const bool is_https,
+                      isc_tlsctx_client_session_cache_t **psess_cache) {
        isc_result_t result;
        isc_tlsctx_t *ctx = NULL, *found_ctx = NULL;
        isc_tls_cert_store_t *store = NULL, *found_store = NULL;
@@ -2775,6 +2776,8 @@ get_create_tls_context(dig_query_t *query, const bool is_https) {
        isc_tlsctx_cache_transport_t transport =
                is_https ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls;
        const bool hostname_ignore_subject = !is_https;
+       isc_tlsctx_client_session_cache_t *sess_cache = NULL,
+                                         *found_sess_cache = NULL;
 
        if (query->lookup->tls_key_file_set != query->lookup->tls_cert_file_set)
        {
@@ -2785,7 +2788,7 @@ get_create_tls_context(dig_query_t *query, const bool is_https) {
 
        result = isc_tlsctx_cache_find(query->lookup->tls_ctx_cache, tlsctxname,
                                       transport, family, &found_ctx,
-                                      &found_store);
+                                      &found_store, &found_sess_cache);
        if (result != ISC_R_SUCCESS) {
                if (query->lookup->tls_ca_set) {
                        if (found_store == NULL) {
@@ -2844,13 +2847,26 @@ get_create_tls_context(dig_query_t *query, const bool is_https) {
                }
 #endif /* HAVE_LIBNGHTTP2 */
 
-               result = isc_tlsctx_cache_add(query->lookup->tls_ctx_cache,
-                                             tlsctxname, transport, family,
-                                             ctx, store, NULL, NULL);
+               sess_cache = isc_tlsctx_client_session_cache_new(
+                       mctx, ctx,
+                       ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE);
+
+               result = isc_tlsctx_cache_add(
+                       query->lookup->tls_ctx_cache, tlsctxname, transport,
+                       family, ctx, store, sess_cache, NULL, NULL, NULL);
                RUNTIME_CHECK(result == ISC_R_SUCCESS);
+               if (psess_cache != NULL) {
+                       INSIST(*psess_cache == NULL);
+                       *psess_cache = sess_cache;
+               }
                return (ctx);
        }
 
+       if (psess_cache != NULL) {
+               INSIST(*psess_cache == NULL);
+               *psess_cache = found_sess_cache;
+       }
+
        INSIST(!query->lookup->tls_ca_set || found_store != NULL);
        return (found_ctx);
 failure:
@@ -2860,6 +2876,9 @@ failure:
        if (store != NULL && store != found_store) {
                isc_tls_cert_store_free(&store);
        }
+       if (sess_cache != NULL && sess_cache != found_sess_cache) {
+               isc_tlsctx_client_session_cache_detach(&sess_cache);
+       }
        return (NULL);
 }
 
@@ -2878,6 +2897,7 @@ start_tcp(dig_query_t *query) {
        dig_query_t *connectquery = NULL;
        isc_tlsctx_t *tlsctx = NULL;
        bool tls_mode = false;
+       isc_tlsctx_client_session_cache_t *sess_cache = NULL;
        REQUIRE(DIG_VALID_QUERY(query));
 
        debug("start_tcp(%p)", query);
@@ -2972,7 +2992,8 @@ start_tcp(dig_query_t *query) {
                query_attach(query, &connectquery);
 
                if (tls_mode) {
-                       tlsctx = get_create_tls_context(connectquery, false);
+                       tlsctx = get_create_tls_context(connectquery, false,
+                                                       &sess_cache);
                        if (tlsctx == NULL) {
                                goto failure_tls;
                        }
@@ -2989,8 +3010,8 @@ start_tcp(dig_query_t *query) {
                                            uri, sizeof(uri));
 
                        if (!query->lookup->http_plain) {
-                               tlsctx = get_create_tls_context(connectquery,
-                                                               true);
+                               tlsctx = get_create_tls_context(
+                                       connectquery, true, &sess_cache);
                                if (tlsctx == NULL) {
                                        goto failure_tls;
                                }
index 2cd2f809419f8b0461adc1498bf605d3b10b5b81..42f8114e9d3a7cb95e635c58d2bace7cad75efb7 100644 (file)
@@ -934,6 +934,8 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
        dns_transport_type_t transport_type = DNS_TRANSPORT_TCP;
        isc_tlsctx_t *tlsctx = NULL, *found = NULL;
        isc_tls_cert_store_t *store = NULL, *found_store = NULL;
+       isc_tlsctx_client_session_cache_t *sess_cache = NULL,
+                                         *found_sess_cache = NULL;
 
        (void)isc_refcount_increment0(&xfr->connects);
        dns_xfrin_attach(xfr, &connect_xfr);
@@ -972,9 +974,9 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
                 * full TLS handshake procedure, making establishing
                 * subsequent TLS connections for XoT faster.
                 */
-               result = isc_tlsctx_cache_find(xfr->tlsctx_cache, tlsname,
-                                              isc_tlsctx_cache_tls, family,
-                                              &tlsctx, &found_store);
+               result = isc_tlsctx_cache_find(
+                       xfr->tlsctx_cache, tlsname, isc_tlsctx_cache_tls,
+                       family, &tlsctx, &found_store, &found_sess_cache);
                if (result != ISC_R_SUCCESS) {
                        const char *hostname =
                                dns_transport_get_remote_hostname(
@@ -1079,11 +1081,16 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
 
                        isc_tlsctx_enable_dot_client_alpn(tlsctx);
 
+                       sess_cache = isc_tlsctx_client_session_cache_new(
+                               xfr->mctx, tlsctx,
+                               ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE);
+
                        found_store = NULL;
                        result = isc_tlsctx_cache_add(
                                xfr->tlsctx_cache, tlsname,
                                isc_tlsctx_cache_tls, family, tlsctx, store,
-                               &found, &found_store);
+                               sess_cache, &found, &found_store,
+                               &found_sess_cache);
                        if (result == ISC_R_EXISTS) {
                                /*
                                 * It seems the entry has just been created
@@ -1101,7 +1108,10 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
                                INSIST(found != NULL);
                                isc_tlsctx_free(&tlsctx);
                                isc_tls_cert_store_free(&store);
+                               isc_tlsctx_client_session_cache_detach(
+                                       &sess_cache);
                                tlsctx = found;
+                               sess_cache = found_sess_cache;
                        } else {
                                INSIST(result == ISC_R_SUCCESS);
                        }
@@ -1129,6 +1139,11 @@ failure:
        if (store != NULL && store != found_store) {
                isc_tls_cert_store_free(&store);
        }
+
+       if (sess_cache != NULL && sess_cache != found_sess_cache) {
+               isc_tlsctx_client_session_cache_detach(&sess_cache);
+       }
+
        isc_refcount_decrement0(&xfr->connects);
        dns_xfrin_detach(&connect_xfr);
        return (result);
index ab44a2348457de22cb822c737db991e305ff507d..86265ddb7c9d225c0a2fdbd26711218c20f8b574 100644 (file)
@@ -366,7 +366,7 @@ isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache,
  *
  * Requires:
  *\li  'cache' is a pointer to a valid TLS client session cache object;
- *\li  'remote_peer_name' is a pointer to a non empty character string.
+ *\li  'remote_peer_name' is a pointer to a non empty character string;
  *\li  'tls' is a valid, non-'NULL' pointer to a TLS connection state object.
  */
 
@@ -375,11 +375,11 @@ isc_tlsctx_client_session_cache_keep_sockaddr(
        isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
        isc_tls_t *tls);
 /*%<
- * The same as 'isc_tlsctx_client_session_cache_keep()', but using a
+ * The same as 'isc_tlsctx_client_session_cache_keep()', but uses a
  * 'isc_sockaddr_t' as a key, instead of a character string.
  *
  * Requires:
- *\li  'remote_peer' is a valid, non-'NULL' pointer to an 'isc_sockaddr_t'
+ *\li  'remote_peer' is a valid, non-'NULL', pointer to an 'isc_sockaddr_t'
  *object.
  */
 
@@ -395,7 +395,7 @@ isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache,
  * Requires:
  *\li  'cache' is a pointer to a valid TLS client session cache object;
  *\li  'remote_peer_name' is a pointer to a non empty character string;
- *\li  'tls' is a valid, non-'NULL', pointer to a TLS connection state object.
+ *\li  'tls' is a valid, non-'NULL' pointer to a TLS connection state object.
  */
 
 void
@@ -413,6 +413,22 @@ isc_tlsctx_client_session_cache_reuse_sockaddr(
 
 const isc_tlsctx_t *
 isc_tlsctx_client_session_cache_getctx(isc_tlsctx_client_session_cache_t *cache);
+/*%<
+ * Returns a TLS context associated with the given TLS client
+ * session cache object. The function is intended to be used to
+ * implement the sanity checks ('INSIST()'s and 'REQUIRE()'s).
+ *
+ * Requires:
+ *\li  'cache' is a pointer to a valid TLS client session cache object.
+ */
+
+#define ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE (150)
+/*%<
+ * The default maximum size of a TLS client session cache. The value
+ * should be large enough to hold enough sessions to successfully
+ * re-establish connections to the most remote TLS servers, but not
+ * too big to avoid keeping too much obsolete sessions.
+ */
 
 typedef struct isc_tlsctx_cache isc_tlsctx_cache_t;
 /*%<
@@ -481,28 +497,39 @@ isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep);
  */
 
 isc_result_t
-isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
-                    const isc_tlsctx_cache_transport_t transport,
-                    const uint16_t family, isc_tlsctx_t *ctx,
-                    isc_tls_cert_store_t *store, isc_tlsctx_t **pfound,
-                    isc_tls_cert_store_t **pfound_store);
+isc_tlsctx_cache_add(
+       isc_tlsctx_cache_t *cache, const char *name,
+       const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+       isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
+       isc_tlsctx_client_session_cache_t *client_sess_cache,
+       isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
+       isc_tlsctx_client_session_cache_t **pfound_client_sess_cache);
 /*%<
  *
- * Add a new TLS context to the TLS context cache. 'pfound' is an
- * optional pointer, which can be used to retrieve an already
- * existing TLS context object in a case it exists.
+ * Add a new TLS context and its associated data to the TLS context
+ * cache. 'pfound' is an optional pointer, which can be used to
+ * retrieve an already existing TLS context object in a case it
+ * exists.
  *
  * The passed certificates store object ('store') possession is
  * transferred to the cache object in a case of success. In some cases
  * it might be destroyed immediately upon the call completion.
  *
+ * The possession of the passed TLS client session cache
+ * ('client_sess_cache') is also transferred to the cache object in a
+ * case of success.
+ *
  * Requires:
  *\li  'cache' is a valid pointer to a TLS context cache object;
  *\li  'name' is a valid pointer to a non-empty string;
  *\li  'transport' is a valid transport identifier (currently only
  *       TLS/DoT and HTTPS/DoH are supported);
  *\li  'family' - either 'AF_INET' or 'AF_INET6';
- *\li   'ctx' - a valid pointer to a valid TLS context object.
+ *\li   'ctx' - a valid pointer to a valid TLS context object;
+ *\li   'store' - a valid pointer to a valid TLS certificates store object or
+ *             'NULL';
+ *\li   'client_sess_cache' - a valid pointer to a valid TLS client sessions
+ *cache object or 'NULL.
  *
  * Returns:
  *\li  #ISC_R_EXISTS - node of the same key already exists;
@@ -510,12 +537,13 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
  */
 
 isc_result_t
-isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
-                     const isc_tlsctx_cache_transport_t transport,
-                     const uint16_t family, isc_tlsctx_t **pctx,
-                     isc_tls_cert_store_t **pstore);
+isc_tlsctx_cache_find(
+       isc_tlsctx_cache_t *cache, const char *name,
+       const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+       isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
+       isc_tlsctx_client_session_cache_t **pfound_client_sess_cache);
 /*%<
- * Look up a TLS context in the TLS context cache.
+ * Look up a TLS context and its associated data in the TLS context cache.
  *
  * Requires:
  *\li  'cache' is a valid pointer to a TLS context cache object;
@@ -523,7 +551,10 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
  *\li  'transport' - a valid transport identifier (currently only
  *       TLS/DoT and HTTPS/DoH are supported;
  *\li  'family' - either 'AF_INET' or 'AF_INET6';
- *\li   'pctx' - a valid pointer to a non-NULL pointer.
+ *\li   'pctx' - a valid pointer to a non-NULL pointer;
+ *\li   'pstore' - a valid pointer to a non-NULL pointer or 'NULL'.
+ *\li   'pfound_client_sess_cache' - a valid pointer to a non-NULL pointer or
+ *'NULL'.
  *
  * Returns:
  *\li  #ISC_R_SUCCESS - the context has been found;
index 282ec45f7e7e303229e98fe9b12e83cc89db5353..84b3330e7b1ea802a09698d9ffa6e0a000698fca 100644 (file)
@@ -1086,6 +1086,8 @@ typedef struct isc_tlsctx_cache_entry {
         * session-resumption cache.
         */
        isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2];
+       isc_tlsctx_client_session_cache_t
+               *client_sess_cache[isc_tlsctx_cache_count - 1][2];
        /*
         * One certificate store is enough for all the contexts defined
         * above. We need that for peer validation.
@@ -1138,6 +1140,11 @@ tlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) {
                        if (entry->ctx[i][k] != NULL) {
                                isc_tlsctx_free(&entry->ctx[i][k]);
                        }
+
+                       if (entry->client_sess_cache[i][k] != NULL) {
+                               isc_tlsctx_client_session_cache_detach(
+                                       &entry->client_sess_cache[i][k]);
+                       }
                }
        }
        if (entry->ca_store != NULL) {
@@ -1187,17 +1194,21 @@ isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep) {
 }
 
 isc_result_t
-isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
-                    const isc_tlsctx_cache_transport_t transport,
-                    const uint16_t family, isc_tlsctx_t *ctx,
-                    isc_tls_cert_store_t *store, isc_tlsctx_t **pfound,
-                    isc_tls_cert_store_t **pfound_store) {
+isc_tlsctx_cache_add(
+       isc_tlsctx_cache_t *cache, const char *name,
+       const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+       isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
+       isc_tlsctx_client_session_cache_t *client_sess_cache,
+       isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
+       isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
        isc_result_t result = ISC_R_FAILURE;
        size_t name_len, tr_offset;
        isc_tlsctx_cache_entry_t *entry = NULL;
        bool ipv6;
 
        REQUIRE(VALID_TLSCTX_CACHE(cache));
+       REQUIRE(client_sess_cache == NULL ||
+               VALID_TLSCTX_CLIENT_SESSION_CACHE(client_sess_cache));
        REQUIRE(name != NULL && *name != '\0');
        REQUIRE(transport > isc_tlsctx_cache_none &&
                transport < isc_tlsctx_cache_count);
@@ -1213,6 +1224,7 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
        result = isc_ht_find(cache->data, (const uint8_t *)name, name_len,
                             (void **)&entry);
        if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
+               isc_tlsctx_client_session_cache_t *found_client_sess_cache;
                /* The entry exists. */
                if (pfound != NULL) {
                        INSIST(*pfound == NULL);
@@ -1223,6 +1235,14 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
                        INSIST(*pfound_store == NULL);
                        *pfound_store = entry->ca_store;
                }
+
+               found_client_sess_cache =
+                       entry->client_sess_cache[tr_offset][ipv6];
+               if (pfound_client_sess_cache != NULL &&
+                   found_client_sess_cache != NULL) {
+                       INSIST(*pfound_client_sess_cache == NULL);
+                       *pfound_client_sess_cache = found_client_sess_cache;
+               }
                result = ISC_R_EXISTS;
        } else if (result == ISC_R_SUCCESS &&
                   entry->ctx[tr_offset][ipv6] == NULL) {
@@ -1231,10 +1251,11 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
                 * particular transport/IP type combination.
                 */
                entry->ctx[tr_offset][ipv6] = ctx;
+               entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
                /*
-                * As the passed certificates store object is supposed to be
-                * internally managed by the cache object anyway, we might
-                * destroy the unneeded store object right now.
+                * As the passed certificates store object is supposed
+                * to be internally managed by the cache object anyway,
+                * we might destroy the unneeded store object right now.
                 */
                if (store != NULL && store != entry->ca_store) {
                        isc_tls_cert_store_free(&store);
@@ -1249,6 +1270,7 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
                /* Oracle/Red Hat Linux, GCC bug #53119 */
                memset(entry, 0, sizeof(*entry));
                entry->ctx[tr_offset][ipv6] = ctx;
+               entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
                entry->ca_store = store;
                RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name,
                                         name_len,
@@ -1262,10 +1284,11 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
 }
 
 isc_result_t
-isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
-                     const isc_tlsctx_cache_transport_t transport,
-                     const uint16_t family, isc_tlsctx_t **pctx,
-                     isc_tls_cert_store_t **pstore) {
+isc_tlsctx_cache_find(
+       isc_tlsctx_cache_t *cache, const char *name,
+       const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+       isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
+       isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
        isc_result_t result = ISC_R_FAILURE;
        size_t tr_offset;
        isc_tlsctx_cache_entry_t *entry = NULL;
@@ -1292,7 +1315,16 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
        }
 
        if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
+               isc_tlsctx_client_session_cache_t *found_client_sess_cache =
+                       entry->client_sess_cache[tr_offset][ipv6];
+
                *pctx = entry->ctx[tr_offset][ipv6];
+
+               if (pfound_client_sess_cache != NULL &&
+                   found_client_sess_cache != NULL) {
+                       INSIST(*pfound_client_sess_cache == NULL);
+                       *pfound_client_sess_cache = found_client_sess_cache;
+               }
        } else if (result == ISC_R_SUCCESS &&
                   entry->ctx[tr_offset][ipv6] == NULL) {
                result = ISC_R_NOTFOUND;
index 32a72888d50c40921a0c78d67c974af3d36942eb..f852f06d567e141aad12c480357376f93dae9945 100644 (file)
@@ -49,7 +49,7 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
                 */
                result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name,
                                               transport, family, &sslctx,
-                                              &found_store);
+                                              &found_store, NULL);
                if (result != ISC_R_SUCCESS) {
                        /*
                         * The lookup failed, let's try to create a new context
@@ -150,7 +150,8 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
                        RUNTIME_CHECK(isc_tlsctx_cache_add(
                                              tlsctx_cache, tls_params->name,
                                              transport, family, sslctx, store,
-                                             NULL, NULL) == ISC_R_SUCCESS);
+                                             NULL, NULL, NULL,
+                                             NULL) == ISC_R_SUCCESS);
                } else {
                        INSIST(sslctx != NULL);
                }