/** * Copyright (C) 2024 Graham Leggett * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * redwax_ldns - DNS handling routines. * */ #include #include "config.h" #include "redwax-tool.h" #include "redwax_util.h" #if HAVE_LDNS_LDNS_H #include module ldns_module; static apr_status_t redwax_ldns_initialise(redwax_tool_t *r) { return OK; } static apr_status_t redwax_ldns_tlsa_metadata_data(redwax_tool_t *r, redwax_metadata_t *m, const redwax_certificate_t *cert, ldns_tlsa_selector selector, ldns_tlsa_matching_type matching_type, X509 *x) { ldns_rr* tlsa; if (LDNS_STATUS_OK == ldns_dane_create_tlsa_rr(&tlsa, LDNS_TLSA_USAGE_PKIX_EE, selector, matching_type, x)) { ldns_buffer* buf = ldns_buffer_new(LDNS_MAX_PACKETLEN); char* str; ldns_status s; ldns_buffer_clear(buf); if (ldns_rr_rd_count(tlsa) > 3) { s = ldns_rdf2buffer_str(buf, ldns_rr_rdf(tlsa, 3)); if (s != LDNS_STATUS_OK) { redwax_print_error(r, "metadata-out: TLSA: %s\n", ldns_get_errorstr_by_id(s)); ldns_buffer_free(buf); return APR_EINVAL; } } str = ldns_buffer_export2str(buf); ldns_buffer_free(buf); switch (matching_type) { case LDNS_TLSA_MATCHING_TYPE_FULL: redwax_metadata_push_object(m, "Full", 0); break; case LDNS_TLSA_MATCHING_TYPE_SHA2_256: redwax_metadata_push_object(m, "SHA2-256", 0); break; case LDNS_TLSA_MATCHING_TYPE_SHA2_512: redwax_metadata_push_object(m, "SHA2-512", 0); break; case LDNS_TLSA_MATCHING_TYPE_PRIVMATCH: redwax_metadata_push_object(m, "PrivMatch", 0); break; } if (cert->common.category == REDWAX_CERTIFICATE_END_ENTITY) { redwax_metadata_add_string(m, "PKIX-EE", apr_psprintf(r->pool, "%d %d %d", LDNS_TLSA_USAGE_PKIX_EE, selector, matching_type)); redwax_metadata_add_string(m, "DANE-EE", apr_psprintf(r->pool, "%d %d %d", LDNS_TLSA_USAGE_DANE_EE, selector, matching_type)); } else { redwax_metadata_add_string(m, "PKIX-TA", apr_psprintf(r->pool, "%d %d %d", LDNS_TLSA_USAGE_PKIX_TA, selector, matching_type)); redwax_metadata_add_string(m, "DANE-TA", apr_psprintf(r->pool, "%d %d %d", LDNS_TLSA_USAGE_DANE_TA, selector, matching_type)); } redwax_metadata_add_string(m, "CertificateAssociationData", apr_pstrdup(r->pool, str)); redwax_metadata_pop_object(m); LDNS_FREE(str); } else { return APR_EINVAL; } return APR_SUCCESS; } static apr_status_t redwax_ldns_add_tlsa_metadata(redwax_tool_t *r, redwax_metadata_t *m, const redwax_certificate_t *cert) { const unsigned char *der = cert->der; X509 *x = d2i_X509(NULL, &der, cert->len); if (!x) { return APR_EINVAL; } redwax_metadata_push_object(m, "TLSA", 0); redwax_metadata_push_object(m, "Cert", 0); redwax_ldns_tlsa_metadata_data(r, m, cert, LDNS_TLSA_SELECTOR_CERT, LDNS_TLSA_MATCHING_TYPE_FULL, x); redwax_ldns_tlsa_metadata_data(r, m, cert, LDNS_TLSA_SELECTOR_CERT, LDNS_TLSA_MATCHING_TYPE_SHA2_256, x); redwax_ldns_tlsa_metadata_data(r, m, cert, LDNS_TLSA_SELECTOR_CERT, LDNS_TLSA_MATCHING_TYPE_SHA2_512, x); redwax_metadata_pop_object(m); redwax_metadata_push_object(m, "SPKI", 0); redwax_ldns_tlsa_metadata_data(r, m, cert, LDNS_TLSA_SELECTOR_SPKI, LDNS_TLSA_MATCHING_TYPE_FULL, x); redwax_ldns_tlsa_metadata_data(r, m, cert, LDNS_TLSA_SELECTOR_SPKI, LDNS_TLSA_MATCHING_TYPE_SHA2_256, x); redwax_ldns_tlsa_metadata_data(r, m, cert, LDNS_TLSA_SELECTOR_SPKI, LDNS_TLSA_MATCHING_TYPE_SHA2_512, x); redwax_metadata_pop_object(m); redwax_metadata_pop_object(m); return OK; } static apr_status_t redwax_ldns_process_dns(redwax_tool_t *r, redwax_dns_t *dns, redwax_rdata_t *rdata) { switch (dns->rrtype) { case LDNS_RR_TYPE_TLSA: { ldns_buffer buffer; ldns_buffer_new_frm_data(&buffer, rdata->data, rdata->len); if (!ldns_buffer_available(&buffer, 3)) { redwax_print_error(r, "process-dns: TLSA record too short (<3)\n"); return APR_EGENERAL; } else { rdata->rr.tlsa.usage = ldns_buffer_read_u8(&buffer); rdata->rr.tlsa.selector = ldns_buffer_read_u8(&buffer); rdata->rr.tlsa.mtype = ldns_buffer_read_u8(&buffer); rdata->rr.tlsa.len = ldns_buffer_remaining(&buffer); rdata->rr.tlsa.data = apr_palloc(r->pool, rdata->rr.tlsa.len); ldns_buffer_read(&buffer, rdata->rr.tlsa.data, rdata->rr.tlsa.len); switch (rdata->rr.tlsa.usage) { case LDNS_TLSA_USAGE_CA_CONSTRAINT: rdata->rr.tlsa.usage_name = "CA constraint"; break; case LDNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT: rdata->rr.tlsa.usage_name = "Service certificate constraint"; break; case LDNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION: rdata->rr.tlsa.usage_name = "Trust anchor assertion"; break; case LDNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE: rdata->rr.tlsa.usage_name = "Domain issued certificate"; break; case LDNS_TLSA_USAGE_PRIVCERT: rdata->rr.tlsa.usage_name = "Private use"; break; default: rdata->rr.tlsa.usage_name = "Unassigned"; break; } switch (rdata->rr.tlsa.selector) { case LDNS_TLSA_SELECTOR_FULL_CERTIFICATE: rdata->rr.tlsa.selector_name = "CA constraint"; break; case LDNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO: rdata->rr.tlsa.selector_name = "Service certificate constraint"; break; case LDNS_TLSA_SELECTOR_PRIVSEL: rdata->rr.tlsa.selector_name = "Private use"; break; default: rdata->rr.tlsa.selector_name = "Unassigned"; break; } switch (rdata->rr.tlsa.mtype) { case LDNS_TLSA_MATCHING_TYPE_NO_HASH_USED: rdata->rr.tlsa.mtype_name = "No hash used"; break; case LDNS_TLSA_MATCHING_TYPE_SHA256: rdata->rr.tlsa.mtype_name = "SHA-256"; break; case LDNS_TLSA_MATCHING_TYPE_SHA512: rdata->rr.tlsa.mtype_name = "SHA-512"; break; case LDNS_TLSA_MATCHING_TYPE_PRIVMATCH: rdata->rr.tlsa.mtype_name = "Private use"; break; default: rdata->rr.tlsa.mtype_name = "Unassigned"; break; } } break; } default: { redwax_print_error(r, "process-dns: unexpected record type: %d\n", dns->rrtype); return APR_ENOTIMPL; } } return APR_SUCCESS; } void redwax_add_default_ldns_hooks() { rt_hook_initialise(redwax_ldns_initialise, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_add_dns_metadata(redwax_ldns_add_tlsa_metadata, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_process_dns(redwax_ldns_process_dns, NULL, NULL, APR_HOOK_MIDDLE); } #else void redwax_add_default_ldns_hooks() { } #endif REDWAX_DECLARE_MODULE(ldns) = { STANDARD_MODULE_STUFF, redwax_add_default_ldns_hooks /* register hooks */ };