BSDSec

deadsimple BSD Security Advisories and Announcements

libssl patch available

When CVE-2015-0204 (RSA silently downgrades to EXPORT_RSA) was announced, 
it was labeled "Severity: Low". Our assessment at the time was that export
ciphers had already been removed prior to the release of 5.6, and that the 
fix was not worth backporting to 5.5.

Then CVE-2015-0204 was renamed the FREAK attack. Now it has a fancy name so
you know it's important.

Unfortunately, our original assessment was not entirely correct. Some of the
features exploited by FREAK were not deleted until after 5.6, although this
was not known until testing tools became available. We've corrected libssl
by backporting the necessary changes to 5.6.

The patch below includes the fix for CVE-2015-0204 as well as some other "low
severity" fixes for similar downgrade issues relating to ECDHE.

Statement regarding 5.5: SSL/TLS is hooped. There have been too many changes,
large and small, that make backporting and testing indvidual fixes difficult.
Additionally, many small fixes get overlooked.

Thanks to Florian Riehm for pointing out that 5.6 was still vulnerable to
FREAK.

http://ftp.openbsd.org/pub/OpenBSD/patches/5.6/common/017_openssl.patch.sig

untrusted comment: signature from openbsd 5.6 base private key
RWR0EANmo9nqhpxHVrEZWmf2qAA9zblsetB0gpcMFrkMumxjVXGdcaRNHc7TS+IkdiYNDncAU2qjYSaM8bDI+nQp9HUayjp3RQQ
OpenBSD 5.6 errata 17, Mar 13, 2015:

Don't permit TLS client connections to be downgraded to weak keys.

Apply by doing:
    cd /usr/src
    signify -Vep /etc/signify/openbsd-56-base.pub -x 017_openssl.patch.sig -m - | \
        patch -p0

And then rebuild and install libssl:
    cd lib/libssl/ssl
    make obj
    make depend
    make
    make install


Index: lib/libssl/src/ssl/d1_clnt.c
===================================================================
RCS file: /cvs/src/lib/libssl/src/ssl/d1_clnt.c,v
retrieving revision 1.33
diff -u -p -r1.33 d1_clnt.c
--- lib/libssl/src/ssl/d1_clnt.c	7 Aug 2014 20:02:23 -0000	1.33
+++ lib/libssl/src/ssl/d1_clnt.c	10 Mar 2015 17:11:46 -0000
@@ -939,20 +939,16 @@ dtls1_send_client_key_exchange(SSL *s)
 			RSA *rsa;
 			unsigned char tmp_buf[SSL_MAX_MASTER_KEY_LENGTH];
 
-			if (s->session->sess_cert->peer_rsa_tmp != NULL)
-				rsa = s->session->sess_cert->peer_rsa_tmp;
-			else {
-				pkey = X509_get_pubkey(s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509);
-				if ((pkey == NULL) ||
-				    (pkey->type != EVP_PKEY_RSA) ||
-				    (pkey->pkey.rsa == NULL)) {
-					SSLerr(SSL_F_DTLS1_SEND_CLIENT_KEY_EXCHANGE,
-					    ERR_R_INTERNAL_ERROR);
-					goto err;
-				}
-				rsa = pkey->pkey.rsa;
-				EVP_PKEY_free(pkey);
+			pkey = X509_get_pubkey(s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509);
+			if ((pkey == NULL) ||
+			    (pkey->type != EVP_PKEY_RSA) ||
+			    (pkey->pkey.rsa == NULL)) {
+				SSLerr(SSL_F_DTLS1_SEND_CLIENT_KEY_EXCHANGE,
+				    ERR_R_INTERNAL_ERROR);
+				goto err;
 			}
+			rsa = pkey->pkey.rsa;
+			EVP_PKEY_free(pkey);
 
 			tmp_buf[0] = s->client_version >> 8;
 			tmp_buf[1] = s->client_version&0xff;
Index: lib/libssl/src/ssl/s3_clnt.c
===================================================================
RCS file: /cvs/src/lib/libssl/src/ssl/s3_clnt.c,v
retrieving revision 1.85
diff -u -p -r1.85 s3_clnt.c
--- lib/libssl/src/ssl/s3_clnt.c	7 Aug 2014 01:24:10 -0000	1.85
+++ lib/libssl/src/ssl/s3_clnt.c	10 Mar 2015 17:11:59 -0000
@@ -1162,6 +1162,9 @@ ssl3_get_key_exchange(SSL *s)
 	int		 curve_nid = 0;
 	int		 encoded_pt_len = 0;
 
+	alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
+	alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+
 	/*
 	 * Use same message size as in ssl3_get_certificate_request()
 	 * as ServerKeyExchange message may be skipped.
@@ -1170,17 +1173,27 @@ ssl3_get_key_exchange(SSL *s)
 	    SSL3_ST_CR_KEY_EXCH_B, -1, s->max_cert_list, &ok);
 	if (!ok)
 		return ((int)n);
+	
+	EVP_MD_CTX_init(&md_ctx);
 
 	if (s->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) {
+		/*
+		 * Do not skip server key exchange if this cipher suite uses
+		 * ephemeral keys.
+		 */
+		if (alg_k & (SSL_kDHE|SSL_kECDHE)) {
+			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
+			    SSL_R_UNEXPECTED_MESSAGE);
+			al = SSL_AD_UNEXPECTED_MESSAGE;
+			goto f_err;
+		}
+
 		s->s3->tmp.reuse_message = 1;
