CVE-2015-0204: Cryptographic Issues (CWE-310)
value: The ssl3_get_key_exchange function in s3_clnt.c in OpenSSL before 0.9.8zd, 1.0.0 before 1.0.0p, and 1.0.1 before 1.0.1k allows remote SSL servers to conduct RSA-to-EXPORT_RSA downgrade attacks and facilitate brute-force decryption by offering a weak ephemeral RSA key in a noncompliant role, related to the "FREAK" issue.  NOTE: the scope of this CVE is only client code based on OpenSSL, not EXPORT_RSA issues associated with servers or other TLS implementations.
CWE description: Weaknesses in this category are related to the design and implementation of data confidentiality and integrity. Frequently these deal with the use of encoding techniques, encryption libraries, and hashing algorithms. The weaknesses in this category could lead to a degradation of the quality data if they are not addressed.
Published: 2015-01-09T02:59Z
Repository: https://github.com/openssl/openssl
Language: C
Files changed: CHANGES, doc/ssl/SSL_CTX_set_options.pod, doc/ssl/SSL_CTX_set_tmp_rsa_callback.pod, ssl/d1_srvr.c, ssl/s3_clnt.c, ssl/s3_srvr.c, ssl/ssl.h
Commit date: 2015-01-06 02:06:39 +0000
Commit message: Only allow ephemeral RSA keys in export ciphersuites.

OpenSSL clients would tolerate temporary RSA keys in non-export
ciphersuites. It also had an option SSL_OP_EPHEMERAL_RSA which
enabled this server side. Remove both options as they are a
protocol violation.

Thanks to Karthikeyan Bhargavan for reporting this issue.
(CVE-2015-0204)
Reviewed-by: Matt Caswell <matt@openssl.org>

Diff: diff --git a/CHANGES b/CHANGES
index 0252eb570..c9c19c3ae 100644
--- a/CHANGES
+++ b/CHANGES
@@ -664,10 +664,18 @@
 
      Thanks to Karthikeyan Bhargavan for reporting this issue.
      (CVE-2014-3572)
      [Steve Henson]
 
+  *) Remove non-export ephemeral RSA code on client and server. This code
+     violated the TLS standard by allowing the use of temporary RSA keys in
+     non-export ciphersuites and could be used by a server to effectively
+     downgrade the RSA key length used to a value smaller than the server
+     certificate. Thanks for Karthikeyan Bhargavan for reporting this issue.
+     (CVE-2015-0204)
+     [Steve Henson]
+
   *) Ensure that the session ID context of an SSL is updated when its
      SSL_CTX is updated via SSL_set_SSL_CTX.
 
      The session ID context is typically set from the parent SSL_CTX,
      and can vary with the CTX.
diff --git a/doc/ssl/SSL_CTX_set_options.pod b/doc/ssl/SSL_CTX_set_options.pod
index 1594fb6ee..593435c49 100644
--- a/doc/ssl/SSL_CTX_set_options.pod
+++ b/doc/ssl/SSL_CTX_set_options.pod
@@ -149,19 +149,11 @@ a new DH key during each handshake but it is also recommended.
 B<SSL_OP_SINGLE_DH_USE> should therefore be enabled whenever
 temporary/ephemeral DH parameters are used.
 
 =item SSL_OP_EPHEMERAL_RSA
 
-Always use ephemeral (temporary) RSA key when doing RSA operations
-(see L<SSL_CTX_set_tmp_rsa_callback(3)|SSL_CTX_set_tmp_rsa_callback(3)>).
-According to the specifications this is only done, when a RSA key
-can only be used for signature operations (namely under export ciphers
-with restricted RSA keylength). By setting this option, ephemeral
-RSA keys are always used. This option breaks compatibility with the
-SSL/TLS specifications and may lead to interoperability problems with
-clients and should therefore never be used. Ciphers with DHE (ephemeral
-Diffie-Hellman) key exchange should be used instead.
+This option is no longer implemented and is treated as no op.
 
 =item SSL_OP_CIPHER_SERVER_PREFERENCE
 
 When choosing a cipher, use the server's preferences instead of the client
 preferences. When not set, the SSL server will always follow the clients
