From rt-commit at redwax.eu Wed Apr 2 15:10:28 2025 From: rt-commit at redwax.eu (rt-commit at redwax.eu) Date: Wed, 02 Apr 2025 13:10:28 -0000 Subject: [rt-commit] r215 - in /redwax-tool/trunk: ChangeLog configure.ac redwax-tool.c redwax-tool.h redwax-tool.spec.in redwax_ldns.c redwax_openssl.c redwax_unbound.c Message-ID: <20250402131028.831226A9C96@chestnut.redwax.eu> Author: minfrin at redwax.eu Date: Wed Apr 2 15:10:26 2025 New Revision: 215 Log: Add --tls-in to read certificates from endpoints pointed to by A and AAAA DNS records. Modified: redwax-tool/trunk/ChangeLog redwax-tool/trunk/configure.ac redwax-tool/trunk/redwax-tool.c redwax-tool/trunk/redwax-tool.h redwax-tool/trunk/redwax-tool.spec.in redwax-tool/trunk/redwax_ldns.c redwax-tool/trunk/redwax_openssl.c redwax-tool/trunk/redwax_unbound.c Modified: redwax-tool/trunk/ChangeLog ============================================================================== --- redwax-tool/trunk/ChangeLog (original) +++ redwax-tool/trunk/ChangeLog Wed Apr 2 15:10:26 2025 @@ -1,3 +1,9 @@ + +Changes with v0.9.10 + + *) Add --tls-in to read certificates from endpoints + pointed to by A and AAAA DNS records. + [Graham Leggett] Changes with v0.9.9 Modified: redwax-tool/trunk/configure.ac ============================================================================== --- redwax-tool/trunk/configure.ac (original) +++ redwax-tool/trunk/configure.ac Wed Apr 2 15:10:26 2025 @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT(redwax-tool, 0.9.9, minfrin at sharp.fm) +AC_INIT(redwax-tool, 0.9.10, minfrin at sharp.fm) AC_CONFIG_AUX_DIR(build-aux) AC_CONFIG_MACRO_DIRS([m4]) AM_INIT_AUTOMAKE([dist-bzip2]) Modified: redwax-tool/trunk/redwax-tool.c ============================================================================== --- redwax-tool/trunk/redwax-tool.c (original) +++ redwax-tool/trunk/redwax-tool.c Wed Apr 2 15:10:26 2025 @@ -98,6 +98,8 @@ APR_HOOK_LINK(process_pkcs12_in) APR_HOOK_LINK(complete_keychain_in) APR_HOOK_LINK(process_keychain_in) + APR_HOOK_LINK(set_tls_in) + APR_HOOK_LINK(process_tls_in) APR_HOOK_LINK(complete_filter) APR_HOOK_LINK(process_filter) APR_HOOK_LINK(complete_nss_out) @@ -183,6 +185,10 @@ (redwax_tool_t * r, const char *arg), (r, arg), DECLINED) APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_keychain_in, (redwax_tool_t * r, const char *url, apr_hash_t *urls), (r, url, urls), DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_tls_in, + (redwax_tool_t * r, const char *arg), (r, arg), DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_tls_in, + (redwax_tool_t * r, redwax_dns_t *dns), (r, dns), DECLINED) APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, complete_filter, (redwax_tool_t * r, apr_hash_t *filters), (r, filters), OK, DECLINED) APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_filter, @@ -257,69 +263,70 @@ #define REDWAX_TOOL_PKCS11_MODULE_IN 259 #define REDWAX_TOOL_PKCS12_IN 260 #define REDWAX_TOOL_KEYCHAIN_IN 261 -#define REDWAX_TOOL_FILTER 262 -#define REDWAX_TOOL_FILTER_EMAIL 263 -#define REDWAX_TOOL_FILTER_HOSTNAME 264 -#define REDWAX_TOOL_FILTER_IP 265 -#define REDWAX_TOOL_FILTER_CURRENT 266 -#define REDWAX_TOOL_FILTER_DATE 267 -#define REDWAX_TOOL_FILTER_EXPIRY 268 -#define REDWAX_TOOL_FILTER_DANE 269 -#define REDWAX_TOOL_FILTER_PURPOSE 270 -#define REDWAX_TOOL_FILTER_VERIFY_PARAM 271 -#define REDWAX_TOOL_FILTER_VERIFY_TLSA 272 -#define REDWAX_TOOL_FILTER_VERIFY_SMIMEA 273 -#define REDWAX_TOOL_CERT_OUT 274 -#define REDWAX_TOOL_NO_CERT_OUT 275 -#define REDWAX_TOOL_CHAIN_OUT 276 -#define REDWAX_TOOL_NO_CHAIN_OUT 277 -#define REDWAX_TOOL_ROOT_OUT 278 -#define REDWAX_TOOL_NO_ROOT_OUT 279 -#define REDWAX_TOOL_TRUST_OUT 280 -#define REDWAX_TOOL_NO_TRUST_OUT 281 -#define REDWAX_TOOL_CRL_OUT 282 -#define REDWAX_TOOL_NO_CRL_OUT 283 -#define REDWAX_TOOL_PARAM_OUT 284 -#define REDWAX_TOOL_NO_PARAM_OUT 285 -#define REDWAX_TOOL_KEY_IN 286 -#define REDWAX_TOOL_NO_KEY_IN 287 -#define REDWAX_TOOL_KEY_OUT 288 -#define REDWAX_TOOL_NO_KEY_OUT 289 -#define REDWAX_TOOL_AUTO_OUT 290 -#define REDWAX_TOOL_NO_AUTO_OUT 291 -#define REDWAX_TOOL_SECRET_SUFFIX_IN 292 -#define REDWAX_TOOL_SECRET_SUFFIX_OUT 293 -#define REDWAX_TOOL_SECRET_TOKEN_IN 294 -#define REDWAX_TOOL_SECRET_TOKEN_OUT 295 -#define REDWAX_TOOL_LABEL_OUT 296 -#define REDWAX_TOOL_NSS_OUT 297 -#define REDWAX_TOOL_NSS_SLOT_OUT 298 -#define REDWAX_TOOL_DER_OUT 299 -#define REDWAX_TOOL_PEM_OUT 300 -#define REDWAX_TOOL_PKCS12_OUT 301 -#define REDWAX_TOOL_PKCS11_OUT 302 -#define REDWAX_TOOL_PKCS11_MODULE_OUT 303 -#define REDWAX_TOOL_METADATA_OUT 304 -#define REDWAX_TOOL_METADATA_THRESHOLD 305 -#define REDWAX_TOOL_FORMAT_OUT 306 -#define REDWAX_TOOL_CALENDAR_OUT 307 -#define REDWAX_TOOL_CALENDAR_ALARM 308 -#define REDWAX_TOOL_REMINDER_OUT 309 -#define REDWAX_TOOL_JWKS_OUT 310 -#define REDWAX_TOOL_TEXT_OUT 311 -#define REDWAX_TOOL_NO_TEXT_OUT 312 -#define REDWAX_TOOL_SSH_PRIVATE_OUT 313 -#define REDWAX_TOOL_SSH_PUBLIC_OUT 314 -#define REDWAX_TOOL_SMIMEA_OUT 315 -#define REDWAX_TOOL_SSHFP_OUT 316 -#define REDWAX_TOOL_TLSA_OUT 317 -#define REDWAX_TOOL_USER_IN 318 -#define REDWAX_TOOL_USER_OUT 319 -#define REDWAX_TOOL_GROUP_IN 320 -#define REDWAX_TOOL_GROUP_OUT 321 -#define REDWAX_TOOL_ORDER_OUT 322 -#define REDWAX_TOOL_DNS_SERVER 323 -#define REDWAX_TOOL_DNS_TRUST_ANCHOR 324 +#define REDWAX_TOOL_TLS_IN 262 +#define REDWAX_TOOL_FILTER 264 +#define REDWAX_TOOL_FILTER_EMAIL 265 +#define REDWAX_TOOL_FILTER_HOSTNAME 266 +#define REDWAX_TOOL_FILTER_IP 267 +#define REDWAX_TOOL_FILTER_CURRENT 268 +#define REDWAX_TOOL_FILTER_DATE 269 +#define REDWAX_TOOL_FILTER_EXPIRY 270 +#define REDWAX_TOOL_FILTER_DANE 271 +#define REDWAX_TOOL_FILTER_PURPOSE 272 +#define REDWAX_TOOL_FILTER_VERIFY_PARAM 273 +#define REDWAX_TOOL_FILTER_VERIFY_TLSA 274 +#define REDWAX_TOOL_FILTER_VERIFY_SMIMEA 275 +#define REDWAX_TOOL_CERT_OUT 276 +#define REDWAX_TOOL_NO_CERT_OUT 277 +#define REDWAX_TOOL_CHAIN_OUT 278 +#define REDWAX_TOOL_NO_CHAIN_OUT 279 +#define REDWAX_TOOL_ROOT_OUT 280 +#define REDWAX_TOOL_NO_ROOT_OUT 281 +#define REDWAX_TOOL_TRUST_OUT 282 +#define REDWAX_TOOL_NO_TRUST_OUT 283 +#define REDWAX_TOOL_CRL_OUT 284 +#define REDWAX_TOOL_NO_CRL_OUT 285 +#define REDWAX_TOOL_PARAM_OUT 286 +#define REDWAX_TOOL_NO_PARAM_OUT 287 +#define REDWAX_TOOL_KEY_IN 288 +#define REDWAX_TOOL_NO_KEY_IN 289 +#define REDWAX_TOOL_KEY_OUT 290 +#define REDWAX_TOOL_NO_KEY_OUT 291 +#define REDWAX_TOOL_AUTO_OUT 292 +#define REDWAX_TOOL_NO_AUTO_OUT 293 +#define REDWAX_TOOL_SECRET_SUFFIX_IN 294 +#define REDWAX_TOOL_SECRET_SUFFIX_OUT 295 +#define REDWAX_TOOL_SECRET_TOKEN_IN 296 +#define REDWAX_TOOL_SECRET_TOKEN_OUT 297 +#define REDWAX_TOOL_LABEL_OUT 298 +#define REDWAX_TOOL_NSS_OUT 299 +#define REDWAX_TOOL_NSS_SLOT_OUT 300 +#define REDWAX_TOOL_DER_OUT 301 +#define REDWAX_TOOL_PEM_OUT 302 +#define REDWAX_TOOL_PKCS12_OUT 303 +#define REDWAX_TOOL_PKCS11_OUT 304 +#define REDWAX_TOOL_PKCS11_MODULE_OUT 305 +#define REDWAX_TOOL_METADATA_OUT 306 +#define REDWAX_TOOL_METADATA_THRESHOLD 307 +#define REDWAX_TOOL_FORMAT_OUT 308 +#define REDWAX_TOOL_CALENDAR_OUT 309 +#define REDWAX_TOOL_CALENDAR_ALARM 310 +#define REDWAX_TOOL_REMINDER_OUT 311 +#define REDWAX_TOOL_JWKS_OUT 312 +#define REDWAX_TOOL_TEXT_OUT 313 +#define REDWAX_TOOL_NO_TEXT_OUT 314 +#define REDWAX_TOOL_SSH_PRIVATE_OUT 315 +#define REDWAX_TOOL_SSH_PUBLIC_OUT 316 +#define REDWAX_TOOL_SMIMEA_OUT 317 +#define REDWAX_TOOL_SSHFP_OUT 318 +#define REDWAX_TOOL_TLSA_OUT 319 +#define REDWAX_TOOL_USER_IN 320 +#define REDWAX_TOOL_USER_OUT 321 +#define REDWAX_TOOL_GROUP_IN 322 +#define REDWAX_TOOL_GROUP_OUT 323 +#define REDWAX_TOOL_ORDER_OUT 324 +#define REDWAX_TOOL_DNS_SERVER 325 +#define REDWAX_TOOL_DNS_TRUST_ANCHOR 326 #define REDWAX_EXIT_OK 0 #define REDWAX_EXIT_INIT 1 @@ -408,6 +415,9 @@ " --keychain-in=keychain\tRead certificates, intermediate certificates,\n" "\t\t\t\troot certificates, crls, and keys from a MacOS\n" "\t\t\t\tkeychain identified by the given name." }, + { "tls-in", REDWAX_TOOL_TLS_IN, 1, + " --tls-in=url\t\t\tRead certificates and intermediate certificates\n" + "\t\t\t\tfrom a TLS or DTLS endpoint at the given url." }, { "filter", REDWAX_TOOL_FILTER, 1, " --filter=type\t\t\tApply the given filter to pass inputs to the\n" "\t\t\t\toutputs. \"search\" will pass through all\n" @@ -2675,6 +2685,14 @@ return redwax_process(r, status, name); } +static apr_status_t redwax_tls_in(redwax_tool_t *r, const char *arg, const char *name) +{ + + apr_status_t status = rt_run_set_tls_in(r, arg); + + return redwax_process(r, status, name); +} + void redwax_add_default_hooks(apr_pool_t *pool) { rt_hook_filter_poll(redwax_filter_poll, NULL, NULL, APR_HOOK_LAST); @@ -2806,6 +2824,12 @@ } case REDWAX_TOOL_KEYCHAIN_IN: { if (redwax_keychain_in(r, optarg, "keychain-in")) { + return r->rc; + } + break; + } + case REDWAX_TOOL_TLS_IN: { + if (redwax_tls_in(r, optarg, "tls-in")) { return r->rc; } break; Modified: redwax-tool/trunk/redwax-tool.h ============================================================================== --- redwax-tool/trunk/redwax-tool.h (original) +++ redwax-tool/trunk/redwax-tool.h Wed Apr 2 15:10:26 2025 @@ -26,9 +26,11 @@ #include #include #include +#include #include #include #include +#include #define REDWAX_DECLARE_MODULE(foo) \ module foo##_module @@ -167,6 +169,11 @@ int dane_nxdomain; int dane_malformed; int dane_record; + int tls_bogus; + int tls_insecure; + int tls_nxdomain; + int tls_malformed; + int tls_record; int poll_work; int current; int cert_out; @@ -356,6 +363,14 @@ typedef apr_status_t(* redwax_dns_cb_t) (redwax_tool_t *r, redwax_dns_t *dns); +typedef struct redwax_rdata_a_t { + apr_sockaddr_t *sockaddr; +} redwax_rdata_a_t; + +typedef struct redwax_rdata_aaaa_t { + apr_sockaddr_t *sockaddr; +} redwax_rdata_aaaa_t; + typedef struct redwax_rdata_tlsa_t { const char *usage_name; const char *selector_name; @@ -371,6 +386,8 @@ unsigned char *data; int len; union { + redwax_rdata_a_t a; + redwax_rdata_a_t aaaa; redwax_rdata_tlsa_t tlsa; } rr; } redwax_rdata_t; @@ -388,9 +405,18 @@ int secure; int bogus; const char *why_bogus; + int port; redwax_dns_cb_t cb; void *ctx; } redwax_dns_t; + +typedef struct redwax_tls_t { + const char *unparsed_uri; + apr_uri_t uri; + int family; + int type; + int protocol; +} redwax_tls_t; typedef apr_status_t(* redwax_pollfd_cb_t) (redwax_tool_t *r, void *ctx, apr_pollfd_t *descriptor); @@ -580,6 +606,22 @@ (redwax_tool_t *r, const char *arg)) /** + * Hook to trigger a DNS lookup to read certificates from a TLS connection. + * + * @param r The redwax-tool context. + */ +APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, set_tls_in, + (redwax_tool_t *r, const char *arg)) + +/** + * Hook to read certificates from a DTLS connection. + * + * @param r The redwax-tool context. + */ +APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_tls_in, + (redwax_tool_t *r, redwax_dns_t *dns)) + +/** * Hook to complete incoming certificates / intermediates / keys * and pass filtered results to the outgoing certificates / * intermediates / keys. Modified: redwax-tool/trunk/redwax-tool.spec.in ============================================================================== --- redwax-tool/trunk/redwax-tool.spec.in (original) +++ redwax-tool/trunk/redwax-tool.spec.in Wed Apr 2 15:10:26 2025 @@ -47,6 +47,16 @@ %license COPYING %changelog +* Thu Feb 27 2025 Graham Leggett 0.9.9-1 +- Bugfix release +* Thu Feb 27 2025 Graham Leggett 0.9.8-1 +- Bugfix release +* Thu Nov 28 2024 Graham Leggett 0.9.7-1 +- Bugfix release +* Mon Nov 11 2024 Graham Leggett 0.9.6-1 +- Feature release +* Mon Nov 11 2024 Graham Leggett 0.9.5-1 +- Feature release * Thu Feb 08 2024 Graham Leggett 0.9.4-1 - Feature release * Sun Oct 15 2023 Graham Leggett 0.9.3-1 Modified: redwax-tool/trunk/redwax_ldns.c ============================================================================== --- redwax-tool/trunk/redwax_ldns.c (original) +++ redwax-tool/trunk/redwax_ldns.c Wed Apr 2 15:10:26 2025 @@ -128,6 +128,8 @@ { const unsigned char *der = cert->der; + /* ldns is tightly bound to openssl */ + X509 *x = d2i_X509(NULL, &der, cert->len); if (!x) { @@ -178,6 +180,58 @@ { switch (dns->rrtype) { + case LDNS_RR_TYPE_A: { + + apr_sockaddr_t *new_sa = apr_pcalloc(r->pool, sizeof(apr_sockaddr_t)); + + new_sa->pool = r->pool; + + new_sa->family = AF_INET; + new_sa->sa.sin.sin_family = AF_INET; + + new_sa->sa.sin.sin_addr = *(struct in_addr *)rdata->data; + + new_sa->hostname = apr_pstrdup(r->pool, dns->qname); + + new_sa->salen = sizeof(struct sockaddr_in); + new_sa->addr_str_len = 16; + new_sa->ipaddr_ptr = &(new_sa->sa.sin.sin_addr); + new_sa->ipaddr_len = sizeof(struct in_addr); + + new_sa->sa.sin.sin_port = htons(dns->port); + new_sa->port = dns->port; + + rdata->rr.a.sockaddr = new_sa; + + break; + } + case LDNS_RR_TYPE_AAAA: { + + redwax_tls_t *tls = dns->ctx; + + apr_sockaddr_t *new_sa = apr_pcalloc(r->pool, sizeof(apr_sockaddr_t)); + + new_sa->pool = r->pool; + + new_sa->family = AF_INET6; + new_sa->sa.sin.sin_family = AF_INET6; + + new_sa->sa.sin6.sin6_addr = *(struct in6_addr *)rdata->data; + + new_sa->hostname = apr_pstrdup(r->pool, dns->qname); + + new_sa->salen = sizeof(struct sockaddr_in6); + new_sa->addr_str_len = 46; + new_sa->ipaddr_ptr = &(new_sa->sa.sin6.sin6_addr); + new_sa->ipaddr_len = sizeof(struct in6_addr); + + new_sa->sa.sin6.sin6_port = htons(dns->port); + new_sa->port = dns->port; + + rdata->rr.aaaa.sockaddr = new_sa; + + break; + } case LDNS_RR_TYPE_TLSA: { ldns_buffer buffer; Modified: redwax-tool/trunk/redwax_openssl.c ============================================================================== --- redwax-tool/trunk/redwax_openssl.c (original) +++ redwax-tool/trunk/redwax_openssl.c Wed Apr 2 15:10:26 2025 @@ -22,6 +22,7 @@ #include #include +#include #include #include "config.h" @@ -72,6 +73,8 @@ typedef struct { SSL_CTX *dane_ctx; SSL *dane_ssl; + apr_array_header_t *tls_hosts; + int tls_in_flight; } openssl_config_t; typedef struct { @@ -82,6 +85,20 @@ typedef struct { /* nothing yet */ } openssl_key_config_t; + +typedef struct { + apr_pool_t *pool; + redwax_tls_t *tls; + const char *hostname; + apr_sockaddr_t *sockaddr; + apr_socket_t *socket; + apr_pollfd_t socket_read; + redwax_pollfd_t read_ctx; + apr_pollfd_t socket_write; + redwax_pollfd_t write_ctx; + SSL *ssl; + int do_shutdown; +} openssl_tls_config_t; // move to config above static STACK_OF(X509) *cert_index; @@ -1129,6 +1146,8 @@ apr_pool_cleanup_register(r->pool, config->dane_ssl, cleanup_SSL, apr_pool_cleanup_null); + config->tls_hosts = apr_array_make(r->pool, 16, sizeof(openssl_tls_config_t *)); + cert_index = sk_X509_new_null(); chain_index = sk_X509_new_null(); trusted_index = sk_X509_new_null(); @@ -3743,6 +3762,490 @@ } return APR_SUCCESS; +} + +static apr_status_t tls_connect(redwax_tool_t *r); + +static apr_status_t cleanup_tls_in_flight(void *dummy) +{ + if (dummy) { + openssl_config_t *config = dummy; + + config->tls_in_flight--; + } + + return APR_SUCCESS; +} + +static apr_status_t tls_in_flight(redwax_tool_t *r, openssl_tls_config_t *tls_host) +{ + openssl_config_t *config = redwax_get_module_config(r->per_module, &openssl_module); + + config->tls_in_flight++; + + apr_pool_cleanup_register(tls_host->pool, config, cleanup_tls_in_flight, + apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +static apr_status_t redwax_openssl_handshake_cb(redwax_tool_t *r, openssl_tls_config_t *tls_host) +{ + apr_status_t status = APR_SUCCESS; + + int rv, label_len; + + redwax_tls_t *tls = tls_host->tls; + + rv = SSL_do_handshake(tls_host->ssl); + + if (1 == rv || 2 == rv) { + + STACK_OF(X509) *sk; + + int i; + + /* negotiated connection took place */ + + sk = SSL_get_peer_cert_chain(tls_host->ssl); + + for (i = 0; i < sk_X509_num(sk); i++) { + + unsigned char *der = NULL; + const char *label; + + redwax_certificate_t *cert; + + X509 *x = sk_X509_value(sk, i); + + if (!x) { + redwax_print_error(r, "tls-in: could not read certificate from %pI (%s), skipping.\n", + tls_host->sockaddr, tls->unparsed_uri); + redwax_openssl_print_errors(r); + continue; + } + + if (X509_check_ca(x)) { + + cert = apr_array_push(r->intermediates_in); + + apr_pool_create(&cert->pool, r->pool); + + cert->common.type = REDWAX_CERTIFICATE_X509; + cert->common.category = REDWAX_CERTIFICATE_INTERMEDIATE; + + redwax_print_error(r, "tls-in: intermediate: %s\n", + redwax_openssl_name(cert->pool, + X509_get_subject_name(x))); + } + else { + + cert = apr_array_push(r->certs_in); + + apr_pool_create(&cert->pool, r->pool); + + cert->common.type = REDWAX_CERTIFICATE_X509; + cert->common.category = REDWAX_CERTIFICATE_END_ENTITY; + + redwax_print_error(r, "tls-in: certificate: %s\n", + redwax_openssl_name(cert->pool, + X509_get_subject_name(x))); + + } + + cert->per_module = redwax_create_module_config(cert->pool); + + label = (const char *)X509_alias_get0(x, &label_len); + cert->label = apr_pstrndup(cert->pool, label, label_len); + cert->label_len = label_len; + + cert->len = i2d_X509(x, &der); + cert->der = der; + + apr_pool_cleanup_register(cert->pool, der, cleanup_alloc, + apr_pool_cleanup_null); + + cert->origin = apr_pstrdup(cert->pool, tls_host->hostname); + + rt_run_normalise_certificate(r, cert, 1); + + } + + tls_host->do_shutdown = 1; + + status = apr_pollcb_add(r->poll, &tls_host->socket_write); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add write descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + } + else { + switch (SSL_get_error(tls_host->ssl, rv)) { + case SSL_ERROR_WANT_WRITE: { + + status = apr_pollcb_add(r->poll, &tls_host->socket_write); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add write descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + break; + } + case SSL_ERROR_WANT_ASYNC: { + + redwax_print_error(r, + "tls-in: unexpected SSL_ERROR_WANT_ASYNC in the bagging area\n"); + return APR_EGENERAL; + } + case SSL_ERROR_WANT_READ: { + + status = apr_pollcb_add(r->poll, &tls_host->socket_read); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add read descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + break; + } + default: + + redwax_print_error(r, + "tls-in: TLS handshake of '%s' connection to %pI failed, ingoring.\n", + tls->unparsed_uri, tls_host->sockaddr); + redwax_openssl_print_errors(r); + + return APR_SUCCESS; + } + } + + return status; +} + +static apr_status_t redwax_openssl_shutdown_cb(redwax_tool_t *r, openssl_tls_config_t *tls_host) +{ + apr_status_t status = APR_SUCCESS; + + int rv; + + rv = SSL_shutdown(tls_host->ssl); + + if (0 == rv) { + + /* not yet done, wait for the response */ + status = apr_pollcb_add(r->poll, &tls_host->socket_read); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add read descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + } + else if (1 == rv) { + + /* we're done */ + apr_pool_destroy(tls_host->pool); + + /* line up the next connection(s) */ + return tls_connect(r); + } + else { + + redwax_tls_t *tls = tls_host->tls; + + switch (SSL_get_error(tls_host->ssl, rv)) { + case SSL_ERROR_WANT_WRITE: { + + status = apr_pollcb_add(r->poll, &tls_host->socket_write); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add write descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + break; + } + case SSL_ERROR_WANT_ASYNC: { + + redwax_print_error(r, + "tls-in: unexpected SSL_ERROR_WANT_ASYNC in the bagging area\n"); + return APR_EGENERAL; + } + case SSL_ERROR_WANT_READ: { + + status = apr_pollcb_add(r->poll, &tls_host->socket_read); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add read descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + break; + } + default: + + redwax_print_error(r, + "tls-in: shutdown of '%s' connection to %pI failed, ingoring.\n", + tls->unparsed_uri, tls_host->sockaddr); + redwax_openssl_print_errors(r); + + return APR_SUCCESS; + } + } + + return status; +} + +static apr_status_t redwax_openssl_filter_read_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor) +{ + + redwax_pollfd_t *read_ctx = descriptor->client_data; + openssl_tls_config_t *tls_host = read_ctx->ctx; + + /* ack readcb work */ + r->poll_work--; + + if (tls_host->do_shutdown) { + return redwax_openssl_shutdown_cb(r, tls_host); + } + + return redwax_openssl_handshake_cb(r, tls_host); +} + +static apr_status_t redwax_openssl_filter_write_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor) +{ + + redwax_pollfd_t *write_ctx = descriptor->client_data; + openssl_tls_config_t *tls_host = write_ctx->ctx; + redwax_tls_t *tls = tls_host->tls; + + apr_status_t status = APR_SUCCESS; + + /* ack readcb work */ + r->poll_work--; + + if (!tls_host->ssl) { + + SSL_CTX *ctx; + + apr_os_sock_t fd; + + status = apr_os_sock_get(&fd, tls_host->socket); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not convert SSL fd: %pm\n", &status); + return status; + } + + if (tls->protocol == APR_PROTO_TCP) { + ctx = SSL_CTX_new(TLS_client_method()); + } + else { + ctx = SSL_CTX_new(DTLS_client_method()); + } + + apr_pool_cleanup_register(tls_host->pool, ctx, cleanup_SSL_CTX, + apr_pool_cleanup_null); + + tls_host->ssl = SSL_new(ctx); + + apr_pool_cleanup_register(tls_host->pool, tls_host->ssl, cleanup_SSL, + apr_pool_cleanup_null); + + SSL_set_connect_state(tls_host->ssl); + + SSL_set_verify(tls_host->ssl, SSL_VERIFY_NONE, NULL); + + if (SSL_set_fd(tls_host->ssl, fd) <= 0) { + redwax_openssl_print_errors(r); + return APR_EGENERAL; + } + + if (SSL_set1_host(tls_host->ssl, tls_host->hostname) <= 0) { + redwax_openssl_print_errors(r); + return APR_EGENERAL; + } + + if (SSL_set_tlsext_host_name(tls_host->ssl, tls_host->hostname) <= 0) { + redwax_openssl_print_errors(r); + return APR_EGENERAL; + } + + /* todo: SSL_set_alpn_protos */ + } + + if (tls_host->do_shutdown) { + return redwax_openssl_shutdown_cb(r, tls_host); + } + + return redwax_openssl_handshake_cb(r, tls_host); +} + +static apr_status_t tls_connect(redwax_tool_t *r) +{ + openssl_config_t *config = redwax_get_module_config(r->per_module, &openssl_module); + + apr_status_t status; + + while (config->tls_hosts->nelts && config->tls_in_flight < 2) { + + openssl_tls_config_t **ts = apr_array_pop(config->tls_hosts); + openssl_tls_config_t *tls_host = *ts; + + tls_in_flight(r, tls_host); + + status = apr_socket_create(&tls_host->socket, tls_host->sockaddr->family, SOCK_STREAM, APR_PROTO_TCP, + tls_host->pool); + + status = apr_socket_connect(tls_host->socket, tls_host->sockaddr); + + status = apr_socket_opt_set(tls_host->socket, APR_SO_NONBLOCK, 1); + + /* set up the read callback */ + tls_host->read_ctx.cb = redwax_openssl_filter_read_cb; + tls_host->read_ctx.ctx = tls_host; + + /* set up read descriptor */ + tls_host->socket_read.desc_type = APR_POLL_SOCKET; + tls_host->socket_read.reqevents = APR_POLLIN; + tls_host->socket_read.desc.s = tls_host->socket; + tls_host->socket_read.client_data = &tls_host->read_ctx; + + /* set up the write callback */ + tls_host->write_ctx.cb = redwax_openssl_filter_write_cb; + tls_host->write_ctx.ctx = tls_host; + + /* set up write descriptor */ + tls_host->socket_write.desc_type = APR_POLL_SOCKET; + tls_host->socket_write.reqevents = APR_POLLOUT; + tls_host->socket_write.desc.s = tls_host->socket; + tls_host->socket_write.client_data = &tls_host->write_ctx; + + status = apr_pollcb_add(r->poll, &tls_host->socket_write); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "tls-in: could not add write descriptor to poll: %pm\n", &status); + return status; + } + + /* req writecb work */ + r->poll_work++; + + } + + return APR_SUCCESS; +} + +static apr_status_t redwax_openssl_process_tls_in(redwax_tool_t *r, redwax_dns_t *dns) +{ + openssl_config_t *config = redwax_get_module_config(r->per_module, &openssl_module); + + if (dns->bogus) { + + redwax_print_error(r, + "tls-in: DNS response for '%s' failed " + "DNSSEC validation, ignoring: %s.\n", dns->qname, dns->why_bogus); + + r->tls_bogus++; + + return APR_SUCCESS; + } + + if (!dns->secure) { + + redwax_print_error(r, + "tls-in: warning: DNS response for '%s' is " + "not DNSSEC secured.\n", dns->qname); + + r->tls_insecure++; + } + + if (dns->nxdomain) { + + redwax_print_error(r, + "tls-in: DNS record for '%s' does " + "not exist, ignoring.\n", dns->qname); + + r->tls_nxdomain++; + + return APR_SUCCESS; + } + + if (dns->rdata) { + + int i; + + redwax_tls_t *tls = dns->ctx; + + for (i = 0; i < dns->rdata->nelts; i++) + { + apr_pool_t *pool; + openssl_tls_config_t *tls_host; + + const redwax_rdata_t *rdata = &APR_ARRAY_IDX(dns->rdata, i, + const redwax_rdata_t); + + openssl_tls_config_t **ts = apr_array_push(config->tls_hosts); + + apr_pool_create(&pool, r->pool); + + *ts = tls_host = apr_pcalloc(pool, sizeof(openssl_tls_config_t)); + + tls_host->pool = pool; + + switch (dns->rrtype) { + case 1: + tls_host->sockaddr = rdata->rr.a.sockaddr; + tls_host->hostname = dns->qname; + break; + case 28: + tls_host->sockaddr = rdata->rr.aaaa.sockaddr; + tls_host->hostname = dns->qname; + break; + default: + return APR_EINVAL; + } + + tls_host->tls = tls; + + redwax_print_error(r, + "tls-in: DNS record for '%s' maps to %pI.\n", + tls->unparsed_uri, tls_host->sockaddr); + + } + + } + + return tls_connect(r); } static apr_status_t redwax_openssl_process_pkcs12_out(redwax_tool_t *r, @@ -6398,6 +6901,7 @@ rt_hook_process_pem_in(redwax_openssl_process_pem_in, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_process_trust_pem_in(redwax_openssl_process_trust_pem_in, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_process_pkcs12_in(redwax_openssl_process_pkcs12_in, NULL, NULL, APR_HOOK_MIDDLE); + rt_hook_process_tls_in(redwax_openssl_process_tls_in, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_set_tlsa(redwax_openssl_set_tlsa, NULL, NULL, APR_HOOK_LAST); rt_hook_process_tlsa(redwax_openssl_process_tlsa, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_complete_filter(redwax_openssl_complete_filter_verify, NULL, NULL, APR_HOOK_MIDDLE); Modified: redwax-tool/trunk/redwax_unbound.c ============================================================================== --- redwax-tool/trunk/redwax_unbound.c (original) +++ redwax-tool/trunk/redwax_unbound.c Wed Apr 2 15:10:26 2025 @@ -41,12 +41,9 @@ typedef struct { struct ub_ctx *ctx; - apr_pollfd_t socket_read; - redwax_pollfd_t read_ctx; - apr_pollfd_t socket_write; - redwax_pollfd_t write_ctx; + apr_pollfd_t socket_poll; + redwax_pollfd_t poll_ctx; apr_status_t status; - int socket_read_work; int dns_work; int dns_server_set; int dns_trust_anchor_set; @@ -162,6 +159,7 @@ dns->rrtype = 52 /* TYPE TLSA */; dns->rrclass = 1 /* CLASS IN (internet) */; dns->cb = rt_run_process_tlsa; + dns->port = uri.port; qname = apr_array_push(r->tlsa_qnames); *qname = tlsa; @@ -201,6 +199,7 @@ dns->rrtype = 52 /* TYPE TLSA */; dns->rrclass = 1 /* CLASS IN (internet) */; dns->cb = rt_run_process_tlsa; + dns->port = uri.port ? uri.port : ntohs(ent->s_port); qname = apr_array_push(r->tlsa_qnames); *qname = tlsa; @@ -237,6 +236,164 @@ return APR_ENOTIMPL; } +static apr_status_t redwax_unbound_set_tls_in(redwax_tool_t *r, const char *arg) +{ + apr_uri_t uri; + + apr_status_t status; + + int protocol = 0; + int type = 0; + + status = apr_uri_parse(r->pool, arg, &uri); + if (APR_SUCCESS != status) { + redwax_print_error(r, + "Could not parse URI '%s': %pm\n", arg, &status); + return status; + } + + if (!uri.hostname) { + redwax_print_error(r, + "URI '%s': hostname missing\n", arg); + return APR_EINVAL; + } + + /* fixme: Use inet_pton to detect raw IP/IPv6 addresses and skip lookup */ + + if (!strcmp(uri.scheme, "tcp")) { + protocol = APR_PROTO_TCP; + type = SOCK_STREAM; + } + else if (!strcmp(uri.scheme, "udp")) { + protocol = APR_PROTO_UDP; + type = SOCK_DGRAM; + } + else if (!strcmp(uri.scheme, "sctp")) { + protocol = APR_PROTO_SCTP; + type= SOCK_SEQPACKET; + } + + if (protocol) { + + if (uri.port) { + + redwax_dns_t *dns; + redwax_tls_t *tls; + + tls = apr_palloc(r->pool, sizeof(redwax_tls_t)); + + tls->unparsed_uri = arg; + tls->uri = uri; + tls->protocol = protocol; + tls->type = type; + + dns = apr_array_push(r->dns_requests); + + dns->r = r; + dns->basename = uri.hostname; + dns->qname = uri.hostname; + dns->rrtype = 28 /* TYPE AAAA */; + dns->rrclass = 1 /* CLASS IN (internet) */; + dns->cb = rt_run_process_tls_in; + dns->ctx = tls; + dns->port = uri.port; + + dns = apr_array_push(r->dns_requests); + + dns->r = r; + dns->basename = uri.hostname; + dns->qname = uri.hostname; + dns->rrtype = 1 /* TYPE A */; + dns->rrclass = 1 /* CLASS IN (internet) */; + dns->cb = rt_run_process_tls_in; + dns->ctx = tls; + dns->port = uri.port; + + return APR_SUCCESS; + } + else { + redwax_print_error(r, + "URI '%s': port missing\n", arg); + return APR_EINVAL; + } + } + + else if (uri.scheme) { + + int found = 0; + + setservent(1); + + struct servent *ent; + + while ((ent = getservent())) { + + if (!strcmp(uri.scheme, ent->s_name)) { + + redwax_dns_t *dns; + redwax_tls_t *tls; + + tls = apr_palloc(r->pool, sizeof(redwax_tls_t)); + + /* we hard code the protocol to TCP, as the services + * file contacts lots of bogus tcp/udp entries. + */ + + tls->unparsed_uri = arg; + tls->uri = uri; + tls->protocol = APR_PROTO_TCP; + tls->type = SOCK_STREAM; + + dns = apr_array_push(r->dns_requests); + + dns->r = r; + dns->basename = uri.hostname; + dns->qname = uri.hostname; + dns->rrtype = 28 /* TYPE AAAA */; + dns->rrclass = 1 /* CLASS IN (internet) */; + dns->cb = rt_run_process_tls_in; + dns->ctx = tls; + dns->port = uri.port ? uri.port : ntohs(ent->s_port); + + dns = apr_array_push(r->dns_requests); + + dns->r = r; + dns->basename = uri.hostname; + dns->qname = uri.hostname; + dns->rrtype = 1 /* TYPE A */; + dns->rrclass = 1 /* CLASS IN (internet) */; + dns->cb = rt_run_process_tls_in; + dns->ctx = tls; + dns->port = uri.port ? uri.port : ntohs(ent->s_port); + + found = 1; + + break; + } + + } + + endservent(); + + if (!found) { + redwax_print_error(r, + "URI '%s': protocol '%s' not found\n", arg, uri.scheme); + return APR_EINVAL; + } + else { + return APR_SUCCESS; + } + + } + + else { + redwax_print_error(r, + "URI '%s': scheme missing\n", arg); + return APR_EINVAL; + } + +} + static apr_status_t redwax_unbound_filter_read_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor) { unbound_config_t *config = redwax_get_module_config(r->per_module, &unbound_module); @@ -246,9 +403,6 @@ /* ack readcb work */ r->poll_work--; - - /* ack socket read work */ - config->socket_read_work--; rv = ub_process(config->ctx); @@ -265,19 +419,17 @@ if (config->dns_work) { - status = apr_pollcb_add(r->poll, &config->socket_read); + /* line up next event */ + status = apr_pollcb_add(r->poll, &config->socket_poll); if (APR_SUCCESS != status) { redwax_print_error(r, - "Could not add read descriptor to poll: %pm\n", &status); + "unbound: could not add descriptor to poll: %pm\n", &status); return status; } /* req readcb work */ r->poll_work++; - - /* req socket read work */ - config->socket_read_work++; } return status; @@ -361,40 +513,42 @@ /* req dns lookup work */ config->dns_work++; - if (!config->socket_read_work) { - - status = apr_pollcb_add(r->poll, &config->socket_read); - - if (APR_SUCCESS != status) { - redwax_print_error(r, - "Could not add read descriptor to poll: %pm\n", &status); - return status; - } - - /* req readcb work */ - r->poll_work++; - - /* req socket read work */ - config->socket_read_work++; - - } - - if (r->dns_requests->nelts) { - - status = apr_pollcb_add(r->poll, &config->socket_write); - - if (APR_SUCCESS != status) { - redwax_print_error(r, - "Could not add write descriptor to poll: %pm\n", &status); - return status; - } - - /* req writecb work */ - r->poll_work++; - - } + /* we are done sending requests, poll for responses only */ + if (!r->dns_requests->nelts) { + config->socket_poll.reqevents = APR_POLLIN; + } + + /* line up next event */ + status = apr_pollcb_add(r->poll, &config->socket_poll); + + if (APR_SUCCESS != status) { + redwax_print_error(r, + "Could not add read descriptor to poll: %pm\n", &status); + return status; + } + + /* req readcb work */ + r->poll_work++; return status; +} + +static apr_status_t redwax_unbound_filter_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor) +{ + if (descriptor->rtnevents & APR_POLLIN) { + /* process reads first if available to relieve pressure */ + return redwax_unbound_filter_read_cb(r, baton, descriptor); + } + else if (descriptor->rtnevents & APR_POLLOUT) { + /* do writes if needed */ + return redwax_unbound_filter_write_cb(r, baton, descriptor); + } + else { + /* errors are handled by ub_process() */ + return redwax_unbound_filter_read_cb(r, baton, descriptor); + } + + return APR_SUCCESS; } static apr_status_t redwax_unbound_filter_poll(redwax_tool_t *r) @@ -458,25 +612,17 @@ return status; } - /* set up the read callback */ - config->read_ctx.cb = redwax_unbound_filter_read_cb; - - /* set up read descriptor */ - config->socket_read.desc_type = APR_POLL_SOCKET; - config->socket_read.reqevents = APR_POLLIN; - config->socket_read.desc.s = s; - config->socket_read.client_data = &config->read_ctx; - - /* set up the write callback */ - config->write_ctx.cb = redwax_unbound_filter_write_cb; - - /* set up write descriptor */ - config->socket_write.desc_type = APR_POLL_SOCKET; - config->socket_write.reqevents = APR_POLLOUT; - config->socket_write.desc.s = s; - config->socket_write.client_data = &config->write_ctx; - - status = apr_pollcb_add(r->poll, &config->socket_write); + /* set up the read/write callback */ + + config->poll_ctx.cb = redwax_unbound_filter_cb; + + /* set up read/write descriptor */ + config->socket_poll.desc_type = APR_POLL_SOCKET; + config->socket_poll.reqevents = APR_POLLIN | APR_POLLOUT; + config->socket_poll.desc.s = s; + config->socket_poll.client_data = &config->poll_ctx; + + status = apr_pollcb_add(r->poll, &config->socket_poll); if (APR_SUCCESS != status) { redwax_print_error(r, @@ -497,6 +643,7 @@ rt_hook_set_dns_trust_anchor(redwax_unbound_set_dns_trust_anchor, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_set_tlsa(redwax_unbound_set_tlsa, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_set_smimea(redwax_unbound_set_smimea, NULL, NULL, APR_HOOK_MIDDLE); + rt_hook_set_tls_in(redwax_unbound_set_tls_in, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_filter_poll(redwax_unbound_filter_poll, NULL, NULL, APR_HOOK_MIDDLE); } From rt-commit at redwax.eu Wed Apr 2 15:45:36 2025 From: rt-commit at redwax.eu (rt-commit at redwax.eu) Date: Wed, 02 Apr 2025 13:45:36 -0000 Subject: [rt-commit] r216 - /redwax-tool/trunk/redwax_openssl.c Message-ID: <20250402134536.6C8C96A9C96@chestnut.redwax.eu> Author: minfrin at redwax.eu Date: Wed Apr 2 15:45:35 2025 New Revision: 216 Log: Free a small leak. Modified: redwax-tool/trunk/redwax_openssl.c Modified: redwax-tool/trunk/redwax_openssl.c ============================================================================== --- redwax-tool/trunk/redwax_openssl.c (original) +++ redwax-tool/trunk/redwax_openssl.c Wed Apr 2 15:45:35 2025 @@ -6837,6 +6837,7 @@ if (skid) { x509->skid_len = skid->length; x509->skid_der = apr_pmemdup(cert->pool, skid->data, skid->length); + ASN1_OCTET_STRING_free(skid); } name = X509_get_issuer_name(x); From rt-commit at redwax.eu Wed Apr 2 16:08:56 2025 From: rt-commit at redwax.eu (rt-commit at redwax.eu) Date: Wed, 02 Apr 2025 14:08:56 -0000 Subject: [rt-commit] r217 - in /redwax-tool/trunk: redwax-tool.h redwax_openssl.c Message-ID: <20250402140856.EF7ED6A9C99@chestnut.redwax.eu> Author: minfrin at redwax.eu Date: Wed Apr 2 16:08:56 2025 New Revision: 217 Log: Add TLS address to certificate metadata. Modified: redwax-tool/trunk/redwax-tool.h redwax-tool/trunk/redwax_openssl.c Modified: redwax-tool/trunk/redwax-tool.h ============================================================================== --- redwax-tool/trunk/redwax-tool.h (original) +++ redwax-tool/trunk/redwax-tool.h Wed Apr 2 16:08:56 2025 @@ -256,6 +256,7 @@ const unsigned char *der; apr_size_t len; const char *origin; + const char *address; /* ID from the input certificate */ const unsigned char *id_der; apr_size_t id_len; Modified: redwax-tool/trunk/redwax_openssl.c ============================================================================== --- redwax-tool/trunk/redwax_openssl.c (original) +++ redwax-tool/trunk/redwax_openssl.c Wed Apr 2 16:08:56 2025 @@ -3866,6 +3866,7 @@ apr_pool_cleanup_null); cert->origin = apr_pstrdup(cert->pool, tls_host->hostname); + cert->address = apr_psprintf(cert->pool, "%pI", tls_host->sockaddr); rt_run_normalise_certificate(r, cert, 1); @@ -5551,6 +5552,9 @@ redwax_metadata_push_object(m, "Certificate", 0); redwax_metadata_add_string(m, "Origin", cert->origin); + if (cert->address) { + redwax_metadata_add_string(m, "Address", cert->address); + } if (cert->common.type == REDWAX_CERTIFICATE_X509 && cert->x509 && cert->id_der && cert->id_len) { redwax_metadata_add_string(m, "Id",