diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 08:42:06 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 08:42:06 +0000 |
commit | 426ff88c97805d5359804bcfd7186dcd2c9fbf47 (patch) | |
tree | 048956abd950b325107bcc2febb1ba6763d770e1 /src/tls/tls_client.c | |
parent | Adding debian version 3.8.6-1. (diff) | |
download | postfix-426ff88c97805d5359804bcfd7186dcd2c9fbf47.tar.xz postfix-426ff88c97805d5359804bcfd7186dcd2c9fbf47.zip |
Merging upstream version 3.9.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tls/tls_client.c')
-rw-r--r-- | src/tls/tls_client.c | 157 |
1 files changed, 136 insertions, 21 deletions
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); + } } /* |