diff --git a/doc/ssl/SSL_CTX_set_tmp_rsa_callback.pod b/doc/ssl/SSL_CTX_set_tmp_rsa_callback.pod
index b23e43a96..94c55b804 100644
--- a/doc/ssl/SSL_CTX_set_tmp_rsa_callback.pod
+++ b/doc/ssl/SSL_CTX_set_tmp_rsa_callback.pod
@@ -72,25 +72,18 @@ violates the standard and can break interoperability with clients.
 It is therefore strongly recommended to not use ephemeral RSA key
 exchange and use DHE (Ephemeral Diffie-Hellman) key exchange instead
 in order to achieve forward secrecy (see
 L<SSL_CTX_set_tmp_dh_callback(3)|SSL_CTX_set_tmp_dh_callback(3)>).
 
-On OpenSSL servers ephemeral RSA key exchange is therefore disabled by default
-and must be explicitly enabled  using the SSL_OP_EPHEMERAL_RSA option of
-L<SSL_CTX_set_options(3)|SSL_CTX_set_options(3)>, violating the TLS/SSL
-standard. When ephemeral RSA key exchange is required for export ciphers,
-it will automatically be used without this option!
-
-An application may either directly specify the key or can supply the key via
-a callback function. The callback approach has the advantage, that the
-callback may generate the key only in case it is actually needed. As the
-generation of a RSA key is however costly, it will lead to a significant
-delay in the handshake procedure.  Another advantage of the callback function
-is that it can supply keys of different size (e.g. for SSL_OP_EPHEMERAL_RSA
-usage) while the explicit setting of the key is only useful for key size of
-512 bits to satisfy the export restricted ciphers and does give away key length
-if a longer key would be allowed.
+An application may either directly specify the key or can supply the key via a
+callback function. The callback approach has the advantage, that the callback
+may generate the key only in case it is actually needed. As the generation of a
+RSA key is however costly, it will lead to a significant delay in the handshake
+procedure.  Another advantage of the callback function is that it can supply
+keys of different size while the explicit setting of the key is only useful for
+key size of 512 bits to satisfy the export restricted ciphers and does give
+away key length if a longer key would be allowed.
 
 The B<tmp_rsa_callback> is called with the B<keylength> needed and
 the B<is_export> information. The B<is_export> flag is set, when the
 ephemeral RSA key exchange is performed with an export cipher.
 
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index bcadd310f..8a5c5a1cd 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -470,28 +470,19 @@ int dtls1_accept(SSL *s)
 
 		case SSL3_ST_SW_KEY_EXCH_A:
 		case SSL3_ST_SW_KEY_EXCH_B:
 			alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
 
-			/* clear this, it may get reset by
-			 * send_server_key_exchange */
-			if ((s->options & SSL_OP_EPHEMERAL_RSA)
-#ifndef OPENSSL_NO_KRB5
-				&& !(alg_k & SSL_kKRB5)
-#endif /* OPENSSL_NO_KRB5 */
-				)
-				/* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key
-				 * even when forbidden by protocol specs
-				 * (handshake may fail as clients are not required to
-				 * be able to handle this) */
-				s->s3->tmp.use_rsa_tmp=1;
-			else
-				s->s3->tmp.use_rsa_tmp=0;
+			/*
+			 * clear this, it may get reset by
+			 * send_server_key_exchange
+			 */
+			s->s3->tmp.use_rsa_tmp=0;
 
 			/* only send if a DH key exchange or
 			 * RSA but we have a sign only certificate */
