diff options
Diffstat (limited to 'src/tls')
-rw-r--r-- | src/tls/tls.h | 31 | ||||
-rw-r--r-- | src/tls/tls_client.c | 157 | ||||
-rw-r--r-- | src/tls/tls_dane.c | 77 | ||||
-rw-r--r-- | src/tls/tls_fprint.c | 53 | ||||
-rw-r--r-- | src/tls/tls_misc.c | 129 | ||||
-rw-r--r-- | src/tls/tls_proxy.h | 13 | ||||
-rw-r--r-- | src/tls/tls_proxy_client_print.c | 1 | ||||
-rw-r--r-- | src/tls/tls_proxy_client_scan.c | 3 | ||||
-rw-r--r-- | src/tls/tls_proxy_context_print.c | 4 | ||||
-rw-r--r-- | src/tls/tls_proxy_context_scan.c | 4 | ||||
-rw-r--r-- | src/tls/tls_server.c | 73 | ||||
-rw-r--r-- | src/tls/tls_verify.c | 18 |
12 files changed, 435 insertions, 128 deletions
diff --git a/src/tls/tls.h b/src/tls/tls.h index 73eebae..3ec41ba 100644 --- a/src/tls/tls.h +++ b/src/tls/tls.h @@ -78,6 +78,7 @@ extern const char *str_tls_level(int); #include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */ #include <openssl/ssl.h> #include <openssl/conf.h> +#include <openssl/tls1.h> /* TLS extensions */ /* Appease indent(1) */ #define x509_stack_t STACK_OF(X509) @@ -203,8 +204,8 @@ extern void tls_dane_flush(void); extern TLS_DANE *tls_dane_alloc(void); extern void tls_tlsa_free(TLS_TLSA *); extern void tls_dane_free(TLS_DANE *); -extern void tls_dane_add_fpt_digests(TLS_DANE *, const char *, const char *, - int); +extern void tls_dane_add_fpt_digests(TLS_DANE *, int, const char *, + const char *, int); extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int); extern int tls_dane_load_trustfile(TLS_DANE *, const char *); @@ -232,6 +233,8 @@ typedef struct { const char *kex_name; /* shared key-exchange algorithm */ const char *kex_curve; /* shared key-exchange ECDHE curve */ int kex_bits; /* shared FFDHE key exchange bits */ + int ctos_rpk; /* Did the client send an RPK? */ + int stoc_rpk; /* Did the server send an RPK? */ const char *clnt_sig_name; /* client's signature key algorithm */ const char *clnt_sig_curve; /* client's ECDSA curve name */ int clnt_sig_bits; /* client's RSA signature key bits */ @@ -264,13 +267,17 @@ typedef struct { * Peer status bits. TLS_CERT_FLAG_MATCHED implies TLS_CERT_FLAG_TRUSTED * only in the case of a hostname match. */ -#define TLS_CERT_FLAG_PRESENT (1<<0) +#define TLS_CRED_FLAG_CERT (1<<0) #define TLS_CERT_FLAG_ALTNAME (1<<1) #define TLS_CERT_FLAG_TRUSTED (1<<2) #define TLS_CERT_FLAG_MATCHED (1<<3) #define TLS_CERT_FLAG_SECURED (1<<4) +#define TLS_CRED_FLAG_RPK (1<<5) +#define TLS_CRED_FLAG_ANY (TLS_CRED_FLAG_CERT|TLS_CRED_FLAG_RPK) -#define TLS_CERT_IS_PRESENT(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_PRESENT)) +#define TLS_CRED_IS_PRESENT(c) ((c) && ((c)->peer_status&TLS_CRED_FLAG_ANY)) +#define TLS_CERT_IS_PRESENT(c) ((c) && ((c)->peer_status&TLS_CRED_FLAG_CERT)) +#define TLS_RPK_IS_PRESENT(c) ((c) && ((c)->peer_status&TLS_CRED_FLAG_RPK)) #define TLS_CERT_IS_ALTNAME(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_ALTNAME)) #define TLS_CERT_IS_TRUSTED(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_TRUSTED)) #define TLS_CERT_IS_MATCHED(c) ((c) && ((c)->peer_status&TLS_CERT_FLAG_MATCHED)) @@ -472,6 +479,7 @@ typedef struct { VSTREAM *stream; int fd; /* Event-driven file descriptor */ int timeout; + int enable_rpk; /* Solicit server raw public keys */ int tls_level; /* Security level */ const char *nexthop; /* destination domain */ const char *host; /* MX hostname */ @@ -508,12 +516,12 @@ extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *, a6, a7, a8, a9, a10, a11, a12, a13, a14)) #define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \ - a10, a11, a12, a13, a14, a15, a16, a17) \ + a10, a11, a12, a13, a14, a15, a16, a17, a18) \ tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \ - ((props)->a16), ((props)->a17), (props))) + ((props)->a16), ((props)->a17), ((props)->a18), (props))) /* * tls_server.c @@ -546,6 +554,7 @@ typedef struct { VSTREAM *stream; /* Client stream */ int fd; /* Event-driven file descriptor */ int timeout; /* TLS handshake timeout */ + int enable_rpk; /* Solicit client raw public keys */ int requirecert; /* Insist on client cert? */ const char *serverid; /* Server instance (salt cache key) */ const char *namaddr; /* Client nam[addr] for logging */ @@ -570,10 +579,12 @@ extern TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *); ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \ ((props)->a20), (props))) -#define TLS_SERVER_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \ +#define TLS_SERVER_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \ + a10, a11) \ tls_server_start((((props)->a1), ((props)->a2), ((props)->a3), \ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \ - ((props)->a8), ((props)->a9), ((props)->a10), (props))) + ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \ + (props))) /* * tls_session.c @@ -660,7 +671,7 @@ extern TLS_TLSA *tlsa_prepend(TLS_TLSA *, uint8_t, uint8_t, uint8_t, extern const EVP_MD *tls_digest_byname(const char *, EVP_MD_CTX **); extern char *tls_digest_encode(const unsigned char *, int); extern char *tls_cert_fprint(X509 *, const char *); -extern char *tls_pkey_fprint(X509 *, const char *); +extern char *tls_pkey_fprint(EVP_PKEY *, const char *); extern char *tls_serverid_digest(TLS_SESS_STATE *, const TLS_CLIENT_START_PROPS *, const char *); @@ -696,6 +707,8 @@ extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long); #endif extern const EVP_MD *tls_validate_digest(const char *); +extern void tls_enable_client_rpk(SSL_CTX *, SSL *); +extern void tls_enable_server_rpk(SSL_CTX *, SSL *); /* * tls_seed.c diff --git a/src/tls/tls_client.c b/src/tls/tls_client.c index e0dfe15..3eda859 100644 --- a/src/tls/tls_client.c +++ b/src/tls/tls_client.c @@ -86,8 +86,9 @@ /* available as: /* .IP TLScontext->peer_status /* A bitmask field that records the status of the peer certificate -/* verification. This consists of one or more of TLS_CERT_FLAG_PRESENT, -/* TLS_CERT_FLAG_TRUSTED, TLS_CERT_FLAG_MATCHED and TLS_CERT_FLAG_SECURED. +/* verification. This consists of one or more of TLS_CRED_FLAG_CERT, +/* TLS_CRED_FLAG_RPK, TLS_CERT_FLAG_TRUSTED, TLS_CERT_FLAG_MATCHED and +/* TLS_CERT_FLAG_SECURED. /* .IP TLScontext->peer_CN /* Extracted CommonName of the peer, or zero-length string if the /* information could not be extracted. @@ -303,15 +304,11 @@ static void uncache_session(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext) tls_mgr_delete(TLScontext->cache_type, TLScontext->serverid); } -/* verify_extract_name - verify peer name and extract peer information */ +/* verify_x509 - process X.509 certificate verification status */ -static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, - const TLS_CLIENT_START_PROPS *props) +static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert, + const TLS_CLIENT_START_PROPS *props) { - int verbose; - - verbose = TLScontext->log_mask & - (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT); /* * On exit both peer_CN and issuer_CN should be set. @@ -345,7 +342,8 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, TLScontext->peer_status |= TLS_CERT_FLAG_SECURED; TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; - if (verbose) { + if (TLScontext->log_mask & + (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) { const char *peername = SSL_get0_peername(TLScontext->con); if (peername) @@ -367,6 +365,50 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, if (TLScontext->session_reused == 0) tls_log_verify_error(TLScontext); else + msg_info("%s: re-using session with untrusted peer credential, " + "look for details earlier in the log", props->namaddr); + } +} + +/* verify_rpk - process RFC7250 raw public key verification status */ + +static void verify_rpk(TLS_SESS_STATE *TLScontext, EVP_PKEY *peerpkey, + const TLS_CLIENT_START_PROPS *props) +{ + /* Was the raw public key (type of cert) matched? */ + if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) { + TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; + if (TLScontext->must_fail) { + msg_panic("%s: raw public key valid despite trust init failure", + TLScontext->namaddr); + } else if (TLS_MUST_MATCH(TLScontext->level)) { + + /* + * Fully secured only if not insecure like half-dane. We use + * TLS_CERT_FLAG_MATCHED to satisfy policy, but + * TLS_CERT_FLAG_SECURED to log the effective security. + */ + if (!TLS_NEVER_SECURED(TLScontext->level)) + TLScontext->peer_status |= TLS_CERT_FLAG_SECURED; + TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; + + if (TLScontext->log_mask & + (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) + tls_dane_log(TLScontext); + } + } + + /* + * Give them a clue. Problems with trust chain verification are logged + * when the session is first negotiated, before the session is stored + * into the cache. We don't want mystery failures, so log the fact the + * real problem is to be found in the past. + */ + if (!TLS_CERT_IS_MATCHED(TLScontext) + && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) { + if (TLScontext->session_reused == 0) + tls_log_verify_error(TLScontext); + else msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); } @@ -793,6 +835,30 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) } /* + * Enable support for client->server raw public keys, provided we actually + * have keys to send. They'll only be used if the server also enables + * client RPKs. + * + * XXX: When the server requests client auth, the TLS 1.2 protocol does not + * provide an unambiguous mechanism for the client to not send an RPK (as + * it can with client X.509 certs or TLS 1.3). This is why we don't just + * enable client RPK also with no keys in hand. + * + * A very unlikely scenario is that the server allows clients to not send + * keys, but only accepts keys for a set of algorithms we don't have. Then + * we still can't send a key, but have agreed to RPK. OpenSSL will attempt + * to send an empty RPK even with TLS 1.2 (and will accept such a message), + * but other implementations may be more strict. + * + * We could limit client RPK support to connections that support only TLS + * 1.3 and up, but that's practical only decades in the future, and the + * risk scenario is contrived and very unlikely. + */ + if (SSL_CTX_get0_certificate(client_ctx) != NULL && + SSL_CTX_get0_privatekey(client_ctx) != NULL) + tls_enable_client_rpk(client_ctx, NULL); + + /* * With OpenSSL 1.0.2 and later the client EECDH curve list becomes * configurable with the preferred curve negotiated via the supported * curves extension. With OpenSSL 3.0 and TLS 1.3, the same applies @@ -1008,6 +1074,24 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) } /* + * Possibly enable RFC7250 raw public keys in non-DANE/non-PKI levels + * when the fingerprint mask includes only public keys. For "may" and + * "encrypt" this is a heuristic, since we don't use the fingerprints + * beyond reporting them in verbose logging. If you always want certs + * with "may" and "encrypt" you'll have to tolerate them with + * "fingerprint", or use a separate transport. + */ + switch (props->tls_level) { + case TLS_LEV_MAY: + case TLS_LEV_ENCRYPT: + case TLS_LEV_FPRINT: + if (props->enable_rpk) + tls_enable_server_rpk(NULL, TLScontext->con); + default: + break; + } + + /* * Try to convey the configured TLSA records for this connection to the * OpenSSL library. If none are "usable", we'll fall back to "encrypt" * when authentication is not mandatory, otherwise we must arrange to @@ -1175,6 +1259,7 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, { const SSL_CIPHER *cipher; X509 *peercert; + EVP_PKEY *peerpkey = 0; /* Turn off packet dump if only dumping the handshake */ if ((TLScontext->log_mask & TLS_LOG_ALLPKTS) == 0) @@ -1192,31 +1277,61 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, * Do peername verification if requested and extract useful information * from the certificate for later use. */ - if ((peercert = TLS_PEEK_PEER_CERT(TLScontext->con)) != 0) { - TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; + peercert = TLS_PEEK_PEER_CERT(TLScontext->con); + if (peercert != 0) { + peerpkey = X509_get0_pubkey(peercert); + } +#if OPENSSL_VERSION_PREREQ(3,2) + else { + peerpkey = SSL_get0_peer_rpk(TLScontext->con); + } +#endif + + if (peercert != 0) { + TLScontext->peer_status |= TLS_CRED_FLAG_CERT; /* * Peer name or fingerprint verification as requested. * Unconditionally set peer_CN, issuer_CN and peer_cert_fprint. Check * fingerprint first, and avoid logging verified as untrusted in the - * call to verify_extract_name(). + * call to verify_x509(). */ - TLScontext->peer_cert_fprint = tls_cert_fprint(peercert, props->mdalg); - TLScontext->peer_pkey_fprint = tls_pkey_fprint(peercert, props->mdalg); - verify_extract_name(TLScontext, peercert, props); + TLScontext->peer_cert_fprint = + tls_cert_fprint(peercert, props->mdalg); + TLScontext->peer_pkey_fprint = + tls_pkey_fprint(peerpkey, props->mdalg); + verify_x509(TLScontext, peercert, props); if (TLScontext->log_mask & (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) - msg_info("%s: subject_CN=%s, issuer_CN=%s, " - "fingerprint=%s, pkey_fingerprint=%s", props->namaddr, + msg_info("%s: subject_CN=%s, issuer=%s%s%s%s%s", + TLScontext->namaddr, TLScontext->peer_CN, TLScontext->issuer_CN, - TLScontext->peer_cert_fprint, - TLScontext->peer_pkey_fprint); + *TLScontext->peer_cert_fprint ? + ", cert fingerprint=" : "", + *TLScontext->peer_cert_fprint ? + TLScontext->peer_cert_fprint : "", + *TLScontext->peer_pkey_fprint ? + ", pkey fingerprint=" : "", + *TLScontext->peer_pkey_fprint ? + TLScontext->peer_pkey_fprint : ""); } else { TLScontext->issuer_CN = mystrdup(""); TLScontext->peer_CN = mystrdup(""); TLScontext->peer_cert_fprint = mystrdup(""); - TLScontext->peer_pkey_fprint = mystrdup(""); + + if (!peerpkey) { + TLScontext->peer_pkey_fprint = mystrdup(""); + } else { + TLScontext->peer_status |= TLS_CRED_FLAG_RPK; + TLScontext->peer_pkey_fprint = + tls_pkey_fprint(peerpkey, props->mdalg); + if (TLScontext->log_mask & + (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) + msg_info("%s: raw public key fingerprint=%s", props->namaddr, + TLScontext->peer_pkey_fprint); + verify_rpk(TLScontext, peerpkey, props); + } } /* 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 diff --git a/src/tls/tls_fprint.c b/src/tls/tls_fprint.c index 39b5a52..dc3f99e 100644 --- a/src/tls/tls_fprint.c +++ b/src/tls/tls_fprint.c @@ -24,7 +24,7 @@ /* const char *mdalg; /* /* char *tls_pkey_fprint(peercert, mdalg) -/* X509 *peercert; +/* EVP_PKEY *peerpkey; /* const char *mdalg; /* DESCRIPTION /* tls_digest_byname() constructs, and optionally returns, an EVP_MD_CTX @@ -48,8 +48,6 @@ /* /* tls_pkey_fprint() returns a public-key fingerprint; in all /* other respects the function behaves as tls_cert_fprint(). -/* The var_tls_bc_pkey_fprint variable enables an incorrect -/* algorithm that was used in Postfix versions 2.9.[0-5]. /* The return value is dynamically allocated with mymalloc(), /* and the caller must eventually free it with myfree(). /* @@ -274,6 +272,9 @@ char *tls_serverid_digest(TLS_SESS_STATE *TLScontext, CHECK_OK_AND_DIGEST_CHARS(mdctx, props->protocols); CHECK_OK_AND_DIGEST_CHARS(mdctx, ciphers); + /* Just in case we make this destination-policy specific */ + CHECK_OK_AND_DIGEST_OBJECT(mdctx, &props->enable_rpk); + /* * Ensure separation of caches for sessions where DANE trust * configuration succeeded from those where it did not. The latter @@ -398,38 +399,24 @@ char *tls_cert_fprint(X509 *peercert, const char *mdalg) return (result); } -/* tls_pkey_fprint - extract public key fingerprint from certificate */ +/* tls_pkey_fprint - extract public key fingerprint */ -char *tls_pkey_fprint(X509 *peercert, const char *mdalg) +char *tls_pkey_fprint(EVP_PKEY *peerpkey, const char *mdalg) { - if (var_tls_bc_pkey_fprint) { - const char *myname = "tls_pkey_fprint"; - ASN1_BIT_STRING *key; - char *result; - - key = X509_get0_pubkey_bitstr(peercert); - if (key == 0) - msg_fatal("%s: error extracting legacy public-key fingerprint: %m", - myname); - - result = tls_data_fprint(key->data, key->length, mdalg); - return (result); - } else { - int len; - unsigned char *buf; - unsigned char *buf2; - char *result; - - len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), NULL); - buf2 = buf = mymalloc(len); - i2d_X509_PUBKEY(X509_get_X509_PUBKEY(peercert), &buf2); - if (buf2 - buf != len) - msg_panic("i2d_X509_PUBKEY invalid result length"); - - result = tls_data_fprint(buf, len, mdalg); - myfree(buf); - return (result); - } + int len; + unsigned char *buf; + unsigned char *buf2; + char *result; + + len = i2d_PUBKEY(peerpkey, NULL); + buf2 = buf = mymalloc(len); + i2d_PUBKEY(peerpkey, &buf2); + if (buf2 - buf != len) + msg_panic("i2d_PUBKEY invalid result length"); + + result = tls_data_fprint(buf, len, mdalg); + myfree(buf); + return (result); } #endif diff --git a/src/tls/tls_misc.c b/src/tls/tls_misc.c index b7acd1d..cf8f6fa 100644 --- a/src/tls/tls_misc.c +++ b/src/tls/tls_misc.c @@ -118,6 +118,14 @@ /* /* const EVP_MD *tls_validate_digest(dgst) /* const char *dgst; +/* +/* void tls_enable_client_rpk(ctx, ssl) +/* SSL_CTX *ctx; +/* SSL *ssl; +/* +/* void tls_enable_server_rpk(ctx, ssl) +/* SSL_CTX *ctx; +/* SSL *ssl; /* DESCRIPTION /* This module implements public and internal routines that /* support the TLS client and server. @@ -215,6 +223,12 @@ /* /* tls_validate_digest() returns a static handle for the named /* digest algorithm, or NULL on error. +/* +/* tls_enable_client_rpk() enables the use of raw public keys in the +/* client to server direction, if supported by the OpenSSL library. +/* +/* tls_enable_server_rpk() enables the use of raw public keys in the +/* server to client direction, if supported by the OpenSSL library. /* LICENSE /* .ad /* .fi @@ -762,7 +776,7 @@ int tls_library_init(void) /* * The default global config file is optional. With "default" - * initialisation we don't insist on a match for the requested + * initialization we don't insist on a match for the requested * application name, allowing fallback to the default application * name, even when a non-default application name is specified. * Errors in loading the default configuration are ignored. @@ -1028,7 +1042,6 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) SSL *ssl = TLScontext->con; int srvr = SSL_is_server(ssl); EVP_PKEY *dh_pkey = 0; - X509 *local_cert; EVP_PKEY *local_pkey = 0; X509 *peer_cert; EVP_PKEY *peer_pkey = 0; @@ -1060,18 +1073,23 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) } /* - * On the client end, the certificate may be preset, but not used, so we + * On the client end, the certificate may be present, but not used, so we * check via SSL_get_signature_nid(). This means that local signature * data on clients requires at least 1.1.1a. */ - if (srvr || SSL_get_signature_nid(ssl, &nid)) - local_cert = SSL_get_certificate(ssl); - else - local_cert = 0; - + if (srvr || SSL_get_signature_nid(ssl, &nid)) { + local_pkey = SSL_get_privatekey(ssl); + } /* Signature algorithms for the local end of the connection */ - if (local_cert) { - local_pkey = X509_get0_pubkey(local_cert); + if (local_pkey) { +#if OPENSSL_VERSION_PREREQ(3,2) + if (srvr) + TLScontext->stoc_rpk = TLSEXT_cert_type_rpk == + SSL_get_negotiated_server_cert_type(ssl); + else + TLScontext->ctos_rpk = TLSEXT_cert_type_rpk == + SSL_get_negotiated_client_cert_type(ssl); +#endif /* * Override the built-in name for the "ECDSA" algorithms OID, with @@ -1097,7 +1115,6 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) break; #endif } - /* No X509_free(local_cert) */ } /* @@ -1107,9 +1124,26 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) if (SSL_get_signature_nid(ssl, &nid) && nid != NID_undef) locl_sig_dgst = OBJ_nid2sn(nid); } - /* Signature algorithms for the peer end of the connection */ - if ((peer_cert = TLS_PEEK_PEER_CERT(ssl)) != 0) { + peer_cert = TLS_PEEK_PEER_CERT(ssl); + if (peer_cert != 0) { peer_pkey = X509_get0_pubkey(peer_cert); + } +#if OPENSSL_VERSION_PREREQ(3,2) + else { + peer_pkey = SSL_get0_peer_rpk(ssl); + } +#endif + + /* Signature algorithms for the peer end of the connection */ + if (peer_pkey != 0) { +#if OPENSSL_VERSION_PREREQ(3,2) + if (srvr) + TLScontext->ctos_rpk = TLSEXT_cert_type_rpk == + SSL_get_negotiated_client_cert_type(ssl); + else + TLScontext->stoc_rpk = TLSEXT_cert_type_rpk == + SSL_get_negotiated_server_cert_type(ssl); +#endif /* * Override the built-in name for the "ECDSA" algorithms OID, with @@ -1144,8 +1178,9 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) if (SSL_get_peer_signature_nid(ssl, &nid) && nid != NID_undef) peer_sig_dgst = OBJ_nid2sn(nid); - TLS_FREE_PEER_CERT(peer_cert); } + TLS_FREE_PEER_CERT(peer_cert); + if (kex_name) { TLScontext->kex_name = mystrdup(kex_name); TLScontext->kex_curve = kex_curve; @@ -1180,7 +1215,7 @@ void tls_log_summary(TLS_ROLE role, TLS_USAGE usage, TLS_SESS_STATE *ctx) */ vstring_sprintf(msg, "%s TLS connection %s %s %s%s%s: %s" " with cipher %s (%d/%d bits)", - !TLS_CERT_IS_PRESENT(ctx) ? "Anonymous" : + !TLS_CRED_IS_PRESENT(ctx) ? "Anonymous" : TLS_CERT_IS_SECURED(ctx) ? "Verified" : TLS_CERT_IS_TRUSTED(ctx) ? "Trusted" : "Untrusted", usage == TLS_USAGE_NEW ? "established" : "reused", @@ -1199,9 +1234,13 @@ void tls_log_summary(TLS_ROLE role, TLS_USAGE usage, TLS_SESS_STATE *ctx) vstring_sprintf_append(msg, " server-signature %s", ctx->srvr_sig_name); if (ctx->srvr_sig_curve && *ctx->srvr_sig_curve) - vstring_sprintf_append(msg, " (%s)", ctx->srvr_sig_curve); + vstring_sprintf_append(msg, " (%s%s)", ctx->srvr_sig_curve, + ctx->stoc_rpk ? " raw public key" : ""); else if (ctx->srvr_sig_bits > 0) - vstring_sprintf_append(msg, " (%d bits)", ctx->srvr_sig_bits); + vstring_sprintf_append(msg, " (%d bit%s)", ctx->srvr_sig_bits, + ctx->stoc_rpk ? " raw public key" : "s"); + else if (ctx->stoc_rpk) + vstring_sprintf_append(msg, " (raw public key)"); if (ctx->srvr_sig_dgst && *ctx->srvr_sig_dgst) vstring_sprintf_append(msg, " server-digest %s", ctx->srvr_sig_dgst); @@ -1210,9 +1249,13 @@ void tls_log_summary(TLS_ROLE role, TLS_USAGE usage, TLS_SESS_STATE *ctx) vstring_sprintf_append(msg, " client-signature %s", ctx->clnt_sig_name); if (ctx->clnt_sig_curve && *ctx->clnt_sig_curve) - vstring_sprintf_append(msg, " (%s)", ctx->clnt_sig_curve); + vstring_sprintf_append(msg, " (%s%s)", ctx->clnt_sig_curve, + ctx->ctos_rpk ? " raw public key" : ""); else if (ctx->clnt_sig_bits > 0) - vstring_sprintf_append(msg, " (%d bits)", ctx->clnt_sig_bits); + vstring_sprintf_append(msg, " (%d bit%s)", ctx->clnt_sig_bits, + ctx->ctos_rpk ? " raw public key" : "s"); + else if (ctx->ctos_rpk) + vstring_sprintf_append(msg, " (raw public key)"); if (ctx->clnt_sig_dgst && *ctx->clnt_sig_dgst) vstring_sprintf_append(msg, " client-digest %s", ctx->clnt_sig_dgst); @@ -1288,6 +1331,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr) TLScontext->cipher_name = 0; TLScontext->kex_name = 0; TLScontext->kex_curve = 0; + TLScontext->ctos_rpk = 0; + TLScontext->stoc_rpk = 0; TLScontext->clnt_sig_name = 0; TLScontext->clnt_sig_curve = 0; TLScontext->clnt_sig_dgst = 0; @@ -1702,6 +1747,52 @@ const EVP_MD *tls_validate_digest(const char *dgst) return md_alg; } +void tls_enable_client_rpk(SSL_CTX *ctx, SSL *ssl) +{ +#if OPENSSL_VERSION_PREREQ(3,2) + static int warned = 0; + static const unsigned char cert_types_rpk[] = { + TLSEXT_cert_type_rpk, + TLSEXT_cert_type_x509 + }; + + if ((ctx && !SSL_CTX_set1_client_cert_type(ctx, cert_types_rpk, + sizeof(cert_types_rpk))) || + (ssl && !SSL_set1_client_cert_type(ssl, cert_types_rpk, + sizeof(cert_types_rpk)))) { + if (warned++) { + ERR_clear_error(); + return; + } + msg_warn("Failed to enable client to server raw public key support"); + tls_print_errors(); + } +#endif +} + +void tls_enable_server_rpk(SSL_CTX *ctx, SSL *ssl) +{ +#if OPENSSL_VERSION_PREREQ(3,2) + static int warned = 0; + static const unsigned char cert_types_rpk[] = { + TLSEXT_cert_type_rpk, + TLSEXT_cert_type_x509 + }; + + if ((ctx && !SSL_CTX_set1_server_cert_type(ctx, cert_types_rpk, + sizeof(cert_types_rpk))) || + (ssl && !SSL_set1_server_cert_type(ssl, cert_types_rpk, + sizeof(cert_types_rpk)))) { + if (warned++) { + ERR_clear_error(); + return; + } + msg_warn("Failed to enable server to client raw public key support"); + tls_print_errors(); + } +#endif +} + #else /* diff --git a/src/tls/tls_proxy.h b/src/tls/tls_proxy.h index ca664c6..6528639 100644 --- a/src/tls/tls_proxy.h +++ b/src/tls/tls_proxy.h @@ -32,8 +32,10 @@ #ifdef USE_TLS /* - * TLS_CLIENT_PARAMS structure. If this changes, update all - * TLS_CLIENT_PARAMS related functions in tls_proxy_client_*.c. + * TLS_CLIENT_PARAMS structure, to communicate global TLS library settings + * that are the same for all TLS client contexts. This information is used + * in tlsproxy(8) to detect inconsistencies. If this structure is changed, + * update all TLS_CLIENT_PARAMS related functions in tls_proxy_client_*.c. * * In the serialization these attributes are identified by their configuration * parameter names. @@ -106,11 +108,11 @@ extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *, ((props)->a12), ((props)->a13), ((props)->a14)) #define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \ - a9, a10, a11, a12, a13, a14) \ + a9, a10, a11, a12, a13, a14, a15) \ (((props)->a1), ((props)->a2), ((props)->a3), \ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \ - ((props)->a12), ((props)->a13), ((props)->a14)) + ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15)) extern TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *); extern void tls_proxy_context_free(TLS_SESS_STATE *); @@ -168,6 +170,8 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); #define TLS_ATTR_KEX_NAME "key_exchange" #define TLS_ATTR_KEX_CURVE "key_exchange_curve" #define TLS_ATTR_KEX_BITS "key_exchange_bits" +#define TLS_ATTR_CTOS_RPK "ctos_rpk" +#define TLS_ATTR_STOC_RPK "stoc_rpk" #define TLS_ATTR_CLNT_SIG_NAME "clnt_signature" #define TLS_ATTR_CLNT_SIG_CURVE "clnt_signature_curve" #define TLS_ATTR_CLNT_SIG_BITS "clnt_signature_bits" @@ -237,6 +241,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); * TLS_CLIENT_START_PROPS attributes. */ #define TLS_ATTR_TIMEOUT "timeout" +#define TLS_ATTR_ENABLE_RPK "enable_rpk" #define TLS_ATTR_TLS_LEVEL "tls_level" #define TLS_ATTR_NEXTHOP "nexthop" #define TLS_ATTR_HOST "host" diff --git a/src/tls/tls_proxy_client_print.c b/src/tls/tls_proxy_client_print.c index 1cc5778..81e50b9 100644 --- a/src/tls/tls_proxy_client_print.c +++ b/src/tls/tls_proxy_client_print.c @@ -257,6 +257,7 @@ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn, ret = print_fn(fp, flags | ATTR_FLAG_MORE, SEND_ATTR_INT(TLS_ATTR_TIMEOUT, props->timeout), + SEND_ATTR_INT(TLS_ATTR_ENABLE_RPK, props->enable_rpk), SEND_ATTR_INT(TLS_ATTR_TLS_LEVEL, props->tls_level), SEND_ATTR_STR(TLS_ATTR_NEXTHOP, STRING_OR_EMPTY(props->nexthop)), diff --git a/src/tls/tls_proxy_client_scan.c b/src/tls/tls_proxy_client_scan.c index a69388c..d36cf4d 100644 --- a/src/tls/tls_proxy_client_scan.c +++ b/src/tls/tls_proxy_client_scan.c @@ -451,6 +451,7 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, props->dane = 0; /* scan_fn may return early */ ret = scan_fn(fp, flags | ATTR_FLAG_MORE, RECV_ATTR_INT(TLS_ATTR_TIMEOUT, &props->timeout), + RECV_ATTR_INT(TLS_ATTR_ENABLE_RPK, &props->enable_rpk), RECV_ATTR_INT(TLS_ATTR_TLS_LEVEL, &props->tls_level), RECV_ATTR_STR(TLS_ATTR_NEXTHOP, nexthop), RECV_ATTR_STR(TLS_ATTR_HOST, host), @@ -478,7 +479,7 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, props->cipher_grade = vstring_export(cipher_grade); props->cipher_exclusions = vstring_export(cipher_exclusions); props->mdalg = vstring_export(mdalg); - ret = (ret == 14 ? 1 : -1); + ret = (ret == 15 ? 1 : -1); if (ret != 1) { tls_proxy_client_start_free(props); props = 0; diff --git a/src/tls/tls_proxy_context_print.c b/src/tls/tls_proxy_context_print.c index 04123cb..930410a 100644 --- a/src/tls/tls_proxy_context_print.c +++ b/src/tls/tls_proxy_context_print.c @@ -88,6 +88,10 @@ int tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp, STRING_OR_EMPTY(tp->kex_curve)), SEND_ATTR_INT(TLS_ATTR_KEX_BITS, tp->kex_bits), + SEND_ATTR_INT(TLS_ATTR_CTOS_RPK, + tp->ctos_rpk), + SEND_ATTR_INT(TLS_ATTR_STOC_RPK, + tp->stoc_rpk), SEND_ATTR_STR(TLS_ATTR_CLNT_SIG_NAME, STRING_OR_EMPTY(tp->clnt_sig_name)), SEND_ATTR_STR(TLS_ATTR_CLNT_SIG_CURVE, diff --git a/src/tls/tls_proxy_context_scan.c b/src/tls/tls_proxy_context_scan.c index 1d463ad..48aaff6 100644 --- a/src/tls/tls_proxy_context_scan.c +++ b/src/tls/tls_proxy_context_scan.c @@ -113,6 +113,8 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, RECV_ATTR_STR(TLS_ATTR_KEX_NAME, kex_name), RECV_ATTR_STR(TLS_ATTR_KEX_CURVE, kex_curve), RECV_ATTR_INT(TLS_ATTR_KEX_BITS, &tls_context->kex_bits), + RECV_ATTR_INT(TLS_ATTR_CTOS_RPK, &tls_context->ctos_rpk), + RECV_ATTR_INT(TLS_ATTR_STOC_RPK, &tls_context->stoc_rpk), RECV_ATTR_STR(TLS_ATTR_CLNT_SIG_NAME, clnt_sig_name), RECV_ATTR_STR(TLS_ATTR_CLNT_SIG_CURVE, clnt_sig_curve), RECV_ATTR_INT(TLS_ATTR_CLNT_SIG_BITS, &tls_context->clnt_sig_bits), @@ -139,7 +141,7 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve); tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst); tls_context->namaddr = vstring_export(namaddr); - ret = (ret == 22 ? 1 : -1); + ret = (ret == 24 ? 1 : -1); if (ret != 1) { tls_proxy_context_free(tls_context); tls_context = 0; diff --git a/src/tls/tls_server.c b/src/tls/tls_server.c index 262cda9..88b3326 100644 --- a/src/tls/tls_server.c +++ b/src/tls/tls_server.c @@ -62,8 +62,8 @@ /* available as: /* .IP TLScontext->peer_status /* A bitmask field that records the status of the peer certificate -/* verification. One or more of TLS_CERT_FLAG_PRESENT and -/* TLS_CERT_FLAG_TRUSTED. +/* verification. One or more of TLS_CRED_FLAG_CERT, TLS_CRED_FLAG_RPK +/* and TLS_CERT_FLAG_TRUSTED. /* .IP TLScontext->peer_CN /* Extracted CommonName of the peer, or zero-length string /* when information could not be extracted. @@ -637,6 +637,13 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) } /* + * Always support server->client raw public keys, if they're good enough + * for the client, they're good enough for us. + */ + tls_enable_server_rpk(server_ctx, NULL); + tls_enable_server_rpk(sni_ctx, NULL); + + /* * Upref and share the cert store. Sadly we can't yet use * SSL_CTX_set1_cert_store(3) which was added in OpenSSL 1.1.0. */ @@ -865,11 +872,19 @@ TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props) tls_free_context(TLScontext); return (0); } -#ifdef SSL_SECOP_PEER - /* When authenticating the peer, use 80-bit plus OpenSSL security level */ + + /* + * When encryption is mandatory use the 80-bit plus OpenSSL security level. + */ if (props->requirecert) SSL_set_security_level(TLScontext->con, 1); -#endif + + /* + * Also enable client->server raw public keys, provided we're not + * interested in client certificate fingerprints. + */ + if (props->enable_rpk) + tls_enable_client_rpk(NULL, TLScontext->con); /* * Before really starting anything, try to seed the PRNG a little bit @@ -946,6 +961,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) { const SSL_CIPHER *cipher; X509 *peer; + EVP_PKEY *pkey = 0; char buf[CCERT_BUFSIZ]; /* Turn off packet dump if only dumping the handshake */ @@ -966,8 +982,17 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) * actual information. We want to save it for later use. */ peer = TLS_PEEK_PEER_CERT(TLScontext->con); + if (peer) { + pkey = X509_get0_pubkey(peer); + } +#if OPENSSL_VERSION_PREREQ(3,2) + else { + pkey = SSL_get0_peer_rpk(TLScontext->con); + } +#endif + if (peer != NULL) { - TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; + TLScontext->peer_status |= TLS_CRED_FLAG_CERT; if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; @@ -981,16 +1006,23 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) } TLScontext->peer_CN = tls_peer_CN(peer, TLScontext); TLScontext->issuer_CN = tls_issuer_CN(peer, TLScontext); - TLScontext->peer_cert_fprint = tls_cert_fprint(peer, TLScontext->mdalg); - TLScontext->peer_pkey_fprint = tls_pkey_fprint(peer, TLScontext->mdalg); + TLScontext->peer_cert_fprint = + tls_cert_fprint(peer, TLScontext->mdalg); + TLScontext->peer_pkey_fprint = + tls_pkey_fprint(pkey, TLScontext->mdalg); if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) { - msg_info("%s: subject_CN=%s, issuer=%s, fingerprint=%s" - ", pkey_fingerprint=%s", + msg_info("%s: subject_CN=%s, issuer=%s%s%s%s%s", TLScontext->namaddr, TLScontext->peer_CN, TLScontext->issuer_CN, - TLScontext->peer_cert_fprint, - TLScontext->peer_pkey_fprint); + *TLScontext->peer_cert_fprint ? + ", cert fingerprint=" : "", + *TLScontext->peer_cert_fprint ? + TLScontext->peer_cert_fprint : "", + *TLScontext->peer_pkey_fprint ? + ", pkey fingerprint=" : "", + *TLScontext->peer_pkey_fprint ? + TLScontext->peer_pkey_fprint : ""); } TLS_FREE_PEER_CERT(peer); @@ -1013,7 +1045,22 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) TLScontext->peer_CN = mystrdup(""); TLScontext->issuer_CN = mystrdup(""); TLScontext->peer_cert_fprint = mystrdup(""); - TLScontext->peer_pkey_fprint = mystrdup(""); + if (!pkey) { + TLScontext->peer_pkey_fprint = mystrdup(""); + } else { + + /* + * Raw public keys don't involve CA trust, and we don't have a + * way to associate DANE TLSA RRs with clients just yet, we just + * make the fingerprint available to the access(5) layer. + */ + TLScontext->peer_status |= TLS_CRED_FLAG_RPK; + TLScontext->peer_pkey_fprint = + tls_pkey_fprint(pkey, TLScontext->mdalg); + if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) + msg_info("%s: raw public key fingerprint=%s", + TLScontext->namaddr, TLScontext->peer_pkey_fprint); + } } /* diff --git a/src/tls/tls_verify.c b/src/tls/tls_verify.c index f32f32b..c643f18 100644 --- a/src/tls/tls_verify.c +++ b/src/tls/tls_verify.c @@ -144,6 +144,7 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) int depth; SSL *con; TLS_SESS_STATE *TLScontext; + EVP_PKEY *rpk = 0; /* May be NULL as of OpenSSL 1.0, thanks for the API change! */ cert = X509_STORE_CTX_get_current_cert(ctx); @@ -151,6 +152,10 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); TLScontext = SSL_get_ex_data(con, TLScontext_index); depth = X509_STORE_CTX_get_error_depth(ctx); +#if OPENSSL_VERSION_PREREQ(3,2) + if (cert == 0) + rpk = X509_STORE_CTX_get0_rpk(ctx); +#endif /* * Transient failures to load the (DNS or synthetic TLSA) trust settings @@ -174,12 +179,15 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) update_error_state(TLScontext, depth, cert, err); if (TLScontext->log_mask & TLS_LOG_VERBOSE) { - if (cert) + if (cert) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); - else - strcpy(buf, "<unknown>"); - msg_info("%s: depth=%d verify=%d subject=%s", - TLScontext->namaddr, depth, ok, printable(buf, '?')); + msg_info("%s: depth=%d verify=%d subject=%s", + TLScontext->namaddr, depth, ok, printable(buf, '?')); + } else if (rpk) { + msg_info("%s: verify=%d raw public key", TLScontext->namaddr, ok); + } else { + msg_info("%s: depth=%d verify=%d", TLScontext->namaddr, depth, ok); + } } return (1); } |