Line data Source code
1 : /*
2 : * Copyright (C) 2012,2013 Free Software Foundation, Inc.
3 : * Copyright (C) 2013 Nikos Mavrogiannopoulos
4 : *
5 : * Author: Nikos Mavrogiannopoulos
6 : *
7 : * This file is part of GnuTLS.
8 : *
9 : * The GnuTLS is free software; you can redistribute it and/or
10 : * modify it under the terms of the GNU Lesser General Public License
11 : * as published by the Free Software Foundation; either version 2.1 of
12 : * the License, or (at your option) any later version.
13 : *
14 : * This library is distributed in the hope that it will be useful, but
15 : * WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : * Lesser General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU Lesser General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>
21 : *
22 : */
23 :
24 : /* This file implements the TLS heartbeat extension.
25 : */
26 :
27 : #include "errors.h"
28 : #include "gnutls_int.h"
29 : #include <dtls.h>
30 : #include <record.h>
31 : #include <ext/heartbeat.h>
32 : #include <hello_ext.h>
33 : #include <random.h>
34 :
35 : #ifdef ENABLE_HEARTBEAT
36 : /**
37 : * gnutls_heartbeat_enable:
38 : * @session: is a #gnutls_session_t type.
39 : * @type: one of the GNUTLS_HB_* flags
40 : *
41 : * If this function is called with the %GNUTLS_HB_PEER_ALLOWED_TO_SEND
42 : * @type, GnuTLS will allow heartbeat messages to be received. Moreover it also
43 : * request the peer to accept heartbeat messages. This function
44 : * must be called prior to TLS handshake.
45 : *
46 : * If the @type used is %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND, then the peer
47 : * will be asked to accept heartbeat messages but not send ones.
48 : *
49 : * The function gnutls_heartbeat_allowed() can be used to test Whether
50 : * locally generated heartbeat messages can be accepted by the peer.
51 : *
52 : * Since: 3.1.2
53 : **/
54 18 : void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type)
55 : {
56 18 : gnutls_ext_priv_data_t epriv;
57 :
58 18 : epriv = (void*)(intptr_t)type;
59 18 : _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT,
60 : epriv);
61 18 : }
62 :
63 : /**
64 : * gnutls_heartbeat_allowed:
65 : * @session: is a #gnutls_session_t type.
66 : * @type: one of %GNUTLS_HB_LOCAL_ALLOWED_TO_SEND and %GNUTLS_HB_PEER_ALLOWED_TO_SEND
67 : *
68 : * This function will check whether heartbeats are allowed
69 : * to be sent or received in this session.
70 : *
71 : * Returns: Non zero if heartbeats are allowed.
72 : *
73 : * Since: 3.1.2
74 : **/
75 630 : unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type)
76 : {
77 630 : gnutls_ext_priv_data_t epriv;
78 :
79 630 : if (session->internals.handshake_in_progress != 0)
80 : return 0; /* not allowed */
81 :
82 459 : if (_gnutls_hello_ext_get_priv
83 : (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
84 : return 0; /* Not enabled */
85 :
86 13 : if (type == GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) {
87 8 : if (((intptr_t)epriv) & LOCAL_ALLOWED_TO_SEND)
88 3 : return 1;
89 5 : } else if (((intptr_t)epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
90 5 : return 1;
91 :
92 : return 0;
93 : }
94 :
95 : #define DEFAULT_PADDING_SIZE 16
96 :
97 : /*
98 : * Sends heartbeat data.
99 : */
100 : static int
101 5 : heartbeat_send_data(gnutls_session_t session, const void *data,
102 : size_t data_size, uint8_t type)
103 : {
104 5 : int ret, pos;
105 5 : uint8_t *response;
106 :
107 5 : response = gnutls_malloc(1 + 2 + data_size + DEFAULT_PADDING_SIZE);
108 5 : if (response == NULL)
109 0 : return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
110 :
111 5 : pos = 0;
112 5 : response[pos++] = type;
113 :
114 5 : _gnutls_write_uint16(data_size, &response[pos]);
115 5 : pos += 2;
116 :
117 5 : memcpy(&response[pos], data, data_size);
118 5 : pos += data_size;
119 :
120 5 : ret =
121 5 : gnutls_rnd(GNUTLS_RND_NONCE, &response[pos],
122 : DEFAULT_PADDING_SIZE);
123 5 : if (ret < 0) {
124 0 : gnutls_assert();
125 0 : goto cleanup;
126 : }
127 5 : pos += DEFAULT_PADDING_SIZE;
128 :
129 10 : ret =
130 5 : _gnutls_send_int(session, GNUTLS_HEARTBEAT, -1,
131 : EPOCH_WRITE_CURRENT, response, pos,
132 : MBUFFER_FLUSH);
133 :
134 5 : cleanup:
135 5 : gnutls_free(response);
136 5 : return ret;
137 : }
138 :
139 : /**
140 : * gnutls_heartbeat_ping:
141 : * @session: is a #gnutls_session_t type.
142 : * @data_size: is the length of the ping payload.
143 : * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout).
144 : * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately.
145 : *
146 : * This function sends a ping to the peer. If the @flags is set
147 : * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer.
148 : *
149 : * Note that it is highly recommended to use this function with the
150 : * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions
151 : * and timeouts manually.
152 : *
153 : * The total TLS data transmitted as part of the ping message are given by
154 : * the following formula: MAX(16, @data_size)+gnutls_record_overhead_size()+3.
155 : *
156 : * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
157 : *
158 : * Since: 3.1.2
159 : **/
160 : int
161 3 : gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size,
162 : unsigned int max_tries, unsigned int flags)
163 : {
164 3 : int ret;
165 3 : unsigned int retries = 1, diff;
166 3 : struct timespec now;
167 :
168 3 : if (data_size > MAX_HEARTBEAT_LENGTH)
169 0 : return
170 0 : gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
171 :
172 3 : if (gnutls_heartbeat_allowed
173 : (session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 0)
174 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
175 :
176 : /* resume previous call if interrupted */
177 3 : if (session->internals.record_send_buffer.byte_length > 0 &&
178 0 : session->internals.record_send_buffer.head != NULL &&
179 0 : session->internals.record_send_buffer.head->type ==
180 : GNUTLS_HEARTBEAT)
181 0 : return _gnutls_io_write_flush(session);
182 :
183 3 : switch (session->internals.hb_state) {
184 3 : case SHB_SEND1:
185 3 : if (data_size > DEFAULT_PADDING_SIZE)
186 3 : data_size -= DEFAULT_PADDING_SIZE;
187 : else
188 : data_size = 0;
189 :
190 3 : _gnutls_buffer_reset(&session->internals.hb_local_data);
191 :
192 3 : ret =
193 3 : _gnutls_buffer_resize(&session->internals.
194 : hb_local_data, data_size);
195 3 : if (ret < 0)
196 0 : return gnutls_assert_val(ret);
197 :
198 3 : ret =
199 6 : gnutls_rnd(GNUTLS_RND_NONCE,
200 3 : session->internals.hb_local_data.data,
201 : data_size);
202 3 : if (ret < 0)
203 0 : return gnutls_assert_val(ret);
204 :
205 3 : gnutls_gettime(&session->internals.hb_ping_start);
206 3 : session->internals.hb_local_data.length = data_size;
207 3 : session->internals.hb_state = SHB_SEND2;
208 :
209 3 : FALLTHROUGH;
210 3 : case SHB_SEND2:
211 3 : session->internals.hb_actual_retrans_timeout_ms =
212 3 : session->internals.hb_retrans_timeout_ms;
213 3 : retry:
214 3 : ret =
215 6 : heartbeat_send_data(session,
216 3 : session->internals.hb_local_data.
217 : data,
218 : session->internals.hb_local_data.
219 : length, HEARTBEAT_REQUEST);
220 3 : if (ret < 0)
221 0 : return gnutls_assert_val(ret);
222 :
223 3 : gnutls_gettime(&session->internals.hb_ping_sent);
224 :
225 3 : if (!(flags & GNUTLS_HEARTBEAT_WAIT)) {
226 0 : session->internals.hb_state = SHB_SEND1;
227 0 : break;
228 : }
229 :
230 3 : session->internals.hb_state = SHB_RECV;
231 3 : FALLTHROUGH;
232 :
233 3 : case SHB_RECV:
234 6 : ret =
235 3 : _gnutls_recv_int(session, GNUTLS_HEARTBEAT,
236 : NULL, 0, NULL,
237 : session->internals.
238 : hb_actual_retrans_timeout_ms);
239 3 : if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) {
240 3 : session->internals.hb_state = SHB_SEND1;
241 3 : break;
242 0 : } else if (ret == GNUTLS_E_TIMEDOUT) {
243 0 : retries++;
244 0 : if (max_tries > 0 && retries > max_tries) {
245 0 : session->internals.hb_state = SHB_SEND1;
246 0 : return gnutls_assert_val(ret);
247 : }
248 :
249 0 : gnutls_gettime(&now);
250 0 : diff =
251 0 : timespec_sub_ms(&now,
252 : &session->internals.
253 : hb_ping_start);
254 0 : if (diff > session->internals.hb_total_timeout_ms) {
255 0 : session->internals.hb_state = SHB_SEND1;
256 0 : return
257 0 : gnutls_assert_val(GNUTLS_E_TIMEDOUT);
258 : }
259 :
260 0 : session->internals.hb_actual_retrans_timeout_ms *=
261 : 2;
262 0 : session->internals.hb_actual_retrans_timeout_ms %=
263 : MAX_DTLS_TIMEOUT;
264 :
265 0 : session->internals.hb_state = SHB_SEND2;
266 0 : goto retry;
267 0 : } else if (ret < 0) {
268 0 : session->internals.hb_state = SHB_SEND1;
269 0 : return gnutls_assert_val(ret);
270 : }
271 : }
272 :
273 : return 0;
274 : }
275 :
276 : /**
277 : * gnutls_heartbeat_pong:
278 : * @session: is a #gnutls_session_t type.
279 : * @flags: should be zero
280 : *
281 : * This function replies to a ping by sending a pong to the peer.
282 : *
283 : * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
284 : *
285 : * Since: 3.1.2
286 : **/
287 2 : int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags)
288 : {
289 2 : int ret;
290 :
291 2 : if (session->internals.record_send_buffer.byte_length > 0 &&
292 0 : session->internals.record_send_buffer.head != NULL &&
293 0 : session->internals.record_send_buffer.head->type ==
294 : GNUTLS_HEARTBEAT)
295 0 : return _gnutls_io_write_flush(session);
296 :
297 2 : if (session->internals.hb_remote_data.length == 0)
298 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
299 :
300 2 : ret =
301 4 : heartbeat_send_data(session,
302 2 : session->internals.hb_remote_data.data,
303 : session->internals.hb_remote_data.length,
304 : HEARTBEAT_RESPONSE);
305 :
306 2 : _gnutls_buffer_reset(&session->internals.hb_remote_data);
307 :
308 2 : if (ret < 0)
309 0 : return gnutls_assert_val(ret);
310 :
311 : return 0;
312 : }
313 :
314 : /*
315 : * Processes a heartbeat message.
316 : */
317 18 : int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel)
318 : {
319 18 : int ret;
320 18 : unsigned type;
321 18 : unsigned pos;
322 18 : uint8_t *msg = _mbuffer_get_udata_ptr(bufel);
323 18 : size_t hb_len, len = _mbuffer_get_udata_size(bufel);
324 :
325 18 : if (gnutls_heartbeat_allowed
326 : (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0)
327 20 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
328 :
329 5 : if (len < 3 + DEFAULT_PADDING_SIZE)
330 0 : return
331 0 : gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
332 :
333 5 : pos = 0;
334 5 : type = msg[pos++];
335 :
336 5 : hb_len = _gnutls_read_uint16(&msg[pos]);
337 5 : if (hb_len > len - 3 - DEFAULT_PADDING_SIZE)
338 0 : return
339 0 : gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
340 :
341 5 : pos += 2;
342 :
343 5 : switch (type) {
344 2 : case HEARTBEAT_REQUEST:
345 2 : _gnutls_buffer_reset(&session->internals.hb_remote_data);
346 :
347 2 : ret =
348 2 : _gnutls_buffer_resize(&session->internals.
349 : hb_remote_data, hb_len);
350 2 : if (ret < 0)
351 0 : return gnutls_assert_val(ret);
352 :
353 2 : if (hb_len > 0)
354 2 : memcpy(session->internals.hb_remote_data.data,
355 2 : &msg[pos], hb_len);
356 2 : session->internals.hb_remote_data.length = hb_len;
357 :
358 2 : return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED);
359 :
360 3 : case HEARTBEAT_RESPONSE:
361 :
362 3 : if (hb_len != session->internals.hb_local_data.length)
363 0 : return
364 0 : gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
365 :
366 3 : if (hb_len > 0 &&
367 3 : memcmp(&msg[pos],
368 3 : session->internals.hb_local_data.data,
369 : hb_len) != 0) {
370 0 : if (IS_DTLS(session))
371 0 : return gnutls_assert_val(GNUTLS_E_AGAIN); /* ignore it */
372 : else
373 0 : return
374 0 : gnutls_assert_val
375 : (GNUTLS_E_UNEXPECTED_PACKET);
376 : }
377 :
378 3 : _gnutls_buffer_reset(&session->internals.hb_local_data);
379 :
380 3 : return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED);
381 0 : default:
382 0 : _gnutls_record_log
383 : ("REC[%p]: HB: received unknown type %u\n", session,
384 : type);
385 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
386 : }
387 : }
388 :
389 : /**
390 : * gnutls_heartbeat_get_timeout:
391 : * @session: is a #gnutls_session_t type.
392 : *
393 : * This function will return the milliseconds remaining
394 : * for a retransmission of the previously sent ping
395 : * message. This function is useful when ping is used in
396 : * non-blocking mode, to estimate when to call gnutls_heartbeat_ping()
397 : * if no packets have been received.
398 : *
399 : * Returns: the remaining time in milliseconds.
400 : *
401 : * Since: 3.1.2
402 : **/
403 0 : unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session)
404 : {
405 0 : struct timespec now;
406 0 : unsigned int diff;
407 :
408 0 : gnutls_gettime(&now);
409 0 : diff = timespec_sub_ms(&now, &session->internals.hb_ping_sent);
410 0 : if (diff >= session->internals.hb_actual_retrans_timeout_ms)
411 : return 0;
412 : else
413 0 : return session->internals.hb_actual_retrans_timeout_ms -
414 : diff;
415 : }
416 :
417 : /**
418 : * gnutls_heartbeat_set_timeouts:
419 : * @session: is a #gnutls_session_t type.
420 : * @retrans_timeout: The time at which a retransmission will occur in milliseconds
421 : * @total_timeout: The time at which the connection will be aborted, in milliseconds.
422 : *
423 : * This function will override the timeouts for the DTLS heartbeat
424 : * protocol. The retransmission timeout is the time after which a
425 : * message from the peer is not received, the previous request will
426 : * be retransmitted. The total timeout is the time after which the
427 : * handshake will be aborted with %GNUTLS_E_TIMEDOUT.
428 : *
429 : * Since: 3.1.2
430 : **/
431 0 : void gnutls_heartbeat_set_timeouts(gnutls_session_t session,
432 : unsigned int retrans_timeout,
433 : unsigned int total_timeout)
434 : {
435 0 : session->internals.hb_retrans_timeout_ms = retrans_timeout;
436 0 : session->internals.hb_total_timeout_ms = total_timeout;
437 0 : }
438 :
439 :
440 : static int
441 56 : _gnutls_heartbeat_recv_params(gnutls_session_t session,
442 : const uint8_t * data, size_t _data_size)
443 : {
444 56 : unsigned policy;
445 56 : gnutls_ext_priv_data_t epriv;
446 :
447 56 : if (_gnutls_hello_ext_get_priv
448 : (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) {
449 44 : if (session->security_parameters.entity == GNUTLS_CLIENT)
450 0 : return
451 0 : gnutls_assert_val
452 : (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
453 : return 0; /* Not enabled */
454 : }
455 :
456 12 : if (_data_size == 0)
457 : return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
458 :
459 12 : policy = (intptr_t)epriv;
460 :
461 12 : if (data[0] == 1)
462 12 : policy |= LOCAL_ALLOWED_TO_SEND;
463 0 : else if (data[0] == 2)
464 0 : policy |= LOCAL_NOT_ALLOWED_TO_SEND;
465 : else
466 0 : return
467 0 : gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
468 :
469 12 : epriv = (void*)(intptr_t)policy;
470 12 : _gnutls_hello_ext_set_priv(session, GNUTLS_EXTENSION_HEARTBEAT,
471 : epriv);
472 :
473 12 : return 0;
474 : }
475 :
476 : static int
477 3818 : _gnutls_heartbeat_send_params(gnutls_session_t session,
478 : gnutls_buffer_st * extdata)
479 : {
480 3818 : gnutls_ext_priv_data_t epriv;
481 3818 : uint8_t p;
482 :
483 3818 : if (_gnutls_hello_ext_get_priv
484 : (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0)
485 : return 0; /* nothing to send - not enabled */
486 :
487 18 : if (((intptr_t)epriv) & GNUTLS_HB_PEER_ALLOWED_TO_SEND)
488 18 : p = 1;
489 : else /*if (epriv.num & GNUTLS_HB_PEER_NOT_ALLOWED_TO_SEND) */
490 0 : p = 2;
491 :
492 18 : if (_gnutls_buffer_append_data(extdata, &p, 1) < 0)
493 0 : return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
494 :
495 : return 1;
496 : }
497 :
498 : static int
499 17 : _gnutls_heartbeat_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps)
500 : {
501 17 : int ret;
502 :
503 17 : BUFFER_APPEND_NUM(ps, (intptr_t)epriv);
504 :
505 : return 0;
506 :
507 : }
508 :
509 : static int
510 0 : _gnutls_heartbeat_unpack(gnutls_buffer_st * ps,
511 : gnutls_ext_priv_data_t * _priv)
512 : {
513 0 : gnutls_ext_priv_data_t epriv;
514 0 : int ret;
515 :
516 0 : BUFFER_POP_CAST_NUM(ps, epriv);
517 :
518 0 : *_priv = epriv;
519 :
520 0 : ret = 0;
521 0 : error:
522 0 : return ret;
523 : }
524 :
525 : const hello_ext_entry_st ext_mod_heartbeat = {
526 : .name = "Heartbeat",
527 : .tls_id = 15,
528 : .gid = GNUTLS_EXTENSION_HEARTBEAT,
529 : .client_parse_point = GNUTLS_EXT_TLS,
530 : .server_parse_point = GNUTLS_EXT_TLS,
531 : .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
532 : GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
533 : .recv_func = _gnutls_heartbeat_recv_params,
534 : .send_func = _gnutls_heartbeat_send_params,
535 : .pack_func = _gnutls_heartbeat_pack,
536 : .unpack_func = _gnutls_heartbeat_unpack,
537 : .deinit_func = NULL,
538 : .cannot_be_overriden = 1
539 : };
540 :
541 : #else
542 : void gnutls_heartbeat_enable(gnutls_session_t session, unsigned int type)
543 : {
544 : }
545 :
546 : unsigned gnutls_heartbeat_allowed(gnutls_session_t session, unsigned int type)
547 : {
548 : return 0;
549 : }
550 :
551 : int
552 : gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size,
553 : unsigned int max_tries, unsigned int flags)
554 : {
555 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
556 : }
557 :
558 : int gnutls_heartbeat_pong(gnutls_session_t session, unsigned int flags)
559 : {
560 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
561 : }
562 :
563 : unsigned int gnutls_heartbeat_get_timeout(gnutls_session_t session)
564 : {
565 : return 0;
566 : }
567 :
568 : void gnutls_heartbeat_set_timeouts(gnutls_session_t session,
569 : unsigned int retrans_timeout,
570 : unsigned int total_timeout)
571 : {
572 : return;
573 : }
574 : #endif
|