-			if (s->s3->tmp.use_rsa_tmp
+			if (
 			/* PSK: send ServerKeyExchange if PSK identity
 			 * hint if provided */
 #ifndef OPENSSL_NO_PSK
 			    || ((alg_k & SSL_kPSK) && s->ctx->psk_identity_hint)
 #endif
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 2313fbc1e..aa9dcbb5b 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1634,10 +1634,17 @@ int ssl3_get_key_exchange(SSL *s)
 	else
 #endif /* !OPENSSL_NO_SRP */
 #ifndef OPENSSL_NO_RSA
 	if (alg_k & SSL_kRSA)
 		{
+		/* Temporary RSA keys only allowed in export ciphersuites */
+		if (!SSL_C_IS_EXPORT(s->s3->tmp.new_cipher))
+			{
+			al=SSL_AD_UNEXPECTED_MESSAGE;
+			SSLerr(SSL_F_SSL3_GET_SERVER_CERTIFICATE,SSL_R_UNEXPECTED_MESSAGE);
+			goto f_err;
+			}
 		if ((rsa=RSA_new()) == NULL)
 			{
 			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE,ERR_R_MALLOC_FAILURE);
 			goto err;
 			}
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 6c0bdcf43..e5a32ee90 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -451,24 +451,15 @@ int ssl3_accept(SSL *s)
 
 		case SSL3_ST_SW_KEY_EXCH_A:
 		case SSL3_ST_SW_KEY_EXCH_B:
 			alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
 
-			/* clear this, it may get reset by
-			 * send_server_key_exchange */
-			if ((s->options & SSL_OP_EPHEMERAL_RSA)
-#ifndef OPENSSL_NO_KRB5
-				&& !(alg_k & SSL_kKRB5)
-#endif /* OPENSSL_NO_KRB5 */
-				)
-				/* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key
-				 * even when forbidden by protocol specs
-				 * (handshake may fail as clients are not required to
-				 * be able to handle this) */
-				s->s3->tmp.use_rsa_tmp=1;
-			else
-				s->s3->tmp.use_rsa_tmp=0;
+			/*
+			 * clear this, it may get reset by
+			 * send_server_key_exchange
+			 */
+			s->s3->tmp.use_rsa_tmp=0;
 
 
 			/* only send if a DH key exchange, fortezza or
 			 * RSA but we have a sign only certificate
 			 *
@@ -478,11 +469,11 @@ int ssl3_accept(SSL *s)
 			 * message only if the cipher suite is either
 			 * ECDH-anon or ECDHE. In other cases, the
 			 * server certificate contains the server's
 			 * public key for key exchange.
 			 */
-			if (s->s3->tmp.use_rsa_tmp
+			if (
 			/* PSK: send ServerKeyExchange if PSK identity
 			 * hint if provided */
 #ifndef OPENSSL_NO_PSK
 			    || ((alg_k & SSL_kPSK) && s->ctx->psk_identity_hint)
 #endif
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 31d01b624..98661d0ab 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -604,13 +604,12 @@ struct ssl_session_st
 #define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION	0x00040000L
 /* If set, always create a new key when using tmp_ecdh parameters */
 #define SSL_OP_SINGLE_ECDH_USE				0x00080000L
 /* If set, always create a new key when using tmp_dh parameters */
 #define SSL_OP_SINGLE_DH_USE				0x00100000L
-/* Set to always use the tmp_rsa key when doing RSA operations,
- * even when this violates protocol specs */
-#define SSL_OP_EPHEMERAL_RSA				0x00200000L
+/* Does nothing: retained for compatibiity */
+#define SSL_OP_EPHEMERAL_RSA				0x0
 /* Set on servers to choose the cipher according to the server's
  * preferences */
 #define SSL_OP_CIPHER_SERVER_PREFERENCE			0x00400000L
 /* If set, a server will allow a client to issue a SSLv3.0 version number
  * as latest version supported in the premaster secret, even when TLSv1.0

Vulnerable code: Always use ephemeral (temporary) RSA key when doing RSA operations
(see L<SSL_CTX_set_tmp_rsa_callback(3)|SSL_CTX_set_tmp_rsa_callback(3)>).
According to the specifications this is only done, when a RSA key
can only be used for signature operations (namely under export ciphers
with restricted RSA keylength). By setting this option, ephemeral
RSA keys are always used. This option breaks compatibility with the
SSL/TLS specifications and may lead to interoperability problems with
clients and should therefore never be used. Ciphers with DHE (ephemeral
Diffie-Hellman) key exchange should be used instead.
On OpenSSL servers ephemeral RSA key exchange is therefore disabled by default
and must be explicitly enabled  using the SSL_OP_EPHEMERAL_RSA option of
L<SSL_CTX_set_options(3)|SSL_CTX_set_options(3)>, violating the TLS/SSL
standard. When ephemeral RSA key exchange is required for export ciphers,
it will automatically be used without this option!

An application may either directly specify the key or can supply the key via
a callback function. The callback approach has the advantage, that the
callback may generate the key only in case it is actually needed. As the
generation of a RSA key is however costly, it will lead to a significant
delay in the handshake procedure.  Another advantage of the callback function
is that it can supply keys of different size (e.g. for SSL_OP_EPHEMERAL_RSA
usage) while the explicit setting of the key is only useful for key size of
512 bits to satisfy the export restricted ciphers and does give away key length
if a longer key would be allowed.
			/* clear this, it may get reset by
			 * send_server_key_exchange */
			if ((s->options & SSL_OP_EPHEMERAL_RSA)
#ifndef OPENSSL_NO_KRB5
				&& !(alg_k & SSL_kKRB5)
#endif /* OPENSSL_NO_KRB5 */
				)
				/* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key
				 * even when forbidden by protocol specs
				 * (handshake may fail as clients are not required to
				 * be able to handle this) */
				s->s3->tmp.use_rsa_tmp=1;
			else
				s->s3->tmp.use_rsa_tmp=0;
			if (s->s3->tmp.use_rsa_tmp
			/* clear this, it may get reset by
			 * send_server_key_exchange */
			if ((s->options & SSL_OP_EPHEMERAL_RSA)
#ifndef OPENSSL_NO_KRB5
				&& !(alg_k & SSL_kKRB5)
#endif /* OPENSSL_NO_KRB5 */
				)
				/* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key
				 * even when forbidden by protocol specs
				 * (handshake may fail as clients are not required to
				 * be able to handle this) */
				s->s3->tmp.use_rsa_tmp=1;
			else
				s->s3->tmp.use_rsa_tmp=0;
			if (s->s3->tmp.use_rsa_tmp
/* Set to always use the tmp_rsa key when doing RSA operations,
 * even when this violates protocol specs */
#define SSL_OP_EPHEMERAL_RSA				0x00200000L
Fixed code:   *) Remove non-export ephemeral RSA code on client and server. This code
     violated the TLS standard by allowing the use of temporary RSA keys in
     non-export ciphersuites and could be used by a server to effectively
     downgrade the RSA key length used to a value smaller than the server
     certificate. Thanks for Karthikeyan Bhargavan for reporting this issue.
     (CVE-2015-0204)
     [Steve Henson]

This option is no longer implemented and is treated as no op.
An application may either directly specify the key or can supply the key via a
callback function. The callback approach has the advantage, that the callback
may generate the key only in case it is actually needed. As the generation of a
RSA key is however costly, it will lead to a significant delay in the handshake
procedure.  Another advantage of the callback function is that it can supply
keys of different size while the explicit setting of the key is only useful for
key size of 512 bits to satisfy the export restricted ciphers and does give
away key length if a longer key would be allowed.
			/*
			 * clear this, it may get reset by
			 * send_server_key_exchange
			 */
			s->s3->tmp.use_rsa_tmp=0;
			if (
		/* Temporary RSA keys only allowed in export ciphersuites */
		if (!SSL_C_IS_EXPORT(s->s3->tmp.new_cipher))
			{
			al=SSL_AD_UNEXPECTED_MESSAGE;
			SSLerr(SSL_F_SSL3_GET_SERVER_CERTIFICATE,SSL_R_UNEXPECTED_MESSAGE);
			goto f_err;
			}
			/*
			 * clear this, it may get reset by
			 * send_server_key_exchange
			 */
			s->s3->tmp.use_rsa_tmp=0;
			if (
/* Does nothing: retained for compatibiity */
#define SSL_OP_EPHEMERAL_RSA				0x0