From 5733919dbca0b2469673a38ec4290d663d594ae4 Mon Sep 17 00:00:00 2001
From: "Dr. Stephen Henson" <steve@openssl.org>
Date: Tue, 3 Jan 2012 22:03:20 +0000
Subject: [PATCH] only send heartbeat extension from server if client sent one

---
 ssl/s3_srvr.c | 10 ++++++++++
 ssl/ssl.h     |  2 ++
 ssl/ssl3.h    | 11 +++++++++++
 ssl/ssl_err.c |  2 ++
 ssl/t1_lib.c  | 25 ++++++++++++++-----------
 5 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 9e73d62921..587dc86175 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -297,6 +297,7 @@ int ssl3_accept(SSL *s)
 				}
 
 			s->init_num=0;
+			s->s3->flags &= ~SSL3_FLAGS_SGC_RESTART_DONE;
 
 			if (s->state != SSL_ST_RENEGOTIATE)
 				{
@@ -871,6 +872,14 @@ int ssl3_check_client_hello(SSL *s)
 	int ok;
 	long n;
 
+	/* We only allow the client to restart the handshake once per
+	 * negotiation. */
+	if (s->s3->flags & SSL3_FLAGS_SGC_RESTART_DONE)
+		{
+		SSLerr(SSL_F_SSL3_CHECK_CLIENT_HELLO, SSL_R_MULTIPLE_SGC_RESTARTS);
+		return -1;
+		}
+
 	/* this function is called when we really expect a Certificate message,
 	 * so permit appropriate message length */
 	n=s->method->ssl_get_message(s,
@@ -899,6 +908,7 @@ int ssl3_check_client_hello(SSL *s)
 			s->s3->tmp.ecdh = NULL;
 			}
 #endif
+		s->s3->flags |= SSL3_FLAGS_SGC_RESTART_DONE;
 		return 2;
 		}
 	return 1;
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 4bf477a2f8..9ce2684f4e 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -2133,6 +2133,7 @@ void ERR_load_SSL_strings(void);
 #define SSL_F_SSL3_CALLBACK_CTRL			 233
 #define SSL_F_SSL3_CHANGE_CIPHER_STATE			 129
 #define SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM		 130
+#define SSL_F_SSL3_CHECK_CLIENT_HELLO			 315
 #define SSL_F_SSL3_CLIENT_HELLO				 131
 #define SSL_F_SSL3_CONNECT				 132
 #define SSL_F_SSL3_CTRL					 213
@@ -2412,6 +2413,7 @@ void ERR_load_SSL_strings(void);
 #define SSL_R_MISSING_TMP_RSA_KEY			 172
 #define SSL_R_MISSING_TMP_RSA_PKEY			 173
 #define SSL_R_MISSING_VERIFY_MESSAGE			 174
+#define SSL_R_MULTIPLE_SGC_RESTARTS			 370
 #define SSL_R_NON_SSLV2_INITIAL_PACKET			 175
 #define SSL_R_NO_CERTIFICATES_RETURNED			 176
 #define SSL_R_NO_CERTIFICATE_ASSIGNED			 177
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index 93f9ead305..68a66e2b00 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -389,6 +389,17 @@ typedef struct ssl3_buffer_st
 #define TLS1_FLAGS_SKIP_CERT_VERIFY		0x0010
 #define TLS1_FLAGS_KEEP_HANDSHAKE		0x0020
 
+/* SSL3_FLAGS_SGC_RESTART_DONE is set when we
+ * restart a handshake because of MS SGC and so prevents us
+ * from restarting the handshake in a loop. It's reset on a
+ * renegotiation, so effectively limits the client to one restart
+ * per negotiation. This limits the possibility of a DDoS
+ * attack where the client handshakes in a loop using SGC to
+ * restart. Servers which permit renegotiation can still be
+ * effected, but we can't prevent that.
+ */
+#define SSL3_FLAGS_SGC_RESTART_DONE		0x0040
+
 #ifndef OPENSSL_NO_SSL_INTERN
 
 typedef struct ssl3_state_st
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 0c3838a55d..4eb2e44f5d 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -138,6 +138,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
 {ERR_FUNC(SSL_F_SSL3_CALLBACK_CTRL),	"SSL3_CALLBACK_CTRL"},
 {ERR_FUNC(SSL_F_SSL3_CHANGE_CIPHER_STATE),	"SSL3_CHANGE_CIPHER_STATE"},
 {ERR_FUNC(SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM),	"SSL3_CHECK_CERT_AND_ALGORITHM"},
+{ERR_FUNC(SSL_F_SSL3_CHECK_CLIENT_HELLO),	"SSL3_CHECK_CLIENT_HELLO"},
 {ERR_FUNC(SSL_F_SSL3_CLIENT_HELLO),	"SSL3_CLIENT_HELLO"},
 {ERR_FUNC(SSL_F_SSL3_CONNECT),	"SSL3_CONNECT"},
 {ERR_FUNC(SSL_F_SSL3_CTRL),	"SSL3_CTRL"},
@@ -420,6 +421,7 @@ static ERR_STRING_DATA SSL_str_reasons[]=
 {ERR_REASON(SSL_R_MISSING_TMP_RSA_KEY)   ,"missing tmp rsa key"},
 {ERR_REASON(SSL_R_MISSING_TMP_RSA_PKEY)  ,"missing tmp rsa pkey"},
 {ERR_REASON(SSL_R_MISSING_VERIFY_MESSAGE),"missing verify message"},
+{ERR_REASON(SSL_R_MULTIPLE_SGC_RESTARTS) ,"multiple sgc restarts"},
 {ERR_REASON(SSL_R_NON_SSLV2_INITIAL_PACKET),"non sslv2 initial packet"},
 {ERR_REASON(SSL_R_NO_CERTIFICATES_RETURNED),"no certificates returned"},
 {ERR_REASON(SSL_R_NO_CERTIFICATE_ASSIGNED),"no certificate assigned"},
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index c5c805cce2..e38bd9f0ba 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -812,17 +812,20 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
 		}
 
 #ifndef OPENSSL_NO_HEARTBEATS
-	/* Add Heartbeat extension */
-	s2n(TLSEXT_TYPE_heartbeat,ret);
-	s2n(1,ret);
-	/* Set mode:
-	 * 1: peer may send requests
-	 * 2: peer not allowed to send requests
-	 */
-	if (s->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_RECV_REQUESTS)
-		*(ret++) = SSL_TLSEXT_HB_DONT_SEND_REQUESTS;
-	else
-		*(ret++) = SSL_TLSEXT_HB_ENABLED;
+	/* Add Heartbeat extension if we've received one */
+	if (s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED)
+		{
+		s2n(TLSEXT_TYPE_heartbeat,ret);
+		s2n(1,ret);
+		/* Set mode:
+		 * 1: peer may send requests
+		 * 2: peer not allowed to send requests
+		 */
+		if (s->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_RECV_REQUESTS)
+			*(ret++) = SSL_TLSEXT_HB_DONT_SEND_REQUESTS;
+		else
+			*(ret++) = SSL_TLSEXT_HB_ENABLED;
+		}
 #endif
 
 #ifndef OPENSSL_NO_NEXTPROTONEG
-- 
2.25.1