Skip to content

Commit 4817504

Browse files
committed
PR: 2658
Submitted by: Robin Seggelmann <seggelmann@fh-muenster.de> Reviewed by: steve Support for TLS/DTLS heartbeats.
1 parent 84b6e27 commit 4817504

20 files changed

+561
-4
lines changed

CHANGES

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@
255255

256256
Changes between 1.0.0f and 1.0.1 [xx XXX xxxx]
257257

258+
*) Add support for TLS/DTLS heartbeats.
259+
[Robin Seggelmann <seggelmann@fh-muenster.de>]
260+
258261
*) Improved PRNG seeding for VOS.
259262
[Paul Green <Paul.Green@stratus.com>]
260263

apps/s_cb.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,26 @@ void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *
603603
}
604604
}
605605
}
606+
607+
#ifndef OPENSSL_NO_HEARTBEATS
608+
if (content_type == 24) /* Heartbeat */
609+
{
610+
str_details1 = ", Heartbeat";
611+
612+
if (len > 0)
613+
{
614+
switch (((const unsigned char*)buf)[0])
615+
{
616+
case 1:
617+
str_details1 = ", HeartbeatRequest";
618+
break;
619+
case 2:
620+
str_details1 = ", HeartbeatResponse";
621+
break;
622+
}
623+
}
624+
}
625+
#endif
606626
}
607627

608628
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);

