1 /* $NetBSD: listenlist.c,v 1.8 2025/01/26 16:25:45 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <stdbool.h> 19 20 #include <isc/mem.h> 21 #include <isc/netmgr.h> 22 #include <isc/util.h> 23 24 #include <dns/acl.h> 25 26 #include <ns/listenlist.h> 27 #include <ns/log.h> 28 29 static void 30 destroy(ns_listenlist_t *list); 31 32 static isc_result_t 33 listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl, 34 const uint16_t family, const bool is_http, bool tls, 35 const ns_listen_tls_params_t *tls_params, 36 isc_tlsctx_cache_t *tlsctx_cache, isc_nm_proxy_type_t proxy, 37 ns_listenelt_t **target) { 38 ns_listenelt_t *elt = NULL; 39 isc_result_t result = ISC_R_SUCCESS; 40 isc_tlsctx_t *sslctx = NULL; 41 isc_tls_cert_store_t *store = NULL, *found_store = NULL; 42 43 REQUIRE(target != NULL && *target == NULL); 44 REQUIRE(!tls || (tls_params != NULL && tlsctx_cache != NULL)); 45 46 if (tls) { 47 const isc_tlsctx_cache_transport_t transport = 48 is_http ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls; 49 50 /* 51 * Let's try to reuse the existing context from the cache in 52 * order to avoid excessive TLS contexts creation. 53 */ 54 result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name, 55 transport, family, &sslctx, 56 &found_store, NULL); 57 if (result != ISC_R_SUCCESS) { 58 /* 59 * The lookup failed, let's try to create a new context 60 * and store it within the cache. 61 */ 62 INSIST(tls_params->name != NULL && 63 *tls_params->name != '\0'); 64 65 result = isc_tlsctx_createserver( 66 tls_params->key, tls_params->cert, &sslctx); 67 if (result != ISC_R_SUCCESS) { 68 goto tls_error; 69 } 70 71 /* 72 * We need to initialise session ID context to make TLS 73 * session resumption work correctly - in particular in 74 * the case when client certificates are used (Mutual 75 * TLS) - otherwise resumption attempts will lead to 76 * handshake failures. See OpenSSL documentation for 77 * 'SSL_CTX_set_session_id_context()', the "Warnings" 78 * section. 79 */ 80 isc_tlsctx_set_random_session_id_context(sslctx); 81 82 /* 83 * If CA-bundle file is specified - enable client 84 * certificates validation. 85 */ 86 if (tls_params->ca_file != NULL) { 87 if (found_store == NULL) { 88 result = isc_tls_cert_store_create( 89 tls_params->ca_file, &store); 90 if (result != ISC_R_SUCCESS) { 91 goto tls_error; 92 } 93 } else { 94 store = found_store; 95 } 96 97 result = isc_tlsctx_enable_peer_verification( 98 sslctx, true, store, NULL, false); 99 if (result != ISC_R_SUCCESS) { 100 goto tls_error; 101 } 102 103 /* 104 * Load the list of allowed client certificate 105 * issuers to send to TLS clients. 106 */ 107 result = isc_tlsctx_load_client_ca_names( 108 sslctx, tls_params->ca_file); 109 if (result != ISC_R_SUCCESS) { 110 goto tls_error; 111 } 112 } 113 114 if (tls_params->protocols != 0) { 115 isc_tlsctx_set_protocols(sslctx, 116 tls_params->protocols); 117 } 118 119 if (tls_params->dhparam_file != NULL) { 120 if (!isc_tlsctx_load_dhparams( 121 sslctx, tls_params->dhparam_file)) 122 { 123 isc_log_write(ns_lctx, 124 NS_LOGCATEGORY_GENERAL, 125 NS_LOGMODULE_INTERFACEMGR, 126 ISC_LOG_ERROR, 127 "loading of dhparam-file " 128 "'%s' failed", 129 tls_params->dhparam_file); 130 result = ISC_R_FAILURE; 131 goto tls_error; 132 } 133 } 134 135 if (tls_params->ciphers != NULL) { 136 isc_tlsctx_set_cipherlist(sslctx, 137 tls_params->ciphers); 138 } 139 140 if (tls_params->cipher_suites != NULL) { 141 isc_tlsctx_set_cipher_suites( 142 sslctx, tls_params->cipher_suites); 143 } 144 145 if (tls_params->prefer_server_ciphers_set) { 146 isc_tlsctx_prefer_server_ciphers( 147 sslctx, 148 tls_params->prefer_server_ciphers); 149 } 150 151 if (tls_params->session_tickets_set) { 152 isc_tlsctx_session_tickets( 153 sslctx, tls_params->session_tickets); 154 } 155 156 #ifdef HAVE_LIBNGHTTP2 157 if (is_http) { 158 isc_tlsctx_enable_http2server_alpn(sslctx); 159 } 160 #endif /* HAVE_LIBNGHTTP2 */ 161 162 if (!is_http) { 163 isc_tlsctx_enable_dot_server_alpn(sslctx); 164 } 165 166 /* 167 * The storing in the cache should not fail because the 168 * (re)initialisation happens from within a single 169 * thread. 170 * 171 * Taking into account that the most recent call to 172 * 'isc_tlsctx_cache_find()' has failed, it means that 173 * the TLS context has not been found. Considering that 174 * the initialisation happens from within the context of 175 * a single thread, the call to 'isc_tlsctx_cache_add()' 176 * is expected not to fail. 177 */ 178 RUNTIME_CHECK(isc_tlsctx_cache_add( 179 tlsctx_cache, tls_params->name, 180 transport, family, sslctx, store, 181 NULL, NULL, NULL, 182 NULL) == ISC_R_SUCCESS); 183 } else { 184 INSIST(sslctx != NULL); 185 } 186 } 187 188 elt = isc_mem_get(mctx, sizeof(*elt)); 189 elt->mctx = mctx; 190 ISC_LINK_INIT(elt, link); 191 elt->port = port; 192 elt->is_http = false; 193 elt->acl = acl; 194 elt->sslctx = sslctx; 195 elt->sslctx_cache = NULL; 196 if (sslctx != NULL && tlsctx_cache != NULL) { 197 isc_tlsctx_cache_attach(tlsctx_cache, &elt->sslctx_cache); 198 } 199 elt->http_endpoints = NULL; 200 elt->http_endpoints_number = 0; 201 elt->http_max_clients = 0; 202 elt->max_concurrent_streams = 0; 203 elt->proxy = proxy; 204 205 *target = elt; 206 return ISC_R_SUCCESS; 207 tls_error: 208 if (sslctx != NULL) { 209 isc_tlsctx_free(&sslctx); 210 } 211 212 if (store != NULL && store != found_store) { 213 isc_tls_cert_store_free(&store); 214 } 215 return result; 216 } 217 218 isc_result_t 219 ns_listenelt_create(isc_mem_t *mctx, in_port_t port, dns_acl_t *acl, 220 const uint16_t family, bool tls, 221 const ns_listen_tls_params_t *tls_params, 222 isc_tlsctx_cache_t *tlsctx_cache, isc_nm_proxy_type_t proxy, 223 ns_listenelt_t **target) { 224 return listenelt_create(mctx, port, acl, family, false, tls, tls_params, 225 tlsctx_cache, proxy, target); 226 } 227 228 isc_result_t 229 ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, dns_acl_t *acl, 230 const uint16_t family, bool tls, 231 const ns_listen_tls_params_t *tls_params, 232 isc_tlsctx_cache_t *tlsctx_cache, 233 isc_nm_proxy_type_t proxy, char **endpoints, 234 size_t nendpoints, const uint32_t max_clients, 235 const uint32_t max_streams, ns_listenelt_t **target) { 236 isc_result_t result; 237 238 REQUIRE(target != NULL && *target == NULL); 239 REQUIRE(endpoints != NULL && *endpoints != NULL); 240 REQUIRE(nendpoints > 0); 241 242 result = listenelt_create(mctx, http_port, acl, family, true, tls, 243 tls_params, tlsctx_cache, proxy, target); 244 if (result == ISC_R_SUCCESS) { 245 (*target)->is_http = true; 246 (*target)->http_endpoints = endpoints; 247 (*target)->http_endpoints_number = nendpoints; 248 /* 249 * 0 sized quota - means unlimited quota. We used to not 250 * create a quota object in such a case, but we might need to 251 * update the value of the quota during reconfiguration, so we 252 * need to have a quota object in place anyway. 253 */ 254 (*target)->http_max_clients = max_clients == 0 ? UINT32_MAX 255 : max_clients; 256 (*target)->max_concurrent_streams = max_streams; 257 } else { 258 size_t i; 259 for (i = 0; i < nendpoints; i++) { 260 isc_mem_free(mctx, endpoints[i]); 261 } 262 isc_mem_free(mctx, endpoints); 263 } 264 return result; 265 } 266 267 void 268 ns_listenelt_destroy(ns_listenelt_t *elt) { 269 if (elt->acl != NULL) { 270 dns_acl_detach(&elt->acl); 271 } 272 273 elt->sslctx = NULL; /* this one is going to be destroyed alongside the 274 sslctx_cache */ 275 if (elt->sslctx_cache != NULL) { 276 isc_tlsctx_cache_detach(&elt->sslctx_cache); 277 } 278 if (elt->http_endpoints != NULL) { 279 size_t i; 280 INSIST(elt->http_endpoints_number > 0); 281 for (i = 0; i < elt->http_endpoints_number; i++) { 282 isc_mem_free(elt->mctx, elt->http_endpoints[i]); 283 } 284 isc_mem_free(elt->mctx, elt->http_endpoints); 285 } 286 isc_mem_put(elt->mctx, elt, sizeof(*elt)); 287 } 288 289 isc_result_t 290 ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target) { 291 ns_listenlist_t *list = NULL; 292 REQUIRE(target != NULL && *target == NULL); 293 list = isc_mem_get(mctx, sizeof(*list)); 294 list->mctx = mctx; 295 list->refcount = 1; 296 ISC_LIST_INIT(list->elts); 297 *target = list; 298 return ISC_R_SUCCESS; 299 } 300 301 static void 302 destroy(ns_listenlist_t *list) { 303 ns_listenelt_t *elt, *next; 304 for (elt = ISC_LIST_HEAD(list->elts); elt != NULL; elt = next) { 305 next = ISC_LIST_NEXT(elt, link); 306 ns_listenelt_destroy(elt); 307 } 308 isc_mem_put(list->mctx, list, sizeof(*list)); 309 } 310 311 void 312 ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target) { 313 INSIST(source->refcount > 0); 314 source->refcount++; 315 *target = source; 316 } 317 318 void 319 ns_listenlist_detach(ns_listenlist_t **listp) { 320 ns_listenlist_t *list = *listp; 321 *listp = NULL; 322 INSIST(list->refcount > 0); 323 list->refcount--; 324 if (list->refcount == 0) { 325 destroy(list); 326 } 327 } 328 329 isc_result_t 330 ns_listenlist_default(isc_mem_t *mctx, in_port_t port, bool enabled, 331 const uint16_t family, ns_listenlist_t **target) { 332 isc_result_t result; 333 dns_acl_t *acl = NULL; 334 ns_listenelt_t *elt = NULL; 335 ns_listenlist_t *list = NULL; 336 337 REQUIRE(target != NULL && *target == NULL); 338 if (enabled) { 339 result = dns_acl_any(mctx, &acl); 340 } else { 341 result = dns_acl_none(mctx, &acl); 342 } 343 if (result != ISC_R_SUCCESS) { 344 goto cleanup; 345 } 346 347 result = ns_listenelt_create(mctx, port, acl, family, false, NULL, NULL, 348 ISC_NM_PROXY_NONE, &elt); 349 if (result != ISC_R_SUCCESS) { 350 goto cleanup_acl; 351 } 352 353 result = ns_listenlist_create(mctx, &list); 354 if (result != ISC_R_SUCCESS) { 355 goto cleanup_listenelt; 356 } 357 358 ISC_LIST_APPEND(list->elts, elt, link); 359 360 *target = list; 361 return ISC_R_SUCCESS; 362 363 cleanup_listenelt: 364 ns_listenelt_destroy(elt); 365 cleanup_acl: 366 dns_acl_detach(&acl); 367 cleanup: 368 return result; 369 } 370