ssl/ssl_cert.c: DANE update.

This commit is contained in:
Andy Polyakov 2014-02-21 12:12:25 +01:00
parent 7743be3aac
commit 6b3b6beaa1
5 changed files with 249 additions and 68 deletions

View File

@ -129,12 +129,14 @@ unsigned char *SSL_get_tlsa_record_byname (const char *name,int port,int type)
if ((ret = OPENSSL_malloc(dlen)) == NULL) break;
for (data=ret, i=0; tlsa->data[i]; i++) {
*(unsigned int *)data = dlen = (unsigned int)tlsa->len[i];
data += sizeof(unsigned int);
dlen = (unsigned int)tlsa->len[i];
memcpy(data,&dlen,sizeof(dlen));
data += sizeof(dlen);
memcpy(data,tlsa->data[i],dlen);
data += dlen;
}
*(unsigned int *)data = 0; /* trailing zero */
dlen = 0;
memcpy(data,&dlen,sizeof(dlen)); /* trailing zero */
} while (0);
p_ub_resolve_free.f(tlsa);
}

View File

@ -1652,10 +1652,6 @@ struct ssl_st
unsigned char* alpn_client_proto_list;
unsigned alpn_client_proto_list_len;
#endif /* OPENSSL_NO_TLSEXT */
#ifndef OPENSSL_NO_DANE
unsigned char *tlsa_record;
int tlsa_witness;
#endif
};
#endif

View File

