Commit 6b3b6bea authored by Andy Polyakov's avatar Andy Polyakov
Browse files

ssl/ssl_cert.c: DANE update.

parent 7743be3a
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -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);
	}
+0 −4
Original line number Diff line number Diff line
@@ -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
+201 −54
Original line number Diff line number Diff line
@@ -137,6 +137,26 @@ 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 */
	{
	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];
@@ -790,27 +889,44 @@ int tlsa_cmp(const X509 *cert, const unsigned char *tlsa_record, unsigned int re
	}
	}

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,45 +938,40 @@ 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;
	    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;
	}

	if (s->verify_callback)	return s->verify_callback(ok,ctx);
	else			return ok;
	}

	/*
	 * returning 1 makes verify procedure traverse the whole chain,
	 * not actually approve it...
	 */
	return 1;
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

@@ -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
	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);
	s->tlsa_witness = -1;
#else
		}
	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 */
+31 −6
Original line number Diff line number Diff line
@@ -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:
+11 −0
Original line number Diff line number Diff line
@@ -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