summaryrefslogtreecommitdiffstats
path: root/src/tls/tls_dane.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tls/tls_dane.c77
1 files changed, 55 insertions, 22 deletions
diff --git a/src/tls/tls_dane.c b/src/tls/tls_dane.c
index a2b9b80..ac7f05f 100644
--- a/src/tls/tls_dane.c
+++ b/src/tls/tls_dane.c
@@ -22,8 +22,9 @@
/* void tls_dane_free(dane)
/* TLS_DANE *dane;
/*
-/* void tls_dane_add_fpt_digests(dane, digest, delim, smtp_mode)
+/* void tls_dane_add_fpt_digests(dane, pkey_only, digest, delim, smtp_mode)
/* TLS_DANE *dane;
+/* int pkey_only;
/* const char *digest;
/* const char *delim;
/* int smtp_mode;
@@ -130,6 +131,9 @@
/* SSL context to be configured with the chosen digest algorithms.
/* .IP fpt_alg
/* The OpenSSL EVP digest algorithm handle for the fingerprint digest.
+/* .IP pkey_only
+/* When true, generate "fingerprint" TLSA records for just the public
+/* keys. Otherwise, for both certificates and public keys.
/* .IP tlsa
/* TLSA record linked list head, initially NULL.
/* .IP usage
@@ -415,8 +419,9 @@ static void dane_free(void *dane, void *unused_context)
/* tls_dane_add_fpt_digests - map fingerprint list to DANE TLSA RRset */
-void tls_dane_add_fpt_digests(TLS_DANE *dane, const char *digest,
- const char *delim, int smtp_mode)
+void tls_dane_add_fpt_digests(TLS_DANE *dane, int pkey_only,
+ const char *digest, const char *delim,
+ int smtp_mode)
{
ARGV *values = argv_split(digest, delim);
ssize_t i;
@@ -455,31 +460,41 @@ void tls_dane_add_fpt_digests(TLS_DANE *dane, const char *digest,
continue;
}
+#define USTR_LEN(raw) (unsigned char *) STR(raw), VSTRING_LEN(raw)
+
/*
* At the "fingerprint" security level certificate digests and public
- * key digests are interchangeable. Each leaf certificate is matched
- * via either the public key digest or full certificate digest. The
- * DER encoding of a certificate is not a valid public key, and
- * conversely, the DER encoding of a public key is not a valid
- * certificate. An attacker would need a 2nd-preimage that is
+ * key digests are by default interchangeable. Each leaf certificate
+ * is matched via either the public key digest or full certificate
+ * digest. The DER encoding of a certificate is not a valid public
+ * key, and conversely, the DER encoding of a public key is not a
+ * valid certificate. An attacker would need a 2nd-preimage that is
* feasible across types (given cert digest == some pkey digest) and
* yet presumably difficult within a type (e.g. given cert digest ==
* some other cert digest). No such attacks are known at this time,
* and it is expected that if any are found they would work within as
* well as across the cert/pkey data types.
+ *
+ * That said, when `pkey_only` is true, we match only public keys.
*
* The private-use matching type "255" is mapped to the configured
* fingerprint digest, which may (harmlessly) coincide with one of
* the standard DANE digest algorithms. The private code point is
* however unconditionally enabled.
*/
+ if (!pkey_only) {
+ dane->tlsa = tlsa_prepend(dane->tlsa, 3, 0, 255, USTR_LEN(raw));
+ if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
+ tlsa_info("fingerprint", "digest as private-use TLSA record",
+ 3, 0, 255, USTR_LEN(raw));
+ }
+
+ /* The public key match is unconditional */
+ dane->tlsa = tlsa_prepend(dane->tlsa, 3, 1, 255, USTR_LEN(raw));
if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
tlsa_info("fingerprint", "digest as private-use TLSA record",
- 3, 0, 255, (unsigned char *) STR(raw), VSTRING_LEN(raw));
- dane->tlsa = tlsa_prepend(dane->tlsa, 3, 0, 255,
- (unsigned char *) STR(raw), VSTRING_LEN(raw));
- dane->tlsa = tlsa_prepend(dane->tlsa, 3, 1, 255,
- (unsigned char *) STR(raw), VSTRING_LEN(raw));
+ 3, 1, 255, USTR_LEN(raw));
+
vstring_free(raw);
}
argv_free(values);
@@ -798,12 +813,21 @@ int tls_dane_enable(TLS_SESS_STATE *TLScontext)
SSL *ssl = TLScontext->con;
int usable = 0;
int ret;
+ int rpk_compat = 1;
for (tp = dane->tlsa; tp != 0; tp = tp->next) {
ret = SSL_dane_tlsa_add(ssl, tp->usage, tp->selector,
tp->mtype, tp->data, tp->length);
if (ret > 0) {
++usable;
+ /*
+ * Disable use of RFC7250 raw public keys if any TLSA record
+ * depends on X.509 certificates. Only DANE-EE(3) SPKI(1) records
+ * can get by with just a public key.
+ */
+ if (tp->usage != DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE
+ || tp->selector != DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO)
+ rpk_compat = 0;
continue;
}
if (ret == 0) {
@@ -818,6 +842,9 @@ int tls_dane_enable(TLS_SESS_STATE *TLScontext)
tls_print_errors();
return (-1);
}
+ if (rpk_compat)
+ tls_enable_server_rpk(NULL, ssl);
+
return (usable);
}
@@ -964,8 +991,9 @@ void tls_dane_log(TLS_SESS_STATE *TLScontext)
{
static VSTRING *top;
static VSTRING *bot;
+ X509 *mcert = 0;
EVP_PKEY *mspki = 0;
- int depth = SSL_get0_dane_authority(TLScontext->con, NULL, &mspki);
+ int depth = SSL_get0_dane_authority(TLScontext->con, &mcert, &mspki);
uint8_t u, s, m;
unsigned const char *data;
size_t dlen;
@@ -994,22 +1022,27 @@ void tls_dane_log(TLS_SESS_STATE *TLScontext)
hex_encode(top, (char *) data, dlen);
}
- switch (TLScontext->level) {
- case TLS_LEV_FPRINT:
+ if (TLScontext->level == TLS_LEV_FPRINT) {
msg_info("%s: Matched fingerprint: %s%s%s", TLScontext->namaddr,
STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
dlen > MAX_DUMP_BYTES ? STR(bot) : "");
return;
-
- default:
- msg_info("%s: Matched DANE %s at depth %d: %u %u %u %s%s%s",
- TLScontext->namaddr, mspki ?
- "TA public key verified certificate" : depth ?
- "TA certificate" : "EE certificate", depth, u, s, m,
+ }
+#if OPENSSL_VERSION_PREREQ(3,2)
+ if (SSL_get0_peer_rpk(TLScontext->con) != NULL) {
+ msg_info("%s: Matched DANE raw public key: %u %u %u %s%s%s",
+ TLScontext->namaddr, u, s, m,
STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
dlen > MAX_DUMP_BYTES ? STR(bot) : "");
return;
}
+#endif
+ msg_info("%s: Matched DANE %s at depth %d: %u %u %u %s%s%s",
+ TLScontext->namaddr, mspki ?
+ "TA public key verified certificate" : depth ?
+ "TA certificate" : "EE certificate", depth, u, s, m,
+ STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
+ dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}
#ifdef TEST