Compare commits

...

1 Commits

Author SHA1 Message Date
Aydın Mercan
f336084d01 add support for certificate transparency requirement
Resolving DoT and DoH can now require CT logged certificates. At least
two trusted log entries are required for certificates with a lifetime of
180 days or less and at least three trusted log entries are required
otherwise.

This policy is a sane choice for PKI deployments certificate logs have
value (WebPKI) and thus the limits are not tweakable.
2025-02-26 11:53:58 +00:00
8 changed files with 111 additions and 1 deletions

View File

@@ -79,5 +79,6 @@ PenaltyExcessCharacter: 100
Standard: Cpp11
ContinuationIndentWidth: 8
ForEachMacros: [ 'cds_lfs_for_each', 'cds_lfs_for_each_safe', 'cds_list_for_each_entry_safe', 'ISC_LIST_FOREACH', 'ISC_LIST_FOREACH_SAFE', 'ISC_LIST_FOREACH_REV', 'ISC_LIST_FOREACH_REV_SAFE' ]
TypenameMacros: [ 'STACK_OF' ]
RemoveParentheses: ReturnStatement
RemoveSemicolon: true

View File

@@ -128,6 +128,9 @@ add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) {
dns_transport_set_cafile);
parse_transport_option(doh, transport, "remote-hostname",
dns_transport_set_remote_hostname);
parse_transport_option(
doh, transport, "certificate-transparency",
dns_transport_set_certificate_transparency);
}
return ISC_R_SUCCESS;
@@ -180,6 +183,9 @@ add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) {
dns_transport_set_cafile);
parse_transport_option(tls, transport, "remote-hostname",
dns_transport_set_remote_hostname);
parse_transport_option(
tls, transport, "certificate-transparency",
dns_transport_set_certificate_transparency);
}
return ISC_R_SUCCESS;

View File

