mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-30 05:38:06 +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_REMOTE
|
||||||
-TANVIL_REQ_TABLE
|
-TANVIL_REQ_TABLE
|
||||||
-TARGV
|
-TARGV
|
||||||
|
-TASN1_INTEGER
|
||||||
|
-TASN1_OBJECT
|
||||||
-TATTR_CLNT
|
-TATTR_CLNT
|
||||||
-TATTR_TABLE
|
-TATTR_TABLE
|
||||||
|
-TAUTHORITY_KEYID
|
||||||
-TAUTO_CLNT
|
-TAUTO_CLNT
|
||||||
-TBH_TABLE
|
-TBH_TABLE
|
||||||
-TBINATTR
|
-TBINATTR
|
||||||
@ -116,6 +119,8 @@
|
|||||||
-TDSN_BUF
|
-TDSN_BUF
|
||||||
-TDSN_SPLIT
|
-TDSN_SPLIT
|
||||||
-TDSN_STAT
|
-TDSN_STAT
|
||||||
|
-TEC_KEY
|
||||||
|
-TEC_GROUP
|
||||||
-TEDIT_FILE
|
-TEDIT_FILE
|
||||||
-TEVENT_MASK
|
-TEVENT_MASK
|
||||||
-TEVP_PKEY
|
-TEVP_PKEY
|
||||||
@ -324,8 +329,10 @@
|
|||||||
-TWATCHDOG
|
-TWATCHDOG
|
||||||
-TWATCH_FD
|
-TWATCH_FD
|
||||||
-TX509
|
-TX509
|
||||||
|
-TX509_EXTENSION
|
||||||
-TX509_NAME
|
-TX509_NAME
|
||||||
-TX509_STORE_CTX
|
-TX509_STORE_CTX
|
||||||
|
-TX509V3_CTX
|
||||||
-TXSASL_CLIENT
|
-TXSASL_CLIENT
|
||||||
-TXSASL_CLIENT_CREATE_ARGS
|
-TXSASL_CLIENT_CREATE_ARGS
|
||||||
-TXSASL_CLIENT_IMPL
|
-TXSASL_CLIENT_IMPL
|
||||||
@ -342,6 +349,7 @@
|
|||||||
-TXSASL_SERVER_IMPL
|
-TXSASL_SERVER_IMPL
|
||||||
-TXSASL_SERVER_IMPL_INFO
|
-TXSASL_SERVER_IMPL_INFO
|
||||||
-Tcipher_probe_t
|
-Tcipher_probe_t
|
||||||
|
-Tgeneral_name_stack_t
|
||||||
-Toff_t
|
-Toff_t
|
||||||
-Tregex_t
|
-Tregex_t
|
||||||
-Tregmatch_t
|
-Tregmatch_t
|
||||||
@ -349,5 +357,9 @@
|
|||||||
-Tsasl_secret_t
|
-Tsasl_secret_t
|
||||||
-Tsfsistat
|
-Tsfsistat
|
||||||
-Tsize_t
|
-Tsize_t
|
||||||
|
-Tssl_cipher_stack_t
|
||||||
|
-Tssl_comp_stack_t
|
||||||
-Tssize_t
|
-Tssize_t
|
||||||
-Ttime_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,
|
posttls-finger/posttls-finger.c, smtp/smtp_tls_policy.c,
|
||||||
tls/tls_dane.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
|
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||||
* patchlevel; they change the release date only.
|
* patchlevel; they change the release date only.
|
||||||
*/
|
*/
|
||||||
#define MAIL_RELEASE_DATE "20130613"
|
#define MAIL_RELEASE_DATE "20130616"
|
||||||
#define MAIL_VERSION_NUMBER "2.11"
|
#define MAIL_VERSION_NUMBER "2.11"
|
||||||
|
|
||||||
#ifdef SNAPSHOT
|
#ifdef SNAPSHOT
|
||||||
|
@ -575,39 +575,59 @@ static RESPONSE *ehlo(STATE *state)
|
|||||||
|
|
||||||
#ifdef USE_TLS
|
#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)
|
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) {
|
if (sk != 0) {
|
||||||
int i;
|
BIO_printf(state->tls_bio, "\n---\nCertificate chain\n");
|
||||||
|
print_stack(state, sk, 0);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#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 */
|
/* starttls - SMTP STARTTLS handshake */
|
||||||
@ -615,7 +635,6 @@ static void print_trust_info(STATE *state)
|
|||||||
static int starttls(STATE *state)
|
static int starttls(STATE *state)
|
||||||
{
|
{
|
||||||
VSTRING *cipher_exclusions;
|
VSTRING *cipher_exclusions;
|
||||||
VSTRING *serverid;
|
|
||||||
int except;
|
int except;
|
||||||
RESPONSE *resp;
|
RESPONSE *resp;
|
||||||
VSTREAM *stream = state->stream;
|
VSTREAM *stream = state->stream;
|
||||||
@ -662,9 +681,6 @@ static int starttls(STATE *state)
|
|||||||
else
|
else
|
||||||
ADD_EXCLUDE(cipher_exclusions, "eNULL");
|
ADD_EXCLUDE(cipher_exclusions, "eNULL");
|
||||||
|
|
||||||
serverid = vstring_alloc(10);
|
|
||||||
vstring_sprintf(serverid, "%s:%s", var_procname, state->addrport);
|
|
||||||
|
|
||||||
state->tls_context =
|
state->tls_context =
|
||||||
TLS_CLIENT_START(&tls_props,
|
TLS_CLIENT_START(&tls_props,
|
||||||
ctx = state->tls_ctx,
|
ctx = state->tls_ctx,
|
||||||
@ -674,7 +690,7 @@ static int starttls(STATE *state)
|
|||||||
nexthop = state->nexthop,
|
nexthop = state->nexthop,
|
||||||
host = state->hostname,
|
host = state->hostname,
|
||||||
namaddr = state->namaddrport,
|
namaddr = state->namaddrport,
|
||||||
serverid = STR(serverid),
|
serverid = state->addrport,
|
||||||
helo = state->helo ? state->helo : "",
|
helo = state->helo ? state->helo : "",
|
||||||
protocols = state->protocols,
|
protocols = state->protocols,
|
||||||
cipher_grade = state->grade,
|
cipher_grade = state->grade,
|
||||||
@ -684,7 +700,6 @@ static int starttls(STATE *state)
|
|||||||
mdalg = state->mdalg,
|
mdalg = state->mdalg,
|
||||||
dane = state->ddane ? state->ddane : state->dane);
|
dane = state->ddane ? state->ddane : state->dane);
|
||||||
vstring_free(cipher_exclusions);
|
vstring_free(cipher_exclusions);
|
||||||
vstring_free(serverid);
|
|
||||||
if (state->helo) {
|
if (state->helo) {
|
||||||
myfree(state->helo);
|
myfree(state->helo);
|
||||||
state->helo = 0;
|
state->helo = 0;
|
||||||
@ -1466,10 +1481,12 @@ static void cleanup(STATE *state)
|
|||||||
static void usage(void)
|
static void usage(void)
|
||||||
{
|
{
|
||||||
#ifdef USE_TLS
|
#ifdef USE_TLS
|
||||||
fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s destination [match ...]\n",
|
fprintf(stderr, "usage: %s %s \\\n\t%s \\\n\t%s \\\n\t%s"
|
||||||
var_procname, "[-acCStTv] [-d mdalg] [-g grade] [-p protocols] [-F CAfile.pem]",
|
" destination [match ...]\n", var_procname,
|
||||||
"[-h host_lookup] [-l level] [-L logopts] [-m count]",
|
"[-acCSv] [-t conn_tmout] [-T cmd_tmout] [-L logopts]",
|
||||||
"[-o name=value] [-P CApath/] [-r delay]");
|
"[-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
|
#else
|
||||||
fprintf(stderr, "usage: %s [-acStTv] [-h host_lookup] [-o name=value] destination\n",
|
fprintf(stderr, "usage: %s [-acStTv] [-h host_lookup] [-o name=value] destination\n",
|
||||||
var_procname);
|
var_procname);
|
||||||
@ -1713,7 +1730,6 @@ static void parse_match(STATE *state, int argc, char *argv[])
|
|||||||
while (*argv)
|
while (*argv)
|
||||||
tls_dane_split((TLS_DANE *) state->dane, TLS_DANE_EE, TLS_DANE_PKEY,
|
tls_dane_split((TLS_DANE *) state->dane, TLS_DANE_EE, TLS_DANE_PKEY,
|
||||||
state->mdalg, *argv++, "");
|
state->mdalg, *argv++, "");
|
||||||
tls_dane_final((TLS_DANE *) state->dane);
|
|
||||||
break;
|
break;
|
||||||
case TLS_LEV_DANE:
|
case TLS_LEV_DANE:
|
||||||
state->match = argv_alloc(2);
|
state->match = argv_alloc(2);
|
||||||
@ -1745,7 +1761,6 @@ static void parse_tas(STATE *state)
|
|||||||
}
|
}
|
||||||
if (*file)
|
if (*file)
|
||||||
msg_fatal("Failed to load trust anchor file: %s", *file);
|
msg_fatal("Failed to load trust anchor file: %s", *file);
|
||||||
tls_dane_final((TLS_DANE *) state->dane);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -780,9 +780,10 @@ static int smtp_start_tls(SMTP_STATE *state)
|
|||||||
* SSL session lookup key lengths.
|
* SSL session lookup key lengths.
|
||||||
*/
|
*/
|
||||||
serverid = vstring_alloc(10);
|
serverid = vstring_alloc(10);
|
||||||
smtp_key_prefix(serverid, ":", state->iterator, SMTP_KEY_FLAG_SERVICE
|
smtp_key_prefix(serverid, "&", state->iterator, SMTP_KEY_FLAG_SERVICE
|
||||||
| SMTP_KEY_FLAG_ADDR
|
| SMTP_KEY_FLAG_NEXTHOP /* With port */
|
||||||
| SMTP_KEY_FLAG_PORT);
|
| SMTP_KEY_FLAG_HOSTNAME
|
||||||
|
| SMTP_KEY_FLAG_ADDR);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As of Postfix 2.5, tls_client_start() tries hard to always complete
|
* 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);
|
return ((void *) tls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tls_dane_final(tls->dane);
|
|
||||||
break;
|
break;
|
||||||
case TLS_LEV_VERIFY:
|
case TLS_LEV_VERIFY:
|
||||||
case TLS_LEV_SECURE:
|
case TLS_LEV_SECURE:
|
||||||
@ -584,13 +583,10 @@ static void *policy_create(const char *unused_key, void *context)
|
|||||||
if (*var_smtp_tls_tafile) {
|
if (*var_smtp_tls_tafile) {
|
||||||
if (tls->dane == 0)
|
if (tls->dane == 0)
|
||||||
tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
|
tls->dane = tls_dane_alloc(TLS_DANE_FLAG_MIXED);
|
||||||
if (!TLS_DANE_HASTA(tls->dane)) {
|
if (!TLS_DANE_HASTA(tls->dane)
|
||||||
if (load_tas(tls->dane, var_smtp_tls_tafile))
|
&& !load_tas(tls->dane, var_smtp_tls_tafile)) {
|
||||||
tls_dane_final(tls->dane);
|
MARK_INVALID(tls->why, &tls->level);
|
||||||
else {
|
return ((void *) tls);
|
||||||
MARK_INVALID(tls->why, &tls->level);
|
|
||||||
return ((void *) tls);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -73,6 +73,13 @@ extern const NAME_CODE tls_level_table[];
|
|||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <openssl/ssl.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)
|
#if (OPENSSL_VERSION_NUMBER < 0x00090700f)
|
||||||
#error "need OpenSSL version 0.9.7 or later"
|
#error "need OpenSSL version 0.9.7 or later"
|
||||||
#endif
|
#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_PKEY 1 /* Match the public key digest */
|
||||||
|
|
||||||
#define TLS_DANE_FLAG_MIXED (1<<0) /* Combined pkeys and certs */
|
#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<<1) /* Nothing found in DNS */
|
||||||
#define TLS_DANE_FLAG_NORRS (1<<2) /* Nothing found in DNS */
|
#define TLS_DANE_FLAG_EMPTY (1<<2) /* Nothing usable found in DNS */
|
||||||
#define TLS_DANE_FLAG_EMPTY (1<<3) /* Nothing usable found in DNS */
|
#define TLS_DANE_FLAG_ERROR (1<<3) /* TLSA record lookup error */
|
||||||
#define TLS_DANE_FLAG_ERROR (1<<4) /* TLSA record lookup error */
|
|
||||||
|
|
||||||
#define tls_dane_unusable(dane) ((dane)->flags & TLS_DANE_FLAG_EMPTY)
|
#define tls_dane_unusable(dane) ((dane)->flags & TLS_DANE_FLAG_EMPTY)
|
||||||
#define tls_dane_notfound(dane) ((dane)->flags & TLS_DANE_FLAG_NORRS)
|
#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 TLS_DANE *tls_dane_alloc(int);
|
||||||
extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *,
|
extern void tls_dane_split(TLS_DANE *, int, int, const char *, const char *,
|
||||||
const char *);
|
const char *);
|
||||||
extern TLS_DANE *tls_dane_final(TLS_DANE *);
|
|
||||||
extern void tls_dane_free(TLS_DANE *);
|
extern void tls_dane_free(TLS_DANE *);
|
||||||
extern TLS_DANE *tls_dane_resolve(const char *, const char *, unsigned);
|
extern TLS_DANE *tls_dane_resolve(const char *, const char *, unsigned);
|
||||||
extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
|
extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
|
||||||
@ -202,11 +207,12 @@ typedef struct {
|
|||||||
VSTREAM *stream; /* Blocking-mode SMTP session */
|
VSTREAM *stream; /* Blocking-mode SMTP session */
|
||||||
/* RFC 6698 DANE trust input and verification state */
|
/* RFC 6698 DANE trust input and verification state */
|
||||||
const TLS_DANE *dane; /* DANE TLSA digests */
|
const TLS_DANE *dane; /* DANE TLSA digests */
|
||||||
int trustdepth; /* Chain depth of trusted cert */
|
|
||||||
int errordepth; /* Chain depth of error 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 */
|
int errorcode; /* First error at error depth */
|
||||||
X509 *errorcert; /* Error certificate closest to leaf */
|
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;
|
} 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_peer_CN(X509 *, const TLS_SESS_STATE *);
|
||||||
extern char *tls_issuer_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 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 int tls_verify_certificate_callback(int, X509_STORE_CTX *);
|
||||||
extern void tls_log_verify_error(TLS_SESS_STATE *);
|
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
|
* 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 */
|
/* match_servername - match servername against pattern */
|
||||||
|
|
||||||
static int match_servername(const char *certid,
|
static int match_servername(const char *certid,
|
||||||
const TLS_CLIENT_START_PROPS *props)
|
const TLS_CLIENT_START_PROPS *props)
|
||||||
{
|
{
|
||||||
const ARGV *cmatch_argv;
|
const ARGV *cmatch_argv;
|
||||||
const char *nexthop = props->nexthop;
|
const char *nexthop = props->nexthop;
|
||||||
@ -570,8 +570,7 @@ static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
|
|||||||
int verbose;
|
int verbose;
|
||||||
const char *dnsname;
|
const char *dnsname;
|
||||||
const GENERAL_NAME *gn;
|
const GENERAL_NAME *gn;
|
||||||
|
general_name_stack_t *gens;
|
||||||
STACK_OF(GENERAL_NAME) * gens;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On exit both peer_CN and issuer_CN should be set.
|
* 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().
|
* untrusted in verify_extract_name().
|
||||||
*/
|
*/
|
||||||
if (TLS_DANE_HASEE(props->dane)
|
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 |=
|
TLScontext->peer_status |=
|
||||||
TLS_CERT_FLAG_TRUSTED | TLS_CERT_FLAG_MATCHED;
|
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)
|
if (log_mask & TLS_LOG_TLSPKTS)
|
||||||
BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
|
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
|
* Start TLS negotiations. This process is a black box that invokes our
|
||||||
* call-backs for certificate verification.
|
* call-backs for certificate verification.
|
||||||
|
@ -31,8 +31,15 @@
|
|||||||
/* TLS_DANE *dane;
|
/* TLS_DANE *dane;
|
||||||
/* const char *tafile;
|
/* const char *tafile;
|
||||||
/*
|
/*
|
||||||
/* TLS_DANE *tls_dane_final(dane)
|
/* int tls_dane_match(TLSContext, usage, cert, depth)
|
||||||
/* TLS_DANE *dane;
|
/* 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)
|
/* TLS_DANE *tls_dane_resolve(host, proto, port)
|
||||||
/* const char *host;
|
/* const char *host;
|
||||||
@ -68,17 +75,25 @@
|
|||||||
/* delimiters and stores the results with the requested "certusage"
|
/* delimiters and stores the results with the requested "certusage"
|
||||||
/* and "selector". This is an incremental interface, that builds a
|
/* and "selector". This is an incremental interface, that builds a
|
||||||
/* TLS_DANE structure outside the cache by manually adding entries.
|
/* 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
|
/* tls_dane_load_trustfile() imports trust-anchor certificates and
|
||||||
/* public keys from a file (rather than DNS TLSA records).
|
/* public keys from a file (rather than DNS TLSA records).
|
||||||
/*
|
/*
|
||||||
/* tls_dane_final() completes the construction of a TLS_DANE structure,
|
/* tls_dane_match() matches the full and/or public key digest of
|
||||||
/* obtained via tls_dane_alloc() and populated via tls_dane_split() or
|
/* "cert" against each candidate digest in TLScontext->dane. If usage
|
||||||
/* tls_dane_load_trustfile(). After tls_dane_final() is called, the
|
/* is TLS_DANE_EE, the match is against end-entity digests, otherwise
|
||||||
/* structure must not be modified. The return value is the input
|
/* it is against trust-anchor digests. Returns true if a match is found,
|
||||||
/* argument.
|
/* 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
|
/* tls_dane_resolve() maps a (host, protocol, port) triple to a
|
||||||
/* a corresponding TLS_DANE policy structure found in the DNS. The port
|
/* a corresponding TLS_DANE policy structure found in the DNS. The port
|
||||||
@ -109,6 +124,17 @@
|
|||||||
/* The TCP port in network byte order.
|
/* The TCP port in network byte order.
|
||||||
/* .IP flags
|
/* .IP flags
|
||||||
/* Only one flag is part of the public interface at this time:
|
/* 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
|
/* .RS
|
||||||
/* .IP TLS_DANE_FLAG_MIXED
|
/* .IP TLS_DANE_FLAG_MIXED
|
||||||
/* Don't distinguish between certificate and public-key digests.
|
/* Don't distinguish between certificate and public-key digests.
|
||||||
@ -161,6 +187,7 @@
|
|||||||
#include <events.h> /* event_time() */
|
#include <events.h> /* event_time() */
|
||||||
#include <timecmp.h>
|
#include <timecmp.h>
|
||||||
#include <ctable.h>
|
#include <ctable.h>
|
||||||
|
#include <hex_code.h>
|
||||||
|
|
||||||
#define STR(x) vstring_str(x)
|
#define STR(x) vstring_str(x)
|
||||||
|
|
||||||
@ -179,13 +206,45 @@
|
|||||||
|
|
||||||
/* Application-specific. */
|
/* 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 *sha256 = "sha256";
|
||||||
static const char *sha512 = "sha512";
|
static const EVP_MD *sha256md;
|
||||||
static int sha256len;
|
static int sha256len;
|
||||||
|
|
||||||
|
static const char *sha512 = "sha512";
|
||||||
|
static const EVP_MD *sha512md;
|
||||||
static int sha512len;
|
static int sha512len;
|
||||||
static int dane_verbose;
|
|
||||||
static int digest_mask;
|
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_CC (1<<0) /* ca-constraint digests OK */
|
||||||
#define TLS_DANE_ENABLE_TAA (1<<1) /* trust-anchor-assertion 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
|
#define CACHE_SIZE 20
|
||||||
static CTABLE *dane_cache;
|
static CTABLE *dane_cache;
|
||||||
|
|
||||||
|
static int dane_initialized;
|
||||||
|
static int dane_verbose;
|
||||||
|
|
||||||
/* tls_dane_verbose - enable/disable verbose logging */
|
/* tls_dane_verbose - enable/disable verbose logging */
|
||||||
|
|
||||||
void tls_dane_verbose(int on)
|
void tls_dane_verbose(int on)
|
||||||
@ -206,40 +268,82 @@ void tls_dane_verbose(int on)
|
|||||||
dane_verbose = 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[] = {
|
static NAME_MASK ta_dgsts[] = {
|
||||||
TLS_DANE_CC, TLS_DANE_ENABLE_CC,
|
TLS_DANE_CC, TLS_DANE_ENABLE_CC,
|
||||||
TLS_DANE_TAA, TLS_DANE_ENABLE_TAA,
|
TLS_DANE_TAA, TLS_DANE_ENABLE_TAA,
|
||||||
0,
|
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 =
|
digest_mask =
|
||||||
name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst,
|
name_mask_opt(VAR_TLS_DANE_TA_DGST, ta_dgsts, var_tls_dane_ta_dgst,
|
||||||
NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
|
NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
|
||||||
|
|
||||||
sha256len = EVP_MD_size(sha256md);
|
if ((sha256md = EVP_get_digestbyname(sha256)) != 0)
|
||||||
sha512len = EVP_MD_size(sha512md);
|
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
|
#else
|
||||||
return (0);
|
return (0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,32 +461,6 @@ static void dane_free(void *dane, void *unused_context)
|
|||||||
tls_dane_free((TLS_DANE *) dane);
|
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 */
|
/* dane_locate - list head address of TLSA sublist for given algorithm */
|
||||||
|
|
||||||
static TLS_TLSA **dane_locate(TLS_TLSA **tlsap, const char *mdalg)
|
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;
|
TLS_TLSA *tlsa;
|
||||||
ARGV **argvp;
|
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;
|
tlsap = (certusage == TLS_DANE_EE) ? &dane->ee : &dane->ta;
|
||||||
tlsa = *(tlsap = dane_locate(tlsap, mdalg));
|
tlsa = *(tlsap = dane_locate(tlsap, mdalg));
|
||||||
argvp = ((dane->flags & TLS_DANE_FLAG_MIXED) || selector == TLS_DANE_PKEY) ?
|
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;
|
TLS_TLSA *tlsa;
|
||||||
ARGV **argvp;
|
ARGV **argvp;
|
||||||
|
|
||||||
if (dane->flags & TLS_DANE_FLAG_FINAL)
|
|
||||||
msg_panic("updating frozen TLS_DANE object");
|
|
||||||
|
|
||||||
switch (certusage) {
|
switch (certusage) {
|
||||||
case DNS_TLSA_USAGE_CA_CONSTRAINT:
|
case DNS_TLSA_USAGE_CA_CONSTRAINT:
|
||||||
case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
|
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);
|
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 */
|
/* parse_tlsa_rrs - parse a validated TLSA RRset */
|
||||||
|
|
||||||
static void parse_tlsa_rrs(TLS_DANE *dane, DNS_RR *rr)
|
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);
|
usage, selector, mtype);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Also unusable if public key is malformed */
|
/* Also unusable if public key is malformed */
|
||||||
if ((k = X509_get_pubkey(x)) == 0) {
|
if ((k = X509_get_pubkey(x)) == 0) {
|
||||||
msg_warn("%s public key malformed in RR: "
|
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 */
|
/* One more second to account for discrete time */
|
||||||
dane->expires = 1 + event_time() + rrs->ttl;
|
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);
|
parse_tlsa_rrs(dane, rrs);
|
||||||
else
|
} else
|
||||||
dane->flags |= TLS_DANE_FLAG_NORRS;
|
dane->flags |= TLS_DANE_FLAG_NORRS;
|
||||||
|
|
||||||
dns_rr_free(rrs);
|
dns_rr_free(rrs);
|
||||||
@ -733,19 +817,22 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((void *) tls_dane_final(dane));
|
return (void *) dane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* tls_dane_resolve - cached map: (host, proto, port) -> TLS_DANE */
|
/* tls_dane_resolve - cached map: (host, proto, port) -> TLS_DANE */
|
||||||
|
|
||||||
TLS_DANE *tls_dane_resolve(const char *host, const char *proto,
|
TLS_DANE *tls_dane_resolve(const char *host, const char *proto,
|
||||||
unsigned port)
|
unsigned port)
|
||||||
{
|
{
|
||||||
static VSTRING *qname;
|
static VSTRING *qname;
|
||||||
TLS_DANE *dane;
|
TLS_DANE *dane = 0;
|
||||||
|
|
||||||
|
#ifdef DANE_TLSA_SUPPORT
|
||||||
if (!tls_dane_avail())
|
if (!tls_dane_avail())
|
||||||
return (0);
|
return (dane);
|
||||||
|
|
||||||
if (!dane_cache)
|
if (!dane_cache)
|
||||||
dane_cache = ctable_create(CACHE_SIZE, dane_lookup, dane_free, 0);
|
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);
|
return (0);
|
||||||
|
|
||||||
++dane->refs;
|
++dane->refs;
|
||||||
|
#endif
|
||||||
return (dane);
|
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)
|
int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
|
||||||
{
|
{
|
||||||
|
#ifdef TRUST_ANCHOR_SUPPORT
|
||||||
BIO *bp;
|
BIO *bp;
|
||||||
char *name = 0;
|
char *name = 0;
|
||||||
char *header = 0;
|
char *header = 0;
|
||||||
@ -775,11 +864,21 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
|
|||||||
long len;
|
long len;
|
||||||
int tacount;
|
int tacount;
|
||||||
char *errtype = 0; /* if error: cert or pkey? */
|
char *errtype = 0; /* if error: cert or pkey? */
|
||||||
|
const char *mdalg;
|
||||||
|
|
||||||
/* nop */
|
/* nop */
|
||||||
if (tafile == 0 || *tafile == 0)
|
if (tafile == 0 || *tafile == 0)
|
||||||
return (1);
|
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,
|
* 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
|
* 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) {
|
if (cert && (p - data) == len) {
|
||||||
selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;
|
selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;
|
||||||
digest = tls_data_fprint((char *) data, len, sha256);
|
digest = tls_data_fprint((char *) data, len, mdalg);
|
||||||
dane_add(dane, usage, selector, sha256, digest);
|
dane_add(dane, usage, selector, mdalg, digest);
|
||||||
myfree(digest);
|
myfree(digest);
|
||||||
ta_cert_insert(dane, cert);
|
ta_cert_insert(dane, cert);
|
||||||
} else
|
} else
|
||||||
@ -820,8 +919,8 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
|
|||||||
|
|
||||||
if (pkey && (p - data) == len) {
|
if (pkey && (p - data) == len) {
|
||||||
selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;
|
selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;
|
||||||
digest = tls_data_fprint((char *) data, len, sha256);
|
digest = tls_data_fprint((char *) data, len, mdalg);
|
||||||
dane_add(dane, usage, selector, sha256, digest);
|
dane_add(dane, usage, selector, mdalg, digest);
|
||||||
myfree(digest);
|
myfree(digest);
|
||||||
ta_pkey_insert(dane, pkey);
|
ta_pkey_insert(dane, pkey);
|
||||||
} else
|
} else
|
||||||
@ -852,7 +951,444 @@ int tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
|
|||||||
}
|
}
|
||||||
/* Some other PEM read error */
|
/* Some other PEM read error */
|
||||||
tls_print_errors();
|
tls_print_errors();
|
||||||
|
#else
|
||||||
|
msg_warn("Trust anchor files not supported");
|
||||||
|
#endif
|
||||||
return (0);
|
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().
|
/* and the caller must eventually free it with myfree().
|
||||||
/*
|
/*
|
||||||
/* tls_serverid_digest() suffixes props->serverid computed by the SMTP
|
/* 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
|
/* needed to ensure that re-used sessions are more likely to
|
||||||
/* be reused and that they will satisfy all protocol and
|
/* be reused and that they will satisfy all protocol and
|
||||||
/* security requirements.
|
/* 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
|
* matching data, which is checked separately each time. So we exclude
|
||||||
* the EE part of the DANE structure from the serverid digest.
|
* 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.
|
* 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
|
* This may cause it to respond with a non-default certificate. Since
|
||||||
* certificates for sessions with no or different SNI data may not match,
|
* 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);
|
result = vstring_alloc(strlen(props->serverid) + 1 + 2 * md_len);
|
||||||
vstring_strcpy(result, props->serverid);
|
vstring_strcpy(result, props->serverid);
|
||||||
VSTRING_ADDCH(result, ':');
|
VSTRING_ADDCH(result, '&');
|
||||||
for (i = 0; i < md_len; i++) {
|
for (i = 0; i < md_len; i++) {
|
||||||
VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]);
|
VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0xf0) >> 4U]);
|
||||||
VSTRING_ADDCH(result, hexcodes[(md_buf[i] & 0x0f)]);
|
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";
|
const char *myname = "tls_exclude_missing";
|
||||||
static ARGV *exclude; /* Cached */
|
static ARGV *exclude; /* Cached */
|
||||||
SSL *s = 0;
|
SSL *s = 0;
|
||||||
|
ssl_cipher_stack_t *ciphers;
|
||||||
STACK_OF(SSL_CIPHER) * ciphers;
|
|
||||||
SSL_CIPHER *c;
|
SSL_CIPHER *c;
|
||||||
const cipher_probe_t *probe;
|
const cipher_probe_t *probe;
|
||||||
int alg_bits;
|
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->log_mask = log_mask;
|
||||||
TLScontext->namaddr = lowercase(mystrdup(namaddr));
|
TLScontext->namaddr = lowercase(mystrdup(namaddr));
|
||||||
TLScontext->mdalg = 0; /* Alias for props->mdalg */
|
TLScontext->mdalg = 0; /* Alias for props->mdalg */
|
||||||
TLScontext->dane = 0; /* Alias for client
|
TLScontext->dane = 0; /* Alias for props->dane */
|
||||||
* props->dane */
|
|
||||||
TLScontext->trustdepth = -1;
|
|
||||||
TLScontext->chaindepth = -1;
|
|
||||||
TLScontext->errordepth = -1;
|
TLScontext->errordepth = -1;
|
||||||
|
TLScontext->tadepth = -1;
|
||||||
TLScontext->errorcode = X509_V_OK;
|
TLScontext->errorcode = X509_V_OK;
|
||||||
TLScontext->errorcert = 0;
|
TLScontext->errorcert = 0;
|
||||||
|
TLScontext->untrusted = 0;
|
||||||
|
TLScontext->trusted = 0;
|
||||||
|
|
||||||
return (TLScontext);
|
return (TLScontext);
|
||||||
}
|
}
|
||||||
@ -828,6 +827,10 @@ void tls_free_context(TLS_SESS_STATE *TLScontext)
|
|||||||
myfree(TLScontext->peer_pkey_fprint);
|
myfree(TLScontext->peer_pkey_fprint);
|
||||||
if (TLScontext->errorcert)
|
if (TLScontext->errorcert)
|
||||||
X509_free(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);
|
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.
|
* breaking on all 0.9.8[ab] systems that have zlib support enabled.
|
||||||
*/
|
*/
|
||||||
if (lib_version >= 0x00908000L && lib_version <= 0x0090802fL) {
|
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();
|
comp_methods = SSL_COMP_get_compression_methods();
|
||||||
if (comp_methods != 0 && sk_SSL_COMP_num(comp_methods) > 0)
|
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;
|
const EVP_MD *md_alg;
|
||||||
unsigned int md_len;
|
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
|
* If the administrator specifies an unsupported digest algorithm, fail
|
||||||
* now, rather than in the middle of a TLS handshake.
|
* 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.
|
* Protocol work-arounds, OpenSSL version dependent.
|
||||||
*/
|
*/
|
||||||
|
#ifdef SSL_OP_NO_TICKET
|
||||||
|
off |= SSL_OP_NO_TICKET;
|
||||||
|
#endif
|
||||||
off |= tls_bug_bits();
|
off |= tls_bug_bits();
|
||||||
SSL_CTX_set_options(server_ctx, off);
|
SSL_CTX_set_options(server_ctx, off);
|
||||||
|
|
||||||
|
@ -7,12 +7,6 @@
|
|||||||
/* #define TLS_INTERNAL
|
/* #define TLS_INTERNAL
|
||||||
/* #include <tls.h>
|
/* #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 tls_verify_certificate_callback(ok, ctx)
|
||||||
/* int ok;
|
/* int ok;
|
||||||
/* X509_STORE_CTX *ctx;
|
/* X509_STORE_CTX *ctx;
|
||||||
@ -32,12 +26,6 @@
|
|||||||
/* const GENERAL_NAME *gn;
|
/* const GENERAL_NAME *gn;
|
||||||
/* TLS_SESS_STATE *TLScontext;
|
/* TLS_SESS_STATE *TLScontext;
|
||||||
/* DESCRIPTION
|
/* 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
|
/* tls_verify_certificate_callback() is called several times (directly
|
||||||
/* or indirectly) from crypto/x509/x509_vfy.c. It collects errors
|
/* or indirectly) from crypto/x509/x509_vfy.c. It collects errors
|
||||||
/* and trust information at each element of the trust chain.
|
/* and trust information at each element of the trust chain.
|
||||||
@ -79,12 +67,6 @@
|
|||||||
/* .IP gn
|
/* .IP gn
|
||||||
/* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
|
/* An OpenSSL GENERAL_NAME structure holding a DNS subjectAltName
|
||||||
/* to be decoded and checked for validity.
|
/* 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
|
/* .IP peercert
|
||||||
/* Server or client X.509 certificate.
|
/* Server or client X.509 certificate.
|
||||||
/* .IP TLScontext
|
/* .IP TLScontext
|
||||||
@ -144,8 +126,7 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
|
|||||||
X509 *errorcert, int errorcode)
|
X509 *errorcert, int errorcode)
|
||||||
{
|
{
|
||||||
/* No news is good news */
|
/* No news is good news */
|
||||||
if ((TLScontext->trustdepth >= 0 && TLScontext->trustdepth < depth) ||
|
if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
|
||||||
(TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -160,171 +141,7 @@ static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
|
|||||||
CRYPTO_add(&errorcert->references, 1, CRYPTO_LOCK_X509);
|
CRYPTO_add(&errorcert->references, 1, CRYPTO_LOCK_X509);
|
||||||
TLScontext->errorcert = errorcert;
|
TLScontext->errorcert = errorcert;
|
||||||
TLScontext->errorcode = errorcode;
|
TLScontext->errorcode = errorcode;
|
||||||
|
|
||||||
/*
|
|
||||||
* Maintain an invariant, at most one of errordepth and trustdepth is
|
|
||||||
* non-negative at any given time.
|
|
||||||
*/
|
|
||||||
TLScontext->errordepth = depth;
|
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 */
|
/* 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);
|
err = X509_STORE_CTX_get_error(ctx);
|
||||||
con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
con = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||||
TLScontext = SSL_get_ex_data(con, TLScontext_index);
|
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
|
* 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
|
* present at this depth. This disambiguates trust chain truncation from
|
||||||
* an incomplete trust chain.
|
* an incomplete trust chain.
|
||||||
*/
|
*/
|
||||||
depth = X509_STORE_CTX_get_error_depth(ctx);
|
|
||||||
max_depth = SSL_get_verify_depth(con) - 1;
|
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
|
* rather we allow the TLS handshake to continue, but mark the session as
|
||||||
* unverified. The application is responsible for closing any sessions
|
* unverified. The application is responsible for closing any sessions
|
||||||
* with unverified credentials.
|
* 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) {
|
if (max_depth >= 0 && depth > max_depth) {
|
||||||
update_error_state(TLScontext, depth, cert,
|
X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_CERT_CHAIN_TOO_LONG);
|
||||||
X509_V_ERR_CERT_CHAIN_TOO_LONG);
|
ok = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (ok == 0)
|
if (ok == 0)
|
||||||
update_error_state(TLScontext, depth, cert, err);
|
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 (TLScontext->log_mask & TLS_LOG_VERBOSE) {
|
||||||
if (cert)
|
if (cert)
|
||||||
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
|
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user