Skip to content
Browse files

PR: 2658

Submitted by: Robin Seggelmann <seggelmann@fh-muenster.de>
Reviewed by: steve

Support for TLS/DTLS heartbeats.
  • Loading branch information...
1 parent 578519e commit bd6941cfaa31ee8a3f8661cb98227a5cbcc0f9f3 @snhenson snhenson committed
Showing with 561 additions and 4 deletions.
  1. +3 −0 CHANGES
  2. +20 −0 apps/s_cb.c
  3. +8 −0 apps/s_client.c
  4. +10 −0 apps/s_server.c
  5. +151 −1 ssl/d1_both.c
  6. +13 −0 ssl/d1_clnt.c
  7. +8 −0 ssl/d1_lib.c
  8. +13 −0 ssl/d1_pkt.c
  9. +13 −0 ssl/d1_srvr.c
  10. +1 −1 ssl/dtls1.h
  11. +12 −0 ssl/s3_clnt.c
  12. +21 −0 ssl/s3_lib.c
  13. +13 −0 ssl/s3_pkt.c
  14. +12 −0 ssl/s3_srvr.c
  15. +24 −2 ssl/ssl.h
  16. +4 −0 ssl/ssl3.h
  17. +4 −0 ssl/ssl_err.c
  18. +7 −0 ssl/ssl_locl.h
  19. +211 −0 ssl/t1_lib.c
  20. +13 −0 ssl/tls1.h
