From 6b3b6beaa11d52522092fad4fc151b42f0c4c7b5 Mon Sep 17 00:00:00 2001 From: Andy Polyakov Date: Fri, 21 Feb 2014 12:12:25 +0100 Subject: [PATCH] ssl/ssl_cert.c: DANE update. --- ssl/dnssec.c | 8 +- ssl/ssl.h | 4 - ssl/ssl_cert.c | 255 ++++++++++++++++++++++++++++++++++++++----------- ssl/ssl_lib.c | 37 +++++-- ssl/ssl_locl.h | 11 +++ 5 files changed, 248 insertions(+), 67 deletions(-) diff --git a/ssl/dnssec.c b/ssl/dnssec.c index a1cba6f808..62b04b428b 100644 --- a/ssl/dnssec.c +++ b/ssl/dnssec.c @@ -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); } diff --git a/ssl/ssl.h b/ssl/ssl.h index df40d94249..85c7966634 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -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 diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 1e2ab9da97..5dc841f21b 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -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; + + if (((size_t)&ssl_x509_store_ctx_idx&(sizeof(ssl_x509_store_ctx_idx)-1)) + ==0) /* check alignment, practically always true */ { - static volatile int ssl_x509_store_ctx_idx= -1; - int got_write_lock = 0; + 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 ((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; + } - if (s->verify_callback) ok = s->verify_callback(ok,ctx); + while (1) { + unsigned int reclen, certificate_usage; - if (tlsa_record == NULL) return ok; + memcpy(&reclen,tlsa_record,sizeof(reclen)); - if (tlsa_record == (void*)-1) { - ctx->error = X509_V_ERR_INVALID_CA; /* temporary code? */ - return 0; - } + if (reclen==0) break; - while ((reclen = *(unsigned int *)tlsa_record)) { - tlsa_record += sizeof(unsigned int); + 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 */ diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 1670a87271..e1734d3014 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -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: diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index f0d621614b..d57d83e206 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -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 -- 2.40.0