@ -133,10 +133,30 @@
#include "ssl_locl.h"
int SSL_get_ex_data_X509_STORE_CTX_idx(void)
{
static volatile int ssl_x509_store_ctx_idx= -1;
int got_write_lock = 0;
{
static volatile int ssl_x509_store_ctx_idx= -1;
int got_write_lock = 0;
if (((size_t)&ssl_x509_store_ctx_idx&(sizeof(ssl_x509_store_ctx_idx)-1))
==0) /* check alignment, practically always true */
{
int ret;
if ((ret=ssl_x509_store_ctx_idx) < 0)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
if ((ret=ssl_x509_store_ctx_idx) < 0)
{
ret=ssl_x509_store_ctx_idx=X509_STORE_CTX_get_ex_new_index(
0,"SSL for verify callback",NULL,NULL,NULL);
}
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
}
return ret;
}
else /* commonly eliminated */
{
CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
if (ssl_x509_store_ctx_idx < 0)
@ -159,6 +179,7 @@ int SSL_get_ex_data_X509_STORE_CTX_idx(void)
return ssl_x509_store_ctx_idx;
}
}
void ssl_cert_set_default_md(CERT *cert)
{
@ -733,20 +754,98 @@ int ssl_set_peer_cert_type(SESS_CERT *sc,int type)
}
#ifndef OPENSSL_NO_DANE
static void tlsa_free(void *parent,void *ptr,CRYPTO_EX_DATA *ad,int idx,long argl,void *argp)
{
TLSA_EX_DATA *ex = ptr;
if (ex!=NULL)
{
if (ex->tlsa_record!=NULL && ex->tlsa_record!=(void *)-1)
OPENSSL_free(ex->tlsa_record);
OPENSSL_free(ex);
}
}
int SSL_get_TLSA_ex_data_idx(void)
{
static volatile int ssl_tlsa_idx= -1;
int got_write_lock = 0;
if (((size_t)&ssl_tlsa_idx&(sizeof(ssl_tlsa_idx)-1))
==0) /* check alignment, practically always true */
{
int ret;
if ((ret=ssl_tlsa_idx) < 0)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
if ((ret=ssl_tlsa_idx) < 0)
{
ret=ssl_tlsa_idx=SSL_get_ex_new_index(
0,"per-SSL TLSA",NULL,NULL,tlsa_free);
}
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
}
return ret;
}
else /* commonly eliminated */
{
CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
if (ssl_tlsa_idx < 0)
{
CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
got_write_lock = 1;
if (ssl_tlsa_idx < 0)
{
ssl_tlsa_idx=SSL_get_ex_new_index(
0,"pre-SSL TLSA",NULL,NULL,tlsa_free);
}
}
if (got_write_lock)
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
else
CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
return ssl_tlsa_idx;
}
}
TLSA_EX_DATA *SSL_get_TLSA_ex_data(SSL *ssl)
{
int idx = SSL_get_TLSA_ex_data_idx();
TLSA_EX_DATA *ex;
if ((ex=SSL_get_ex_data(ssl,idx)) == NULL)
{
ex = OPENSSL_malloc(sizeof(TLSA_EX_DATA));
ex->tlsa_record = NULL;
ex->tlsa_witness = -1;
SSL_set_ex_data(ssl,idx,ex);
}
return ex;
}
/*
* return value:
* -1: format or digest error
* 0: match
* 1: no match
*/
int tlsa_cmp(const X509 *cert, const unsigned char *tlsa_record, unsigned int reclen)
{
static int tlsa_cmp(const X509 *cert, const unsigned char *tlsa_record, unsigned int reclen)
{
const EVP_MD *md;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int len, selector, matching_type;
int ret;
if (reclen<3) return -1;
if (reclen<3 || tlsa_record[0]>3) return -1;
selector = tlsa_record[1];
matching_type = tlsa_record[2];
@ -788,29 +887,46 @@ int tlsa_cmp(const X509 *cert, const unsigned char *tlsa_record, unsigned int re
default:
return -1;
}
}
}
int dane_verify_callback(int ok, X509_STORE_CTX *ctx)
{
static int dane_verify_callback(int ok, X509_STORE_CTX *ctx)
{
SSL *s = X509_STORE_CTX_get_ex_data(ctx,SSL_get_ex_data_X509_STORE_CTX_idx());
int depth=X509_STORE_CTX_get_error_depth(ctx);
X509 *cert = sk_X509_value(ctx->chain,depth);
unsigned int reclen, certificate_usage, witness_usage=0x100;
const unsigned char *tlsa_record = s->tlsa_record;
int tlsa_ret = -1;
TLSA_EX_DATA *ex;
const unsigned char *tlsa_record;
int tlsa_ret=-1, mask=1;
if (s->verify_callback) ok = s->verify_callback(ok,ctx);
if (tlsa_record == NULL) return ok;
if (tlsa_record == (void*)-1) {
ctx->error = X509_V_ERR_INVALID_CA; /* temporary code? */
return 0;
if ((ex=SSL_get_ex_data(s, SSL_get_TLSA_ex_data_idx())) == NULL ||
(tlsa_record=ex->tlsa_record) == NULL ||
(tlsa_record==(void *)-1 && (ok=0,ctx->error=X509_V_ERR_INVALID_CA)) || /* temporary code? */
/*
* X509_verify_cert initially starts throwing ok=0 upon
* failure to build certificate chain. As all certificate
* usages except for 3 require verifiable chain, ok=0 at
* non-zero depth is fatal. More specifically ok=0 at zero
* depth is allowed only for usage 3. Special note about
* usage 2. The chain is supposed to be filled by
* dane_get_issuer, or once again we should tolerate ok=0
* only in usage 3 case.
*/
(!ok && depth!=0)) {
if (s->verify_callback) return s->verify_callback(ok,ctx);
else return ok;
}
while ((reclen = *(unsigned int *)tlsa_record)) {
tlsa_record += sizeof(unsigned int);
while (1) {
unsigned int reclen, certificate_usage;
memcpy(&reclen,tlsa_record,sizeof(reclen));
if (reclen==0) break;
tlsa_record += sizeof(reclen);
if (!(ex->tlsa_mask&mask)) { /* not matched yet */
/*
* tlsa_record[0] Certificate Usage field
* tlsa_record[1] Selector field
@ -822,46 +938,41 @@ int dane_verify_callback(int ok, X509_STORE_CTX *ctx)
if (depth==0 || certificate_usage==0 || certificate_usage==2) {
tlsa_ret = tlsa_cmp(cert,tlsa_record,reclen);
if (tlsa_ret==0) {
s->tlsa_witness = depth<<8|certificate_usage;
ex->tlsa_witness = depth<<8|certificate_usage;
ex->tlsa_mask |= mask;
break;
}
else if (tlsa_ret==-1)
s->tlsa_witness = -1; /* something phishy? */
else if (tlsa_ret==-1) {
ex->tlsa_witness = -1; /* something phishy? */
ex->tlsa_mask |= mask;
}
}
tlsa_record += reclen;
}
tlsa_record += reclen;
mask <<= 1;
}
if (depth==0) {
switch (s->tlsa_witness&0xff) { /* witnessed usage */
case 0: /* CA constraint */
if (s->tlsa_witness<0 && ctx->error==X509_V_OK)
ctx->error = X509_V_ERR_INVALID_CA;
return 0;
case 1: /* service certificate constraint */
if (tlsa_ret!=0 && ctx->error==X509_V_OK)
ctx->error = X509_V_ERR_CERT_UNTRUSTED;
return 0;
case 2: /* trust anchor assertion */
if ((s->tlsa_witness>>8)>0 && ctx->error==X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
ctx->error = X509_V_OK;
break;
case 3: /* domain-issued certificate */
if (tlsa_ret==0)
ctx->error = X509_V_OK; /* override all errors? */
break;
default:/* there were TLSA records, but something phishy happened */
ctx->error = X509_V_ERR_CERT_UNTRUSTED;
return ok;
}
if (ex->tlsa_witness==-1) /* no match */
ctx->error = X509_V_ERR_CERT_UNTRUSTED, ok=0;
else
ctx->error = X509_V_OK, ok=1;
}
/*
* returning 1 makes verify procedure traverse the whole chain,
* not actually approve it...
*/
return 1;
}
if (s->verify_callback) return s->verify_callback(ok,ctx);
else return ok;
}
static int dane_get_issuer(X509 **issuer,X509_STORE_CTX *ctx,X509 *x)
{
SSL *s = X509_STORE_CTX_get_ex_data(ctx,SSL_get_ex_data_X509_STORE_CTX_idx());
TLSA_EX_DATA *ex=SSL_get_ex_data(s, SSL_get_TLSA_ex_data_idx());
/* XXX TODO */
return ex->get_issuer(issuer,ctx,x);
}
#endif
int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk)
@ -870,6 +981,9 @@ int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk)
int i;
X509_STORE *verify_store;
X509_STORE_CTX ctx;
#ifndef OPENSSL_NO_DANE
TLSA_EX_DATA *ex;
#endif
if (s->cert->verify_store)
verify_store = s->cert->verify_store;
@ -906,12 +1020,45 @@ int ssl_verify_cert_chain(SSL *s,STACK_OF(X509) *sk)
X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(&ctx), s->param);
#ifndef OPENSSL_NO_DANE
X509_STORE_CTX_set_verify_cb(&ctx, dane_verify_callback);
s->tlsa_witness = -1;
#else
if (!s->server &&
(ex=SSL_get_ex_data(s, SSL_get_TLSA_ex_data_idx()))!=NULL)
{
const unsigned char *tlsa_record = ex->tlsa_record;
/*
* See if there are usable certificates we can add
* to chain.
*/
while (tlsa_record!=(void *)-1)
{
unsigned int reclen;
memcpy (&reclen,tlsa_record,sizeof(reclen));
if (reclen==0) break;
tlsa_record += sizeof(reclen);
if (tlsa_record[0]==2 &&
tlsa_record[1]==0 && /* full certificate */
tlsa_record[2]==0) /* itself */
{
ex->get_issuer = ctx.get_issuer;
ctx.get_issuer = dane_get_issuer;
break;
}
tlsa_record += reclen;
}
ex->tlsa_mask = 0;
ex->tlsa_witness = -1;
X509_STORE_CTX_set_verify_cb(&ctx, dane_verify_callback);
}
else
#endif
if (s->verify_callback)
X509_STORE_CTX_set_verify_cb(&ctx, s->verify_callback);
#endif
if (s->ctx->app_verify_callback != NULL)
#if 1 /* new with OpenSSL 0.9.7 */

