Dear OpenSSL developers,

when having a glance a the source code of the heartbeat feature, I
spontaneously found a flaw in then programming style that probably
concealed what caused the vulnerability.

I created a pull request on Github. Then I noticed that someone
else wrote a similar fix. I submitted a pull request to this user,
"s0enke", and he merged. You can find s0enke's and my porposal as
pull requests 59 and 62 on Gitub/openssl; the pull request merged
by s0enke is at:

  https://github.com/s0enke/openssl/pull/1

According to the "Contribute" section in the README, I write this
mail and attach a unified diff for patching the original
repository.

I admit that I myself just compiled it but did not run it. I have
no information what s0enke did.

Thanks for you interest so far.

Bertram Scharpf


-- 
Bertram Scharpf
Bopserstraße 15, D-70180 Stuttgart
Ruf 0711/2599337, Fax 0711/2184509                   [13*79*2531,prim]
http://www.bertram-scharpf.de

diff --git a/ssl/Makefile b/ssl/Makefile
index e5df585..a88e0d1 100644
--- a/ssl/Makefile
+++ b/ssl/Makefile
@@ -30,7 +30,8 @@ LIBSRC=	\
 	ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \
 	ssl_ciph.c ssl_stat.c ssl_rsa.c \
 	ssl_asn1.c ssl_txt.c ssl_algs.c ssl_conf.c \
-	bio_ssl.c ssl_err.c kssl.c t1_reneg.c tls_srp.c t1_trce.c
+	bio_ssl.c ssl_err.c kssl.c t1_reneg.c tls_srp.c t1_trce.c \
+	heartbeat.c
 LIBOBJ= \
 	s2_meth.o  s2_srvr.o  s2_clnt.o  s2_lib.o  s2_enc.o s2_pkt.o \
 	s3_meth.o  s3_srvr.o  s3_clnt.o  s3_lib.o  s3_enc.o s3_pkt.o s3_both.o s3_cbc.o \
@@ -41,7 +42,8 @@ LIBOBJ= \
 	ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \
 	ssl_ciph.o ssl_stat.o ssl_rsa.o \
 	ssl_asn1.o ssl_txt.o ssl_algs.o ssl_conf.o \
-	bio_ssl.o ssl_err.o kssl.o t1_reneg.o tls_srp.o t1_trce.o
+	bio_ssl.o ssl_err.o kssl.o t1_reneg.o tls_srp.o t1_trce.o \
+	heartbeat.o
 
 SRC= $(LIBSRC)
 
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index d8bcd58..bdf7c94 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -1330,35 +1330,27 @@ dtls1_process_heartbeat(SSL *s)
 	unsigned int payload;
 	unsigned int padding = 16; /* Use minimum padding */
 
-	if (s->msg_callback)
-		s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
-			&s->s3->rrec.data[0], s->s3->rrec.length,
-			s, s->msg_callback_arg);
+	apply_msg_callback(s);
 
-	/* Read type and payload length first */
-	if (1 + 2 + 16 > s->s3->rrec.length)
+	/* Read type and payload length */
+	if (heartbeat_size_std(0) > s->s3->rrec.length)
 		return 0; /* silently discard */
 	hbtype = *p++;
 	n2s(p, payload);
-	if (1 + 2 + payload + 16 > s->s3->rrec.length)
+	if (heartbeat_size_std(payload) > s->s3->rrec.length)
 		return 0; /* silently discard per RFC 6520 sec. 4 */
 	pl = p;
 
 	if (hbtype == TLS1_HB_REQUEST)
 		{
 		unsigned char *buffer, *bp;
-		unsigned int write_length = 1 /* heartbeat type */ +
-					    2 /* heartbeat length */ +
-					    payload + padding;
+		unsigned int write_length = heartbeat_size(payload, padding);
 		int r;
 
 		if (write_length > SSL3_RT_MAX_PLAIN_LENGTH)
 			return 0;
 
-		/* Allocate memory for the response, size is 1 byte
-		 * message type, plus 2 bytes payload length, plus
-		 * payload, plus padding
-		 */
+		/* Allocate memory for the response. */
 		buffer = OPENSSL_malloc(write_length);
 		bp = buffer;
 
@@ -1409,6 +1401,7 @@ dtls1_heartbeat(SSL *s)
 	int ret;
 	unsigned int payload = 18; /* Sequence number + random bytes */
 	unsigned int padding = 16; /* Use minimum padding */
+	unsigned int size;
 
 	/* Only send if peer supports and accepts HB requests... */
 	if (!(s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED) ||
@@ -1440,13 +1433,9 @@ dtls1_heartbeat(SSL *s)
 	/* Create HeartBeat message, we just use a sequence number
 	 * as payload to distuingish different messages and add
 	 * some random stuff.
-	 *  - Message Type, 1 byte
-	 *  - Payload Length, 2 bytes (unsigned int)
-	 *  - Payload, the sequence number (2 bytes uint)
-	 *  - Payload, random bytes (16 bytes uint)
-	 *  - Padding
 	 */
-	buf = OPENSSL_malloc(1 + 2 + payload + padding);
+	size = heartbeat_size(payload, padding);
+	buf = OPENSSL_malloc(size);
 	p = buf;
 	/* Message Type */
 	*p++ = TLS1_HB_REQUEST;
@@ -1460,12 +1449,12 @@ dtls1_heartbeat(SSL *s)
 	/* Random padding */
 	RAND_pseudo_bytes(p, padding);
 
-	ret = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
+	ret = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buf, size);
 	if (ret >= 0)
 		{
 		if (s->msg_callback)
 			s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
-				buf, 3 + payload + padding,
+				buf, size,
 				s, s->msg_callback_arg);
 
 		dtls1_start_timer(s);
diff --git a/ssl/heartbeat.c b/ssl/heartbeat.c
new file mode 100644
index 0000000..eae91d3
--- /dev/null
+++ b/ssl/heartbeat.c
@@ -0,0 +1,22 @@
+#include "heartbeat.h"
+
+int heartbeat_size(int payload, int padding)
+	{
+	return  1 /* heartbeat type */   +
+		2 /* heartbeat length */ +
+		payload + padding;
+	}
+
+int heartbeat_size_std(int payload)
+	{
+	return heartbeat_size(payload, 16);
+	}
+
+void apply_msg_callback(SSL *s)
+	{
+	if (s->msg_callback)
+		s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
+			&s->s3->rrec.data[0], s->s3->rrec.length,
+			s, s->msg_callback_arg);
+	}
+
diff --git a/ssl/heartbeat.h b/ssl/heartbeat.h
new file mode 100644
index 0000000..24b9a01
--- /dev/null
+++ b/ssl/heartbeat.h
@@ -0,0 +1,11 @@
+#ifndef _INCL_HEARTBEAT_H
+#define _INCL_HEARTBEAT_H
+
+#include <openssl/ssl.h>
+
+int heartbeat_size(int payload, int padding);
+int heartbeat_size_std(int payload);
+
+void apply_msg_callback(SSL *s);
+
+#endif
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 0f51594..3498d69 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -165,6 +165,10 @@
 #include <openssl/ssl.h>
 #include <openssl/symhacks.h>
 