+		EVP_MD_CTX_cleanup(&md_ctx);
 		return (1);
 	}
 
-	param = p = (unsigned char *)s->init_msg;
 	if (s->session->sess_cert != NULL) {
-		RSA_free(s->session->sess_cert->peer_rsa_tmp);
-		s->session->sess_cert->peer_rsa_tmp = NULL;
-
 		DH_free(s->session->sess_cert->peer_dh_tmp);
 		s->session->sess_cert->peer_dh_tmp = NULL;
 
@@ -1192,68 +1205,10 @@ ssl3_get_key_exchange(SSL *s)
 			goto err; 
 	}
 
+	param = p = (unsigned char *)s->init_msg;
 	param_len = 0;
-	alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-	alg_a = s->s3->tmp.new_cipher->algorithm_auth;
-	EVP_MD_CTX_init(&md_ctx);
-
-	if (alg_k & SSL_kRSA) {
-		if ((rsa = RSA_new()) == NULL) {
-			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
-			    ERR_R_MALLOC_FAILURE);
-			goto err;
-		}
-		if (2 > n)
-			goto truncated;
-		n2s(p, i);
-		param_len = i + 2;
-		if (param_len > n) {
-			al = SSL_AD_DECODE_ERROR;
-			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
-			    SSL_R_BAD_RSA_MODULUS_LENGTH);
-			goto f_err;
-		}
-		if (!(rsa->n = BN_bin2bn(p, i, rsa->n))) {
-			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
-			    ERR_R_BN_LIB);
-			goto err;
-		}
-		p += i;
 