View File

@ -650,11 +650,6 @@ void SSL_free(SSL *s)
if (s->srtp_profiles)
sk_SRTP_PROTECTION_PROFILE_free(s->srtp_profiles);
#ifndef OPENSSL_NO_DANE
if (s->tlsa_record && s->tlsa_record!=(void *)-1)
OPENSSL_free(s->tlsa_record);
#endif
OPENSSL_free(s);
}
@ -1105,6 +1100,9 @@ int SSL_renegotiate_pending(SSL *s)
long SSL_ctrl(SSL *s,int cmd,long larg,void *parg)
{
long l;
#ifndef OPNESSL_NO_DANE
const char *hostname = NULL;
#endif
switch (cmd)
{
@ -1171,10 +1169,37 @@ long SSL_ctrl(SSL *s,int cmd,long larg,void *parg)
return ssl_put_cipher_by_char(s,NULL,NULL);
#ifndef OPENSSL_NO_DANE
case SSL_CTRL_PULL_TLSA_RECORD:
hostname = parg;
parg = SSL_get_tlsa_record_byname (parg,larg,s->version<0xF000?1:0);
/* yes, fall through */
case SSL_CTRL_SET_TLSA_RECORD:
s->tlsa_record = parg;
if (parg!=NULL)
{
TLSA_EX_DATA *ex = SSL_get_TLSA_ex_data(s);
unsigned char *tlsa_rec = parg;
int tlsa_len = 0;
if (hostname==NULL)
{
while (1)
{
int dlen;
memcpy(&dlen,tlsa_rec,sizeof(dlen));
tlsa_rec += sizeof(dlen)+dlen;
if (dlen==0) break;
}
if ((tlsa_rec = OPENSSL_malloc(tlsa_len)))
memcpy(tlsa_rec,parg,tlsa_len);
else
{
SSLerr(SSL_F_SSL_CTRL,SSL_R_UNINITIALIZED);
return 0;
}
}
ex->tlsa_record = tlsa_rec;
}
return 1;
#endif
default:

View File

@ -1365,4 +1365,15 @@ void tls_fips_digest_extra(
const EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *mac_ctx,
const unsigned char *data, size_t data_len, size_t orig_len);
#ifndef OPENSSL_NO_DANE
typedef struct {
unsigned char *tlsa_record;
int tlsa_witness, tlsa_mask;
int (*get_issuer)(X509 **issuer,X509_STORE_CTX *ctx,X509 *x);
} TLSA_EX_DATA;
TLSA_EX_DATA *SSL_get_TLSA_ex_data(SSL *);
int SSL_get_TLSA_ex_data_idx(void);
#endif
#endif