+#ifndef OPENSSL_NO_HEARTBEATS
+#include "heartbeat.h"
+#endif
+
 #ifdef OPENSSL_BUILD_SHLIBSSL
 # undef OPENSSL_EXTERN
 # define OPENSSL_EXTERN OPENSSL_EXPORT
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index bcb99b8..cd64165 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -3968,18 +3968,16 @@ tls1_process_heartbeat(SSL *s)
 	unsigned short hbtype;
 	unsigned int payload;
 	unsigned int padding = 16; /* Use minimum padding */
+	unsigned int size;
 
-	if (s->msg_callback)
-		s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
-			&s->s3->rrec.data[0], s->s3->rrec.length,
-			s, s->msg_callback_arg);
+	apply_msg_callback(s);
 
-	/* Read type and payload length first */
-	if (1 + 2 + 16 > s->s3->rrec.length)
+	/* Read type and payload length */
+	if (heartbeat_size_std(0) > s->s3->rrec.length)
 		return 0; /* silently discard */
 	hbtype = *p++;
 	n2s(p, payload);
-	if (1 + 2 + payload + 16 > s->s3->rrec.length)
+	if (heartbeat_size_std(payload) > s->s3->rrec.length)
 		return 0; /* silently discard per RFC 6520 sec. 4 */
 	pl = p;
 
@@ -3988,11 +3986,9 @@ tls1_process_heartbeat(SSL *s)
 		unsigned char *buffer, *bp;
 		int r;
 
-		/* Allocate memory for the response, size is 1 bytes
-		 * message type, plus 2 bytes payload length, plus
-		 * payload, plus padding
-		 */
-		buffer = OPENSSL_malloc(1 + 2 + payload + padding);
+		/* Allocate memory for the response. */
+		size = heartbeat_size(payload, padding);
+		buffer = OPENSSL_malloc(size);
 		bp = buffer;
 		
 		/* Enter response type, length and copy payload */
@@ -4003,11 +3999,11 @@ tls1_process_heartbeat(SSL *s)
 		/* Random padding */
 		RAND_pseudo_bytes(bp, padding);
 
-		r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+		r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, size);
 
 		if (r >= 0 && s->msg_callback)
 			s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
-				buffer, 3 + payload + padding,
+				buffer, size,
 				s, s->msg_callback_arg);
 
 		OPENSSL_free(buffer);
@@ -4041,6 +4037,7 @@ tls1_heartbeat(SSL *s)
 	int ret;
 	unsigned int payload = 18; /* Sequence number + random bytes */
 	unsigned int padding = 16; /* Use minimum padding */
+	unsigned int size;
 
 	/* Only send if peer supports and accepts HB requests... */
 	if (!(s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED) ||
@@ -4072,13 +4069,9 @@ tls1_heartbeat(SSL *s)
 	/* Create HeartBeat message, we just use a sequence number
 	 * as payload to distuingish different messages and add
 	 * some random stuff.
-	 *  - Message Type, 1 byte
-	 *  - Payload Length, 2 bytes (unsigned int)
-	 *  - Payload, the sequence number (2 bytes uint)
-	 *  - Payload, random bytes (16 bytes uint)
-	 *  - Padding
 	 */
-	buf = OPENSSL_malloc(1 + 2 + payload + padding);
+	size = heartbeat_size(payload, padding);
+	buf = OPENSSL_malloc(size);
 	p = buf;
 	/* Message Type */
 	*p++ = TLS1_HB_REQUEST;
@@ -4092,12 +4085,12 @@ tls1_heartbeat(SSL *s)
 	/* Random padding */
 	RAND_pseudo_bytes(p, padding);
 
-	ret = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
+	ret = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buf, size);
 	if (ret >= 0)
 		{
 		if (s->msg_callback)
 			s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
-				buf, 3 + payload + padding,
+				buf, size,
 				s, s->msg_callback_arg);
 
 		s->tlsext_hb_pending = 1;

Reply via email to