-		if (param_len + 2 > n)
-			goto truncated;
-		n2s(p, i);
-		param_len += i + 2;
-		if (param_len > n) {
-			al = SSL_AD_DECODE_ERROR;
-			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
-			    SSL_R_BAD_RSA_E_LENGTH);
-			goto f_err;
-		}
-		if (!(rsa->e = BN_bin2bn(p, i, rsa->e))) {
-			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
-			    ERR_R_BN_LIB);
-			goto err;
-		}
-		p += i;
-		n -= param_len;
-
-		/*
-		 * This should be because we are using an
-		 * export cipher
-		 */
-		if (alg_a & SSL_aRSA)
-			pkey = X509_get_pubkey(
-			    s->session->sess_cert->peer_pkeys[
-			    SSL_PKEY_RSA_ENC].x509);
-		else {
-			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
-			    ERR_R_INTERNAL_ERROR);
-			goto err;
-		}
-		s->session->sess_cert->peer_rsa_tmp = rsa;
-		rsa = NULL;
-	} else if (alg_k & SSL_kDHE) {
+	if (alg_k & SSL_kDHE) {
 		if ((dh = DH_new()) == NULL) {
 			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
 			    ERR_R_DH_LIB);
@@ -1311,6 +1266,17 @@ ssl3_get_key_exchange(SSL *s)
 		p += i;
 		n -= param_len;
 
+		/*
+		 * Check the strength of the DH key just constructed.
+		 * Discard keys weaker than 1024 bits.
+		 */
+
+		if (DH_size(dh) < 1024 / 8) {
+			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,
+			    SSL_R_BAD_DH_P_LENGTH);
+			goto err;
+		}
+
 		if (alg_a & SSL_aRSA)
 			pkey = X509_get_pubkey(
 			    s->session->sess_cert->peer_pkeys[
@@ -1953,23 +1919,19 @@ ssl3_send_client_key_exchange(SSL *s)
 				goto err;
 			}
 
-			if (s->session->sess_cert->peer_rsa_tmp != NULL)
-				rsa = s->session->sess_cert->peer_rsa_tmp;
-			else {
-				pkey = X509_get_pubkey(
-				    s->session->sess_cert->peer_pkeys[
-				    SSL_PKEY_RSA_ENC].x509);
-				if ((pkey == NULL) ||
-				    (pkey->type != EVP_PKEY_RSA) ||
-				    (pkey->pkey.rsa == NULL)) {
-					SSLerr(
-					    SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,
-					    ERR_R_INTERNAL_ERROR);
-					goto err;
-				}
-				rsa = pkey->pkey.rsa;
+			pkey = X509_get_pubkey(
+			    s->session->sess_cert->peer_pkeys[
+			    SSL_PKEY_RSA_ENC].x509);
+			if ((pkey == NULL) ||
+			    (pkey->type != EVP_PKEY_RSA) ||
+			    (pkey->pkey.rsa == NULL)) {
+				SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,
+				    ERR_R_INTERNAL_ERROR);
 				EVP_PKEY_free(pkey);
+				goto err;
 			}
+			rsa = pkey->pkey.rsa;
+			EVP_PKEY_free(pkey);
 
 			tmp_buf[0] = s->client_version >> 8;
 			tmp_buf[1] = s->client_version & 0xff;
@@ -2598,7 +2560,6 @@ ssl3_check_cert_and_algorithm(SSL *s)
 	long		 alg_k, alg_a;
 	EVP_PKEY	*pkey = NULL;
 	SESS_CERT	*sc;
-	RSA		*rsa;
 	DH		*dh;
 
 	alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
@@ -2614,8 +2575,6 @@ ssl3_check_cert_and_algorithm(SSL *s)
 		    ERR_R_INTERNAL_ERROR);
 		goto err;
 	}
-
-	rsa = s->session->sess_cert->peer_rsa_tmp;
 	dh = s->session->sess_cert->peer_dh_tmp;
 
 	/* This is the passed certificate. */
@@ -2648,7 +2607,7 @@ ssl3_check_cert_and_algorithm(SSL *s)
 		goto f_err;
 	}
 	if ((alg_k & SSL_kRSA) &&
-	    !(has_bits(i, EVP_PK_RSA|EVP_PKT_ENC) || (rsa != NULL))) {
+	    !has_bits(i, EVP_PK_RSA|EVP_PKT_ENC)) {
 		SSLerr(SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM,
 		    SSL_R_MISSING_RSA_ENCRYPTING_CERT);
 		goto f_err;
Index: lib/libssl/src/ssl/ssl_cert.c
===================================================================
RCS file: /cvs/src/lib/libssl/src/ssl/ssl_cert.c,v
retrieving revision 1.41
diff -u -p -r1.41 ssl_cert.c
--- lib/libssl/src/ssl/ssl_cert.c	10 Jul 2014 08:25:00 -0000	1.41
+++ lib/libssl/src/ssl/ssl_cert.c	10 Mar 2015 17:11:46 -0000
@@ -407,7 +407,6 @@ ssl_sess_cert_free(SESS_CERT *sc)
 			X509_free(sc->peer_pkeys[i].x509);
 	}
 
-	RSA_free(sc->peer_rsa_tmp);
 	DH_free(sc->peer_dh_tmp);
 	EC_KEY_free(sc->peer_ecdh_tmp);
 
Index: lib/libssl/src/ssl/ssl_locl.h
===================================================================
RCS file: /cvs/src/lib/libssl/src/ssl/ssl_locl.h,v
retrieving revision 1.63
diff -u -p -r1.63 ssl_locl.h
--- lib/libssl/src/ssl/ssl_locl.h	28 Jul 2014 04:23:12 -0000	1.63
+++ lib/libssl/src/ssl/ssl_locl.h	10 Mar 2015 17:11:46 -0000
@@ -464,7 +464,7 @@ typedef struct sess_cert_st {
 	/* Obviously we don't have the private keys of these,
 	 * so maybe we shouldn't even use the CERT_PKEY type here. */
 
-	RSA *peer_rsa_tmp;
+	RSA *__peer_rsa_tmp;
 	DH *peer_dh_tmp;
 	EC_KEY *peer_ecdh_tmp;