apps/s_client.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,14 @@ printf("read=%d pending=%d peek=%d\n",k,SSL_pending(con),SSL_peek(con,zbuf,10240
18621862
SSL_renegotiate(con);
18631863
cbuf_len=0;
18641864
}
1865+
#ifndef OPENSSL_NO_HEARTBEATS
1866+
else if ((!c_ign_eof) && (cbuf[0] == 'B'))
1867+
{
1868+
BIO_printf(bio_err,"HEARTBEATING\n");
1869+
SSL_heartbeat(con);
1870+
cbuf_len=0;
1871+
}
1872+
#endif
18651873
else
18661874
{
18671875
cbuf_len=i;

apps/s_server.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,6 +2222,16 @@ static int sv_body(char *hostname, int s, unsigned char *context)
22222222
goto err;
22232223
}
22242224

2225+
#ifndef OPENSSL_NO_HEARTBEATS
2226+
if ((buf[0] == 'B') &&
2227+
((buf[1] == '\n') || (buf[1] == '\r')))
2228+
{
2229+
BIO_printf(bio_err,"HEARTBEATING\n");
2230+
SSL_heartbeat(con);
2231+
i=0;
2232+
continue;
2233+
}
2234+
#endif
22252235
if ((buf[0] == 'r') &&
22262236
((buf[1] == '\n') || (buf[1] == '\r')))
22272237
{

ssl/d1_both.c

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,11 @@ int dtls1_read_failed(SSL *s, int code)
10841084
return code;
10851085
}
10861086

1087-
if ( ! SSL_in_init(s)) /* done, no need to send a retransmit */
1087+
#ifndef OPENSSL_NO_HEARTBEATS
1088+
if (!SSL_in_init(s) && !s->tlsext_hb_pending) /* done, no need to send a retransmit */
1089+
#else
1090+
if (!SSL_in_init(s)) /* done, no need to send a retransmit */
1091+
#endif
10881092
{
10891093
BIO_set_flags(SSL_get_rbio(s), BIO_FLAGS_READ);
10901094
return code;
@@ -1438,3 +1442,149 @@ int dtls1_shutdown(SSL *s)
14381442
#endif
14391443
return ret;
14401444
}
1445+
1446+
#ifndef OPENSSL_NO_HEARTBEATS
1447+
int
1448+
dtls1_process_heartbeat(SSL *s)
1449+
{
1450+
unsigned char *p = &s->s3->rrec.data[0], *pl;
1451+
unsigned short hbtype;
1452+
unsigned int payload;
1453+
unsigned int padding = 16; /* Use minimum padding */
1454+
1455+
/* Read type and payload length first */
1456+
hbtype = *p++;
1457+
n2s(p, payload);
1458+
pl = p;
1459+
1460+
if (s->msg_callback)
1461+
s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
1462+
&s->s3->rrec.data[0], s->s3->rrec.length,
1463+
s, s->msg_callback_arg);
1464+
1465+
if (hbtype == TLS1_HB_REQUEST)
1466+
{
1467+
unsigned char *buffer, *bp;
1468+
int r;
1469+
1470+
/* Allocate memory for the response, size is 1 byte
1471+
* message type, plus 2 bytes payload length, plus
1472+
* payload, plus padding
1473+
*/
1474+
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
1475+
bp = buffer;
1476+
1477+
/* Enter response type, length and copy payload */
1478+
*bp++ = TLS1_HB_RESPONSE;
1479+
s2n(payload, bp);
1480+
memcpy(bp, pl, payload);
1481+
/* Random padding */
1482+
RAND_pseudo_bytes(p, padding);
1483+
1484+
r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);
1485+
1486+
if (r >= 0 && s->msg_callback)
1487+
s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
1488+
buffer, 3 + payload + padding,
1489+
s, s->msg_callback_arg);
1490+
1491+
OPENSSL_free(buffer);
1492+
1493+
if (r < 0)
1494+
return r;
1495+
}
1496+
else if (hbtype == TLS1_HB_RESPONSE)
1497+
{
1498+
unsigned int seq;
1499+
1500+
/* We only send sequence numbers (2 bytes unsigned int),
1501+
* and 16 random bytes, so we just try to read the
1502+
* sequence number */
1503+
n2s(pl, seq);
1504+
1505+
if (payload == 18 && seq == s->tlsext_hb_seq)
1506+
{
1507+
dtls1_stop_timer(s);
1508+
s->tlsext_hb_seq++;
1509+
s->tlsext_hb_pending = 0;
1510+
}
1511+
}
1512+
1513+
return 0;
1514+
}
1515+
1516+
int
1517+
dtls1_heartbeat(SSL *s)
1518+
{
1519+
unsigned char *buf, *p;
1520+
int ret;
1521+
unsigned int payload = 18; /* Sequence number + random bytes */
1522+
unsigned int padding = 16; /* Use minimum padding */
1523+
1524+
/* Only send if peer supports and accepts HB requests... */
1525+
if (!(s->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED) ||
1526+
s->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_SEND_REQUESTS)
1527+
{
1528+
SSLerr(SSL_F_DTLS1_HEARTBEAT,SSL_R_TLS_HEARTBEAT_PEER_DOESNT_ACCEPT);
1529+
return -1;
1530+
}
1531+
1532+
/* ...and there is none in flight yet... */
1533+
if (s->tlsext_hb_pending)
1534+
{
1535+
SSLerr(SSL_F_DTLS1_HEARTBEAT,SSL_R_TLS_HEARTBEAT_PENDING);
1536+
return -1;
1537+
}
1538+
1539+
/* ...and no handshake in progress. */
1540+
if (SSL_in_init(s) || s->in_handshake)
1541+
{
1542+
SSLerr(SSL_F_DTLS1_HEARTBEAT,SSL_R_UNEXPECTED_MESSAGE);
1543+
return -1;
1544+
}
1545+
1546+
/* Check if padding is too long, payload and padding
1547+
* must not exceed 2^14 - 3 = 16381 bytes in total.
1548+
*/
1549+
OPENSSL_assert(payload + padding <= 16381);
1550+
1551+
/* Create HeartBeat message, we just use a sequence number
1552+
* as payload to distuingish different messages and add
1553+
* some random stuff.
1554+
* - Message Type, 1 byte
1555+
* - Payload Length, 2 bytes (unsigned int)
1556+
* - Payload, the sequence number (2 bytes uint)
1557+
* - Payload, random bytes (16 bytes uint)
1558+
* - Padding
1559+
*/
1560+
buf = OPENSSL_malloc(1 + 2 + payload + padding);
1561+
p = buf;
1562+
/* Message Type */
1563+
*p++ = TLS1_HB_REQUEST;
1564+
/* Payload length (18 bytes here) */
1565+
s2n(payload, p);
1566+
/* Sequence number */
1567+
s2n(s->tlsext_hb_seq, p);
1568+
/* 16 random bytes */
1569+
RAND_pseudo_bytes(p, 16);
1570+
p += 16;
1571+
/* Random padding */
1572+
RAND_pseudo_bytes(p, padding);
1573+
1574+
ret = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buf, 3 + payload + padding);
1575+
if (ret >= 0)
1576+
{
1577+
if (s->msg_callback)
1578+
s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
1579+
buf, 3 + payload + padding,
1580+
s, s->msg_callback_arg);
1581+
1582+
dtls1_start_timer(s);
1583+
s->tlsext_hb_pending = 1;
1584+
}
1585+
1586+
OPENSSL_free(buf);
1587+
1588+
return ret;
1589+
}
1590+
#endif

ssl/d1_clnt.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ int dtls1_connect(SSL *s)
177177
BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE, s->in_handshake, NULL);
178178
#endif
179179

180+
#ifndef OPENSSL_NO_HEARTBEATS
181+
/* If we're awaiting a HeartbeatResponse, pretend we
182+
* already got and don't await it anymore, because
183+
* Heartbeats don't make sense during handshakes anyway.
184+
*/
185+
if (s->tlsext_hb_pending)
186+
{
187+
dtls1_stop_timer(s);
188+
s->tlsext_hb_pending = 0;
189+
s->tlsext_hb_seq++;
190+
}
191+
#endif
192+
180193
for (;;)
181194
{
182195
state=s->state;

ssl/d1_lib.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,14 @@ int dtls1_handle_timeout(SSL *s)
424424
state->timeout.read_timeouts = 1;
425425
}
426426