@@ -360,6 +360,7 @@ statistics-channels {
tls <string> {
ca-file <quoted_string>;
cert-file <quoted_string>;
certificate-transparency <boolean>;
cipher-suites <string>;
ciphers <string>;
dhparam-file <quoted_string>;

View File

@@ -66,6 +66,8 @@ dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport,
bool *preferp);
bool
dns_transport_get_always_verify_remote(dns_transport_t *transport);
bool
dns_transport_get_certificate_transparency(dns_transport_t *transport);
/*%<
* Getter functions: return the type, cert file, key file, CA file,
* hostname, HTTP endpoint, HTTP mode (GET or POST), ciphers, cipher suites,
@@ -132,6 +134,10 @@ dns_transport_set_prefer_server_ciphers(dns_transport_t *transport,
void
dns_transport_set_always_verify_remote(dns_transport_t *transport,
const bool always_verify_remote);
void
dns_transport_set_certificate_transparency(dns_transport_t *transport,
const bool certificate_transparency);
/*%<
* Setter functions: set the type, cert file, key file, CA file,
* hostname, HTTP endpoint, HTTP mode (GET or POST), ciphers, cipher suites, TLS

View File

@@ -61,6 +61,7 @@ struct dns_transport {
uint32_t protocol_versions;
ternary_t prefer_server_ciphers;
bool always_verify_remote;
bool certificate_transparency;
} tls;
struct {
char *endpoint;
@@ -380,6 +381,25 @@ dns_transport_get_always_verify_remote(dns_transport_t *transport) {
return transport->tls.always_verify_remote;
}
void
dns_transport_set_certificate_transparency(
dns_transport_t *transport, const bool certificate_transparency) {
REQUIRE(VALID_TRANSPORT(transport));
REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
transport->type == DNS_TRANSPORT_HTTP);
transport->tls.certificate_transparency = certificate_transparency;
}
bool
dns_transport_get_certificate_transparency(dns_transport_t *transport) {
REQUIRE(VALID_TRANSPORT(transport));
REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
transport->type == DNS_TRANSPORT_HTTP);
return transport->tls.certificate_transparency;
}
isc_result_t
dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer,
isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx,
@@ -427,6 +447,8 @@ dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer,
const char *key_file = dns_transport_get_keyfile(transport);
const bool always_verify_remote =
dns_transport_get_always_verify_remote(transport);
const bool certificate_transparency =
dns_transport_get_certificate_transparency(transport);
char peer_addr_str[INET6_ADDRSTRLEN] = { 0 };
isc_netaddr_t peer_netaddr = { 0 };
bool hostname_ignore_subject;
@@ -529,6 +551,14 @@ dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer,
isc_tlsctx_enable_dot_client_alpn(tlsctx);
if (certificate_transparency) {
result = isc_tlsctx_require_certificate_transparency(
tlsctx);
if (result != ISC_R_SUCCESS) {
goto failure;
}
}
isc_tlsctx_client_session_cache_create(
mctx, tlsctx,
ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE,

View File

@@ -608,6 +608,9 @@ isc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx);
*\li 'ctx' - a valid non-NULL pointer;
*/
isc_result_t
isc_tlsctx_require_certificate_transparency(isc_tlsctx_t *ctx);
#define isc_tlserr2result(category, module, funcname, fallback) \
isc__tlserr2result(category, module, funcname, fallback, __FILE__, \
__LINE__)

View File

@@ -885,7 +885,7 @@ isc_tlsctx_enable_peer_verification(isc_tlsctx_t *tlsctx, const bool is_server,
isc_result_t
isc_tlsctx_load_client_ca_names(isc_tlsctx_t *ctx, const char *ca_bundle_file) {
STACK_OF(X509_NAME) * cert_names;
STACK_OF(X509_NAME) *cert_names;
REQUIRE(ctx != NULL);
REQUIRE(ca_bundle_file != NULL);
@@ -1530,6 +1530,68 @@ isc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx) {
SSL_CTX_set_session_id_context(ctx, session_id_ctx, len) == 1);
}
static int
transparency_cb(const CT_POLICY_EVAL_CTX *ctx, const STACK_OF(SCT) *scts,
void *arg) {
size_t trusted, needed;
int daydiff;
X509 *x509;
SCT *sct;
REQUIRE(arg == NULL);
if (scts == NULL) {
goto failure;
}
x509 = CT_POLICY_EVAL_CTX_get0_cert(ctx);
INSIST(x509 != NULL);
if (ASN1_TIME_diff(&daydiff, NULL, X509_get0_notBefore(x509),
X509_get0_notAfter(x509)) != 1)
{
goto failure;
}
needed = daydiff >= 180 ? 3 : 2;
trusted = 0;
for (int i = 0; i < sk_SCT_num(scts); i++) {
sct = sk_SCT_value(scts, i);
switch (SCT_get_validation_status(sct)) {
case SCT_VALIDATION_STATUS_INVALID:
goto failure;
case SCT_VALIDATION_STATUS_VALID:
trusted++;
FALLTHROUGH;
default:
break;
}
}
if (trusted >= needed) {
return 1;
}
failure:
ERR_raise(ERR_LIB_SSL, SSL_R_NO_VALID_SCTS);
return 0;
}
isc_result_t
isc_tlsctx_require_certificate_transparency(isc_tlsctx_t *ctx) {
REQUIRE(ctx != NULL);
if (SSL_CTX_set_ct_validation_callback(ctx, transparency_cb, NULL) != 1)
{
return isc_tlserr2result(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"SSL_CTX_set_ct_validation_callback", ISC_R_TLSERROR);
}
return ISC_R_SUCCESS;
}
static isc_result_t
isc__tls_toresult(isc_result_t fallback) {
isc_result_t result = fallback;

View File

@@ -3959,6 +3959,7 @@ static cfg_clausedef_t tls_clauses[] = {
{ "cipher-suites", &cfg_type_astring, 0 },
{ "prefer-server-ciphers", &cfg_type_boolean, 0 },
{ "session-tickets", &cfg_type_boolean, 0 },
{ "certificate-transparency", &cfg_type_boolean, 0 },
{ NULL, NULL, 0 }
};