-TANVIL_REMOTE
-TANVIL_REQ_TABLE
-TARGV
+-TASN1_INTEGER
+-TASN1_OBJECT
-TATTR_CLNT
-TATTR_TABLE
+-TAUTHORITY_KEYID
-TAUTO_CLNT
-TBH_TABLE
-TBINATTR
-TDSN_BUF
-TDSN_SPLIT
-TDSN_STAT
+-TEC_KEY
+-TEC_GROUP
-TEDIT_FILE
-TEVENT_MASK
-TEVP_PKEY
-TWATCHDOG
-TWATCH_FD
-TX509
+-TX509_EXTENSION
-TX509_NAME
-TX509_STORE_CTX
+-TX509V3_CTX
-TXSASL_CLIENT
-TXSASL_CLIENT_CREATE_ARGS
-TXSASL_CLIENT_IMPL
-TXSASL_SERVER_IMPL
-TXSASL_SERVER_IMPL_INFO
-Tcipher_probe_t
+-Tgeneral_name_stack_t
-Toff_t
-Tregex_t
-Tregmatch_t
-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
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.
+
* 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
#ifdef USE_TLS
-static void print_trust_info(STATE *state)
+static void print_stack(STATE *state, x509_stack_t *sk, int trustout)
{
- STACK_OF(X509) *sk = SSL_get_peer_cert_chain(state->tls_context->con);
-
- if (sk != NULL) {
- int i;
+ 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;
+ 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);
+ 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);
+ 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)
+{
+ x509_stack_t *sk = SSL_get_peer_cert_chain(state->tls_context->con);
+
+ 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 */
static int starttls(STATE *state)
{
VSTRING *cipher_exclusions;
- VSTRING *serverid;
int except;
RESPONSE *resp;
VSTREAM *stream = state->stream;
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,
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,
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;
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);
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);
}
if (*file)
msg_fatal("Failed to load trust anchor file: %s", *file);
- tls_dane_final((TLS_DANE *) state->dane);
break;
}
#endif
* 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
return ((void *) tls);
}
}
- tls_dane_final(tls->dane);
break;
case TLS_LEV_VERIFY:
case TLS_LEV_SECURE:
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;
#include <openssl/rand.h>
#include <openssl/ssl.h>
+ /* 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
#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)
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 *);
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;
/*
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
*/
/* 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;
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.
* 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;
}
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.
/* 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;
/* 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
/* 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.
#include <events.h> /* event_time() */
#include <timecmp.h>
#include <ctable.h>
+#include <hex_code.h>
#define STR(x) vstring_str(x)
/* 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 */
#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)
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();
+
+ /* 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();
- return (avail = 1);
+#ifdef DANE_TLSA_SUPPORT
+ return (sha256md && sha512md && serverAuth);
#else
- return (0);
+ return (0);
#endif
}
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)
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) ?
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:
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)
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: "
/* 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);
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);
return (0);
++dane->refs;
+#endif
return (dane);
}
int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
{
+#ifdef TRUST_ANCHOR_SUPPORT
BIO *bp;
char *name = 0;
char *header = 0;
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
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
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
}
/* 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 */
+
/* 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.
* 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,
*/
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)]);
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;
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);
}
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);
}
* 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)
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.
/*
* 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);
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
-/* 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;
/* 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.
/* .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
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;
/*
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 */
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
* 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;
/*
* 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));