summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlga <olyasib12@gmail.com>2012-08-28 00:12:56 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2012-08-30 20:04:18 +0200
commit3f2e9a0d1231f2555626305b752f73c87afbe6f2 (patch)
treeb903ca9de4bf9dd613627382b6f429e50551ee71
parent23934e9fe103c3026491b25255467637cc1df89f (diff)
downloadgnutls-3f2e9a0d1231f2555626305b752f73c87afbe6f2.tar.gz
Added Heartbeat extension support.
Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
-rw-r--r--doc/cha-internals.texi25
-rw-r--r--doc/cha-intro-tls.texi26
-rw-r--r--doc/manpages/Makefile.am1
-rw-r--r--doc/protocol/rfc6520.txt507
-rw-r--r--lib/debug.c2
-rw-r--r--lib/ext/Makefile.am2
-rw-r--r--lib/ext/heartbeat.c585
-rw-r--r--lib/ext/heartbeat.h67
-rw-r--r--lib/gnutls_buffers.c6
-rw-r--r--lib/gnutls_errors.c5
-rw-r--r--lib/gnutls_extensions.c5
-rw-r--r--lib/gnutls_int.h10
-rw-r--r--lib/gnutls_record.c23
-rw-r--r--lib/gnutls_record.h1
-rw-r--r--lib/gnutls_state.c7
-rw-r--r--lib/gnutls_str.h30
-rw-r--r--lib/includes/gnutls/gnutls.h.in12
-rw-r--r--lib/libgnutls.map7
-rw-r--r--src/cli-args.def7
-rw-r--r--src/cli.c6
-rw-r--r--src/common.c21
-rw-r--r--src/serv-args.def7
-rw-r--r--src/serv.c266
-rw-r--r--src/socket.c4
-rw-r--r--src/tests.c41
-rw-r--r--src/tests.h2
-rw-r--r--src/tls_test.c2
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";
diff --git a/src/cli.c b/src/cli.c
index 47f6d09bc9..e2a1deb287 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -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",