mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-29 13:18:12 +00:00
postfix-2.11-20130616
This commit is contained in:
parent
b7660498e2
commit
2da14e7e2c
12
postfix/.indent.pro
vendored
12
postfix/.indent.pro
vendored
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -73,6 +73,13 @@ extern const NAME_CODE tls_level_table[];
|
||||
#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
|
||||
@ -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
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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 <events.h> /* event_time() */
|
||||
#include <timecmp.h>
|
||||
#include <ctable.h>
|
||||
#include <hex_code.h>
|
||||
|
||||
#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 */
|
||||
|
||||
|
@ -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)]);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -7,12 +7,6 @@
|
||||
/* #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;
|
||||
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user