diff options
author | Olga <olyasib12@gmail.com> | 2012-08-28 00:12:56 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2012-08-30 20:04:18 +0200 |
commit | 3f2e9a0d1231f2555626305b752f73c87afbe6f2 (patch) | |
tree | b903ca9de4bf9dd613627382b6f429e50551ee71 | |
parent | 23934e9fe103c3026491b25255467637cc1df89f (diff) | |
download | gnutls-3f2e9a0d1231f2555626305b752f73c87afbe6f2.tar.gz |
Added Heartbeat extension support.
Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
-rw-r--r-- | doc/cha-internals.texi | 25 | ||||
-rw-r--r-- | doc/cha-intro-tls.texi | 26 | ||||
-rw-r--r-- | doc/manpages/Makefile.am | 1 | ||||
-rw-r--r-- | doc/protocol/rfc6520.txt | 507 | ||||
-rw-r--r-- | lib/debug.c | 2 | ||||
-rw-r--r-- | lib/ext/Makefile.am | 2 | ||||
-rw-r--r-- | lib/ext/heartbeat.c | 585 | ||||
-rw-r--r-- | lib/ext/heartbeat.h | 67 | ||||
-rw-r--r-- | lib/gnutls_buffers.c | 6 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 5 | ||||
-rw-r--r-- | lib/gnutls_extensions.c | 5 | ||||
-rw-r--r-- | lib/gnutls_int.h | 10 | ||||
-rw-r--r-- | lib/gnutls_record.c | 23 | ||||
-rw-r--r-- | lib/gnutls_record.h | 1 | ||||
-rw-r--r-- | lib/gnutls_state.c | 7 | ||||
-rw-r--r-- | lib/gnutls_str.h | 30 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 12 | ||||
-rw-r--r-- | lib/libgnutls.map | 7 | ||||
-rw-r--r-- | src/cli-args.def | 7 | ||||
-rw-r--r-- | src/cli.c | 6 | ||||
-rw-r--r-- | src/common.c | 21 | ||||
-rw-r--r-- | src/serv-args.def | 7 | ||||
-rw-r--r-- | src/serv.c | 266 | ||||
-rw-r--r-- | src/socket.c | 4 | ||||
-rw-r--r-- | src/tests.c | 41 | ||||
-rw-r--r-- | src/tests.h | 2 | ||||
-rw-r--r-- | src/tls_test.c | 2 |
27 files changed, 1533 insertions, 144 deletions
diff --git a/doc/cha-internals.texi b/doc/cha-internals.texi index 69ac8fa13d..e6a01a4846 100644 --- a/doc/cha-internals.texi +++ b/doc/cha-internals.texi @@ -421,6 +421,31 @@ typedef enum @} gnutls_supplemental_data_format_type_t; @end example +@subsubheading Heartbeat extension. + +One such extension is HeartBeat protocol (RFC6520: +@url{https://tools.ietf.org/html/rfc6520}) implementation. To enable +it use option --heartbeat with example client and server supplied with +gnutls: + +@example +./doc/credentials/gnutls-http-serv --priority "NORMAL:-CIPHER-ALL:+NULL" -d 100 --heartbeat --echo +./src/gnutls-cli --priority "NORMAL:-CIPHER-ALL:+NULL" -d 100 localhost -p 5556 --insecure --heartbeat +@end example + +After that pasting +@example +**HEARTBEAT** +@end example +command into gnutls-cli will trigger corresponding command on the server and it will send HeartBeat Request with random length to client. + +Another way is to run capabilities check with: + +@example +./doc/credentials/gnutls-http-serv -d 100 --heartbeat +./src/gnutls-cli-debug localhost -p 5556 +@end example + @node Cryptographic Backend @section Cryptographic Backend Today most new processors, either for embedded or desktop systems diff --git a/doc/cha-intro-tls.texi b/doc/cha-intro-tls.texi index 3ab1d65190..1f56ec55e6 100644 --- a/doc/cha-intro-tls.texi +++ b/doc/cha-intro-tls.texi @@ -379,6 +379,7 @@ in @acronym{GnuTLS} are: @item Maximum fragment length negotiation @item Server name indication @item Session tickets +@item HeartBeat @item Safe Renegotiation @end itemize @@ -388,6 +389,7 @@ and they will be discussed in the subsections that follow. * Maximum fragment length negotiation:: * Server name indication:: * Session tickets:: +* HeartBeat:: * Safe renegotiation:: @end menu @@ -436,6 +438,30 @@ and authenticated with a key only known to the server and then sent to the client. The Session Tickets in RFC 5077 @xcite{TLSTKT}, describe this idea, which is implemented in GnuTLS. +@node HeartBeat +@subsection HeartBeat +@cindex TLS extensions +@cindex heartbeat + +The TLS extension which allows to request response from the peer in a +way similar to ping command described in @xcite{RFC6520}. This +extension is disabled by default - user have to call +@funcref{gnutls_heartbeat_allow} to enable it. Note: this will set +local policy affecting HeartBeat messages coming from the peer - the policy +could be checked via @funcref{gnutls_heartbeat_enabled_local}. The +same policy set by the peer for our messages could be checked via +@funcref{gnutls_heartbeat_enabled_remote}. The requests coming from +peer are answered automatically (if policy permits) inside +@funcref{record_add_to_buffers}, requests to peer could be send via +@funcref{gnutls_heartbeat_ping} or +@funcref{gnutls_heartbeat_ping_rnd}. Each request triggers timeout +which could be checked and manipulated with @funcref{gnutls_heartbeat_timeout}. + +Policy-related functions: +@showfuncB{gnutls_heartbeat_allow,gnutls_heartbeat_deny}, @showfuncB{gnutls_heartbeat_enabled_local,gnutls_heartbeat_enabled_remote} +Operational functions: +@showfuncB{gnutls_heartbeat_timeout},@showfuncB{gnutls_heartbeat_ping,gnutls_heartbeat_ping_rnd} + @node Safe renegotiation @subsection Safe renegotiation @cindex renegotiation diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index d1572efacc..50492548b4 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -281,6 +281,7 @@ APIMANS += gnutls_prf.3 APIMANS += gnutls_prf_raw.3 APIMANS += gnutls_server_name_set.3 APIMANS += gnutls_server_name_get.3 +APIMANS += gnutls_heartbeat_policy_set.3 APIMANS += gnutls_safe_renegotiation_status.3 APIMANS += gnutls_supplemental_get_name.3 APIMANS += gnutls_session_ticket_key_generate.3 diff --git a/doc/protocol/rfc6520.txt b/doc/protocol/rfc6520.txt new file mode 100644 index 0000000000..19329da225 --- /dev/null +++ b/doc/protocol/rfc6520.txt @@ -0,0 +1,507 @@ + + + + + + +Internet Engineering Task Force (IETF) R. Seggelmann +Request for Comments: 6520 M. Tuexen +Category: Standards Track Muenster Univ. of Appl. Sciences +ISSN: 2070-1721 M. Williams + GWhiz Arts & Sciences + February 2012 + + + Transport Layer Security (TLS) and + Datagram Transport Layer Security (DTLS) Heartbeat Extension + +Abstract + + This document describes the Heartbeat Extension for the Transport + Layer Security (TLS) and Datagram Transport Layer Security (DTLS) + protocols. + + The Heartbeat Extension provides a new protocol for TLS/DTLS allowing + the usage of keep-alive functionality without performing a + renegotiation and a basis for path MTU (PMTU) discovery for DTLS. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6520. + + + + + + + + + + + + + + + + + +Seggelmann, et al. Standards Track [Page 1] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + +Copyright Notice + + Copyright (c) 2012 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 2. Heartbeat Hello Extension . . . . . . . . . . . . . . . . . . . 3 + 3. Heartbeat Protocol . . . . . . . . . . . . . . . . . . . . . . 4 + 4. Heartbeat Request and Response Messages . . . . . . . . . . . . 5 + 5. Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 7 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . . 7 + 8. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . 7 + 9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 7 + +1. Introduction + +1.1. Overview + + This document describes the Heartbeat Extension for the Transport + Layer Security (TLS) and Datagram Transport Layer Security (DTLS) + protocols, as defined in [RFC5246] and [RFC6347] and their + adaptations to specific transport protocols described in [RFC3436], + [RFC5238], and [RFC6083]. + + DTLS is designed to secure traffic running on top of unreliable + transport protocols. Usually, such protocols have no session + management. The only mechanism available at the DTLS layer to figure + out if a peer is still alive is a costly renegotiation, particularly + when the application uses unidirectional traffic. Furthermore, DTLS + needs to perform path MTU (PMTU) discovery but has no specific + message type to realize it without affecting the transfer of user + messages. + + + + + + +Seggelmann, et al. Standards Track [Page 2] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + + TLS is based on reliable protocols, but there is not necessarily a + feature available to keep the connection alive without continuous + data transfer. + + The Heartbeat Extension as described in this document overcomes these + limitations. The user can use the new HeartbeatRequest message, + which has to be answered by the peer with a HeartbeartResponse + immediately. To perform PMTU discovery, HeartbeatRequest messages + containing padding can be used as probe packets, as described in + [RFC4821]. + +1.2. Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + +2. Heartbeat Hello Extension + + The support of Heartbeats is indicated with Hello Extensions. A peer + cannot only indicate that its implementation supports Heartbeats, it + can also choose whether it is willing to receive HeartbeatRequest + messages and respond with HeartbeatResponse messages or only willing + to send HeartbeatRequest messages. The former is indicated by using + peer_allowed_to_send as the HeartbeatMode; the latter is indicated by + using peer_not_allowed_to_send as the Heartbeat mode. This decision + can be changed with every renegotiation. HeartbeatRequest messages + MUST NOT be sent to a peer indicating peer_not_allowed_to_send. If + an endpoint that has indicated peer_not_allowed_to_send receives a + HeartbeatRequest message, the endpoint SHOULD drop the message + silently and MAY send an unexpected_message Alert message. + + The format of the Heartbeat Hello Extension is defined by: + + enum { + peer_allowed_to_send(1), + peer_not_allowed_to_send(2), + (255) + } HeartbeatMode; + + struct { + HeartbeatMode mode; + } HeartbeatExtension; + + Upon reception of an unknown mode, an error Alert message using + illegal_parameter as its AlertDescription MUST be sent in response. + + + + + +Seggelmann, et al. Standards Track [Page 3] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + +3. Heartbeat Protocol + + The Heartbeat protocol is a new protocol running on top of the Record + Layer. The protocol itself consists of two message types: + HeartbeatRequest and HeartbeatResponse. + + enum { + heartbeat_request(1), + heartbeat_response(2), + (255) + } HeartbeatMessageType; + + A HeartbeatRequest message can arrive almost at any time during the + lifetime of a connection. Whenever a HeartbeatRequest message is + received, it SHOULD be answered with a corresponding + HeartbeatResponse message. + + However, a HeartbeatRequest message SHOULD NOT be sent during + handshakes. If a handshake is initiated while a HeartbeatRequest is + still in flight, the sending peer MUST stop the DTLS retransmission + timer for it. The receiving peer SHOULD discard the message + silently, if it arrives during the handshake. In case of DTLS, + HeartbeatRequest messages from older epochs SHOULD be discarded. + + There MUST NOT be more than one HeartbeatRequest message in flight at + a time. A HeartbeatRequest message is considered to be in flight + until the corresponding HeartbeatResponse message is received, or + until the retransmit timer expires. + + When using an unreliable transport protocol like the Datagram + Congestion Control Protocol (DCCP) or UDP, HeartbeatRequest messages + MUST be retransmitted using the simple timeout and retransmission + scheme DTLS uses for flights as described in Section 4.2.4 of + [RFC6347]. In particular, after a number of retransmissions without + receiving a corresponding HeartbeatResponse message having the + expected payload, the DTLS connection SHOULD be terminated. The + threshold used for this SHOULD be the same as for DTLS handshake + messages. Please note that after the timer supervising a + HeartbeatRequest messages expires, this message is no longer + considered in flight. Therefore, the HeartbeatRequest message is + eligible for retransmission. The retransmission scheme, in + combination with the restriction that only one HeartbeatRequest is + allowed to be in flight, ensures that congestion control is handled + appropriately in case of the transport protocol not providing one, + like in the case of DTLS over UDP. + + + + + + +Seggelmann, et al. Standards Track [Page 4] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + + When using a reliable transport protocol like the Stream Control + Transmission Protocol (SCTP) or TCP, HeartbeatRequest messages only + need to be sent once. The transport layer will handle + retransmissions. If no corresponding HeartbeatResponse message has + been received after some amount of time, the DTLS/TLS connection MAY + be terminated by the application that initiated the sending of the + HeartbeatRequest message. + +4. Heartbeat Request and Response Messages + + The Heartbeat protocol messages consist of their type and an + arbitrary payload and padding. + + struct { + HeartbeatMessageType type; + uint16 payload_length; + opaque payload[HeartbeatMessage.payload_length]; + opaque padding[padding_length]; + } HeartbeatMessage; + + The total length of a HeartbeatMessage MUST NOT exceed 2^14 or + max_fragment_length when negotiated as defined in [RFC6066]. + + type: The message type, either heartbeat_request or + heartbeat_response. + + payload_length: The length of the payload. + + payload: The payload consists of arbitrary content. + + padding: The padding is random content that MUST be ignored by the + receiver. The length of a HeartbeatMessage is TLSPlaintext.length + for TLS and DTLSPlaintext.length for DTLS. Furthermore, the + length of the type field is 1 byte, and the length of the + payload_length is 2. Therefore, the padding_length is + TLSPlaintext.length - payload_length - 3 for TLS and + DTLSPlaintext.length - payload_length - 3 for DTLS. The + padding_length MUST be at least 16. + + The sender of a HeartbeatMessage MUST use a random padding of at + least 16 bytes. The padding of a received HeartbeatMessage message + MUST be ignored. + + If the payload_length of a received HeartbeatMessage is too large, + the received HeartbeatMessage MUST be discarded silently. + + + + + + +Seggelmann, et al. Standards Track [Page 5] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + + When a HeartbeatRequest message is received and sending a + HeartbeatResponse is not prohibited as described elsewhere in this + document, the receiver MUST send a corresponding HeartbeatResponse + message carrying an exact copy of the payload of the received + HeartbeatRequest. + + If a received HeartbeatResponse message does not contain the expected + payload, the message MUST be discarded silently. If it does contain + the expected payload, the retransmission timer MUST be stopped. + +5. Use Cases + + Each endpoint sends HeartbeatRequest messages at a rate and with the + padding required for the particular use case. The endpoint should + not expect its peer to send HeartbeatRequests. The directions are + independent. + +5.1. Path MTU Discovery + + DTLS performs path MTU discovery as described in Section 4.1.1.1 of + [RFC6347]. A detailed description of how to perform path MTU + discovery is given in [RFC4821]. The necessary probe packets are the + HeartbeatRequest messages. + + This method of using HeartbeatRequest messages for DTLS is similar to + the one for the Stream Control Transmission Protocol (SCTP) using the + padding chunk (PAD-chunk) defined in [RFC4820]. + +5.2. Liveliness Check + + Sending HeartbeatRequest messages allows the sender to make sure that + it can reach the peer and the peer is alive. Even in the case of + TLS/TCP, this allows a check at a much higher rate than the TCP keep- + alive feature would allow. + + Besides making sure that the peer is still reachable, sending + HeartbeatRequest messages refreshes the NAT state of all involved + NATs. + + HeartbeatRequest messages SHOULD only be sent after an idle period + that is at least multiple round-trip times long. This idle period + SHOULD be configurable up to a period of multiple minutes and down to + a period of one second. A default value for the idle period SHOULD + be configurable, but it SHOULD also be tunable on a per-peer basis. + + + + + + + +Seggelmann, et al. Standards Track [Page 6] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + +6. IANA Considerations + + IANA has assigned the heartbeat content type (24) from the "TLS + ContentType Registry" as specified in [RFC5246]. The reference is to + RFC 6520. + + IANA has created and now maintains a new registry for Heartbeat + Message Types. The message types are numbers in the range from 0 to + 255 (decimal). IANA has assigned the heartbeat_request (1) and the + heartbeat_response (2) message types. The values 0 and 255 should be + reserved. This registry uses the Expert Review policy as described + in [RFC5226]. The reference is to RFC 6520. + + IANA has assigned the heartbeat extension type (15) from the TLS + "ExtensionType Values" registry as specified in [RFC5246]. The + reference is to RFC 6520. + + IANA has created and now maintains a new registry for Heartbeat + Modes. The modes are numbers in the range from 0 to 255 (decimal). + IANA has assigned the peer_allowed_to_send (1) and the + peer_not_allowed_to_send (2) modes. The values 0 and 255 should be + reserved. This registry uses the Expert Review policy as described + in [RFC5226]. The reference is to RFC 6520. + +7. Security Considerations + + The security considerations of [RFC5246] and [RFC6347] apply to this + document. This document does not introduce any new security + considerations. + +8. Acknowledgments + + The authors wish to thank Pasi Eronen, Adrian Farrel, Stephen + Farrell, Adam Langley, Nikos Mavrogiannopoulos, Tom Petch, Eric + Rescorla, Peter Saint-Andre, and Juho Vaehae-Herttua for their + invaluable comments. + +9. References + +9.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 5226, + May 2008. + + + + +Seggelmann, et al. Standards Track [Page 7] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security + (TLS) Protocol Version 1.2", RFC 5246, August 2008. + + [RFC6066] Eastlake, D., "Transport Layer Security (TLS) Extensions: + Extension Definitions", RFC 6066, January 2011. + + [RFC6347] Rescorla, E. and N. Modadugu, "Datagram Transport Layer + Security Version 1.2", RFC 6347, January 2012. + +9.2. Informative References + + [RFC3436] Jungmaier, A., Rescorla, E., and M. Tuexen, "Transport + Layer Security over Stream Control Transmission Protocol", + RFC 3436, December 2002. + + [RFC4820] Tuexen, M., Stewart, R., and P. Lei, "Padding Chunk and + Parameter for the Stream Control Transmission Protocol + (SCTP)", RFC 4820, March 2007. + + [RFC4821] Mathis, M. and J. Heffner, "Packetization Layer Path MTU + Discovery", RFC 4821, March 2007. + + [RFC5238] Phelan, T., "Datagram Transport Layer Security (DTLS) over + the Datagram Congestion Control Protocol (DCCP)", + RFC 5238, May 2008. + + [RFC6083] Tuexen, M., Seggelmann, R., and E. Rescorla, "Datagram + Transport Layer Security (DTLS) for Stream Control + Transmission Protocol (SCTP)", RFC 6083, January 2011. + + + + + + + + + + + + + + + + + + + + + + +Seggelmann, et al. Standards Track [Page 8] + +RFC 6520 TLS/DTLS Heartbeat Extension February 2012 + + +Authors' Addresses + + Robin Seggelmann + Muenster University of Applied Sciences + Stegerwaldstr. 39 + 48565 Steinfurt + DE + + EMail: seggelmann@fh-muenster.de + + + Michael Tuexen + Muenster University of Applied Sciences + Stegerwaldstr. 39 + 48565 Steinfurt + DE + + EMail: tuexen@fh-muenster.de + + + Michael Glenn Williams + GWhiz Arts & Sciences + 2885 Denise Court + Newbury Park, CA, 91320 + USA + + EMail: michael.glenn.williams@gmail.com + + + + + + + + + + + + + + + + + + + + + + + + +Seggelmann, et al. Standards Track [Page 9] + diff --git a/lib/debug.c b/lib/debug.c index 1e5b0c6fb8..4a8a13d3ca 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -56,6 +56,8 @@ _gnutls_packet2str (content_type_t packet) return "Handshake"; case GNUTLS_APPLICATION_DATA: return "Application Data"; + case GNUTLS_HEARTBEAT: + return "HeartBeat"; default: return "Unknown Packet"; } diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index 0062d495fe..48a635e902 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -38,4 +38,4 @@ libgnutls_ext_la_SOURCES = max_record.c cert_type.c \ server_name.c signature.c safe_renegotiation.c \ max_record.h cert_type.h server_name.h srp.h \ session_ticket.h signature.h safe_renegotiation.h \ - session_ticket.c srp.c ecc.c ecc.h + session_ticket.c srp.c ecc.c ecc.h heartbeat.c heartbeat.h diff --git a/lib/ext/heartbeat.c b/lib/ext/heartbeat.c new file mode 100644 index 0000000000..558cf1a9c7 --- /dev/null +++ b/lib/ext/heartbeat.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2002-2012 Free Software Foundation, Inc. + * + * Author: Olga Smolenchuk + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include <gnutls_errors.h> +#include <gnutls_int.h> +#include <gnutls_dtls.h> +#include <gnutls_record.h> +#include <ext/heartbeat.h> +#include <gnutls_extensions.h> +#include <random.h> + +/** + * gnutls_heartbeat_policy_set: + * @session: is a #gnutls_session_t structure. + * @policy: the policy to set. + * + * This function will allow you to set the policy to be followed during the + * session regarding Heartbeat messages reception. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise an error code is returned. + **/ +int +_gnutls_heartbeat_policy_set (gnutls_session_t session, unsigned policy) +{ + extension_priv_data_t epriv; + heartbeat_policy_t pol; + _gnutls_debug_log ("HB: setting policy %s\n", _gnutls_heartbeat (policy)); + if (_gnutls_ext_get_session_data + (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) + { + _gnutls_debug_log ("HB: failed to get session data\n"); + } + else + { + pol.num = epriv.num; + } + + switch (policy) + { + case PEER_ALLOWED_TO_SEND: + pol.set.local = 1; + break; + case PEER_NOT_ALLOWED_TO_SEND: + pol.set.local = 0; + break; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + epriv.num = pol.num; + _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_HEARTBEAT, epriv); + session->internals.heartbeat_policy_set = 1; + _gnutls_debug_log ("HB: parameter set to %s\n", _gnutls_heartbeat (policy)); + _gnutls_debug_log ("HB: enabled at local:%d, remote:%d\n", + gnutls_heartbeat_enabled_local (session), + gnutls_heartbeat_enabled_remote (session)); + return GNUTLS_E_SUCCESS; +} + +inline int +gnutls_heartbeat_allow (gnutls_session_t session) +{ + return _gnutls_heartbeat_policy_set (session, PEER_ALLOWED_TO_SEND); +} + +inline int +gnutls_heartbeat_deny (gnutls_session_t session) +{ + return _gnutls_heartbeat_policy_set (session, PEER_NOT_ALLOWED_TO_SEND); +} + +/** + * gnutls_heartbeat_policy_get: + * @session: is a #gnutls_session_t structure. + * + * This function will allow you to get the policy regarding Heartbeat messages. + * + * Returns: policy or error codes: + * 0 - both local and remote policies are set to false + * 1 - local == false, remote == true + * 2 - remote == false, local == true + * 3 - both local and remote policies are set to true + * 4 - policy was not set for this session + **/ +inline unsigned +_gnutls_heartbeat_policy_get (gnutls_session_t session) +{ + extension_priv_data_t epriv; + heartbeat_policy_t pol; + if (!session->internals.heartbeat_policy_set) + return 4; + if (_gnutls_ext_get_session_data + (session, GNUTLS_EXTENSION_HEARTBEAT, &epriv) < 0) + return GNUTLS_E_INTERNAL_ERROR; + pol.num = epriv.num; + + if (pol.set.local) + { + if (pol.set.remote) + return 3; + return 2; + } + else + { + if (pol.set.remote) + return 1; + return 0; + } + return GNUTLS_E_INTERNAL_ERROR; +} + +/** + * Convenience helper: + * + * Returns: + * textual policy description or NULL. + **/ +inline const char * +_gnutls_heartbeat (int policy) +{ + switch (policy) + { + case PEER_ALLOWED_TO_SEND: + return "PEER_ALLOWED_TO_SEND"; + case PEER_NOT_ALLOWED_TO_SEND: + return "PEER_NOT_ALLOWED_TO_SEND"; + default: + return NULL; + } +} + +/** + * Convenience helpers: + * + * Returns: + * local policy if @local is true, remote policy otherwise. + **/ +inline int +_gnutls_heartbeat_enabled (gnutls_session_t session, int local) +{ + unsigned policy = _gnutls_heartbeat_policy_get (session); + + if (policy == 3) + return 1; + if (local) + { + if (policy == 2) + return 1; + } + else + { + if (policy == 1) + return 1; + } + return 0; +} + +int +gnutls_heartbeat_enabled_local (gnutls_session_t session) +{ + return _gnutls_heartbeat_enabled (session, 1); +} + +int +gnutls_heartbeat_enabled_remote (gnutls_session_t session) +{ + return _gnutls_heartbeat_enabled (session, 0); +} + +/** + * _gnutls_heartbeat_send_data: + * @session: is a #gnutls_session_t structure. + * @data: contains the data to send. + * @data_size: is the length of the data. + * @request: true if Request message is about to be send. + * + * This function has the similar semantics with gnutls_record_send() The only + * difference is that it uses GNUTLS_HEARTBEAT content type. + * + * This function send either HeartBeat request or response message + * with proper padding. It set timeout and timestamp without check - it's up to + * caller to make sure no messages are already in-flight and handle timeout expiration. + * + * Returns: The number of bytes sent, or a negative error code. The + * number of bytes sent might be less than @data_size. The maximum + * number of bytes this function can send in a single call depends + * on the negotiated maximum record size. + **/ +ssize_t +_gnutls_heartbeat_send_data (gnutls_session_t session, const void *data, + size_t data_size, int request) +{ + int ret; + char pr[128]; + gnutls_buffer_st response; + uint8_t payload[16]; + payload[0] = request ? HEARTBEAT_REQUEST : HEARTBEAT_RESPONSE; + _gnutls_buffer_init (&response); + + BUFFER_APPEND (&response, payload, 1) + BUFFER_APPEND_PFX2 (&response, data, data_size) + gnutls_rnd (GNUTLS_RND_RANDOM, payload, 16); + BUFFER_APPEND (&response, payload, 16) + ret = _gnutls_send_int (session, GNUTLS_HEARTBEAT, -1, + EPOCH_WRITE_CURRENT, response.data, + response.length, MBUFFER_FLUSH); + if (request) + { + if (ret >= 0) + { + _gnutls_record_log + ("REC[%p]: HB %zu bytes sent OK [%d in packet], saved for response verification:%s\n", + session, data_size, ret, _gnutls_bin2hex ((uint8_t *) data, + data_size, pr, + sizeof (pr), NULL)); + session->internals.dtls.heartbeat_timeout = HEARTBEAT_TIMEOUT; + gettime (&session->internals.dtls.heartbeat_sent); + _gnutls_buffer_reset (&session->internals.heartbeat_payload); + BUFFER_APPEND (&session->internals.heartbeat_payload, data, + data_size)} + } + _gnutls_record_log ("REC[%p]: HB sent: %d\n", session, ret); + + _gnutls_buffer_clear (&response); + return gnutls_assert_val (ret); +} + +/** + * gnutls_heartbeat_ping: + * @session: is a #gnutls_session_t structure. + * @data_size: is the length of the random data. + * @wait_for_it: wait for pong or timeout instead of returning + * immediately. + * + * This function has the similar semantics with gnutls_record_send(). + * The only difference is that it uses GNUTLS_HEARTBEAT content type + * and auto-generate data to send. + * + * This function send HeartBeat request message with proper padding. + * + * + * Returns: The number of bytes sent, or a negative error code. The + * number of bytes sent might be less than @data_size. The maximum + * number of bytes this function can send in a single call depends + * on the negotiated maximum record size. GNUTLS_E_HEARTBEAT_FLIGHT + * is returned if HB Request is alredy in flight. + **/ +ssize_t +gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size) +{ + int ret = GNUTLS_E_HEARTBEAT_FLIGHT; + if (data_size > MAX_HEARTBEAT_LENGTH) + return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + if (!gnutls_heartbeat_enabled_remote (session)) + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + _gnutls_record_log + ("REC[%p]: sending HB_REQUEST with length: %zu to peer %d\n", session, + data_size, gnutls_heartbeat_enabled_remote (session)); + + if (gnutls_heartbeat_timeout (session, 1) == GNUTLS_E_ILLEGAL_PARAMETER) + { + uint8_t data[data_size]; + ret = _gnutls_rnd (GNUTLS_RND_NONCE, data, data_size); + if (ret >= 0) + ret = _gnutls_heartbeat_send_data (session, data, data_size, 1); + } + else + _gnutls_record_log + ("REC[%p]: HB_REQUEST with length %zu already in-flight: %d\n", session, + data_size, gnutls_heartbeat_timeout (session, 1)); + + return ret; +} + +/** + * simple wrapper for ping with random length + **/ +inline ssize_t +gnutls_heartbeat_ping_rnd (gnutls_session_t session) +{ + uint8_t rnd; + gnutls_rnd (GNUTLS_RND_NONCE, &rnd, 1); + return gnutls_heartbeat_ping (session, rnd + 1); +} + +/** + * Process HB message in buffer. + * @bufel: the (suspected) HeartBeat message (TLV+padding) + * + * Returns: + * processing result + * GNUTLS_E_AGAIN if processed OK + * GNUTLS_E_HEARTBEAT_PONG_FAILED on response send failure for request message + * GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER on payload mismatch for response message + **/ +int +_gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel) +{ + char pr[128]; + uint8_t *msg = _mbuffer_get_udata_ptr (bufel); + size_t hb_len, len = _mbuffer_get_udata_size (bufel); + if (!gnutls_heartbeat_enabled_local (session)) + return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); + if (len < 4) + return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + hb_len = _gnutls_read_uint16 (msg + 1); + if (hb_len > len - 3) + return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); + + switch (msg[0]) + { + case HEARTBEAT_REQUEST: + _gnutls_record_log + ("REC[%p]: received HEARTBEAT_REQUEST, responding...\n", session); + if (_gnutls_heartbeat_send_data (session, msg + 3, hb_len, 0) >= 0) + return GNUTLS_E_AGAIN; /* HB responded, no APP_DATA so needs to be called again*/ + else + { /* immediate response failed, TBD: save received data somewhere and let upper layers handle it, loosing single ping is non-critical for HB*/ + return GNUTLS_E_HEARTBEAT_PONG_FAILED; + } + break; + + case HEARTBEAT_RESPONSE: + if (session->internals.heartbeat_payload.length != hb_len) + return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + if (0 != + memcmp (msg + 3, session->internals.heartbeat_payload.data, hb_len)) + { + _gnutls_record_log ("REC[%p]: HB: %s - received\n", session, + _gnutls_bin2hex (msg + 3, hb_len, pr, + sizeof (pr), NULL)); + _gnutls_record_log ("REC[%p]: HB: %s - expected\n", session, + _gnutls_bin2hex (session->internals. + heartbeat_payload.data, hb_len, + pr, sizeof (pr), NULL)); + return gnutls_assert_val (GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); + } + _gnutls_record_log + ("REC[%p]: HB: %zu response bytes received OK (%d msec. left before timeout)\n", + session, hb_len, gnutls_heartbeat_timeout (session, 1)); + + session->internals.dtls.heartbeat_timeout = HEARTBEAT_TIMEOUT; + _gnutls_buffer_reset (&session->internals.heartbeat_payload); + return GNUTLS_E_AGAIN; + break; + + default: + return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); + } + + return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); +} + +/** + * Update HB timeouts: should be called on retransmit to set new timeout. + * @check_only: guarantees lack of side-effects (no variables are written) + * + * Returns: + * number of milliseconds left before timeout expiration OR + * GNUTLS_E_TIMEDOUT if timeout expired + * GNUTLS_E_SUCCESS if timeout updated + * GNUTLS_E_ILLEGAL_PARAMETER if no HB message is in-flight + **/ +int +gnutls_heartbeat_timeout (gnutls_session_t session, int check_only) +{ + struct timespec now; + unsigned int ms; + if (session->internals.heartbeat_payload.length) + { + _gnutls_debug_log + ("HB: %zu bytes are in-flight already: %u msec timeout.\n", + session->internals.heartbeat_payload.length, + (unsigned int) session->internals.dtls.heartbeat_timeout); + gettime (&now); + ms = + _dtls_timespec_sub_ms (&now, &session->internals.dtls.heartbeat_sent); + if (ms > session->internals.dtls.heartbeat_timeout) + { + if (check_only) + return GNUTLS_E_TIMEDOUT; + _gnutls_buffer_reset (&session->internals.heartbeat_payload); + + if (session->internals.dtls.heartbeat_timeout * 2 > + MAX_HEARTBEAT_TIMEOUT) + { /* update impossible */ + session->internals.dtls.heartbeat_timeout = HEARTBEAT_TIMEOUT; + return GNUTLS_E_TIMEDOUT; + } + else + { + session->internals.dtls.heartbeat_timeout *= 2; + gettime (&session->internals.dtls.heartbeat_sent); + return GNUTLS_E_SUCCESS; + } + } + else + { + return ms; + } + } + return GNUTLS_E_ILLEGAL_PARAMETER; +} + +/** + * _gnutls_heartbeat_policy_force: + * @session: is a #gnutls_session_t structure. + * @epriv: pointer to work on + * @enforce: whether default policy should be enforced + * + * This function will try to obtain session data. + * It might try to enforce default policy. + * + * Returns: GNUTLS_E_SUCCESS or error code + **/ +inline static int +_gnutls_heartbeat_policy_force (gnutls_session_t session, + extension_priv_data_t * epriv, int enforce) +{ + if (_gnutls_ext_get_session_data + (session, GNUTLS_EXTENSION_HEARTBEAT, epriv) < 0) + { + if (enforce) + { + _gnutls_debug_log + ("HB: failed to get session data, policy forced to %s\n", + _gnutls_heartbeat (HEARTBEAT_DEFAULT_POLICY)); + _gnutls_heartbeat_policy_set (session, HEARTBEAT_DEFAULT_POLICY); + if (_gnutls_ext_get_session_data + (session, GNUTLS_EXTENSION_HEARTBEAT, epriv) < 0) + return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); + else + { + return GNUTLS_E_SUCCESS; + } + } + else + { + return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); + } + } + else + { + return GNUTLS_E_SUCCESS; + } +} + +static int +_gnutls_heartbeat_recv_params (gnutls_session_t session, const uint8_t * data, + size_t _data_size) +{ + heartbeat_policy_t pol; + extension_priv_data_t epriv; + int enforce; + + if (_data_size == 0) + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + _gnutls_debug_log ("HB: received parameter %s (%zu bytes)\n", + _gnutls_heartbeat (data[0]), _data_size); + + if (session->security_parameters.entity == GNUTLS_SERVER) + enforce = 1; + else + enforce = 0; + + if (GNUTLS_E_SUCCESS != + _gnutls_heartbeat_policy_force (session, &epriv, enforce)) + return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); + + _gnutls_debug_log ("HB: enabled at local:%d, remote:%d\n", + gnutls_heartbeat_enabled_local (session), + gnutls_heartbeat_enabled_remote (session)); + + pol.num = epriv.num; + switch (data[0]) + { + case PEER_ALLOWED_TO_SEND: + pol.set.remote = 1; + break; + case PEER_NOT_ALLOWED_TO_SEND: + pol.set.remote = 0; + break; + default: + return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST); + } + epriv.num = pol.num; + _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_HEARTBEAT, epriv); + _gnutls_debug_log ("HB: set enabled local:%d, remote:%d\n", + gnutls_heartbeat_enabled_local (session), + gnutls_heartbeat_enabled_remote (session)); + + return GNUTLS_E_SUCCESS; +} + +static int +_gnutls_heartbeat_send_params (gnutls_session_t session, + gnutls_buffer_st * extdata) +{ + heartbeat_policy_t pol; + extension_priv_data_t epriv; + uint8_t p; + int enforce; + + if (session->internals.heartbeat_policy_set) + { + + if (session->security_parameters.entity == GNUTLS_CLIENT) + enforce = 1; + else + enforce = 0; + + if (GNUTLS_E_SUCCESS != + _gnutls_heartbeat_policy_force (session, &epriv, enforce)) + return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); + + pol.num = epriv.num; + _gnutls_debug_log ("HB: local set %d\n", pol.set.local); + p = pol.set.local ? PEER_ALLOWED_TO_SEND : PEER_NOT_ALLOWED_TO_SEND; + } + else + { + p = HEARTBEAT_DEFAULT_POLICY; + _gnutls_heartbeat_policy_set (session, HEARTBEAT_DEFAULT_POLICY); + } + + _gnutls_debug_log ("HB: sending parameter %s\n", _gnutls_heartbeat (p)); + if (_gnutls_buffer_append_data (extdata, &p, 1) < 0) + return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR); + + return 1; /* number of bytes added for sending */ +} + +static int +_gnutls_heartbeat_pack (extension_priv_data_t _priv, gnutls_buffer_st * ps) +{ + int ret; + BUFFER_APPEND_NUM (ps, _priv.num); + return GNUTLS_E_SUCCESS; +} + +static int +_gnutls_heartbeat_unpack (gnutls_buffer_st * ps, + extension_priv_data_t * _priv) +{ + int ret; + extension_priv_data_t epriv; + BUFFER_POP_NUM (ps, epriv.num); + *_priv = epriv; + ret = 0; +error: + return ret; +} + + +extension_entry_st ext_mod_heartbeat = { + .name = "HEARTBEAT", + .type = GNUTLS_EXTENSION_HEARTBEAT, + .parse_type = GNUTLS_EXT_TLS, + + .recv_func = _gnutls_heartbeat_recv_params, + .send_func = _gnutls_heartbeat_send_params, + .pack_func = _gnutls_heartbeat_pack, + .unpack_func = _gnutls_heartbeat_unpack, + .deinit_func = NULL +}; diff --git a/lib/ext/heartbeat.h b/lib/ext/heartbeat.h new file mode 100644 index 0000000000..b1bb09e13e --- /dev/null +++ b/lib/ext/heartbeat.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2002-2012 Free Software Foundation, Inc. + * + * Author: Olga Smolenchuk + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#ifndef EXT_HEARTBEAT_H +#define EXT_HEARTBEAT_H + +#include <gnutls_extensions.h> + +#define HEARTBEAT_REQUEST 1 +#define HEARTBEAT_RESPONSE 2 + +#define MAX_HEARTBEAT_LENGTH 16384 +#define HEARTBEAT_TIMEOUT 1000 +#define MAX_HEARTBEAT_TIMEOUT 60000 + +#define PEER_ALLOWED_TO_SEND 1 +#define PEER_NOT_ALLOWED_TO_SEND 2 + +#define HEARTBEAT_DEFAULT_POLICY PEER_NOT_ALLOWED_TO_SEND + +extern extension_entry_st ext_mod_heartbeat; + +typedef union +{ + uint32_t num; + struct + { + uint8_t local:1; + uint8_t remote:1; + } set; +} heartbeat_policy_t; + +int _gnutls_heartbeat_policy_set (gnutls_session_t session, unsigned policy); +int gnutls_heartbeat_allow (gnutls_session_t session); +int gnutls_heartbeat_deny (gnutls_session_t session); +unsigned _gnutls_heartbeat_policy_get (gnutls_session_t session); +int _gnutls_heartbeat_handle (gnutls_session_t session, mbuffer_st * bufel); +ssize_t _gnutls_heartbeat_send_data (gnutls_session_t session, + const void *data, size_t data_size, + int request); +ssize_t gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size); +ssize_t gnutls_heartbeat_ping_rnd (gnutls_session_t session); +int _gnutls_heartbeat_enabled (gnutls_session_t session, int local); +int gnutls_heartbeat_enabled_local (gnutls_session_t session); +int gnutls_heartbeat_enabled_remote (gnutls_session_t session); +int gnutls_heartbeat_timeout (gnutls_session_t session, int check_only); +const char * _gnutls_heartbeat (int policy); +#endif diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index 973fb1c953..c71451953e 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -61,8 +61,8 @@ */ #define MAX_QUEUE 32 -/* Buffers received packets of type APPLICATION DATA and - * HANDSHAKE DATA. +/* Buffers received packets of type APPLICATION DATA, + * HANDSHAKE DATA and HEARTBEAT. */ int _gnutls_record_buffer_put (gnutls_session_t session, @@ -132,7 +132,7 @@ mbuffer_st* bufel; memcpy(data, msg.data, length); _mbuffer_head_remove_bytes(&session->internals.record_buffer, length); - + return length; } diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index d41ef2c863..b43771f11c 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -93,7 +93,10 @@ static const gnutls_error_entry error_algorithms[] = { GNUTLS_E_NO_CERTIFICATE_FOUND, 1), ERROR_ENTRY (N_("The given DSA key is incompatible with the selected TLS protocol."), GNUTLS_E_INCOMPAT_DSA_KEY_WITH_TLS_PROTOCOL, 1), - + ERROR_ENTRY (N_("HeartBeat Response to Request has failed for some reason: pong dropped."), + GNUTLS_E_HEARTBEAT_PONG_FAILED, 1), + ERROR_ENTRY (N_("HeartBeat message is already in-flight."), + GNUTLS_E_HEARTBEAT_FLIGHT, 1), ERROR_ENTRY (N_("There is already a crypto algorithm with lower priority."), GNUTLS_E_CRYPTO_ALREADY_REGISTERED, 1), diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c index 42857d0157..eb07d28621 100644 --- a/lib/gnutls_extensions.c +++ b/lib/gnutls_extensions.c @@ -32,6 +32,7 @@ #include <ext/cert_type.h> #include <ext/server_name.h> #include <ext/srp.h> +#include <ext/heartbeat.h> #include <ext/session_ticket.h> #include <ext/safe_renegotiation.h> #include <ext/signature.h> @@ -329,6 +330,10 @@ _gnutls_ext_init (void) return ret; #endif + ret = _gnutls_ext_register (&ext_mod_heartbeat); + if (ret != GNUTLS_E_SUCCESS) + return ret; + ret = _gnutls_ext_register (&ext_mod_session_ticket); if (ret != GNUTLS_E_SUCCESS) return ret; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 8da69782f2..bf901e0e73 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -246,6 +246,7 @@ typedef enum extensions_t GNUTLS_EXTENSION_SUPPORTED_ECC_PF = 11, GNUTLS_EXTENSION_SRP = 12, GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS = 13, + GNUTLS_EXTENSION_HEARTBEAT = 15, GNUTLS_EXTENSION_SESSION_TICKET = 35, GNUTLS_EXTENSION_SAFE_RENEGOTIATION = 65281 /* aka: 0xff01 */ } extensions_t; @@ -261,6 +262,7 @@ typedef enum content_type_t { GNUTLS_CHANGE_CIPHER_SPEC = 20, GNUTLS_ALERT, GNUTLS_HANDSHAKE, GNUTLS_APPLICATION_DATA, + GNUTLS_HEARTBEAT } content_type_t; @@ -648,7 +650,10 @@ typedef struct * has terminated. Required to handle retransmissions. */ time_t async_term; - + + struct timespec heartbeat_sent; /* timestamp: when last HeartBeat Request was sent*/ + time_t heartbeat_timeout; /* current timeout, in milliseconds*/ + /* last retransmission triggered by record layer */ struct timespec last_retransmit; unsigned int packets_dropped; @@ -883,6 +888,9 @@ typedef struct unsigned int handshake_endtime; /* end time in seconds */ unsigned int handshake_timeout_ms; /* timeout in milliseconds */ + gnutls_buffer_st heartbeat_payload; /* store in-flight payload for heartbeat extension*/ + int heartbeat_policy_set; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index 396a800d9f..de634dcc92 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -46,9 +46,11 @@ #include "gnutls_datum.h" #include "gnutls_constate.h" #include "ext/max_record.h" +#include <ext/heartbeat.h> #include <gnutls_state.h> #include <gnutls_dtls.h> #include <gnutls_dh.h> +#include <random.h> struct tls_record_st { uint16_t header_size; @@ -485,6 +487,7 @@ check_recv_type (gnutls_session_t session, content_type_t recv_type) case GNUTLS_CHANGE_CIPHER_SPEC: case GNUTLS_ALERT: case GNUTLS_HANDSHAKE: + case GNUTLS_HEARTBEAT: case GNUTLS_APPLICATION_DATA: return 0; default: @@ -505,6 +508,7 @@ check_buffers (gnutls_session_t session, content_type_t type, { if ((type == GNUTLS_APPLICATION_DATA || type == GNUTLS_HANDSHAKE || + type == GNUTLS_HEARTBEAT || type == GNUTLS_CHANGE_CIPHER_SPEC) && _gnutls_record_buffer_get_size (session) > 0) { @@ -587,6 +591,7 @@ record_add_to_buffers (gnutls_session_t session, if ((recv->type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_CHANGE_CIPHER_SPEC || + type == GNUTLS_HEARTBEAT || type == GNUTLS_HANDSHAKE)) { _gnutls_record_buffer_put (session, type, seq, bufel); @@ -650,6 +655,12 @@ record_add_to_buffers (gnutls_session_t session, _gnutls_record_buffer_put (session, recv->type, seq, bufel); break; + + case GNUTLS_HEARTBEAT: + ret = _gnutls_heartbeat_handle (session, bufel); + goto cleanup; + break; + case GNUTLS_APPLICATION_DATA: if (session->internals.initial_negotiation_completed == 0) { @@ -683,6 +694,7 @@ record_add_to_buffers (gnutls_session_t session, } break; + case GNUTLS_HANDSHAKE: /* In DTLS we might receive a handshake replay from the peer to indicate * the our last TLS handshake messages were not received. @@ -747,8 +759,8 @@ record_add_to_buffers (gnutls_session_t session, default: _gnutls_record_log - ("REC[%p]: Received Unknown packet %d expecting %d\n", - session, recv->type, type); + ("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n", + session, recv->type, _gnutls_packet2str(recv->type), type, _gnutls_packet2str(type)); gnutls_assert (); ret = GNUTLS_E_UNEXPECTED_PACKET; @@ -1237,7 +1249,6 @@ _gnutls_recv_int (gnutls_session_t session, content_type_t type, return check_buffers (session, type, data, data_size, seq); } - /** * gnutls_record_send: * @session: is a #gnutls_session_t structure. @@ -1302,10 +1313,8 @@ gnutls_record_send (gnutls_session_t session, const void *data, * The number of bytes received might be less than the requested @data_size. **/ ssize_t -gnutls_record_recv (gnutls_session_t session, void *data, size_t data_size) -{ - return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, - data_size, NULL); +gnutls_record_recv (gnutls_session_t session, void *data, size_t data_size) { + return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, data_size, NULL); } /** diff --git a/lib/gnutls_record.h b/lib/gnutls_record.h index b171591a20..4151673f1d 100644 --- a/lib/gnutls_record.h +++ b/lib/gnutls_record.h @@ -33,7 +33,6 @@ ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type, ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t, uint8_t * data, size_t sizeofdata, void* seq); - int _gnutls_get_max_decrypted_data(gnutls_session_t session); #endif diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index c868d48d9e..f80353a77d 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -322,8 +322,11 @@ gnutls_init (gnutls_session_t * session, unsigned int flags) /* the default certificate type for TLS */ (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE; + (*session)->internals.heartbeat_policy_set = 0; + /* Initialize buffers */ _gnutls_buffer_init (&(*session)->internals.handshake_hash_buffer); + _gnutls_buffer_init (&(*session)->internals.heartbeat_payload); _mbuffer_head_init (&(*session)->internals.record_buffer); _mbuffer_head_init (&(*session)->internals.record_send_buffer); @@ -438,7 +441,9 @@ gnutls_deinit (gnutls_session_t session) } _gnutls_buffer_clear (&session->internals.handshake_hash_buffer); - _mbuffer_head_clear (&session->internals.record_buffer); + _gnutls_buffer_clear (&session->internals.heartbeat_payload); + if (&session->internals.record_buffer) + _mbuffer_head_clear (&session->internals.record_buffer); _mbuffer_head_clear (&session->internals.record_recv_buffer); _mbuffer_head_clear (&session->internals.record_send_buffer); diff --git a/lib/gnutls_str.h b/lib/gnutls_str.h index a0fb44e9f6..e128766008 100644 --- a/lib/gnutls_str.h +++ b/lib/gnutls_str.h @@ -124,6 +124,11 @@ int _gnutls_hostname_compare (const char *certname, size_t certnamesize, } #define BUFFER_APPEND_PFX(b, x, s) { \ + BUFFER_APPEND_PFX4(b, x, s) \ + } + +/* append data prefixed with 4-bytes length field*/ +#define BUFFER_APPEND_PFX4(b, x, s) { \ ret = _gnutls_buffer_append_data_prefix(b, 32, x, s); \ if (ret < 0) { \ gnutls_assert(); \ @@ -131,6 +136,30 @@ int _gnutls_hostname_compare (const char *certname, size_t certnamesize, } \ } +#define BUFFER_APPEND_PFX3(b, x, s) { \ + ret = _gnutls_buffer_append_data_prefix(b, 24, x, s); \ + if (ret < 0) { \ + gnutls_assert(); \ + return ret; \ + } \ + } + +#define BUFFER_APPEND_PFX2(b, x, s) { \ + ret = _gnutls_buffer_append_data_prefix(b, 16, x, s); \ + if (ret < 0) { \ + gnutls_assert(); \ + return ret; \ + } \ + } + +#define BUFFER_APPEND_PFX1(b, x, s) { \ + ret = _gnutls_buffer_append_data_prefix(b, 8, x, s); \ + if (ret < 0) { \ + gnutls_assert(); \ + return ret; \ + } \ + } + #define BUFFER_APPEND_NUM(b, s) { \ ret = _gnutls_buffer_append_prefix(b, 32, s); \ if (ret < 0) { \ @@ -139,7 +168,6 @@ int _gnutls_hostname_compare (const char *certname, size_t certnamesize, } \ } - #define BUFFER_POP(b, x, s) { \ size_t is = s; \ _gnutls_buffer_pop_data(b, x, &is); \ diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index ec674038bc..cb3c8ff132 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -856,6 +856,8 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session); /* Record layer functions. */ + ssize_t gnutls_heartbeat_ping (gnutls_session_t session, size_t data_size); + ssize_t gnutls_heartbeat_ping_rnd (gnutls_session_t session); ssize_t gnutls_record_send (gnutls_session_t session, const void *data, size_t data_size); ssize_t gnutls_record_recv (gnutls_session_t session, void *data, @@ -906,6 +908,13 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session); void *data, size_t * data_length, unsigned int *type, unsigned int indx); + /* Heartbeat */ + int gnutls_heartbeat_allow (gnutls_session_t session); + int gnutls_heartbeat_deny (gnutls_session_t session); + int gnutls_heartbeat_enabled_local (gnutls_session_t session); + int gnutls_heartbeat_enabled_remote (gnutls_session_t session); + int gnutls_heartbeat_timeout (gnutls_session_t session, int check_only); + /* Safe renegotiation */ int gnutls_safe_renegotiation_status (gnutls_session_t session); @@ -1966,6 +1975,9 @@ typedef int (*gnutls_pin_callback_t) (void *userdata, int attempt, #define GNUTLS_E_OPENPGP_PREFERRED_KEY_ERROR -215 #define GNUTLS_E_INCOMPAT_DSA_KEY_WITH_TLS_PROTOCOL -216 +#define GNUTLS_E_HEARTBEAT_FLIGHT -298 +#define GNUTLS_E_HEARTBEAT_PONG_FAILED -299 + /* PKCS11 related */ #define GNUTLS_E_PKCS11_ERROR -300 #define GNUTLS_E_PKCS11_LOAD_ERROR -301 diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 50a6872809..86e3fb46bf 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -301,6 +301,8 @@ GNUTLS_1_4 gnutls_record_recv; gnutls_record_send; gnutls_record_set_max_size; + gnutls_heartbeat_ping; + gnutls_heartbeat_ping_rnd; gnutls_rehandshake; gnutls_rsa_export_get_modulus_bits; gnutls_rsa_export_get_pubkey; @@ -333,6 +335,11 @@ GNUTLS_1_4 gnutls_sign_get_id; gnutls_sign_get_name; gnutls_sign_list; + gnutls_heartbeat_allow; + gnutls_heartbeat_deny; + gnutls_heartbeat_enabled_local; + gnutls_heartbeat_enabled_remote; + gnutls_heartbeat_timeout; gnutls_srp_1024_group_generator; gnutls_srp_1024_group_prime; gnutls_srp_1536_group_generator; diff --git a/src/cli-args.def b/src/cli-args.def index 5e2d6c6b65..1d9bc782dd 100644 --- a/src/cli-args.def +++ b/src/cli-args.def @@ -36,6 +36,13 @@ flag = { }; flag = { + name = heartbeat; + value = b; + descrip = "Activate heartbeat support"; + doc = ""; +}; + +flag = { name = rehandshake; value = e; descrip = "Establish a session and rehandshake"; @@ -625,6 +625,9 @@ init_tls_session (const char *hostname) } } + if (HAVE_OPT(HEARTBEAT)) + fprintf (stderr, "Set HeartBeat policy: %d.\n", gnutls_heartbeat_allow (session)); + #ifdef ENABLE_SESSION_TICKET if (disable_extensions == 0 && !HAVE_OPT(NOTICKET)t) gnutls_session_ticket_enable_client (session); @@ -942,7 +945,8 @@ after_handshake: } if (inp == IN_KEYBOARD) - { + { + if (HAVE_OPT(HEARTBEAT)) fprintf (stderr, "*** HeartBeat: %zu\n", gnutls_heartbeat_ping_rnd (hd.session)); if ((bytes = read (fileno (stdin), buffer, MAX_BUF - 1)) <= 0) { if (hd.secure == 0) diff --git a/src/common.c b/src/common.c index 1b881a8bb4..4b21e22c7a 100644 --- a/src/common.c +++ b/src/common.c @@ -34,6 +34,7 @@ #include <gnutls/gnutls.h> #include <gnutls/x509.h> #include <gnutls/openpgp.h> +#include <gnutls/crypto.h> #include <time.h> #include <common.h> @@ -1040,18 +1041,20 @@ print_list (const char *priorities, int verbose) int check_command(gnutls_session_t session, const char* str) { -int len = strlen(str); - + size_t len = strnlen(str, 128); + fprintf (stderr, "*** Processing %zu bytes command: %s\n", len, str); if (len > 2 && str[0] == str[1] && str[0] == '*') - { - if (strncmp(str, "**REHANDSHAKE**", - sizeof ("**REHANDSHAKE**") - 1) == 0) - { + { + if (strncmp(str, "**REHANDSHAKE**", sizeof ("**REHANDSHAKE**") - 1) == 0) + { fprintf (stderr, "*** Sending rehandshake request\n"); - gnutls_rehandshake (session); + gnutls_rehandshake (session); return 1; - } - } + } else if (strncmp(str, "**HEARTBEAT**", sizeof ("**HEARTBEAT**") - 1) == 0) { + gnutls_heartbeat_ping_rnd (session); + return 2; + } + } return 0; } diff --git a/src/serv-args.def b/src/serv-args.def index 9264d137cf..e61034e58d 100644 --- a/src/serv-args.def +++ b/src/serv-args.def @@ -76,6 +76,13 @@ flag = { }; flag = { + name = heartbeat; + value = b; + descrip = "Activate heartbeat support"; + doc = "Regularly ping client via heartbeat extension messages"; +}; + +flag = { name = x509fmtder; descrip = "Use DER format for certificates to read from"; doc = ""; diff --git a/src/serv.c b/src/serv.c index 42317e6b59..2ff60a3c5b 100644 --- a/src/serv.c +++ b/src/serv.c @@ -33,6 +33,7 @@ #include <sys/types.h> #include <string.h> #include <gnutls/gnutls.h> +#include <gnutls/dtls.h> #include <gnutls/openpgp.h> #include <sys/time.h> #include <sys/select.h> @@ -80,10 +81,10 @@ const char *x509_ecccertfile = NULL; const char *x509_cafile = NULL; const char *dh_params_file = NULL; const char *x509_crlfile = NULL; -const char * priorities = NULL; +const char *priorities = NULL; gnutls_datum_t session_ticket_key; -static void tcp_server(const char* name, int port); +static void tcp_server (const char *name, int port); /* end of globals */ @@ -128,13 +129,11 @@ static void cmd_parser (int argc, char **argv); #define HTTP_STATE_RESPONSE 2 #define HTTP_STATE_CLOSING 3 -LIST_TYPE_DECLARE (listener_item, char *http_request; char *http_response; - int request_length; int response_length; - int response_written; int http_state; - int listen_socket; int fd; - gnutls_session_t tls_session; - int handshake_ok; - ); +LIST_TYPE_DECLARE (listener_item, char *http_request; + char *http_response; int request_length; + int response_length; int response_written; + int http_state; int listen_socket; + int fd; gnutls_session_t tls_session; int handshake_ok;); static const char * safe_strerror (int value) @@ -171,7 +170,8 @@ gnutls_rsa_params_t rsa_params = NULL; static int generate_dh_primes (void) { - int prime_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL); + int prime_bits = + gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL); if (gnutls_dh_params_init (&dh_params) < 0) { @@ -252,7 +252,7 @@ static char pkcs3[] = static int static_dh_params (void) { - gnutls_datum_t params = { (void*)pkcs3, sizeof (pkcs3) }; + gnutls_datum_t params = { (void *) pkcs3, sizeof (pkcs3) }; int ret; if (gnutls_dh_params_init (&dh_params) < 0) @@ -329,7 +329,8 @@ generate_rsa_params (void) LIST_DECLARE_INIT (listener_list, listener_item, listener_free); -gnutls_session_t initialize_session (int dtls) +gnutls_session_t +initialize_session (int dtls) { gnutls_session_t session; const char *err; @@ -338,7 +339,7 @@ gnutls_session_t initialize_session (int dtls) priorities = "NORMAL"; if (dtls) - gnutls_init (&session, GNUTLS_SERVER|GNUTLS_DATAGRAM); + gnutls_init (&session, GNUTLS_SERVER | GNUTLS_DATAGRAM); else gnutls_init (&session, GNUTLS_SERVER); @@ -385,6 +386,10 @@ gnutls_session_t initialize_session (int dtls) gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); } + if (HAVE_OPT (HEARTBEAT)) + fprintf (stderr, "Set HeartBeat policy: %d.\n", + gnutls_heartbeat_allow (session)); + return session; } @@ -489,7 +494,8 @@ peer_print_info (gnutls_session_t session, int *ret_length, if (gnutls_server_name_get (session, dns, &dns_size, &type, 0) == 0) { - snprintf (tmp_buffer, tmp_buffer_size, "\n<p>Server Name: %s</p>\n", dns); + snprintf (tmp_buffer, tmp_buffer_size, "\n<p>Server Name: %s</p>\n", + dns); } } @@ -500,7 +506,8 @@ peer_print_info (gnutls_session_t session, int *ret_length, #ifdef ENABLE_SRP if (kx_alg == GNUTLS_KX_SRP) { - snprintf (tmp_buffer, tmp_buffer_size, "<p>Connected as user '%s'.</p>\n", + snprintf (tmp_buffer, tmp_buffer_size, + "<p>Connected as user '%s'.</p>\n", gnutls_srp_server_get_username (session)); } #endif @@ -508,7 +515,8 @@ peer_print_info (gnutls_session_t session, int *ret_length, #ifdef ENABLE_PSK if (kx_alg == GNUTLS_KX_PSK) { - snprintf (tmp_buffer, tmp_buffer_size, "<p>Connected as user '%s'.</p>\n", + snprintf (tmp_buffer, tmp_buffer_size, + "<p>Connected as user '%s'.</p>\n", gnutls_psk_server_get_username (session)); } #endif @@ -546,45 +554,51 @@ peer_print_info (gnutls_session_t session, int *ret_length, (session)); if (tmp == NULL) tmp = str_unknown; - snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>Certificate Type:</TD><TD>%s</TD></TR>\n", - tmp); + snprintf (tmp_buffer, tmp_buffer_size, + "<TR><TD>Certificate Type:</TD><TD>%s</TD></TR>\n", tmp); } tmp = gnutls_kx_get_name (kx_alg); if (tmp == NULL) tmp = str_unknown; - snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>Key Exchange:</TD><TD>%s</TD></TR>\n", tmp); + snprintf (tmp_buffer, tmp_buffer_size, + "<TR><TD>Key Exchange:</TD><TD>%s</TD></TR>\n", tmp); tmp = gnutls_compression_get_name (gnutls_compression_get (session)); if (tmp == NULL) tmp = str_unknown; - snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>Compression</TD><TD>%s</TD></TR>\n", tmp); + snprintf (tmp_buffer, tmp_buffer_size, + "<TR><TD>Compression</TD><TD>%s</TD></TR>\n", tmp); tmp = gnutls_cipher_get_name (gnutls_cipher_get (session)); if (tmp == NULL) tmp = str_unknown; - snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>Cipher</TD><TD>%s</TD></TR>\n", tmp); + snprintf (tmp_buffer, tmp_buffer_size, + "<TR><TD>Cipher</TD><TD>%s</TD></TR>\n", tmp); tmp = gnutls_mac_get_name (gnutls_mac_get (session)); if (tmp == NULL) tmp = str_unknown; - snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>MAC</TD><TD>%s</TD></TR>\n", tmp); + snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>MAC</TD><TD>%s</TD></TR>\n", + tmp); tmp = gnutls_cipher_suite_get_name (kx_alg, gnutls_cipher_get (session), gnutls_mac_get (session)); if (tmp == NULL) tmp = str_unknown; - snprintf (tmp_buffer, tmp_buffer_size, "<TR><TD>Ciphersuite</TD><TD>%s</TD></TR></p></TABLE>\n", - tmp); + snprintf (tmp_buffer, tmp_buffer_size, + "<TR><TD>Ciphersuite</TD><TD>%s</TD></TR></p></TABLE>\n", tmp); if (crtinfo) { - snprintf (tmp_buffer, tmp_buffer_size, "<hr><PRE>%s\n</PRE>\n", crtinfo); + snprintf (tmp_buffer, tmp_buffer_size, "<hr><PRE>%s\n</PRE>\n", + crtinfo); free (crtinfo); } - snprintf (tmp_buffer, tmp_buffer_size, "<hr><P>Your HTTP header was:<PRE>%s</PRE></P>\n" HTTP_END, + snprintf (tmp_buffer, tmp_buffer_size, + "<hr><P>Your HTTP header was:<PRE>%s</PRE></P>\n" HTTP_END, header); *ret_length = strlen (http_buffer); @@ -640,7 +654,8 @@ human_addr (const struct sockaddr *sa, socklen_t salen, return save_buf; } -int wait_for_connection(void) +int +wait_for_connection (void) { listener_item *j; fd_set rd, wr; @@ -672,14 +687,14 @@ int wait_for_connection(void) /* find which one is ready */ lloopstart (listener_list, j) - { - /* a new connection has arrived */ - if (FD_ISSET (j->fd, &rd) && j->listen_socket) - { - sock = j->fd; - break; - } - } + { + /* a new connection has arrived */ + if (FD_ISSET (j->fd, &rd) && j->listen_socket) + { + sock = j->fd; + break; + } + } lloopend (listener_list, j); return sock; } @@ -698,9 +713,9 @@ listen_socket (const char *name, int listen_port, int socktype) hints.ai_socktype = socktype; hints.ai_flags = AI_PASSIVE #ifdef AI_ADDRCONFIG - | AI_ADDRCONFIG + | AI_ADDRCONFIG #endif - ; + ; if ((s = getaddrinfo (NULL, portname, &hints, &res)) != 0) { @@ -711,7 +726,8 @@ listen_socket (const char *name, int listen_port, int socktype) for (ptr = res; ptr != NULL; ptr = ptr->ai_next) { #ifndef HAVE_IPV6 - if (ptr->ai_family != AF_INET) continue; + if (ptr->ai_family != AF_INET) + continue; #endif /* Print what we are doing. */ @@ -758,12 +774,12 @@ listen_socket (const char *name, int listen_port, int socktype) yes = 1; if (setsockopt (s, IPPROTO_IP, IP_DONTFRAG, (const void *) &yes, sizeof (yes)) < 0) - perror ("setsockopt(IP_DF) failed"); + perror ("setsockopt(IP_DF) failed"); #elif defined(IP_MTU_DISCOVER) yes = IP_PMTUDISC_DO; - if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, - (const void*) &yes, sizeof (yes)) < 0) - perror ("setsockopt(IP_DF) failed"); + if (setsockopt (s, IPPROTO_IP, IP_MTU_DISCOVER, + (const void *) &yes, sizeof (yes)) < 0) + perror ("setsockopt(IP_DF) failed"); #endif } @@ -779,7 +795,7 @@ listen_socket (const char *name, int listen_port, int socktype) if (listen (s, 10) < 0) { perror ("listen() failed"); - exit(1); + exit (1); } } @@ -850,7 +866,7 @@ get_response (gnutls_session_t session, char *request, { strip (request); fprintf (stderr, "received: %s\n", request); - if (check_command(session, request)) + if (check_command (session, request)) { *response = NULL; *response_length = 0; @@ -913,7 +929,7 @@ main (int argc, char **argv) char name[256]; set_program_name (argv[0]); - cmd_parser(argc, argv); + cmd_parser (argc, argv); #ifndef _WIN32 signal (SIGHUP, SIG_IGN); @@ -927,9 +943,10 @@ main (int argc, char **argv) if (nodb == 0) wrap_db_init (); - if (HAVE_OPT(UDP)) - strcpy(name, "UDP "); - else name[0] = 0; + if (HAVE_OPT (UDP)) + strcpy (name, "UDP "); + else + name[0] = 0; if (http == 1) { @@ -1020,11 +1037,11 @@ main (int argc, char **argv) } } - if (HAVE_OPT(PGPCERTFILE)) + if (HAVE_OPT (PGPCERTFILE)) { - if (HAVE_OPT(PGPSUBKEY)) + if (HAVE_OPT (PGPSUBKEY)) ret = gnutls_certificate_set_openpgp_key_file2 - (cert_cred, pgp_certfile, pgp_keyfile, OPT_ARG(PGPSUBKEY), + (cert_cred, pgp_certfile, pgp_keyfile, OPT_ARG (PGPSUBKEY), GNUTLS_OPENPGP_FMT_BASE64); else ret = gnutls_certificate_set_openpgp_key_file @@ -1111,10 +1128,10 @@ main (int argc, char **argv) GERR (ret); } - if (HAVE_OPT(PSKHINT)) + if (HAVE_OPT (PSKHINT)) { ret = gnutls_psk_set_server_credentials_hint (psk_cred, - OPT_ARG(PSKHINT)); + OPT_ARG (PSKHINT)); if (ret) { fprintf (stderr, "Error setting PSK identity hint.\n"); @@ -1138,22 +1155,24 @@ main (int argc, char **argv) gnutls_session_ticket_key_generate (&session_ticket_key); #endif - if (HAVE_OPT(MTU)) + if (HAVE_OPT (MTU)) mtu = OPT_VALUE_MTU; - else mtu = 1300; + else + mtu = 1300; - if (HAVE_OPT(PORT)) + if (HAVE_OPT (PORT)) port = OPT_VALUE_PORT; else port = 5556; - if (HAVE_OPT(UDP)) - udp_server(name, port, mtu); + if (HAVE_OPT (UDP)) + udp_server (name, port, mtu); else - tcp_server(name, port); + tcp_server (name, port); } -static void tcp_server(const char* name, int port) +static void +tcp_server (const char *name, int port) { int n, s; char topbuf[512]; @@ -1312,8 +1331,9 @@ static void tcp_server(const char* name, int port) &client_address, calen, topbuf, sizeof (topbuf))); print_info (j->tls_session, verbose, verbose); - if (gnutls_auth_get_type (j->tls_session) == GNUTLS_CRD_CERTIFICATE) - cert_verify(j->tls_session, NULL); + if (gnutls_auth_get_type (j->tls_session) == + GNUTLS_CRD_CERTIFICATE) + cert_verify (j->tls_session, NULL); } j->handshake_ok = 1; } @@ -1355,12 +1375,19 @@ static void tcp_server(const char* name, int port) } else { - j->http_state = HTTP_STATE_CLOSING; - if (r < 0 && r != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) + if (r < 0) { - check_alert (j->tls_session, r); - fprintf (stderr, "Error while receiving data\n"); - GERR (r); + if (GNUTLS_E_HEARTBEAT_PONG_FAILED == r) + fprintf (stderr, + "HeartBeat pong failed, ping dropped\n"); + else if (r != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) + { + j->http_state = HTTP_STATE_CLOSING; + check_alert (j->tls_session, r); + fprintf (stderr, + "Error while receiving data\n"); + GERR (r); + } } } } @@ -1437,8 +1464,9 @@ static void tcp_server(const char* name, int port) sizeof (topbuf))); print_info (j->tls_session, verbose, verbose); - if (gnutls_auth_get_type (j->tls_session) == GNUTLS_CRD_CERTIFICATE) - cert_verify(j->tls_session, NULL); + if (gnutls_auth_get_type (j->tls_session) == + GNUTLS_CRD_CERTIFICATE) + cert_verify (j->tls_session, NULL); } j->handshake_ok = 1; } @@ -1545,80 +1573,82 @@ static void tcp_server(const char* name, int port) } -static void cmd_parser (int argc, char **argv) +static void +cmd_parser (int argc, char **argv) { - optionProcess( &gnutls_servOptions, argc, argv); + optionProcess (&gnutls_servOptions, argc, argv); - disable_client_cert = HAVE_OPT(DISABLE_CLIENT_CERT); - require_cert = HAVE_OPT(REQUIRE_CLIENT_CERT); - if (HAVE_OPT(DEBUG)) + disable_client_cert = HAVE_OPT (DISABLE_CLIENT_CERT); + require_cert = HAVE_OPT (REQUIRE_CLIENT_CERT); + if (HAVE_OPT (DEBUG)) debug = OPT_VALUE_DEBUG; - if (HAVE_OPT(QUIET)) + if (HAVE_OPT (QUIET)) verbose = 0; - if (HAVE_OPT(PRIORITY)) - priorities = OPT_ARG(PRIORITY); - - if (HAVE_OPT(LIST)) + if (HAVE_OPT (PRIORITY)) + priorities = OPT_ARG (PRIORITY); + + if (HAVE_OPT (LIST)) { - print_list(priorities, verbose); - exit(0); + print_list (priorities, verbose); + exit (0); } - nodb = HAVE_OPT(NODB); - noticket = HAVE_OPT(NOTICKET); + nodb = HAVE_OPT (NODB); + noticket = HAVE_OPT (NOTICKET); - if (HAVE_OPT(ECHO)) + if (HAVE_OPT (ECHO)) http = 0; - else http = 1; + else + http = 1; - if (HAVE_OPT(X509FMTDER)) + if (HAVE_OPT (X509FMTDER)) x509ctype = GNUTLS_X509_FMT_DER; else x509ctype = GNUTLS_X509_FMT_PEM; - generate = HAVE_OPT(GENERATE); + generate = HAVE_OPT (GENERATE); + + if (HAVE_OPT (DHPARAMS)) + dh_params_file = OPT_ARG (DHPARAMS); - if (HAVE_OPT(DHPARAMS)) - dh_params_file = OPT_ARG(DHPARAMS); + if (HAVE_OPT (X509KEYFILE)) + x509_keyfile = OPT_ARG (X509KEYFILE); + if (HAVE_OPT (X509CERTFILE)) + x509_certfile = OPT_ARG (X509CERTFILE); - if (HAVE_OPT(X509KEYFILE)) - x509_keyfile = OPT_ARG(X509KEYFILE); - if (HAVE_OPT(X509CERTFILE)) - x509_certfile = OPT_ARG(X509CERTFILE); + if (HAVE_OPT (X509DSAKEYFILE)) + x509_dsakeyfile = OPT_ARG (X509DSAKEYFILE); + if (HAVE_OPT (X509DSACERTFILE)) + x509_dsacertfile = OPT_ARG (X509DSACERTFILE); - if (HAVE_OPT(X509DSAKEYFILE)) - x509_dsakeyfile = OPT_ARG(X509DSAKEYFILE); - if (HAVE_OPT(X509DSACERTFILE)) - x509_dsacertfile = OPT_ARG(X509DSACERTFILE); + if (HAVE_OPT (X509ECCKEYFILE)) + x509_ecckeyfile = OPT_ARG (X509ECCKEYFILE); + if (HAVE_OPT (X509CERTFILE)) + x509_ecccertfile = OPT_ARG (X509ECCCERTFILE); - if (HAVE_OPT(X509ECCKEYFILE)) - x509_ecckeyfile = OPT_ARG(X509ECCKEYFILE); - if (HAVE_OPT(X509CERTFILE)) - x509_ecccertfile = OPT_ARG(X509ECCCERTFILE); - - if (HAVE_OPT(X509CAFILE)) - x509_cafile = OPT_ARG(X509CAFILE); - if (HAVE_OPT(X509CRLFILE)) - x509_crlfile = OPT_ARG(X509CRLFILE); + if (HAVE_OPT (X509CAFILE)) + x509_cafile = OPT_ARG (X509CAFILE); + if (HAVE_OPT (X509CRLFILE)) + x509_crlfile = OPT_ARG (X509CRLFILE); - if (HAVE_OPT(PGPKEYFILE)) - pgp_keyfile = OPT_ARG(PGPKEYFILE); - if (HAVE_OPT(PGPCERTFILE)) - pgp_certfile = OPT_ARG(PGPCERTFILE); + if (HAVE_OPT (PGPKEYFILE)) + pgp_keyfile = OPT_ARG (PGPKEYFILE); + if (HAVE_OPT (PGPCERTFILE)) + pgp_certfile = OPT_ARG (PGPCERTFILE); - if (HAVE_OPT(PGPKEYRING)) - pgp_keyring = OPT_ARG(PGPKEYRING); + if (HAVE_OPT (PGPKEYRING)) + pgp_keyring = OPT_ARG (PGPKEYRING); - if (HAVE_OPT(SRPPASSWD)) - srp_passwd = OPT_ARG(SRPPASSWD); - if (HAVE_OPT(SRPPASSWDCONF)) - srp_passwd_conf = OPT_ARG(SRPPASSWDCONF); + if (HAVE_OPT (SRPPASSWD)) + srp_passwd = OPT_ARG (SRPPASSWD); + if (HAVE_OPT (SRPPASSWDCONF)) + srp_passwd_conf = OPT_ARG (SRPPASSWDCONF); - if (HAVE_OPT(PSKPASSWD)) - psk_passwd = OPT_ARG(PSKPASSWD); + if (HAVE_OPT (PSKPASSWD)) + psk_passwd = OPT_ARG (PSKPASSWD); } diff --git a/src/socket.c b/src/socket.c index c4d40012f0..b33f6bc58b 100644 --- a/src/socket.c +++ b/src/socket.c @@ -53,6 +53,10 @@ socket_recv (const socket_st * socket, void *buffer, int buffer_size) do { ret = gnutls_record_recv (socket->session, buffer, buffer_size); + if (GNUTLS_E_HEARTBEAT_PONG_FAILED == ret) { + fprintf (stderr, "HeartBeat pong failed, ping dropped\n"); + ret = GNUTLS_E_AGAIN; + } } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); else diff --git a/src/tests.c b/src/tests.c index a3c5383676..13afe702aa 100644 --- a/src/tests.c +++ b/src/tests.c @@ -946,10 +946,51 @@ test_hello_extension (gnutls_session_t session) gnutls_record_set_max_size (session, 4096); ret = do_handshake (session); + + return ret; } test_code_t +test_heartbeat_extension (gnutls_session_t session) +{ + sprintf (prio_str, + INIT_STR ALL_CIPHERS ":" ALL_COMP ":" ALL_CERTTYPES ":%s:" ALL_MACS + ":" ALL_KX ":%s", protocol_str, rest); + _gnutls_priority_set_direct (session, prio_str); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + gnutls_record_set_max_size (session, 4096); + + gnutls_heartbeat_allow (session); + do_handshake (session); + + switch (gnutls_heartbeat_enabled_remote (session)) { + case 1: return TEST_SUCCEED; + case 0: return TEST_FAILED; + default: return TEST_UNSURE; + } +} + +test_code_t +test_heartbeat_policy (gnutls_session_t session) +{ + sprintf (prio_str, + INIT_STR ALL_CIPHERS ":" ALL_COMP ":" ALL_CERTTYPES ":%s:" ALL_MACS + ":" ALL_KX ":%s", protocol_str, rest); + _gnutls_priority_set_direct (session, prio_str); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + gnutls_record_set_max_size (session, 4096); + + gnutls_heartbeat_deny (session); + if (gnutls_heartbeat_enabled_local(session)) return TEST_FAILED; + + gnutls_heartbeat_allow (session); + if (gnutls_heartbeat_enabled_local(session)) return TEST_SUCCEED; + + return TEST_FAILED; +} + +test_code_t test_small_records (gnutls_session_t session) { int ret; diff --git a/src/tests.h b/src/tests.h index 53dda0c49f..87e7433e8f 100644 --- a/src/tests.h +++ b/src/tests.h @@ -28,6 +28,8 @@ test_code_t test_record_padding (gnutls_session_t state); test_code_t test_export (gnutls_session_t state); test_code_t test_export_info (gnutls_session_t state); test_code_t test_hello_extension (gnutls_session_t state); +test_code_t test_heartbeat_extension (gnutls_session_t state); +test_code_t test_heartbeat_policy (gnutls_session_t state); test_code_t test_small_records (gnutls_session_t state); test_code_t test_dhe (gnutls_session_t state); test_code_t test_dhe_group (gnutls_session_t state); diff --git a/src/tls_test.c b/src/tls_test.c index 7552bf82e6..38a80f87a9 100644 --- a/src/tls_test.c +++ b/src/tls_test.c @@ -114,6 +114,8 @@ static const TLS_TEST tls_tests[] = { test_rsa_pms_version_check, "yes", "no", "dunno"}, {"whether the server can accept Hello Extensions", test_hello_extension, "yes", "no", "dunno"}, + {"whether local HeartBeat Extension policy works", test_heartbeat_policy, "yes", "no", "dunno"}, + {"whether the server can accept HeartBeat Extension", test_heartbeat_extension, "yes", "no", "dunno"}, {"whether the server can accept small records (512 bytes)", test_small_records, "yes", "no", "dunno"}, {"whether the server can accept cipher suites not in SSL 3.0 spec", |