summaryrefslogtreecommitdiffstats
path: root/src/tls
diff options
context:
space:
mode:
Diffstat (limited to 'src/tls')
-rw-r--r--src/tls/tls.h31
-rw-r--r--src/tls/tls_client.c157
-rw-r--r--src/tls/tls_dane.c77
-rw-r--r--src/tls/tls_fprint.c53
-rw-r--r--src/tls/tls_misc.c129
-rw-r--r--src/tls/tls_proxy.h13
-rw-r--r--src/tls/tls_proxy_client_print.c1
-rw-r--r--src/tls/tls_proxy_client_scan.c3
-rw-r--r--src/tls/tls_proxy_context_print.c4
-rw-r--r--src/tls/tls_proxy_context_scan.c4
-rw-r--r--src/tls/tls_server.c73
-rw-r--r--src/tls/tls_verify.c18
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);
}