View
3 CHANGES
@@ -4,6 +4,9 @@
Changes between 1.0.0f and 1.0.1 [xx XXX xxxx]
+ *) Add support for TLS/DTLS heartbeats.
+ [Robin Seggelmann <seggelmann@fh-muenster.de>]
+
*) Improved PRNG seeding for VOS.
[Paul Green <Paul.Green@stratus.com>]
View
20 apps/s_cb.c
@@ -600,6 +600,26 @@ void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *
}
}
}
+
+#ifndef OPENSSL_NO_HEARTBEATS
+ if (content_type == 24) /* Heartbeat */
+ {
+ str_details1 = ", Heartbeat";
+
+ if (len > 0)
+ {
+ switch (((const unsigned char*)buf)[0])
+ {
+ case 1:
+ str_details1 = ", HeartbeatRequest";
+ break;
+ case 2:
+ str_details1 = ", HeartbeatResponse";
+ break;
+ }
+ }
+ }
+#endif
}
BIO_printf(bio, "%s %s%s [length %04lx]%s%s\n", str_write_p, str_version, str_content_type, (unsigned long)len, str_details1, str_details2);
View
8 apps/s_client.c
@@ -1862,6 +1862,14 @@ printf("read=%d pending=%d peek=%d\n",k,SSL_pending(con),SSL_peek(con,zbuf,10240
SSL_renegotiate(con);
cbuf_len=0;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ else if ((!c_ign_eof) && (cbuf[0] == 'B'))
+ {
+ BIO_printf(bio_err,"HEARTBEATING\n");
+ SSL_heartbeat(con);
+ cbuf_len=0;
+ }
+#endif
else
{
cbuf_len=i;
View
10 apps/s_server.c
@@ -2192,6 +2192,16 @@ static int sv_body(char *hostname, int s, unsigned char *context)
goto err;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ if ((buf[0] == 'B') &&
+ ((buf[1] == '\n') || (buf[1] == '\r')))
+ {
+ BIO_printf(bio_err,"HEARTBEATING\n");
+ SSL_heartbeat(con);
+ i=0;
+ continue;
+ }
+#endif
if ((buf[0] == 'r') &&
((buf[1] == '\n') || (buf[1] == '\r')))
{
View
152 ssl/d1_both.c
@@ -1084,7 +1084,11 @@ int dtls1_read_failed(SSL *s, int code)
return code;
}
- if ( ! SSL_in_init(s)) /* done, no need to send a retransmit */
+#ifndef OPENSSL_NO_HEARTBEATS
+ if (!SSL_in_init(s) && !s->tlsext_hb_pending) /* done, no need to send a retransmit */
+#else
+ if (!SSL_in_init(s)) /* done, no need to send a retransmit */
+#endif
{
BIO_set_flags(SSL_get_rbio(s), BIO_FLAGS_READ);
return code;
@@ -1438,3 +1442,149 @@ int dtls1_shutdown(SSL *s)
#endif
return ret;
}
+
+#ifndef OPENSSL_NO_HEARTBEATS
+int
+dtls1_process_heartbeat(SSL *s)
+ {
+ unsigned char *p = &s->s3->rrec.data[0], *pl;
+ unsigned short hbtype;
+ unsigned int payload;
+ unsigned int padding = 16; /* Use minimum padding */
+
+ /* Read type and payload length first */
+ hbtype = *p++;
+ n2s(p, payload);
+ pl = p;
+
+ 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);
+
+ if (hbtype == TLS1_HB_REQUEST)
+ {
+ unsigned char *buffer, *bp;
+ int r;
+
+ /* Allocate memory for the response, size is 1 byte
+ * message type, plus 2 bytes payload length, plus
+ * payload, plus padding
+ */
+ buffer = OPENSSL_malloc(1 + 2 + payload + padding);
+ bp = buffer;
+
+ /* Enter response type, length and copy payload */
+ *bp++ = TLS1_HB_RESPONSE;
+ s2n(payload, bp);
+ memcpy(bp, pl, payload);
+ /* Random padding */
+ RAND_pseudo_bytes(p, padding);
+
+ r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+
+ if (r >= 0 && s->msg_callback)
+ s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+ buffer, 3 + payload + padding,
+ s, s->msg_callback_arg);
+
+ OPENSSL_free(buffer);
+
+ if (r < 0)
+ return r;
+ }
+ else if (hbtype == TLS1_HB_RESPONSE)
+ {
+ unsigned int seq;
+
+ /* We only send sequence numbers (2 bytes unsigned int),
+ * and 16 random bytes, so we just try to read the
+ * sequence number */
+ n2s(pl, seq);
+
+ if (payload == 18 && seq == s->tlsext_hb_seq)
+ {
+ dtls1_stop_timer(s);
+ s->tlsext_hb_seq++;
+ s->tlsext_hb_pending = 0;
+ }
+ }
+
+ return 0;
+ }
+
+int
+dtls1_heartbeat(SSL *s)
+ {
+ unsigned char *buf, *p;
+ int ret;
+ unsigned int payload = 18; /* Sequence number + random bytes */
+ unsigned int padding = 16; /* Use minimum padding */
+
+ /* Only send if peer supports and accepts HB requests... */
+ if (!(s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED) ||
+ s->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_SEND_REQUESTS)
+ {
+ SSLerr(SSL_F_DTLS1_HEARTBEAT,SSL_R_TLS_HEARTBEAT_PEER_DOESNT_ACCEPT);
+ return -1;
+ }
+
+ /* ...and there is none in flight yet... */
+ if (s->tlsext_hb_pending)
+ {
+ SSLerr(SSL_F_DTLS1_HEARTBEAT,SSL_R_TLS_HEARTBEAT_PENDING);
+ return -1;
+ }
+
+ /* ...and no handshake in progress. */
+ if (SSL_in_init(s) || s->in_handshake)
+ {
+ SSLerr(SSL_F_DTLS1_HEARTBEAT,SSL_R_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ /* Check if padding is too long, payload and padding
+ * must not exceed 2^14 - 3 = 16381 bytes in total.
+ */
+ OPENSSL_assert(payload + padding <= 16381);
+
+ /* 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);
+ p = buf;
+ /* Message Type */
+ *p++ = TLS1_HB_REQUEST;
+ /* Payload length (18 bytes here) */
+ s2n(payload, p);
+ /* Sequence number */
+ s2n(s->tlsext_hb_seq, p);
+ /* 16 random bytes */
+ RAND_pseudo_bytes(p, 16);
+ p += 16;
+ /* Random padding */
+ RAND_pseudo_bytes(p, padding);
+
+ ret = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
+ if (ret >= 0)
+ {
+ if (s->msg_callback)
+ s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+ buf, 3 + payload + padding,
+ s, s->msg_callback_arg);
+
+ dtls1_start_timer(s);
+ s->tlsext_hb_pending = 1;
+ }
+
+ OPENSSL_free(buf);
+
+ return ret;
+ }
+#endif
View
13 ssl/d1_clnt.c
@@ -176,6 +176,19 @@ int dtls1_connect(SSL *s)
BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, s->in_handshake, NULL);
#endif
+#ifndef OPENSSL_NO_HEARTBEATS
+ /* If we're awaiting a HeartbeatResponse, pretend we
+ * already got and don't await it anymore, because
+ * Heartbeats don't make sense during handshakes anyway.
+ */
+ if (s->tlsext_hb_pending)
+ {
+ dtls1_stop_timer(s);
+ s->tlsext_hb_pending = 0;
+ s->tlsext_hb_seq++;
+ }
+#endif
+
for (;;)
{
state=s->state;
View
8 ssl/d1_lib.c
@@ -424,6 +424,14 @@ int dtls1_handle_timeout(SSL *s)
state->timeout.read_timeouts = 1;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ if (s->tlsext_hb_pending)
+ {
+ s->tlsext_hb_pending = 0;
+ return dtls1_heartbeat(s);
+ }
+#endif
+
dtls1_start_timer(s);
return dtls1_retransmit_buffered_messages(s);
}
View
13 ssl/d1_pkt.c
@@ -937,6 +937,19 @@ int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
dest = s->d1->alert_fragment;
dest_len = &s->d1->alert_fragment_len;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ else if (rr->type == TLS1_RT_HEARTBEAT)
+ {
+ dtls1_process_heartbeat(s);
+
+ /* Exit and notify application to read again */
+ rr->length = 0;
+ s->rwstate=SSL_READING;
+ BIO_clear_retry_flags(SSL_get_rbio(s));
+ BIO_set_retry_read(SSL_get_rbio(s));
+ return(-1);
+ }
+#endif
/* else it's a CCS message, or application data or wrong */
else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC)
{
View
13 ssl/d1_srvr.c
@@ -186,6 +186,19 @@ int dtls1_accept(SSL *s)
return(-1);
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ /* If we're awaiting a HeartbeatResponse, pretend we
+ * already got and don't await it anymore, because
+ * Heartbeats don't make sense during handshakes anyway.
+ */
+ if (s->tlsext_hb_pending)
+ {
+ dtls1_stop_timer(s);
+ s->tlsext_hb_pending = 0;
+ s->tlsext_hb_seq++;
+ }
+#endif
+
for (;;)
{
state=s->state;
View
2 ssl/dtls1.h
@@ -232,7 +232,7 @@ typedef struct dtls1_state_st
struct dtls1_timeout_st timeout;
- /* Indicates when the last handshake msg sent will timeout */
+ /* Indicates when the last handshake msg or heartbeat sent will timeout */
struct timeval next_timeout;
/* Timeout duration */
View
12 ssl/s3_clnt.c
@@ -203,6 +203,18 @@ int ssl3_connect(SSL *s)
s->in_handshake++;
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
+#ifndef OPENSSL_NO_HEARTBEATS
+ /* If we're awaiting a HeartbeatResponse, pretend we
+ * already got and don't await it anymore, because
+ * Heartbeats don't make sense during handshakes anyway.
+ */
+ if (s->tlsext_hb_pending)
+ {
+ s->tlsext_hb_pending = 0;
+ s->tlsext_hb_seq++;
+ }
+#endif
+
for (;;)
{
state=s->state;
View
21 ssl/s3_lib.c
@@ -3328,6 +3328,27 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
ret = 1;
break;
+#ifndef OPENSSL_NO_HEARTBEATS
+ case SSL_CTRL_TLS_EXT_SEND_HEARTBEAT:
+ if (SSL_version(s) == DTLS1_VERSION || SSL_version(s) == DTLS1_BAD_VER)
+ ret = dtls1_heartbeat(s);
+ else
+ ret = tls1_heartbeat(s);
+ break;
+
+ case SSL_CTRL_GET_TLS_EXT_HEARTBEAT_PENDING:
+ ret = s->tlsext_hb_pending;
+ break;
+
+ case SSL_CTRL_SET_TLS_EXT_HEARTBEAT_NO_REQUESTS:
+ if (larg)
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_DONT_RECV_REQUESTS;
+ else
+ s->tlsext_heartbeat &= ~SSL_TLSEXT_HB_DONT_RECV_REQUESTS;
+ ret = 1;
+ break;
+#endif
+
#endif /* !OPENSSL_NO_TLSEXT */
default:
break;
View
13 ssl/s3_pkt.c
@@ -1070,6 +1070,19 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
dest = s->s3->alert_fragment;
dest_len = &s->s3->alert_fragment_len;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ else if (rr->type == TLS1_RT_HEARTBEAT)
+ {
+ tls1_process_heartbeat(s);
+
+ /* Exit and notify application to read again */
+ rr->length = 0;
+ s->rwstate=SSL_READING;
+ BIO_clear_retry_flags(SSL_get_rbio(s));
+ BIO_set_retry_read(SSL_get_rbio(s));
+ return(-1);
+ }
+#endif
if (dest_maxlen > 0)
{
View
12 ssl/s3_srvr.c
@@ -236,6 +236,18 @@ int ssl3_accept(SSL *s)
return(-1);
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ /* If we're awaiting a HeartbeatResponse, pretend we
+ * already got and don't await it anymore, because
+ * Heartbeats don't make sense during handshakes anyway.
+ */
+ if (s->tlsext_hb_pending)
+ {
+ s->tlsext_hb_pending = 0;
+ s->tlsext_hb_seq++;
+ }
+#endif
+
for (;;)
{
state=s->state;
View
26 ssl/ssl.h
@@ -673,6 +673,11 @@ struct ssl_session_st
#define SSL_get_secure_renegotiation_support(ssl) \
SSL_ctrl((ssl), SSL_CTRL_GET_RI_SUPPORT, 0, NULL)
+#ifndef OPENSSL_NO_HEARTBEATS
+#define SSL_heartbeat(ssl) \
+ SSL_ctrl((ssl),SSL_CTRL_TLS_EXT_SEND_HEARTBEAT,0,NULL)
+#endif
+
void SSL_CTX_set_msg_callback(SSL_CTX *ctx, void (*cb)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg));
void SSL_set_msg_callback(SSL *ssl, void (*cb)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg));
#define SSL_CTX_set_msg_callback_arg(ctx, arg) SSL_CTX_ctrl((ctx), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg))
@@ -1331,8 +1336,16 @@ struct ssl_st
#define session_ctx initial_ctx
- STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; /* What we'll do */
- SRTP_PROTECTION_PROFILE *srtp_profile; /* What's been chosen */
+ STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; /* What we'll do */
+ SRTP_PROTECTION_PROFILE *srtp_profile; /* What's been chosen */
+
+ unsigned int tlsext_heartbeat; /* Is use of the Heartbeat extension negotiated?
+ 0: disabled
+ 1: enabled
+ 2: enabled, but not allowed to send Requests
+ */
+ unsigned int tlsext_hb_pending; /* Indicates if a HeartbeatRequest is in flight */
+ unsigned int tlsext_hb_seq; /* HeartbeatRequest sequence number */
#else
#define session_ctx ctx
#endif /* OPENSSL_NO_TLSEXT */
@@ -1575,6 +1588,11 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
#define SSL_CTRL_SET_TLS_EXT_SRP_USERNAME 79
#define SSL_CTRL_SET_TLS_EXT_SRP_STRENGTH 80
#define SSL_CTRL_SET_TLS_EXT_SRP_PASSWORD 81
+#ifndef OPENSSL_NO_HEARTBEATS
+#define SSL_CTRL_TLS_EXT_SEND_HEARTBEAT 85
+#define SSL_CTRL_GET_TLS_EXT_HEARTBEAT_PENDING 86
+#define SSL_CTRL_SET_TLS_EXT_HEARTBEAT_NO_REQUESTS 87
+#endif
#endif
#define DTLS_CTRL_GET_TIMEOUT 73
@@ -2050,6 +2068,7 @@ void ERR_load_SSL_strings(void);
#define SSL_F_DTLS1_GET_MESSAGE_FRAGMENT 253
#define SSL_F_DTLS1_GET_RECORD 254
#define SSL_F_DTLS1_HANDLE_TIMEOUT 297
+#define SSL_F_DTLS1_HEARTBEAT 314
#define SSL_F_DTLS1_OUTPUT_CERT_CHAIN 255
#define SSL_F_DTLS1_PREPROCESS_FRAGMENT 288
#define SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE 256
@@ -2240,6 +2259,7 @@ void ERR_load_SSL_strings(void);
#define SSL_F_TLS1_CHECK_SERVERHELLO_TLSEXT 274
#define SSL_F_TLS1_ENC 210
#define SSL_F_TLS1_EXPORT_KEYING_MATERIAL 312
+#define SSL_F_TLS1_HEARTBEAT 313
#define SSL_F_TLS1_PREPARE_CLIENTHELLO_TLSEXT 275
#define SSL_F_TLS1_PREPARE_SERVERHELLO_TLSEXT 276
#define SSL_F_TLS1_PRF 284
@@ -2494,6 +2514,8 @@ void ERR_load_SSL_strings(void);
#define SSL_R_TLSV1_UNRECOGNIZED_NAME 1112
#define SSL_R_TLSV1_UNSUPPORTED_EXTENSION 1110
#define SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER 232
+#define SSL_R_TLS_HEARTBEAT_PEER_DOESNT_ACCEPT 368
+#define SSL_R_TLS_HEARTBEAT_PENDING 369
#define SSL_R_TLS_ILLEGAL_EXPORTER_LABEL 367
#define SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST 157
#define SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST 233
View
4 ssl/ssl3.h
@@ -322,6 +322,7 @@ extern "C" {
#define SSL3_RT_ALERT 21
#define SSL3_RT_HANDSHAKE 22
#define SSL3_RT_APPLICATION_DATA 23
+#define TLS1_RT_HEARTBEAT 24
#define SSL3_AL_WARNING 1
#define SSL3_AL_FATAL 2
@@ -339,6 +340,9 @@ extern "C" {
#define SSL3_AD_CERTIFICATE_UNKNOWN 46
#define SSL3_AD_ILLEGAL_PARAMETER 47 /* fatal */
+#define TLS1_HB_REQUEST 1
+#define TLS1_HB_RESPONSE 2
+
#ifndef OPENSSL_NO_SSL_INTERN
typedef struct ssl3_record_st
View
4 ssl/ssl_err.c
@@ -88,6 +88,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
{ERR_FUNC(SSL_F_DTLS1_GET_MESSAGE_FRAGMENT), "DTLS1_GET_MESSAGE_FRAGMENT"},
{ERR_FUNC(SSL_F_DTLS1_GET_RECORD), "DTLS1_GET_RECORD"},
{ERR_FUNC(SSL_F_DTLS1_HANDLE_TIMEOUT), "DTLS1_HANDLE_TIMEOUT"},
+{ERR_FUNC(SSL_F_DTLS1_HEARTBEAT), "DTLS1_HEARTBEAT"},
{ERR_FUNC(SSL_F_DTLS1_OUTPUT_CERT_CHAIN), "DTLS1_OUTPUT_CERT_CHAIN"},
{ERR_FUNC(SSL_F_DTLS1_PREPROCESS_FRAGMENT), "DTLS1_PREPROCESS_FRAGMENT"},
{ERR_FUNC(SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE), "DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE"},
@@ -278,6 +279,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
{ERR_FUNC(SSL_F_TLS1_CHECK_SERVERHELLO_TLSEXT), "TLS1_CHECK_SERVERHELLO_TLSEXT"},
{ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"},
{ERR_FUNC(SSL_F_TLS1_EXPORT_KEYING_MATERIAL), "TLS1_EXPORT_KEYING_MATERIAL"},
+{ERR_FUNC(SSL_F_TLS1_HEARTBEAT), "SSL_F_TLS1_HEARTBEAT"},
{ERR_FUNC(SSL_F_TLS1_PREPARE_CLIENTHELLO_TLSEXT), "TLS1_PREPARE_CLIENTHELLO_TLSEXT"},
{ERR_FUNC(SSL_F_TLS1_PREPARE_SERVERHELLO_TLSEXT), "TLS1_PREPARE_SERVERHELLO_TLSEXT"},
{ERR_FUNC(SSL_F_TLS1_PRF), "tls1_prf"},
@@ -535,6 +537,8 @@ static ERR_STRING_DATA SSL_str_reasons[]=
{ERR_REASON(SSL_R_TLSV1_UNRECOGNIZED_NAME),"tlsv1 unrecognized name"},
{ERR_REASON(SSL_R_TLSV1_UNSUPPORTED_EXTENSION),"tlsv1 unsupported extension"},
{ERR_REASON(SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER),"tls client cert req with anon cipher"},
+{ERR_REASON(SSL_R_TLS_HEARTBEAT_PEER_DOESNT_ACCEPT),"peer does not accept heartbearts"},
+{ERR_REASON(SSL_R_TLS_HEARTBEAT_PENDING) ,"heartbeat request already pending"},
{ERR_REASON(SSL_R_TLS_ILLEGAL_EXPORTER_LABEL),"tls illegal exporter label"},
{ERR_REASON(SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST),"tls invalid ecpointformat list"},
{ERR_REASON(SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST),"tls peer did not respond with certificate list"},
View
7 ssl/ssl_locl.h
@@ -1090,6 +1090,13 @@ int ssl_prepare_serverhello_tlsext(SSL *s);
int ssl_check_clienthello_tlsext(SSL *s);
int ssl_check_serverhello_tlsext(SSL *s);
+#ifndef OPENSSL_NO_HEARTBEATS
+int tls1_heartbeat(SSL *s);
+int dtls1_heartbeat(SSL *s);
+int tls1_process_heartbeat(SSL *s);
+int dtls1_process_heartbeat(SSL *s);
+#endif
+
#ifdef OPENSSL_NO_SHA256
#define tlsext_tick_md EVP_sha1
#else
View
211 ssl/t1_lib.c
@@ -114,6 +114,7 @@
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/ocsp.h>
+#include <openssl/rand.h>
#include "ssl_locl.h"
const char tls1_version_str[]="TLSv1" OPENSSL_VERSION_PTEXT;
@@ -618,6 +619,20 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
i2d_X509_EXTENSIONS(s->tlsext_ocsp_exts, &ret);
}
+#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;
+#endif
+
#ifndef OPENSSL_NO_NEXTPROTONEG
if (s->ctx->next_proto_select_cb && !s->s3->tmp.finish_md_len)
{
@@ -796,6 +811,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;
+#endif
+
#ifndef OPENSSL_NO_NEXTPROTONEG
next_proto_neg_seen = s->s3->next_proto_neg_seen;
s->s3->next_proto_neg_seen = 0;
@@ -840,6 +869,11 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
s->s3->next_proto_neg_seen = 0;
#endif
+#ifndef OPENSSL_NO_HEARTBEATS
+ s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
+ SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
+#endif
+
if (data >= (d+n-2))
goto ri_check;
n2s(data,len);
@@ -1227,6 +1261,21 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
else
s->tlsext_status_type = -1;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ else if (type == TLSEXT_TYPE_heartbeat)
+ {
+ switch(data[0])
+ {
+ case 0x01: /* Client allows us to send HB requests */
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED;
+ break;
+ case 0x02: /* Client doesn't accept HB requests */
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED;
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_DONT_SEND_REQUESTS;
+ break;
+ }
+ }
+#endif
#ifndef OPENSSL_NO_NEXTPROTONEG
else if (type == TLSEXT_TYPE_next_proto_neg &&
s->s3->tmp.finish_md_len == 0)
@@ -1312,6 +1361,11 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
s->s3->next_proto_neg_seen = 0;
#endif
+#ifndef OPENSSL_NO_HEARTBEATS
+ s->tlsext_heartbeat &= ~(SSL_TLSEXT_HB_ENABLED |
+ SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
+#endif
+
if (data >= (d+n-2))
goto ri_check;
@@ -1478,6 +1532,21 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
return 0;
renegotiate_seen = 1;
}
+#ifndef OPENSSL_NO_HEARTBEATS
+ else if (type == TLSEXT_TYPE_heartbeat)
+ {
+ switch(data[0])
+ {
+ case 0x01: /* Server allows us to send HB requests */
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED;
+ break;
+ case 0x02: /* Server doesn't accept HB requests */
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_ENABLED;
+ s->tlsext_heartbeat |= SSL_TLSEXT_HB_DONT_SEND_REQUESTS;
+ break;
+ }
+ }
+#endif
else if (type == TLSEXT_TYPE_use_srtp)
{
if(ssl_parse_serverhello_use_srtp_ext(s, data, size,
@@ -2330,3 +2399,145 @@ int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize)
}
#endif
+
+#ifndef OPENSSL_NO_HEARTBEATS
+int
+tls1_process_heartbeat(SSL *s)
+ {
+ unsigned char *p = &s->s3->rrec.data[0], *pl;
+ unsigned short hbtype;
+ unsigned int payload;
+ unsigned int padding = 16; /* Use minimum padding */
+
+ /* Read type and payload length first */
+ hbtype = *p++;
+ n2s(p, payload);
+ pl = p;
+
+ 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);
+
+ if (hbtype == TLS1_HB_REQUEST)
+ {
+ 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);
+ bp = buffer;
+
+ /* Enter response type, length and copy payload */
+ *bp++ = TLS1_HB_RESPONSE;
+ s2n(payload, bp);
+ memcpy(bp, pl, payload);
+
+ r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
+
+ if (r >= 0 && s->msg_callback)
+ s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+ buffer, 3 + payload + padding,
+ s, s->msg_callback_arg);
+
+ OPENSSL_free(buffer);
+
+ if (r < 0)
+ return r;
+ }
+ else if (hbtype == TLS1_HB_RESPONSE)
+ {
+ unsigned int seq;
+
+ /* We only send sequence numbers (2 bytes unsigned int),
+ * and 16 random bytes, so we just try to read the
+ * sequence number */
+ n2s(pl, seq);
+
+ if (payload == 18 && seq == s->tlsext_hb_seq)
+ {
+ s->tlsext_hb_seq++;
+ s->tlsext_hb_pending = 0;
+ }
+ }
+
+ return 0;
+ }
+
+int
+tls1_heartbeat(SSL *s)
+ {
+ unsigned char *buf, *p;
+ int ret;
+ unsigned int payload = 18; /* Sequence number + random bytes */
+ unsigned int padding = 16; /* Use minimum padding */
+
+ /* Only send if peer supports and accepts HB requests... */
+ if (!(s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED) ||
+ s->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_SEND_REQUESTS)
+ {
+ SSLerr(SSL_F_TLS1_HEARTBEAT,SSL_R_TLS_HEARTBEAT_PEER_DOESNT_ACCEPT);
+ return -1;
+ }
+
+ /* ...and there is none in flight yet... */
+ if (s->tlsext_hb_pending)
+ {
+ SSLerr(SSL_F_TLS1_HEARTBEAT,SSL_R_TLS_HEARTBEAT_PENDING);
+ return -1;
+ }
+
+ /* ...and no handshake in progress. */
+ if (SSL_in_init(s) || s->in_handshake)
+ {
+ SSLerr(SSL_F_TLS1_HEARTBEAT,SSL_R_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ /* Check if padding is too long, payload and padding
+ * must not exceed 2^14 - 3 = 16381 bytes in total.
+ */
+ OPENSSL_assert(payload + padding <= 16381);
+
+ /* 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);
+ p = buf;
+ /* Message Type */
+ *p++ = TLS1_HB_REQUEST;
+ /* Payload length (18 bytes here) */
+ s2n(payload, p);
+ /* Sequence number */
+ s2n(s->tlsext_hb_seq, p);
+ /* 16 random bytes */
+ RAND_pseudo_bytes(p, 16);
+ p += 16;
+ /* Random padding */
+ RAND_pseudo_bytes(p, padding);
+
+ ret = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
+ if (ret >= 0)
+ {
+ if (s->msg_callback)
+ s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
+ buf, 3 + payload + padding,
+ s, s->msg_callback_arg);
+
+ s->tlsext_hb_pending = 1;
+ }
+
+ OPENSSL_free(buf);
+
+ return ret;
+ }
+#endif
View
13 ssl/tls1.h
@@ -256,6 +256,9 @@ extern "C" {
/* ExtensionType value from RFC5764 */
#define TLSEXT_TYPE_use_srtp 14
+/* Heartbeat extension */
+#define TLSEXT_TYPE_heartbeat 15
+
#ifndef OPENSSL_NO_TLSEXT
#define TLSEXT_MAXLEN_host_name 255
@@ -335,6 +338,16 @@ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB_ARG, 0, arg)
#define SSL_CTX_set_tlsext_ticket_key_cb(ssl, cb) \
SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB,(void (*)(void))cb)
+#ifndef OPENSSL_NO_HEARTBEATS
+#define SSL_TLSEXT_HB_ENABLED 0x01
+#define SSL_TLSEXT_HB_DONT_SEND_REQUESTS 0x02
+#define SSL_TLSEXT_HB_DONT_RECV_REQUESTS 0x04
+
+#define SSL_get_tlsext_heartbeat_pending(ssl) \
+ SSL_ctrl((ssl),SSL_CTRL_GET_TLS_EXT_HEARTBEAT_PENDING,0,NULL)
+#define SSL_set_tlsext_heartbeat_no_requests(ssl, arg) \
+ SSL_ctrl((ssl),SSL_CTRL_SET_TLS_EXT_HEARTBEAT_NO_REQUESTS,arg,NULL)
+#endif
#endif
/* PSK ciphersuites from 4279 */

29 comments on commit bd6941c

@Atomfire

Doh!

@scp

The ramifications are of truly epic proportions.

@dijit

I don't see you guys implementing this.

perhaps you should criticize less, and maybe peruse the code looking for other bugs.

@mcmillan

@Dijit That's a little uncalled for. I don't think anyone here is directly criticising this commit or its authors (everybody writes bugs!) however the code here is currently causing massive ramifications for a large chunk of the internet and it's therefore worthy of note in at least some way.

@AnthonyAkentiev

Oh, shit!

@zeroasterisk

I too have had some bad commits... or even good commits with horrible unforeseen ramifications.

I'll totally buy you a drink @snhenson -- I bet you could use one.

Best of luck all.

@Amelek

Sorry, but whoever wrote this rubbish shouldn't be allowed near C compiler, much less critical libraries like this one. Also, it is blatantly obvious that this code was either not reviewed, or reviewer was at least as incompetent as person who wrote this.

Take a look:
@tls1_heartbeat(SSL *s)

    buf = OPENSSL_malloc(1 + 2 + payload + padding);
    p = buf;
    /* Message Type */
    *p++ = TLS1_HB_REQUEST;

and
@tls1_process_heartbeat(SSL *s)

        buffer = OPENSSL_malloc(1 + 2 + payload + padding);
        bp = buffer;

        /* Enter response type, length and copy payload */
        *bp++ = TLS1_HB_RESPONSE;

one does not simply check if malloc failed? Mind you, that all other calls to OPENSSL_malloc in relevant file do check if null was returned, so it wasn't designed in infinite memory / abort on failure environment. How had such code even passed the review?

Also, I love how it's "1 + 2 + payload + padding" in one place and "3 + payload + padding" in other, in case someone was attempting to understand this.

@AdditionalPylons

@Amelek I agree, but it's not only this guy. The OpenSSL source is plagued with antiquated and terrible programming practices - more specifically the terrible naming conventions (or lack thereof) that are completely non-descriptive, ambiguous, and quite frankly just plain stupid. Look at char * buffer, char * p, int r, n2s and s2n.

It's impossible to know what these are without reading the code around it for context. Why not just put the extra effort and name it response_buffer and response_buffer_ptr and save time for everyone else who has to inspect that code.

These bugs happen because the people who write them can neither read nor review it themselves, and obviously neither can anybody else without spending a disproportionate amount of time figuring out what the hell you were trying to do.

Also if you use bp++ =, shave your unix beard and remember you live in 2014. Writing code for a large scale project doesn't need any of your 1337 bullsht.

@brbeaird

Classic Robin and steve.

@mculp

Fucking steve.

@kivikakk

@AdditionalPylons

Also if you use *bp++ =, shave your unix beard and remember you live in 2014.

Are you serious? Who doesn't use this?

@Mackaber

People should write and review the code (preferably while still in dev) instead of ranting about it...

@Amelek

@kivikakk

Are you serious? Who doesn't use this?

Probably everybody uses this. There is however major difference between everybody and person writing user-facing code of one of the most critical libraries used in Linux environment. There is no place for 1337 code in OpenSSL nor any user exposed code. And even if we stomach single letter local variables, look at this:

&s->s3->rrec.data[0]

"s" is SSL* context, then "s3" is ssl3_state_st*, "rrec" is actually decrypted user content. How is this 21 century coding practice? And &x[0] seem to be an idiom for people who never attempted to understand C.

Tbh, I think this whole patch should be reverted and this feature removed. There is no need to have keepalive protocol on the encryption layer, and even if there exists some bizarre environment where this is required, keepalive protocol should not allow user defined payload. 64kb of user payload is criminal.

@tylerl

@Amelek The thread needed a "this programmer shouldn't be allowed near a computer" Dunning-Kruger representative. So thanks for stepping up.

The effective problem with this code, as I'm sure you can figure out by reading your nearest tech journal, has nothing to do with anything you've pointed out. Yes, the style of OpenSSL is arcane, but it's consistently so throughout the library. That's not the problem. This particular change set is neither worse nor better than the core of the library.

If you want to understand what's wrong with the OpenSSL code, compare it against the Linux kernel code. The kernel is the largest collaborative software project in the world and is easily one of the most readable, but by your standards, none of the developers should be allowed near a compiler. Just look at those variable names! And the pointer arithmetic!

No, If you want to understand what's really wrong with OpenSSL, look through the code to see how a function like EVP_CIPHER_CTX_new() is implemented. Along the way you'll discover a very key difference between the two codebases. That is the real reason why you shouldn't trust OpenSSL. Not this code here. This was just a logic failure which would have been just as easy to make if your own coding standards had been followed.

@jpgohlke

@tylerl Damn it. You had to give just enough tantalizing hints without any explanation, so that now I have to spend the next day or two crawling through code bases to satisfy my curiosity. Haha

@okorz001

@Amelek

FWIW, I've worked with a compiler where &x[0] was necessary because char[] would not implicitly decay to char*. I believe even the C89 standard requires this decay, but you've got to work with the toolchain you're given.

@nolim1t

Epic

@sensahin

Dope!

@thirdknife

Nightmare!

@O1O1O1O

So it is generally the case for OpenSSL that major additions of features have no tests for them whatsoever? That makes me sad. Shouldn't I be able to have my browser/client/server refuse SSL connections with implementations that I don't trust or are know to have flaws? Sure an endpoint could lie about what code it was running, but it would at least be effective against non-malicious flaws.

@onetom

I just wanted to ask the same as @O1O1O1O. Even small projects doesn't accept contributions without the corresponding test suite... But hey, it's written in C, what do you expect... Not that I know of any seriously practical OS or core library which was written in a test driven way, using some other language than C, BUT maybe it's time to raise the bar and start leaving this computing dark medieval age behind us...

We saw better security architecture before (centralized into the OS, seamlessly exposed to applications):
http://plan9.bell-labs.com/sys/doc/auth.html

Integrated systems has been written in a different language from C before (not suffering obvious plagues like, null pointer dereference or buffer overflow):
http://en.wikipedia.org/wiki/Lisp_machine

Multi-core friendly, high performance code can be generated from a domain friendly custom language:
http://nv.github.io/nile/demo_shape.html

And let's not forget the newly budding, full-stack language, which can bring these concepts under one, common hood:
http://www.red-lang.org/
(which is kind of following the principles of low fat computing: http://www.ultratechnology.com/lowfat.htm)

I'm aware of http://www.jwz.org/doc/worse-is-better.html but does it really have to be that way?

@andrewchambers

@Amelek
I would just like to point at that you have no public repositories so I cant review your code at all. Perhaps you could submit a pull request renaming some variables so its more to your liking.

@Amelek

@andrewchambers
Doing full pull request seem like a waste of time to me and I don't really feel like contributing to that project now.

Regardless, there is refactored version of tls1_process_heartbeat
https://github.com/Amelek/RandomUploads/blob/master/openSSL/openssl_refactored.patch

Is it better? No - it's not reviewed and thus can't be used anywhere. There may be new bugs introduced in it.

Observation while doing that patch: OpenSSL's fixed code:

  • does not adhere to RFC - you can send requests up to 0xFFFF in length while 2^14 is max length
  • does not check if malloc failed - server will crash under load. Seem unlikely
  • does not check if random actually filled the buffer - server will leak 16 bytes of uninitialized memory if it hadn't. I doubt that it can be the case though.
  • I have no idea what msg_callback does and if it can change rrec or not. It does take entire SSL* as argument, so it has to be taken into consideration
@dennis-fedco

@Amelek I like the renamed variables. Truly while "hbtype" makes sense to you while you are writing the code, "heartbeat_type" is just so much more user friendly, and makes it for a much easier time when you or someone looks at the code later. And so I approve this message.

@4of92000

First person to find another critical bug with OpenSSL wins!

@Niteshvgupta

Can someone point out exactly where the actual bug is? I'm curious.

@Junkyard-Jack

It was simultaneously discovered after 2 long years by two independent groups? Really? That's so improbable. Anyone that's been in US government has heard the phrase, "Cover your ass". Sounds like one group knew for a while and only admitted it after an independent group reported it. Or were they using the same test suite? Seems like with that much money and safety at stake it shouldn't be criminals that discover and exploit these things first as has been confirmed in at least two places. They need to cut back on the coffee & cigarettes.

I'm not going to pretend I know whether or not this code is good or not - I just know that I avoided pointers when possible; something it seems would be near impossible to do in kernel code but very desirable when writing something like openSSL.

@O1O1O1O

@BarneysBombers I believe it was far more likely that someone, somewhere was either suspicious or actually new there was an issue with OpenSSL can somehow various teams were pointed in the right direction looking for holes. My guess is that there were investigations around the various Bitcoin exchanges getting plundered even when people said they had extremely secure passwords (but no 2FA). This is separate from any other Bitcoin related issues that resulted in losses although one can never rule out exchanges that had admin accounts publicly accessible and compromised due to the SSL issue. Of course some other high value targets like government or banking entities could also have gathered evidence that SSL was being compromised in a way they could not identify. If you had gathered enough reports you could correlate OpenSSL version to those reports and narrow you focus...

@Junkyard-Jack

Well, security isn't something that should be strictly outsourced. There needs to be competitive and competent security teams investigating these things internal to the government and big businesses as well. It's not like these organizations can't afford it. And it's not like if they couldn't afford it the government wouldn't print the money so that they could if they so desired, a bit like they all do with their defense budgets.

I find it a bit unreasonable to believe that the Russian, Chinese, Ukrainian, American, and various other security agencies didn't know about this flaw, almost from the get go. Indeed with the news about the NSA and the behavior of these governments it wouldn't be unreasonable to think this at all.

We do not need a situation in security such as what caused financial markets to collapse all over the world a few years back; where the 'independent regulatory and ratings outsiders, the experts' were almost as irresponsible as the financial firms and traders. Well, it's already been exposed it wasn't so much irresponsibility as corruption, regardless that I spend more time in jail as a 19 year old forgetting I had a traffic ticket than any of the perpetrators of those abuses.

Please sign in to comment.
Something went wrong with that request. Please try again.