diff --git a/postfix/.indent.pro b/postfix/.indent.pro index bd70a8bcb..3d53b41ca 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -8,8 +8,11 @@ -TANVIL_REMOTE -TANVIL_REQ_TABLE -TARGV +-TASN1_INTEGER +-TASN1_OBJECT -TATTR_CLNT -TATTR_TABLE +-TAUTHORITY_KEYID -TAUTO_CLNT -TBH_TABLE -TBINATTR @@ -116,6 +119,8 @@ -TDSN_BUF -TDSN_SPLIT -TDSN_STAT +-TEC_KEY +-TEC_GROUP -TEDIT_FILE -TEVENT_MASK -TEVP_PKEY @@ -324,8 +329,10 @@ -TWATCHDOG -TWATCH_FD -TX509 +-TX509_EXTENSION -TX509_NAME -TX509_STORE_CTX +-TX509V3_CTX -TXSASL_CLIENT -TXSASL_CLIENT_CREATE_ARGS -TXSASL_CLIENT_IMPL @@ -342,6 +349,7 @@ -TXSASL_SERVER_IMPL -TXSASL_SERVER_IMPL_INFO -Tcipher_probe_t +-Tgeneral_name_stack_t -Toff_t -Tregex_t -Tregmatch_t @@ -349,5 +357,9 @@ -Tsasl_secret_t -Tsfsistat -Tsize_t +-Tssl_cipher_stack_t +-Tssl_comp_stack_t -Tssize_t -Ttime_t +-Tx509_extension_stack_t +-Tx509_stack_t diff --git a/postfix/HISTORY b/postfix/HISTORY index 658fbe6c6..1db43c106 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -18716,3 +18716,28 @@ Apologies for any names omitted. posttls-finger/posttls-finger.c, smtp/smtp_tls_policy.c, tls/tls_dane.c. +20130615 + + Interoperability: turn on SHA-2XX digests by force. This + improves interoperability with clients and servers with + ancient OpenSSL versions and that that prematurely deploy + SHA-2 certificates. Viktor Dukhovni. File: tls/tls_misc.c + +20130616 + + Workaround: The Postfix SMTP server TLS session cache was + broken because OpenSSL now enables session tickets by + default, resulting in different ticket encryption key for + each smtpd(8) process. the workaround turns off session + tickets. In 2.11 we'll enable session tickets properly. + Viktor Dukhovni. File: tls/tls_server.c. + + Updated DANE support (trust in DNS instead of PKI). With + OpenSSL 1.0.2 (under development) trusted certificates don't + need to be self-signed roots. Otherwise we use an ephemeral + root certificate to sign the trust anchor. Viktor Dukhovni. + Files: posttls-finger/posttls-finger.c, smtp/smtp_proto.c, + smtp/smtp_tls_policy.c, tls/tls.h, tls/tls_client.c, + tls/tls_dane.c, tls/tls_fprint.c, tls/tls_misc.c, + tls/tls_verify.c. + diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 7132bde41..fb0c0f83f 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20130613" +#define MAIL_RELEASE_DATE "20130616" #define MAIL_VERSION_NUMBER "2.11" #ifdef SNAPSHOT diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index 03dc1ead6..66edbe9f6 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -575,39 +575,59 @@ static RESPONSE *ehlo(STATE *state) #ifdef USE_TLS +static void print_stack(STATE *state, x509_stack_t *sk, int trustout) +{ + int i; + + for (i = 0; i < sk_X509_num(sk); i++) { + X509 *cert = sk_X509_value(sk, i); + char buf[CCERT_BUFSIZ]; + X509_NAME *xn; + char *digest; + + if ((xn = X509_get_subject_name(cert)) != 0) { + X509_NAME_oneline(xn, buf, sizeof buf); + BIO_printf(state->tls_bio, "%2d subject: %s\n", i, buf); + } + if ((xn = X509_get_issuer_name(cert)) != 0) { + X509_NAME_oneline(xn, buf, sizeof buf); + BIO_printf(state->tls_bio, " issuer: %s\n", buf); + } + digest = tls_cert_fprint(cert, state->mdalg); + BIO_printf(state->tls_bio, " cert digest=%s\n", digest); + myfree(digest); + + digest = tls_pkey_fprint(cert, state->mdalg); + BIO_printf(state->tls_bio, " pkey digest=%s\n", digest); + myfree(digest); + + if (trustout) + PEM_write_bio_X509_AUX(state->tls_bio, cert); + else + PEM_write_bio_X509(state->tls_bio, cert); + } +} + static void print_trust_info(STATE *state) { - STACK_OF(X509) *sk = SSL_get_peer_cert_chain(state->tls_context->con); + x509_stack_t *sk = SSL_get_peer_cert_chain(state->tls_context->con); - if (sk != NULL) { - int i; - - BIO_printf(state->tls_bio, "---\nCertificate chain\n"); - for (i = 0; i < sk_X509_num(sk); i++) { - X509 *cert = sk_X509_value(sk, i); - char buf[CCERT_BUFSIZ]; - X509_NAME *xn; - char *digest; - - if ((xn = X509_get_subject_name(cert)) != 0) { - X509_NAME_oneline(xn, buf, sizeof buf); - BIO_printf(state->tls_bio, "%2d subject: %s\n", i, buf); - } - if ((xn = X509_get_issuer_name(cert)) != 0) { - X509_NAME_oneline(xn, buf, sizeof buf); - BIO_printf(state->tls_bio, " issuer: %s\n", buf); - } - digest = tls_cert_fprint(cert, state->mdalg); - BIO_printf(state->tls_bio, " cert digest=%s\n", digest); - myfree(digest); - - digest = tls_pkey_fprint(cert, state->mdalg); - BIO_printf(state->tls_bio, " pkey digest=%s\n", digest); - myfree(digest); - - PEM_write_bio_X509(state->tls_bio, cert); - } + if (sk != 0) { + BIO_printf(state->tls_bio, "\n---\nCertificate chain\n"); + print_stack(state, sk, 0); } +#ifdef dane_verify_debug + /* print internally constructed untrusted chain */ + if ((sk = state->tls_context->untrusted) != 0) { + BIO_printf(state->tls_bio, "\n---\nUntrusted chain\n"); + print_stack(state, sk, 0); + } + /* print associated root CA */ + if ((sk = state->tls_context->trusted) != 0) { + BIO_printf(state->tls_bio, "\n---\nTrusted chain\n"); + print_stack(state, sk, 1); + } +#endif } /* starttls - SMTP STARTTLS handshake */ @@ -615,7 +635,6 @@ static void print_trust_info(STATE *state) static int starttls(STATE *state) { VSTRING *cipher_exclusions; - VSTRING *serverid; int except; RESPONSE *resp; VSTREAM *stream = state->stream; @@ -662,9 +681,6 @@ static int starttls(STATE *state) else ADD_EXCLUDE(cipher_exclusions, "eNULL"); - serverid = vstring_alloc(10); - vstring_sprintf(serverid, "%s:%s", var_procname, state->addrport); - state->tls_context = TLS_CLIENT_START(&tls_props, ctx = state->tls_ctx, @@ -674,7 +690,7 @@ static int starttls(STATE *state) nexthop = state->nexthop, host = state->hostname, namaddr = state->namaddrport, - serverid = STR(serverid), + serverid = state->addrport, helo = state->helo ? state->helo : "", protocols = state->protocols, cipher_grade = state->grade, @@ -684,7 +700,6 @@ static int starttls(STATE *state) mdalg = state->mdalg, dane = state->ddane ? state->ddane : state->dane); vstring_free(cipher_exclusions); - vstring_free(serverid); if (state->helo) { myfree(state->helo); state->helo = 0; @@ -1466,10 +1481,12 @@ static void cleanup(STATE *state) static void usage(void) { #ifdef USE_TLS - fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s destination [match ...]\n", - var_procname, "[-acCStTv] [-d mdalg] [-g grade] [-p protocols] [-F CAfile.pem]", - "[-h host_lookup] [-l level] [-L logopts] [-m count]", - "[-o name=value] [-P CApath/] [-r delay]"); + fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s \\\n\t%s" + " destination [match ...]\n", var_procname, + "[-acCSv] [-t conn_tmout] [-T cmd_tmout] [-L logopts]", + "[-h host_lookup] [-l level] [-d mdalg] [-g grade] [-p protocols]", + "[-A tafile] [-F CAfile.pem] [-P CApath/] [-m count] [-r delay]", + "[-o name=value]"); #else fprintf(stderr, "usage: %s [-acStTv] [-h host_lookup] [-o name=value] destination\n", var_procname); @@ -1713,7 +1730,6 @@ static void parse_match(STATE *state, int argc, char *argv[]) while (*argv) tls_dane_split((TLS_DANE *) state->dane, TLS_DANE_EE, TLS_DANE_PKEY, state->mdalg, *argv++, ""); - tls_dane_final((TLS_DANE *) state->dane); break; case TLS_LEV_DANE: state->match = argv_alloc(2); @@ -1745,7 +1761,6 @@ static void parse_tas(STATE *state) } if (*file) msg_fatal("Failed to load trust anchor file: %s", *file); - tls_dane_final((TLS_DANE *) state->dane); break; } #endif diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 7a69a5d93..5d442df41 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -780,9 +780,10 @@ static int smtp_start_tls(SMTP_STATE *state) * SSL session lookup key lengths. */ serverid = vstring_alloc(10); - smtp_key_prefix(serverid, ":", state->iterator, SMTP_KEY_FLAG_SERVICE - | SMTP_KEY_FLAG_ADDR - | SMTP_KEY_FLAG_PORT); + smtp_key_prefix(serverid, "&", state->iterator, SMTP_KEY_FLAG_SERVICE + | SMTP_KEY_FLAG_NEXTHOP /* With port */ + | SMTP_KEY_FLAG_HOSTNAME + | SMTP_KEY_FLAG_ADDR); /* * As of Postfix 2.5, tls_client_start() tries hard to always complete diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c index bad77d2af..c41cc9917 100644 --- a/postfix/src/smtp/smtp_tls_policy.c +++ b/postfix/src/smtp/smtp_tls_policy.c @@ -572,7 +572,6 @@ static void *policy_create(const char *unused_key, void *context) return ((void *) tls); } } - tls_dane_final(tls->dane); break; case TLS_LEV_VERIFY: case TLS_LEV_SECURE: @@ -584,13 +583,10 @@ static void *policy_create(const char *unused_key, void *context) if (*var_smtp_tls_tafile) { if (tls->dane == 0) tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED); - if (!TLS_DANE_HASTA(tls->dane)) { - if (load_tas(tls->dane, var_smtp_tls_tafile)) - tls_dane_final(tls->dane); - else { - MARK_INVALID(tls->why, &tls->level); - return ((void *) tls); - } + if (!TLS_DANE_HASTA(tls->dane) + && !load_tas(tls->dane, var_smtp_tls_tafile)) { + MARK_INVALID(tls->why, &tls->level); + return ((void *) tls); } } break; diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 5519dcc54..47f60e996 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -73,6 +73,13 @@ extern const NAME_CODE tls_level_table[]; #include #include + /* Appease indent(1) */ +#define x509_stack_t STACK_OF(X509) +#define x509_extension_stack_t STACK_OF(X509_EXTENSION) +#define general_name_stack_t STACK_OF(GENERAL_NAME) +#define ssl_cipher_stack_t STACK_OF(SSL_CIPHER) +#define ssl_comp_stack_t STACK_OF(SSL_COMP) + #if (OPENSSL_VERSION_NUMBER < 0x00090700f) #error "need OpenSSL version 0.9.7 or later" #endif @@ -101,10 +108,9 @@ extern const NAME_CODE tls_level_table[]; #define TLS_DANE_PKEY 1 /* Match the public key digest */ #define TLS_DANE_FLAG_MIXED (1<<0) /* Combined pkeys and certs */ -#define TLS_DANE_FLAG_FINAL (1<<1) /* No further changes */ -#define TLS_DANE_FLAG_NORRS (1<<2) /* Nothing found in DNS */ -#define TLS_DANE_FLAG_EMPTY (1<<3) /* Nothing usable found in DNS */ -#define TLS_DANE_FLAG_ERROR (1<<4) /* TLSA record lookup error */ +#define TLS_DANE_FLAG_NORRS (1<<1) /* Nothing found in DNS */ +#define TLS_DANE_FLAG_EMPTY (1<<2) /* Nothing usable found in DNS */ +#define TLS_DANE_FLAG_ERROR (1<<3) /* TLSA record lookup error */ #define tls_dane_unusable(dane) ((dane)->flags & TLS_DANE_FLAG_EMPTY) #define tls_dane_notfound(dane) ((dane)->flags & TLS_DANE_FLAG_NORRS) @@ -165,7 +171,6 @@ extern void tls_dane_verbose(int); extern TLS_DANE *tls_dane_alloc(int); extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *, const char *); -extern TLS_DANE *tls_dane_final(TLS_DANE *); extern void tls_dane_free(TLS_DANE *); extern TLS_DANE *tls_dane_resolve(const char *, const char *, unsigned); extern int tls_dane_load_trustfile(TLS_DANE *, const char *); @@ -202,11 +207,12 @@ typedef struct { VSTREAM *stream; /* Blocking-mode SMTP session */ /* RFC 6698 DANE trust input and verification state */ const TLS_DANE *dane; /* DANE TLSA digests */ - int trustdepth; /* Chain depth of trusted cert */ int errordepth; /* Chain depth of error cert */ - int chaindepth; /* Chain depth of top cert */ + int tadepth; /* Chain depth of trust anchor */ int errorcode; /* First error at error depth */ X509 *errorcert; /* Error certificate closest to leaf */ + x509_stack_t *untrusted; /* Certificate chain fodder */ + x509_stack_t *trusted; /* Internal root CA list */ } TLS_SESS_STATE; /* @@ -520,10 +526,15 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int); extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *); extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *); extern const char *tls_dns_name(const GENERAL_NAME *, const TLS_SESS_STATE *); -extern int tls_cert_match(TLS_SESS_STATE *, int, X509 *, int); extern int tls_verify_certificate_callback(int, X509_STORE_CTX *); extern void tls_log_verify_error(TLS_SESS_STATE *); + /* + * tls_dane.c + */ +extern int tls_dane_match(TLS_SESS_STATE *, int, X509 *, int); +extern void tls_dane_set_callback(SSL_CTX *, TLS_SESS_STATE *); + /* * tls_fprint.c */ diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 43d0fb2ed..321ed6575 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -491,7 +491,7 @@ TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *props) /* match_servername - match servername against pattern */ static int match_servername(const char *certid, - const TLS_CLIENT_START_PROPS *props) + const TLS_CLIENT_START_PROPS *props) { const ARGV *cmatch_argv; const char *nexthop = props->nexthop; @@ -570,8 +570,7 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, int verbose; const char *dnsname; const GENERAL_NAME *gn; - - STACK_OF(GENERAL_NAME) * gens; + general_name_stack_t *gens; /* * On exit both peer_CN and issuer_CN should be set. @@ -728,7 +727,7 @@ static void verify_extract_print(TLS_SESS_STATE *TLScontext, X509 *peercert, * untrusted in verify_extract_name(). */ if (TLS_DANE_HASEE(props->dane) - && tls_cert_match(TLScontext, TLS_DANE_EE, peercert, 0)) + && tls_dane_match(TLScontext, TLS_DANE_EE, peercert, 0)) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED; } @@ -945,6 +944,8 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) if (log_mask & TLS_LOG_TLSPKTS) BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); + tls_dane_set_callback(app_ctx->ssl_ctx, TLScontext); + /* * Start TLS negotiations. This process is a black box that invokes our * call-backs for certificate verification. diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c index b9c327ff5..cc068af4a 100644 --- a/postfix/src/tls/tls_dane.c +++ b/postfix/src/tls/tls_dane.c @@ -31,8 +31,15 @@ /* TLS_DANE *dane; /* const char *tafile; /* -/* TLS_DANE *tls_dane_final(dane) -/* TLS_DANE *dane; +/* int tls_dane_match(TLSContext, usage, cert, depth) +/* TLS_SESS_STATE *TLScontext; +/* int usage; +/* X509 *cert; +/* int depth; +/* +/* void tls_dane_set_callback(ssl_ctx, TLScontext) +/* SSL_CTX *ssl_ctx; +/* TLS_SESS_STATE *TLScontext; /* /* TLS_DANE *tls_dane_resolve(host, proto, port) /* const char *host; @@ -68,17 +75,25 @@ /* delimiters and stores the results with the requested "certusage" /* and "selector". This is an incremental interface, that builds a /* TLS_DANE structure outside the cache by manually adding entries. -/* Once all the entries have been added, the caller must call -/* tls_dane_final() to complete its construction. /* /* tls_dane_load_trustfile() imports trust-anchor certificates and /* public keys from a file (rather than DNS TLSA records). /* -/* tls_dane_final() completes the construction of a TLS_DANE structure, -/* obtained via tls_dane_alloc() and populated via tls_dane_split() or -/* tls_dane_load_trustfile(). After tls_dane_final() is called, the -/* structure must not be modified. The return value is the input -/* argument. +/* tls_dane_match() matches the full and/or public key digest of +/* "cert" against each candidate digest in TLScontext->dane. If usage +/* is TLS_DANE_EE, the match is against end-entity digests, otherwise +/* it is against trust-anchor digests. Returns true if a match is found, +/* false otherwise. +/* +/* tls_dane_set_callback() wraps the SSL certificate verification logic +/* in a function that modifies the input trust chain and trusted +/* certificate store to map DANE TA validation onto the existing PKI +/* verification model. When TLScontext is NULL the callback is +/* cleared, otherwise it is set. This callback should only be set +/* when out-of-band trust-anchors (via DNSSEC DANE TLSA records or +/* per-destination local configuration) are provided. Such trust +/* anchors always override the legacy public CA PKI. Otherwise, the +/* callback MUST be cleared. /* /* tls_dane_resolve() maps a (host, protocol, port) triple to a /* a corresponding TLS_DANE policy structure found in the DNS. The port @@ -109,6 +124,17 @@ /* The TCP port in network byte order. /* .IP flags /* Only one flag is part of the public interface at this time: +/* .IP TLScontext +/* Client context with TA/EE matching data and related state. +/* .IP usage +/* Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests? +/* .IP cert +/* Certificate from peer trust chain (CA or leaf server). +/* .IP depth +/* The certificate depth for logging. +/* .IP ssl_ctx +/* The global SSL_CTX structure used to initialize child SSL +/* conenctions. /* .RS /* .IP TLS_DANE_FLAG_MIXED /* Don't distinguish between certificate and public-key digests. @@ -161,6 +187,7 @@ #include /* event_time() */ #include #include +#include #define STR(x) vstring_str(x) @@ -179,13 +206,45 @@ /* Application-specific. */ +#undef TRUST_ANCHOR_SUPPORT +#undef DANE_TLSA_SUPPORT +#undef WRAP_SIGNED + +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL && \ + (defined(X509_V_FLAG_PARTIAL_CHAIN) || !defined(OPENSSL_NO_ECDH)) +#define TRUST_ANCHOR_SUPPORT + +#ifndef X509_V_FLAG_PARTIAL_CHAIN +#define WRAP_SIGNED +#endif + +#if defined(TLSEXT_MAXLEN_host_name) && RES_USE_DNSSEC && RES_USE_EDNS0 +#define DANE_TLSA_SUPPORT +#endif + +#endif /* OPENSSL_VERSION_NUMBER ... */ + +#ifdef WRAP_SIGNED +static int wrap_signed = 1; + +#else +static int wrap_signed = 0; + +#endif +static const EVP_MD *signmd; + +static EVP_PKEY *danekey; +static ASN1_OBJECT *serverAuth; + static const char *sha256 = "sha256"; -static const char *sha512 = "sha512"; +static const EVP_MD *sha256md; static int sha256len; + +static const char *sha512 = "sha512"; +static const EVP_MD *sha512md; static int sha512len; -static int dane_verbose; + static int digest_mask; -static TLS_TLSA **dane_locate(TLS_TLSA **, const char *); #define TLS_DANE_ENABLE_CC (1<<0) /* ca-constraint digests OK */ #define TLS_DANE_ENABLE_TAA (1<<1) /* trust-anchor-assertion digests OK */ @@ -199,6 +258,9 @@ static TLS_TLSA **dane_locate(TLS_TLSA **, const char *); #define CACHE_SIZE 20 static CTABLE *dane_cache; +static int dane_initialized; +static int dane_verbose; + /* tls_dane_verbose - enable/disable verbose logging */ void tls_dane_verbose(int on) @@ -206,40 +268,82 @@ void tls_dane_verbose(int on) dane_verbose = on; } -/* tls_dane_avail - check for availability of dane required digests */ +/* gencakey - generate interal DANE root CA key */ -int tls_dane_avail(void) +static EVP_PKEY *gencakey(void) +{ + EVP_PKEY *key = 0; + +#ifdef WRAP_SIGNED + int len; + unsigned char *p; + EC_KEY *eckey; + EC_GROUP *group; + + ERR_clear_error(); + + if ((eckey = EC_KEY_new()) != 0 + && (group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) != 0 + && (EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE), + EC_KEY_set_group(eckey, group)) + && EC_KEY_generate_key(eckey) + && (key = EVP_PKEY_new()) != 0 + && !EVP_PKEY_set1_EC_KEY(key, eckey)) { + EVP_PKEY_free(key); + key = 0; + } + if (group) + EC_GROUP_free(group); + if (eckey) + EC_KEY_free(eckey); +#endif + return (key); +} + +/* dane_init - initialize DANE parameters */ + +static void dane_init(void) { -#ifdef TLSEXT_MAXLEN_host_name /* DANE mandates client SNI. */ - static int avail = -1; - const EVP_MD *sha256md; - const EVP_MD *sha512md; static NAME_MASK ta_dgsts[] = { TLS_DANE_CC, TLS_DANE_ENABLE_CC, TLS_DANE_TAA, TLS_DANE_ENABLE_TAA, 0, }; - if (avail >= 0) - return (avail); - - sha256md = EVP_get_digestbyname(sha256); - sha512md = EVP_get_digestbyname(sha512); - - if (sha256md == 0 || sha512md == 0 - || RES_USE_DNSSEC == 0 || RES_USE_EDNS0 == 0) - return (avail = 0); - digest_mask = name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst, NAME_MASK_ANY_CASE | NAME_MASK_FATAL); - sha256len = EVP_MD_size(sha256md); - sha512len = EVP_MD_size(sha512md); + if ((sha256md = EVP_get_digestbyname(sha256)) != 0) + sha256len = EVP_MD_size(sha256md); + if ((sha512md = EVP_get_digestbyname(sha512)) != 0) + sha512len = EVP_MD_size(sha512md); + signmd = sha256md ? sha256md : EVP_sha1(); - return (avail = 1); + /* Don't report old news */ + ERR_clear_error(); + +#ifdef TRUST_ANCHOR_SUPPORT + if ((wrap_signed && (danekey = gencakey()) == 0) + || (serverAuth = OBJ_nid2obj(NID_server_auth)) == 0) { + msg_warn("cannot generate TA certificates, no DANE support"); + tls_print_errors(); + } +#endif + dane_initialized = 1; +} + +/* tls_dane_avail - check for availability of dane required digests */ + +int tls_dane_avail(void) +{ + if (!dane_initialized) + dane_init(); + +#ifdef DANE_TLSA_SUPPORT + return (sha256md && sha512md && serverAuth); #else - return (0); + return (0); #endif } @@ -357,32 +461,6 @@ static void dane_free(void *dane, void *unused_context) tls_dane_free((TLS_DANE *) dane); } -/* tlsa_sort - sort digests for a single certusage */ - -static void tlsa_sort(TLS_TLSA *tlsa) -{ - for (; tlsa; tlsa = tlsa->next) { - if (tlsa->pkeys) - argv_sort(tlsa->pkeys); - if (tlsa->certs) - argv_sort(tlsa->certs); - } -} - -/* tls_dane_final - finish by sorting into canonical order */ - -TLS_DANE *tls_dane_final(TLS_DANE *dane) -{ - - /* - * We only sort the trust anchors, see tls_serverid_digest(). - */ - if (dane->ta) - tlsa_sort(dane->ta); - dane->flags |= TLS_DANE_FLAG_FINAL; - return (dane); -} - /* dane_locate - list head address of TLSA sublist for given algorithm */ static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg) @@ -422,9 +500,6 @@ void tls_dane_split(TLS_DANE *dane, int certusage, int selector, TLS_TLSA *tlsa; ARGV **argvp; - if (dane->flags & TLS_DANE_FLAG_FINAL) - msg_panic("updating frozen TLS_DANE object"); - tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta; tlsa = *(tlsap = dane_locate(tlsap, mdalg)); argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ? @@ -457,9 +532,6 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector, TLS_TLSA *tlsa; ARGV **argvp; - if (dane->flags & TLS_DANE_FLAG_FINAL) - msg_panic("updating frozen TLS_DANE object"); - switch (certusage) { case DNS_TLSA_USAGE_CA_CONSTRAINT: case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION: @@ -490,6 +562,17 @@ static void dane_add(TLS_DANE *dane, int certusage, int selector, argv_add(*argvp, digest, ARGV_END); } +#ifdef DANE_TLSA_SUPPORT + +/* tlsa_rr_cmp - qsort TLSA rrs in case shuffled by name server */ + +static int tlsa_rr_cmp(DNS_RR *a, DNS_RR *b) +{ + if (a->data_len == b->data_len) + return (memcmp(a->data, b->data, a->data_len)); + return ((a->data_len > b->data_len) ? 1 : -1); +} + /* parse_tlsa_rrs - parse a validated TLSA RRset */ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) @@ -610,7 +693,6 @@ static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr) usage, selector, mtype); continue; } - /* Also unusable if public key is malformed */ if ((k = X509_get_pubkey(x)) == 0) { msg_warn("%s public key malformed in RR: " @@ -714,9 +796,11 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx) /* One more second to account for discrete time */ dane->expires = 1 + event_time() + rrs->ttl; - if (rrs->dnssec_valid) + if (rrs->dnssec_valid) { + /* Sort for deterministic digest in session cache lookup key */ + rrs = dns_rr_sort(rrs, tlsa_rr_cmp); parse_tlsa_rrs(dane, rrs); - else + } else dane->flags |= TLS_DANE_FLAG_NORRS; dns_rr_free(rrs); @@ -733,19 +817,22 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx) break; } - return ((void *) tls_dane_final(dane)); + return (void *) dane; } +#endif + /* tls_dane_resolve - cached map: (host, proto, port) -> TLS_DANE */ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, unsigned port) { static VSTRING *qname; - TLS_DANE *dane; + TLS_DANE *dane = 0; +#ifdef DANE_TLSA_SUPPORT if (!tls_dane_avail()) - return (0); + return (dane); if (!dane_cache) dane_cache = ctable_create(CACHE_SIZE, dane_lookup, dane_free, 0); @@ -761,6 +848,7 @@ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, return (0); ++dane->refs; +#endif return (dane); } @@ -768,6 +856,7 @@ TLS_DANE *tls_dane_resolve(const char *host, const char *proto, int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) { +#ifdef TRUST_ANCHOR_SUPPORT BIO *bp; char *name = 0; char *header = 0; @@ -775,11 +864,21 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) long len; int tacount; char *errtype = 0; /* if error: cert or pkey? */ + const char *mdalg; /* nop */ if (tafile == 0 || *tafile == 0) return (1); + if (!dane_initialized) + dane_init(); + + if (serverAuth == 0) { + msg_warn("trust-anchor files not supported"); + return (0); + } + mdalg = sha256md ? sha256 : "sha1"; + /* * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio, * calls PEM_read_bio() and then frees the bio. It is just as easy to @@ -807,8 +906,8 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) if (cert && (p - data) == len) { selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE; - digest = tls_data_fprint((char *) data, len, sha256); - dane_add(dane, usage, selector, sha256, digest); + digest = tls_data_fprint((char *) data, len, mdalg); + dane_add(dane, usage, selector, mdalg, digest); myfree(digest); ta_cert_insert(dane, cert); } else @@ -820,8 +919,8 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) if (pkey && (p - data) == len) { selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO; - digest = tls_data_fprint((char *) data, len, sha256); - dane_add(dane, usage, selector, sha256, digest); + digest = tls_data_fprint((char *) data, len, mdalg); + dane_add(dane, usage, selector, mdalg, digest); myfree(digest); ta_pkey_insert(dane, pkey); } else @@ -852,7 +951,444 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile) } /* Some other PEM read error */ tls_print_errors(); +#else + msg_warn("Trust anchor files not supported"); +#endif return (0); } -#endif +/* tls_dane_match - match cert against given list of TA or EE digests */ + +int tls_dane_match(TLS_SESS_STATE *TLScontext, int usage, + X509 *cert, int depth) +{ + const TLS_DANE *dane = TLScontext->dane; + TLS_TLSA *tlsa = (usage == TLS_DANE_EE) ? dane->ee : dane->ta; + const char *namaddr = TLScontext->namaddr; + const char *ustr = (usage == TLS_DANE_EE) ? "end entity" : "trust anchor"; + int mixed = (dane->flags & TLS_DANE_FLAG_MIXED); + int matched; + + for (matched = 0; tlsa && !matched; tlsa = tlsa->next) { + char **dgst; + ARGV *certs; + + if (tlsa->pkeys) { + char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg); + + for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst) + if (strcasecmp(pkey_dgst, *dgst) == 0) + matched = 1; + if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH) + && matched) + msg_info("%s: depth=%d matched %s public-key %s digest=%s", + namaddr, depth, ustr, tlsa->mdalg, pkey_dgst); + myfree(pkey_dgst); + } + + /* + * Backwards compatible "fingerprint" security level interface: + * + * Certificate digests and public key digests are interchangeable, each + * leaf certificate is matched via either the public key digest or + * full certificate digest when "mixed" is true. The combined set of + * digests is stored on the pkeys digest list and the certs list is + * empty. An attacker would need a 2nd-preimage (not just a + * collision) that is feasible across types (given cert digest == + * some key digest) while difficult within a type (e.g. given cert + * some other cert digest). No such attacks are know at this time, + * and it is expected that if any are found they would work within as + * well as across the cert/key data types. + */ + certs = mixed ? tlsa->pkeys : tlsa->certs; + if (certs != 0 && !matched) { + char *cert_dgst = tls_cert_fprint(cert, tlsa->mdalg); + + for (dgst = certs->argv; !matched && *dgst; ++dgst) + if (strcasecmp(cert_dgst, *dgst) == 0) + matched = 1; + if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH) + && matched) + msg_info("%s: depth=%d matched %s certificate %s digest %s", + namaddr, depth, ustr, tlsa->mdalg, cert_dgst); + myfree(cert_dgst); + } + } + + return (matched); +} + +/* add_ext - add simple extension (no config section references) */ + +static int add_ext(X509 *issuer, X509 *subject, int ext_nid, char *ext_val) +{ + X509V3_CTX v3ctx; + X509_EXTENSION *ext; + x509_extension_stack_t *exts; + + X509V3_set_ctx(&v3ctx, issuer, subject, 0, 0, 0); + if ((exts = subject->cert_info->extensions) == 0) + exts = subject->cert_info->extensions = sk_X509_EXTENSION_new_null(); + + if ((ext = X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val)) != 0 + && sk_X509_EXTENSION_push(exts, ext)) + return (1); + if (ext) + X509_EXTENSION_free(ext); + return (0); +} + +/* set_serial - set serial number to match akid or use subject's plus 1 */ + +static int set_serial(X509 *cert, AUTHORITY_KEYID *akid, X509 *subject) +{ + int ret = 0; + BIGNUM *bn; + + if (akid && akid->serial) + return (X509_set_serialNumber(cert, akid->serial)); + + /* + * Add one to subject's serial to avoid collisions between TA serial and + * serial of signing root. + */ + if ((bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(subject), 0)) != 0 + && BN_add_word(bn, 1) + && BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert))) + ret = 1; + + if (bn) + BN_free(bn); + return (ret); +} + +/* add_akid - add authority key identifier */ + +static int add_akid(X509 *cert, AUTHORITY_KEYID *akid) +{ + ASN1_STRING *id; + unsigned char c = 0; + int ret = 0; + + /* + * 0 will never be our subject keyid from a SHA-1 hash, but it could be + * our subject keyid if forced from child's akid. If so, set our + * authority keyid to 1. This way we are never self-signed, and thus + * exempt from any potential (off by default for now in OpenSSL) + * self-signature checks! + */ + id = (ASN1_STRING *) ((akid && akid->keyid) ? akid->keyid : 0); + if (id && M_ASN1_STRING_length(id) == 1 && *M_ASN1_STRING_data(id) == c) + c = 1; + + if ((akid = AUTHORITY_KEYID_new()) != 0 + && (akid->keyid = ASN1_OCTET_STRING_new()) != 0 + && M_ASN1_OCTET_STRING_set(akid->keyid, (void *) &c, 1) + && X509_add1_ext_i2d(cert, NID_authority_key_identifier, akid, 0, 0)) + ret = 1; + if (akid) + AUTHORITY_KEYID_free(akid); + return (ret); +} + +/* add_skid - add subject key identifier to match child's akid */ + +static int add_skid(X509 *cert, AUTHORITY_KEYID *akid) +{ + int ret; + + if (akid && akid->keyid) { + VSTRING *hexid = vstring_alloc(2 * EVP_MAX_MD_SIZE); + ASN1_STRING *id = (ASN1_STRING *) (akid->keyid); + + hex_encode(hexid, (char *) M_ASN1_STRING_data(id), + M_ASN1_STRING_length(id)); + ret = add_ext(0, cert, NID_subject_key_identifier, STR(hexid)); + vstring_free(hexid); + } else { + ret = add_ext(0, cert, NID_subject_key_identifier, "hash"); + } + return (ret); +} + +/* set_issuer - set issuer DN to match akid if specified */ + +static int set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid) +{ + + /* + * If subject's akid specifies an authority key identifer issuer name, we + * must use that. + */ + if (akid && akid->issuer) { + int i; + general_name_stack_t *gens = akid->issuer; + + for (i = 0; i < sk_GENERAL_NAME_num(gens); ++i) { + GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); + + if (gn->type == GEN_DIRNAME) + return (X509_set_issuer_name(cert, gn->d.dirn)); + } + } + return (X509_set_issuer_name(cert, X509_get_subject_name(cert))); +} + +/* grow_chain - add certificate to chain */ + +static void grow_chain(x509_stack_t **skptr, X509 *cert, ASN1_OBJECT *trust) +{ + if (!*skptr && (*skptr = sk_X509_new_null()) == 0) + msg_fatal("out of memory"); + if (cert) { + if (trust && !X509_add1_trust_object(cert, trust)) + msg_fatal("out of memory"); + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + if (!sk_X509_push(*skptr, cert)) + msg_fatal("out of memory"); + } +} + +/* wrap_key - wrap TA "key" as issuer of "subject" */ + +static int wrap_key(TLS_SESS_STATE *TLScontext, EVP_PKEY *key, X509 *subject, + int depth) +{ + int ret = 1; + X509 *cert = 0; + AUTHORITY_KEYID *akid; + X509_NAME *name = X509_get_issuer_name(subject); + + /* + * Record the depth of the intermediate wrapper certificate, logged in + * the verify callback, unlike the parent root CA. + */ + if (!key) + TLScontext->tadepth = depth; + else if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)) + msg_info("%s: depth=%d chain is trust-anchor signed", + TLScontext->namaddr, depth); + + /* + * If key is NULL generate a self-signed root CA, with key "danekey", + * otherwise an intermediate CA signed by above. + */ + if ((cert = X509_new()) == 0) + return (0); + + akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0); + + ERR_clear_error(); + + /* CA cert valid for +/- 30 days */ + if (!X509_set_version(cert, 2) + || !set_serial(cert, akid, subject) + || !X509_set_subject_name(cert, name) + || !set_issuer_name(cert, akid) + || !X509_gmtime_adj(X509_get_notBefore(cert), -30 * 86400L) + || !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L) + || !X509_set_pubkey(cert, key ? key : danekey) + || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE") + || (key && !add_akid(cert, akid)) + || !add_skid(cert, akid) + || (wrap_signed + && (!X509_sign(cert, danekey, signmd) + || (key && !wrap_key(TLScontext, 0, cert, depth + 1))))) { + msg_warn("error generating DANE wrapper certificate"); + tls_print_errors(); + ret = 0; + } + if (akid) + AUTHORITY_KEYID_free(akid); + if (ret) { + if (key && wrap_signed) + grow_chain(&TLScontext->untrusted, cert, 0); + else + grow_chain(&TLScontext->trusted, cert, serverAuth); + } + if (cert) + X509_free(cert); + return (ret); +} + +/* ta_signed - is certificate signed by a TLSA cert or pkey */ + +static int ta_signed(TLS_SESS_STATE *TLScontext, X509 *cert, int depth) +{ + const TLS_DANE *dane = TLScontext->dane; + EVP_PKEY *pk; + TLS_PKEYS *k; + TLS_CERTS *x; + int done = 0; + + /* + * First check whether issued and signed by a TA cert, this is cheaper + * than the bare-public key checks below, since we can determine whether + * the candidate TA certificate issued the certificate to be checked + * first (name comparisons), before we bother with signature checks + * (public key operations). + */ + for (x = dane->certs; !done && x; x = x->next) { + if (X509_check_issued(x->cert, cert) == X509_V_OK) { + if ((pk = X509_get_pubkey(x->cert)) == 0) + continue; + /* Check signature, since some other TA may work if not this. */ + if (X509_verify(cert, pk) > 0) + done = wrap_key(TLScontext, pk, cert, depth); + EVP_PKEY_free(pk); + } + } + + /* + * With bare TA public keys, we can't check whether the trust chain is + * issued by the key, but we can determine whether it is signed by the + * key, so we go with that. + * + * Ideally, the corresponding certificate was presented in the chain, and we + * matched it by its public key digest one level up. This code is here + * to handle adverse conditions imposed by sloppy administrators of + * receiving systems with poorly constructed chains. + * + * We'd like to optimize out keys that should not match when the cert's + * authority key id does not match the key id of this key computed via + * the RFC keyid algorithm (SHA-1 digest of public key bit-string sans + * ASN1 tag and length thus also excluding the unused bits field that is + * logically part of the length). However, some CAs have a non-standard + * authority keyid, so we lose. Too bad. + */ + for (k = dane->pkeys; !done && k; k = k->next) + if (X509_verify(cert, k->pkey) > 0) + done = wrap_key(TLScontext, k->pkey, cert, depth); + + return (done); +} + +/* set_trust - configure for DANE validation */ + +static void set_trust(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx) +{ + int n; + int i; + int depth = 0; + EVP_PKEY *takey; + X509 *ca; + X509 *cert = ctx->cert; /* XXX: Accessor? */ + x509_stack_t *in = ctx->untrusted; /* XXX: Accessor? */ + + /* shallow copy */ + if ((in = sk_X509_dup(in)) == 0) + msg_fatal("out of memory"); + + /* + * At each iteration we consume the issuer of the current cert. This + * reduces the length of the "in" chain by one. If no issuer is found, + * we are done. We also stop when a certificate matches a TA in the + * peer's TLSA RRset. + * + * Caller ensures that the initial certificate is not self-signed. + */ + for (n = sk_X509_num(in); n > 0; --n, ++depth) { + for (i = 0; i < n; ++i) + if (X509_check_issued(sk_X509_value(in, i), cert) == X509_V_OK) + break; + + /* + * Final untrusted element with no issuer in the peer's chain, it may + * however be signed by a pkey or cert obtained via a TLSA RR. + */ + if (i == n) + break; + + /* Peer's chain contains an issuer ca. */ + ca = sk_X509_delete(in, i); + + /* Is it a trust anchor? */ + if (tls_dane_match(TLScontext, TLS_DANE_TA, ca, depth + 1)) { + if ((takey = X509_get_pubkey(ca)) != 0 + && wrap_key(TLScontext, takey, cert, depth)) + EVP_PKEY_free(takey); + cert = 0; + break; + } + /* Add untrusted ca. */ + grow_chain(&TLScontext->untrusted, ca, 0); + + /* Final untrusted self-signed element? */ + if (X509_check_issued(ca, ca) == X509_V_OK) { + cert = 0; + break; + } + /* Restart with issuer as subject */ + cert = ca; + } + + /* + * When the loop exits, if "cert" is set, it is not self-signed and has + * no issuer in the chain, we check for a possible signature via a DNS + * obtained TA cert or public key. Otherwise, we found no TAs and no + * issuer, so set an empty list of TAs. + */ + if (!cert || !ta_signed(TLScontext, cert, depth)) { + /* Create empty trust list if null, else NOP */ + grow_chain(&TLScontext->trusted, 0, 0); + } + /* shallow free */ + if (in) + sk_X509_free(in); +} + +/* dane_cb - wrap chain verification for DANE */ + +static int dane_cb(X509_STORE_CTX *ctx, void *app_ctx) +{ + const char *myname = "dane_cb"; + TLS_SESS_STATE *TLScontext = (TLS_SESS_STATE *) app_ctx; + X509 *cert = ctx->cert; /* XXX: accessor? */ + + /* + * Degenerate case: depth 0 self-signed cert. + * + * XXX: Should we suppress name checks, ... when the leaf certificate is a + * TA. After all they could sign any name they want. However, this + * requires a bit of additional code. For now we allow depth 0 TAs, but + * then the peer name has to match. + */ + if (X509_check_issued(cert, cert) == X509_V_OK) { + + /* + * Empty untrusted chain, could be NULL, but then ABI check less + * reliable, we may zero some other field, ... + */ + grow_chain(&TLScontext->untrusted, 0, 0); + if (tls_dane_match(TLScontext, TLS_DANE_TA, cert, 0)) + grow_chain(&TLScontext->trusted, cert, serverAuth); + else + grow_chain(&TLScontext->trusted, 0, 0); + } else { + set_trust(TLScontext, ctx); + } + + /* + * Check that setting the untrusted chain updates the expected structure + * member at the expected offset. + */ + X509_STORE_CTX_trusted_stack(ctx, TLScontext->trusted); + X509_STORE_CTX_set_chain(ctx, TLScontext->untrusted); + if (ctx->untrusted != TLScontext->untrusted) + msg_panic("%s: OpenSSL ABI change", myname); + + return X509_verify_cert(ctx); +} + +/* tls_dane_set_callback - set or clear verification wrapper callback */ + +void tls_dane_set_callback(SSL_CTX *ctx, TLS_SESS_STATE *TLScontext) +{ + if (!serverAuth || !TLS_DANE_HASTA(TLScontext->dane)) + SSL_CTX_set_cert_verify_callback(ctx, 0, 0); + else + SSL_CTX_set_cert_verify_callback(ctx, dane_cb, (void *) TLScontext); +} + +#endif /* USE_TLS */ + diff --git a/postfix/src/tls/tls_fprint.c b/postfix/src/tls/tls_fprint.c index 8f731e6ea..40746ea55 100644 --- a/postfix/src/tls/tls_fprint.c +++ b/postfix/src/tls/tls_fprint.c @@ -53,7 +53,7 @@ /* and the caller must eventually free it with myfree(). /* /* tls_serverid_digest() suffixes props->serverid computed by the SMTP -/* client with ":" plus a digest of additional parameters +/* client with "&" plus a digest of additional parameters /* needed to ensure that re-used sessions are more likely to /* be reused and that they will satisfy all protocol and /* security requirements. @@ -217,8 +217,6 @@ char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask, * matching data, which is checked separately each time. So we exclude * the EE part of the DANE structure from the serverid digest. * - * If this changes, also update tls_dane_final() in tls_dane.c. - * * If the security level is "dane", we send SNI information to the peer. * This may cause it to respond with a non-default certificate. Since * certificates for sessions with no or different SNI data may not match, @@ -256,7 +254,7 @@ char *tls_serverid_digest(const TLS_CLIENT_START_PROPS *props, long protomask, */ result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len); vstring_strcpy(result, props->serverid); - VSTRING_ADDCH(result, ':'); + VSTRING_ADDCH(result, '&'); for (i = 0; i < md_len; i++) { VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]); VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0x0f)]); diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index fdbac74b5..da4730520 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -439,8 +439,7 @@ static const char *tls_exclude_missing(SSL_CTX *ctx, VSTRING *buf) const char *myname = "tls_exclude_missing"; static ARGV *exclude; /* Cached */ SSL *s = 0; - - STACK_OF(SSL_CIPHER) * ciphers; + ssl_cipher_stack_t *ciphers; SSL_CIPHER *c; const cipher_probe_t *probe; int alg_bits; @@ -789,13 +788,13 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr) TLScontext->log_mask = log_mask; TLScontext->namaddr = lowercase(mystrdup(namaddr)); TLScontext->mdalg = 0; /* Alias for props->mdalg */ - TLScontext->dane = 0; /* Alias for client - * props->dane */ - TLScontext->trustdepth = -1; - TLScontext->chaindepth = -1; + TLScontext->dane = 0; /* Alias for props->dane */ TLScontext->errordepth = -1; + TLScontext->tadepth = -1; TLScontext->errorcode = X509_V_OK; TLScontext->errorcert = 0; + TLScontext->untrusted = 0; + TLScontext->trusted = 0; return (TLScontext); } @@ -828,6 +827,10 @@ void tls_free_context(TLS_SESS_STATE *TLScontext) myfree(TLScontext->peer_pkey_fprint); if (TLScontext->errorcert) X509_free(TLScontext->errorcert); + if (TLScontext->untrusted) + sk_X509_pop_free(TLScontext->untrusted, X509_free); + if (TLScontext->trusted) + sk_X509_pop_free(TLScontext->trusted, X509_free); myfree((char *) TLScontext); } @@ -934,7 +937,7 @@ long tls_bug_bits(void) * breaking on all 0.9.8[ab] systems that have zlib support enabled. */ if (lib_version >= 0x00908000L && lib_version <= 0x0090802fL) { - STACK_OF(SSL_COMP) * comp_methods; + ssl_comp_stack_t *comp_methods = SSL_COMP_get_compression_methods(); comp_methods = SSL_COMP_get_compression_methods(); if (comp_methods != 0 && sk_SSL_COMP_num(comp_methods) > 0) @@ -1128,6 +1131,24 @@ int tls_validate_digest(const char *dgst) const EVP_MD *md_alg; unsigned int md_len; + /* + * Register SHA-2 digests, if implemented and not already registered. + * Improves interoperability with clients and servers that prematurely + * deploy SHA-2 certificates. Also facilitates DANE and TA support. + */ +#if defined(LN_sha256) && defined(NID_sha256) && !defined(OPENSSL_NO_SHA256) + if (!EVP_get_digestbyname(LN_sha224)) + EVP_add_digest(EVP_sha224()); + if (!EVP_get_digestbyname(LN_sha256)) + EVP_add_digest(EVP_sha256()); +#endif +#if defined(LN_sha512) && defined(NID_sha512) && !defined(OPENSSL_NO_SHA512) + if (!EVP_get_digestbyname(LN_sha384)) + EVP_add_digest(EVP_sha384()); + if (!EVP_get_digestbyname(LN_sha512)) + EVP_add_digest(EVP_sha512()); +#endif + /* * If the administrator specifies an unsupported digest algorithm, fail * now, rather than in the middle of a TLS handshake. diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 37cfb8975..d31c772c1 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -383,6 +383,9 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) /* * Protocol work-arounds, OpenSSL version dependent. */ +#ifdef SSL_OP_NO_TICKET + off |= SSL_OP_NO_TICKET; +#endif off |= tls_bug_bits(); SSL_CTX_set_options(server_ctx, off); diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c index b1fe441ce..cbaae83cc 100644 --- a/postfix/src/tls/tls_verify.c +++ b/postfix/src/tls/tls_verify.c @@ -7,12 +7,6 @@ /* #define TLS_INTERNAL /* #include /* -/* int tls_cert_match(TLSContext, usage, cert, depth) -/* TLS_SESS_STATE *TLScontext; -/* int usage; -/* X509 *cert; -/* int depth; -/* /* int tls_verify_certificate_callback(ok, ctx) /* int ok; /* X509_STORE_CTX *ctx; @@ -32,12 +26,6 @@ /* const GENERAL_NAME *gn; /* TLS_SESS_STATE *TLScontext; /* DESCRIPTION -/* tls_cert_match() matches the full and/or public key digest of -/* "cert" against each candidate digest in TLScontext->dane. If usage -/* is TLS_DANE_EE, the match is against end-entity digests, otherwise -/* it is against trust-anchor digests. Returns true if a match is found, -/* false otherwise. -/* /* tls_verify_certificate_callback() is called several times (directly /* or indirectly) from crypto/x509/x509_vfy.c. It collects errors /* and trust information at each element of the trust chain. @@ -79,12 +67,6 @@ /* .IP gn /* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName /* to be decoded and checked for validity. -/* .IP usage -/* Trust anchor (TLS_DANE_TA) or end-entity (TLS_DANE_EE) digests? -/* .IP cert -/* Certificate from peer trust chain (CA or leaf server). -/* .IP depth -/* The certificate depth for logging. /* .IP peercert /* Server or client X.509 certificate. /* .IP TLScontext @@ -144,8 +126,7 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth, X509 *errorcert, int errorcode) { /* No news is good news */ - if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth < depth) || - (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)) + if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth) return; /* @@ -160,171 +141,7 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth, CRYPTO_add(&errorcert->references, 1, CRYPTO_LOCK_X509); TLScontext->errorcert = errorcert; TLScontext->errorcode = errorcode; - - /* - * Maintain an invariant, at most one of errordepth and trustdepth is - * non-negative at any given time. - */ TLScontext->errordepth = depth; - TLScontext->trustdepth = -1; -} - -/* update_trust_state - safely stash away trust state */ - -static void update_trust_state(TLS_SESS_STATE *TLScontext, int depth) -{ - /* No news is bad news */ - if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth <= depth) - || (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)) - return; - - /* - * Maintain an invariant, at most one of errordepth and trustdepth is - * non-negative at any given time. - */ - TLScontext->trustdepth = depth; - TLScontext->errordepth = -1; -} - -/* tls_cert_match - match cert against given list of TA or EE digests */ - -int tls_cert_match(TLS_SESS_STATE *TLScontext, int usage, X509 *cert, int depth) -{ - const TLS_DANE *dane = TLScontext->dane; - TLS_TLSA *tlsa = (usage == TLS_DANE_EE) ? dane->ee : dane->ta; - const char *namaddr = TLScontext->namaddr; - const char *ustr = (usage == TLS_DANE_EE) ? "end entity" : "trust anchor"; - int mixed = (dane->flags & TLS_DANE_FLAG_MIXED); - int matched; - - for (matched = 0; tlsa && !matched; tlsa = tlsa->next) { - char **dgst; - ARGV *certs; - - if (tlsa->pkeys) { - char *pkey_dgst = tls_pkey_fprint(cert, tlsa->mdalg); - - for (dgst = tlsa->pkeys->argv; !matched && *dgst; ++dgst) - if (strcasecmp(pkey_dgst, *dgst) == 0) - matched = 1; - if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)) - msg_info("%s: depth=%d matched=%d %s public-key %s digest=%s", - namaddr, depth, matched, ustr, tlsa->mdalg, pkey_dgst); - myfree(pkey_dgst); - } - certs = mixed ? tlsa->pkeys : tlsa->certs; - if (certs != 0 && !matched) { - char *cert_dgst = tls_cert_fprint(cert, tlsa->mdalg); - - for (dgst = certs->argv; !matched && *dgst; ++dgst) - if (strcasecmp(cert_dgst, *dgst) == 0) - matched = 1; - if (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH)) - msg_info("%s: depth=%d matched=%d %s certificate %s digest %s", - namaddr, depth, matched, ustr, tlsa->mdalg, cert_dgst); - myfree(cert_dgst); - } - } - - return (matched); -} - -/* ta_match - match cert against out-of-band TA keys or digests */ - -static int ta_match(TLS_SESS_STATE *TLScontext, X509_STORE_CTX *ctx, - X509 *cert, int depth, int expired) -{ - const TLS_DANE *dane = TLScontext->dane; - int matched = tls_cert_match(TLScontext, TLS_DANE_TA, cert, depth); - - /* - * If we are the TA, the first trusted certificate is one level below! As - * a degenerate case a self-signed TA at depth 0 is also treated as a TA - * validated trust chain, (even if the certificate is expired). - * - * Note: OpenSSL will flag an error when the chain contains just one - * certificate that is not self-issued. - */ - if (matched) { - if (--depth < 0) - depth = 0; - update_trust_state(TLScontext, depth); - return (1); - } - - /* - * If expired, no need to check for a trust-anchor signature. The TA - * itself is matched by its digest, so we're at best looking at some - * other expired certificate issued by the TA, which we don't accept. - */ - if (expired) - return (0); - - /* - * Compute the index of the topmost chain certificate; it may need to be - * verified via one of our out-of-band trust-anchors. Since we're here, - * the chain contains at least one certificate. - * - * Optimization: if the top is self-issued, we don't need to try to check - * whether it is signed by any ancestor TAs. If it is trusted, it will - * be matched by its fingerprint. - */ - if (TLScontext->trustdepth < 0 && TLScontext->chaindepth < 0) { - STACK_OF(X509) *chain = X509_STORE_CTX_get_chain(ctx); - int i = sk_X509_num(chain) - 1; - X509 *top = sk_X509_value(chain, i); - - if (X509_check_issued(top, top) == X509_V_OK) - TLScontext->chaindepth = i + 1; - else - TLScontext->chaindepth = i; - } - - /* - * Last resort, check whether signed by out-of-band TA public key. - * - * Only the top certificate of the server chain needs this logic, since any - * certs below are signed by their parent, which we checked against the - * TA list more cheaply. Do this at most once (by incrementing the depth - * when we're done). - */ - if (depth == TLScontext->chaindepth) { - TLS_PKEYS *k; - TLS_CERTS *x; - - /* - * First check whether issued and signed by a TA cert, this is - * cheaper than the bare-public key checks below, since we can - * determine whether the candidate TA certificate issued the - * certificate to be checked first (name comparisons), before we - * bother with signature checks (public key operations). - */ - for (x = dane->certs; !matched && x; x = x->next) { - if (X509_check_issued(x->cert, cert) == X509_V_OK) { - EVP_PKEY *pk = X509_get_pubkey(x->cert); - - matched = pk && X509_verify(cert, pk) > 0; - EVP_PKEY_free(pk); - } - } - - /* - * With bare TA public keys, we can't check whether the trust chain - * is issued by the key, but we can determine whether it is signed by - * the key, so we go with that. Ideally, the corresponding - * certificate was presented in the chain, and we matched it by its - * public key digest one level up. This code is here to handle - * adverse conditions imposed by sloppy administrators of receiving - * systems with poorly constructed chains. - */ - for (k = dane->pkeys; !matched && k; k = k->next) - matched = X509_verify(cert, k->pkey) > 0; - - if (matched) - update_trust_state(TLScontext, depth); - ++TLScontext->chaindepth; - } - return (matched); } /* tls_verify_certificate_callback - verify peer certificate info */ @@ -344,6 +161,11 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) err = X509_STORE_CTX_get_error(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); + + /* Don't log the internal root CA unless there's an unexpected error. */ + if (ok && TLScontext->tadepth > 0 && depth > TLScontext->tadepth) + return (1); /* * Certificate chain depth limit violations are mis-reported by the @@ -359,7 +181,6 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) * present at this depth. This disambiguates trust chain truncation from * an incomplete trust chain. */ - depth = X509_STORE_CTX_get_error_depth(ctx); max_depth = SSL_get_verify_depth(con) - 1; /* @@ -367,101 +188,14 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) * rather we allow the TLS handshake to continue, but mark the session as * unverified. The application is responsible for closing any sessions * with unverified credentials. - * - * When we have an explicit list of trusted CA fingerprints, record the - * smallest depth at which we find a trusted certificate. If this below - * the smallest error depth we win and the chain is trusted. Otherwise, - * the chain is untrusted. We make this decision *each* time we are - * called with depth == 0 (yes we may be called more than once). */ if (max_depth >= 0 && depth > max_depth) { - update_error_state(TLScontext, depth, cert, - X509_V_ERR_CERT_CHAIN_TOO_LONG); - return (1); - } - - /* - * Per RFC 5280 and its upstream ITU documents, a trust anchor is just a - * public key, no more no less, and thus certificates bearing the - * trust-anchor public key are just public keys in X.509v3 garb. Any - * meaning attached to their expiration, ... is simply local policy. - * - * We don't punish server administrators for including an expired optional - * TA certificate in their chain. Had they left it out, and provided us - * instead with only the TA public-key via a "2 1 0" TLSA record, there'd - * be no TA certificate from which to learn the expiration dates. - * - * Therefore, in the interests of consistent behavior, we only enforce - * expiration dates BELOW the TA signature. When we find an expired - * certificate, we only check whether it is a TA, and not whether it is - * signed by a TA. - * - * Other than allowing TA certificate expiration, the only errors we allow - * are failure to chain to a trusted root. Our TA set includes - * out-of-band data not available to the X509_STORE_CTX. - * - * More than one of the allowed errors may be reported at a given depth, - * trap all instances, but run the matching code at most once. If the - * current cert is ok, we have a trusted ancestor, and we're not verbose, - * don't bother with matching. - */ - if (cert != 0 - && (ok == 0 - || TLScontext->trustdepth < 0 - || (TLScontext->log_mask & (TLS_LOG_VERBOSE | TLS_LOG_CERTMATCH))) - && TLS_DANE_HASTA(TLScontext->dane) - && (TLScontext->trustdepth == -1 || depth <= TLScontext->trustdepth) - && (TLScontext->errordepth == -1 || depth < TLScontext->errordepth)) { - int expired = 0; /* or not yet valid */ - - switch (ok ? X509_V_OK : err) { - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_CERT_HAS_EXPIRED: - expired = 1; - /* FALLTHROUGH */ - case X509_V_OK: - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - case X509_V_ERR_CERT_UNTRUSTED: - if ((!expired && depth == TLScontext->trustdepth) - || ta_match(TLScontext, ctx, cert, depth, expired)) - ok = 1; - break; - } + X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_CERT_CHAIN_TOO_LONG); + ok = 0; } if (ok == 0) update_error_state(TLScontext, depth, cert, err); - /* - * Perhaps the chain is verified, or perhaps we'll get called again, - * either way the best we know is that if trust depth is below error - * depth we win and otherwise we lose. Set the error state accordingly. - * - * If we are given explicit TA match list, we must match one of them at a - * non-negative depth below any errors, otherwise we just need no errors. - */ - if (depth == 0) { - ok = 0; - if (TLScontext->trustdepth < 0 && TLS_DANE_HASTA(TLScontext->dane)) { - /* Required Policy or DANE certs not present */ - if (TLScontext->errordepth < 0) { - - /* - * For lack of a better choice log the trust problem against - * the leaf cert when PKI says yes, but local policy or DANE - * says no. Logging a root cert as untrusted would far more - * likely confuse users! - */ - update_error_state(TLScontext, depth, cert, - X509_V_ERR_CERT_UNTRUSTED); - } - } else if (TLScontext->errordepth < 0) { - /* No PKI trust errors, or only above a good policy or DANE CA. */ - ok = 1; - } - X509_STORE_CTX_set_error(ctx, ok ? X509_V_OK : TLScontext->errorcode); - } if (TLScontext->log_mask & TLS_LOG_VERBOSE) { if (cert) X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));