427+
#ifndef OPENSSL_NO_HEARTBEATS
428+
if (s->tlsext_hb_pending)
429+
{
430+
s->tlsext_hb_pending = 0;
431+
return dtls1_heartbeat(s);
432+
}
433+
#endif
434+
427435
dtls1_start_timer(s);
428436
return dtls1_retransmit_buffered_messages(s);
429437
}

ssl/d1_pkt.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,19 @@ int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
937937
dest = s->d1->alert_fragment;
938938
dest_len = &s->d1->alert_fragment_len;
939939
}
940+
#ifndef OPENSSL_NO_HEARTBEATS
941+
else if (rr->type == TLS1_RT_HEARTBEAT)
942+
{
943+
dtls1_process_heartbeat(s);
944+
945+
/* Exit and notify application to read again */
946+
rr->length = 0;
947+
s->rwstate=SSL_READING;
948+
BIO_clear_retry_flags(SSL_get_rbio(s));
949+
BIO_set_retry_read(SSL_get_rbio(s));
950+
return(-1);
951+
}
952+
#endif
940953
/* else it's a CCS message, or application data or wrong */
941954
else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC)
942955
{

ssl/d1_srvr.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,19 @@ int dtls1_accept(SSL *s)
186186
return(-1);
187187
}
188188

189+
#ifndef OPENSSL_NO_HEARTBEATS
190+
/* If we're awaiting a HeartbeatResponse, pretend we
191+
* already got and don't await it anymore, because
192+
* Heartbeats don't make sense during handshakes anyway.
193+
*/
194+
if (s->tlsext_hb_pending)
195+
{
196+
dtls1_stop_timer(s);
197+
s->tlsext_hb_pending = 0;
198+
s->tlsext_hb_seq++;
199+
}
200+
#endif
201+
189202
for (;;)
190203
{
191204
state=s->state;

ssl/dtls1.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ typedef struct dtls1_state_st
236236

237237
struct dtls1_timeout_st timeout;
238238

239-
/* Indicates when the last handshake msg sent will timeout */
239+
/* Indicates when the last handshake msg or heartbeat sent will timeout */
240240
struct timeval next_timeout;
241241

242242
/* Timeout duration */

ssl/s3_clnt.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,18 @@ int ssl3_connect(SSL *s)
204204
s->in_handshake++;
205205
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
206206

207+
#ifndef OPENSSL_NO_HEARTBEATS
208+
/* If we're awaiting a HeartbeatResponse, pretend we
209+
* already got and don't await it anymore, because
210+
* Heartbeats don't make sense during handshakes anyway.
211+
*/
212+
if (s->tlsext_hb_pending)
213+
{
214+
s->tlsext_hb_pending = 0;
215+
s->tlsext_hb_seq++;
216+
}
217+
#endif
218+
207219
for (;;)
208220
{
209221
state=s->state;

ssl/s3_lib.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3328,6 +3328,27 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg)
33283328
ret = 1;
33293329
break;
33303330

3331+
#ifndef OPENSSL_NO_HEARTBEATS
3332+
case SSL_CTRL_TLS_EXT_SEND_HEARTBEAT:
3333+
if (SSL_version(s) == DTLS1_VERSION || SSL_version(s) == DTLS1_BAD_VER)
3334+
ret = dtls1_heartbeat(s);
3335+
else
3336+
ret = tls1_heartbeat(s);
3337+
break;
3338+
3339+
case SSL_CTRL_GET_TLS_EXT_HEARTBEAT_PENDING:
3340+
ret = s->tlsext_hb_pending;
3341+
break;
3342+
3343+
case SSL_CTRL_SET_TLS_EXT_HEARTBEAT_NO_REQUESTS:
3344+
if (larg)
3345+
s->tlsext_heartbeat |= SSL_TLSEXT_HB_DONT_RECV_REQUESTS;
3346+
else
3347+
s->tlsext_heartbeat &= ~SSL_TLSEXT_HB_DONT_RECV_REQUESTS;
3348+
ret = 1;
3349+
break;
3350+
#endif
3351+
33313352
#endif /* !OPENSSL_NO_TLSEXT */
33323353
default:
33333354
break;

ssl/s3_pkt.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,19 @@ int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
10701070
dest = s->s3->alert_fragment;
10711071
dest_len = &s->s3->alert_fragment_len;
10721072
}
1073+
#ifndef OPENSSL_NO_HEARTBEATS
1074+
else if (rr->type == TLS1_RT_HEARTBEAT)
1075+
{
1076+
tls1_process_heartbeat(s);
1077+
1078+
/* Exit and notify application to read again */
1079+
rr->length = 0;
1080+
s->rwstate=SSL_READING;
1081+
BIO_clear_retry_flags(SSL_get_rbio(s));
1082+
BIO_set_retry_read(SSL_get_rbio(s));
1083+
return(-1);
1084+
}
1085+
#endif
10731086

10741087
if (dest_maxlen > 0)
10751088
{

0 commit comments

Comments
 (0)