summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2016-04-14 19:20:36 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2016-04-15 10:00:45 +0200
commiteee2b7554f8831a8b32c8f1b68bb23ff3114fea4 (patch)
tree0df0d2c6f700fe1a6d0eb27d0872015c0458d340 /lib
parent17594818f41d5c6e63ae499537f5993c7faa93c9 (diff)
downloadgnutls-eee2b7554f8831a8b32c8f1b68bb23ff3114fea4.tar.gz
Updated false start support to be transparent to applications.
That is, an additional flag GNUTLS_ENABLE_FALSE_START is introduced for gnutls_init(), and that enables support for false start. At this point false start will be performed by the handshake if possible, and gnutls_record_recv() will handle handshake completion.
Diffstat (limited to 'lib')
-rw-r--r--lib/errors.c2
-rw-r--r--lib/gnutls_int.h17
-rw-r--r--lib/handshake.c89
-rw-r--r--lib/includes/gnutls/gnutls.h.in9
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/record.c46
-rw-r--r--lib/state.c8
7 files changed, 90 insertions, 82 deletions
diff --git a/lib/errors.c b/lib/errors.c
index 9dc4d658a1..34fcf21153 100644
--- a/lib/errors.c
+++ b/lib/errors.c
@@ -380,6 +380,8 @@ static const gnutls_error_entry error_entries[] = {
GNUTLS_E_SESSION_CERTIFICATE_CHANGED),
ERROR_ENTRY(N_("The provided string has an embedded null."),
GNUTLS_E_ASN1_EMBEDDED_NULL_IN_STRING),
+ ERROR_ENTRY(N_("Attempted handshake during false start."),
+ GNUTLS_E_HANDSHAKE_DURING_FALSE_START),
{NULL, NULL, 0}
};
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 484bea6edd..cbd5edf082 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -226,19 +226,26 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
STATE9, STATE10, STATE11, STATE12, STATE13, STATE14,
STATE15, STATE16, STATE17, STATE18, STATE19,
STATE20 = 20, STATE21, STATE22,
- STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50,
- STATE60 = 60, STATE61, STATE62,
+ STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50
} handshake_state_t;
+typedef enum bye_state_t {
+ BYE_STATE0 = 0, BYE_STATE1, BYE_STATE2
+} bye_state_t;
+
+#define BYE_STATE session->internals.bye_state
+
typedef enum heartbeat_state_t {
SHB_SEND1 = 0,
SHB_SEND2,
- SHB_RECV,
+ SHB_RECV
} heartbeat_state_t;
typedef enum recv_state_t {
RECV_STATE_0 = 0,
RECV_STATE_DTLS_RETRANSMIT,
+ RECV_STATE_FALSE_START_HANDLING, /* we are calling gnutls_handshake() within record_recv() */
+ RECV_STATE_FALSE_START /* gnutls_record_recv() should complete the handshake */
} recv_state_t;
#include "str.h"
@@ -760,6 +767,7 @@ typedef struct {
* message */
bool resumable; /* TRUE or FALSE - if we can resume that session */
bool ticket_sent; /* whether a session ticket was sent */
+ bye_state_t bye_state; /* used by gnutls_bye() */
handshake_state_t handshake_final_state;
handshake_state_t handshake_state; /* holds
* a number which indicates where
@@ -1016,8 +1024,7 @@ typedef struct {
uint8_t cert_hash[32];
bool cert_hash_set;
- /* function to be called at false start */
- gnutls_handshake_simple_hook_func false_start_func;
+ bool enable_false_start; /* whether TLS false start has been requested */
bool false_start_used; /* non-zero if false start was used for appdata */
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
diff --git a/lib/handshake.c b/lib/handshake.c
index efd0a316fc..0b69a52fc6 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -2415,45 +2415,6 @@ int gnutls_rehandshake(gnutls_session_t session)
return 0;
}
-/**
- * gnutls_handshake_set_false_start_function:
- * @session: is a #gnutls_session_t type.
- * @func: is the function to be called
- * @flags: should be zero
- *
- * This function in client side will ensure that @func is called
- * after the client's side of the handshake is complete. That is,
- * it can be used to send data before gnutls_handshake() completes
- * (i.e., before the peer's Finished message is received)
- * and reduce the handshake to a single round-trip. The callback
- * must only be used to send data using gnutls_record_send().
- *
- * This callback must return 0 on success or a gnutls error code to
- * terminate the handshake. Typically it should include a call to
- * gnutls_record_send().
- *
- * Note that, this utilises the so-called TLS False Start,
- * which may increase the risk of cryptographic failure when
- * combined with certain ciphers and key exchanges. For that
- * GnuTLS will call the provided function prior to receiving
- * the finished messages only in case of known to be secure ciphers.
- * Otherwise @func is called after the handshake is fully complete.
- *
- * On the server side this function always fail.
- *
- * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
- **/
-int gnutls_handshake_set_false_start_function(gnutls_session_t session,
- gnutls_handshake_simple_hook_func func, unsigned flags)
-{
- if (session->security_parameters.entity == GNUTLS_CLIENT) {
- session->internals.false_start_func = func;
- return 0;
- }
-
- return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-}
-
inline static int
_gnutls_abort_handshake(gnutls_session_t session, int ret)
{
@@ -2594,6 +2555,11 @@ int gnutls_handshake(gnutls_session_t session)
session->internals.handshake_timeout_ms / 1000;
}
+ if (session->internals.recv_state == RECV_STATE_FALSE_START) {
+ session_invalidate(session);
+ return gnutls_assert_val(GNUTLS_E_HANDSHAKE_DURING_FALSE_START);
+ }
+
ret =
_gnutls_epoch_get(session,
session->security_parameters.epoch_next,
@@ -2626,17 +2592,22 @@ int gnutls_handshake(gnutls_session_t session)
}
/* clear handshake buffer */
- _gnutls_handshake_hash_buffers_clear(session);
+ if (session->security_parameters.entity != GNUTLS_CLIENT ||
+ !session->internals.enable_false_start ||
+ session->internals.recv_state != RECV_STATE_FALSE_START) {
- if (IS_DTLS(session) == 0) {
- _gnutls_handshake_io_buffer_clear(session);
- } else {
- _dtls_async_timer_init(session);
- }
+ _gnutls_handshake_hash_buffers_clear(session);
+
+ if (IS_DTLS(session) == 0) {
+ _gnutls_handshake_io_buffer_clear(session);
+ } else {
+ _dtls_async_timer_init(session);
+ }
- _gnutls_handshake_internal_state_clear(session);
+ _gnutls_handshake_internal_state_clear(session);
- session->security_parameters.epoch_next++;
+ session->security_parameters.epoch_next++;
+ }
return 0;
}
@@ -2819,7 +2790,6 @@ static int handshake_client(gnutls_session_t session)
session_id_size, buf,
sizeof(buf), NULL));
#endif
-
switch (STATE) {
case STATE0:
case STATE1:
@@ -2964,16 +2934,23 @@ static int handshake_client(gnutls_session_t session)
case STATE17:
STATE = STATE17;
- if (session->internals.resumed == RESUME_FALSE && can_send_false_start(session) && session->internals.false_start_func != 0) {
+ if (session->internals.resumed == RESUME_FALSE && session->internals.enable_false_start != 0 && can_send_false_start(session)) {
session->internals.false_start_used = 1;
- ret = session->internals.false_start_func(session);
- IMED_RET("false start", ret, 1);
+ session->internals.recv_state = RECV_STATE_FALSE_START;
+ /* complete this phase of the handshake. We
+ * should be called again by gnutls_record_recv()
+ */
+ STATE = STATE18;
+ gnutls_assert();
+
+ return 0;
} else {
session->internals.false_start_used = 0;
}
case STATE18:
STATE = STATE18;
+
if (session->internals.resumed == RESUME_FALSE) {
#ifdef ENABLE_SESSION_TICKETS
ret = _gnutls_recv_new_session_ticket(session);
@@ -2995,18 +2972,14 @@ static int handshake_client(gnutls_session_t session)
IMED_RET("send handshake final", ret, 1);
}
- case STATE20:
- STATE = STATE20;
- if ((session->internals.resumed != RESUME_FALSE || !can_send_false_start(session)) && session->internals.false_start_func != 0) {
- ret = session->internals.false_start_func(session);
- IMED_RET("false start", ret, 1);
- }
-
STATE = STATE0;
default:
break;
}
+ /* explicitly reset any false start flags */
+ session->internals.recv_state = RECV_STATE_0;
+
return 0;
}
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 8f2df57049..98014aa11b 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -354,6 +354,7 @@ typedef enum {
* @GNUTLS_NO_EXTENSIONS: Do not enable any TLS extensions by default (since 3.1.2).
* @GNUTLS_NO_REPLAY_PROTECTION: Disable any replay protection in DTLS. This must only be used if replay protection is achieved using other means.
* @GNUTLS_ALLOW_CERT_CHANGE: Allow the peer to replace its certificate during a rehandshake. This change is often used in attacks and thus prohibited by default (since 3.5.0).
+ * @GNUTLS_ENABLE_FALSE_START: Enable the TLS false start on client side if the negotiated ciphersuites allow it. This will enable sending data prior to the handshake being complete, and may introduce a risk of crypto failure when combined with certain key exchanged; for that GnuTLS may not enable that option in ciphersuites that are known to be not safe for false start (since 3.5.0).
*
* Enumeration of different flags for gnutls_init() function. All the flags
* can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually
@@ -367,7 +368,8 @@ typedef enum {
GNUTLS_NO_EXTENSIONS = (1<<4),
GNUTLS_NO_REPLAY_PROTECTION = (1<<5),
GNUTLS_NO_SIGNAL = (1<<6),
- GNUTLS_ALLOW_CERT_CHANGE = (1<<7)
+ GNUTLS_ALLOW_CERT_CHANGE = (1<<7),
+ GNUTLS_ENABLE_FALSE_START = (1<<8)
} gnutls_init_flags_t;
/**
@@ -1488,10 +1490,6 @@ void
gnutls_handshake_set_post_client_hello_function(gnutls_session_t session,
gnutls_handshake_simple_hook_func func);
-int gnutls_handshake_set_false_start_function(gnutls_session_t session,
- gnutls_handshake_simple_hook_func func,
- unsigned flags);
-
void gnutls_handshake_set_max_packet_length(gnutls_session_t session,
size_t max);
@@ -2734,6 +2732,7 @@ int gnutls_fips140_mode_enabled(void);
#define GNUTLS_E_NEED_FALLBACK -405
#define GNUTLS_E_SESSION_CERTIFICATE_CHANGED -406
+#define GNUTLS_E_HANDSHAKE_DURING_FALSE_START -407
#define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index b1787f342d..b1bef2805c 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1078,7 +1078,6 @@ GNUTLS_3_4
gnutls_dh_params_import_dsa;
gnutls_session_get_flags;
gnutls_session_get_master_secret;
- gnutls_handshake_set_false_start_function;
gnutls_x509_crt_get_signature_oid;
gnutls_x509_crt_get_pk_oid;
gnutls_x509_crq_get_signature_oid;
diff --git a/lib/record.c b/lib/record.c
index ca181622c1..52b22393fa 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -275,28 +275,27 @@ int gnutls_bye(gnutls_session_t session, gnutls_close_request_t how)
{
int ret = 0;
- switch (STATE) {
- case STATE0:
- case STATE60:
+ switch (BYE_STATE) {
+ case BYE_STATE0:
ret = _gnutls_io_write_flush(session);
- STATE = STATE60;
+ BYE_STATE = BYE_STATE0;
if (ret < 0) {
gnutls_assert();
return ret;
}
/* fallthrough */
- case STATE61:
+ case BYE_STATE1:
ret =
gnutls_alert_send(session, GNUTLS_AL_WARNING,
GNUTLS_A_CLOSE_NOTIFY);
- STATE = STATE61;
+ BYE_STATE = BYE_STATE1;
if (ret < 0) {
gnutls_assert();
return ret;
}
- case STATE62:
- STATE = STATE62;
+ case BYE_STATE2:
+ BYE_STATE = BYE_STATE2;
if (how == GNUTLS_SHUT_RDWR) {
do {
ret =
@@ -315,7 +314,7 @@ int gnutls_bye(gnutls_session_t session, gnutls_close_request_t how)
return ret;
}
}
- STATE = STATE62;
+ BYE_STATE = BYE_STATE2;
break;
default:
@@ -323,7 +322,7 @@ int gnutls_bye(gnutls_session_t session, gnutls_close_request_t how)
return GNUTLS_E_INTERNAL_ERROR;
}
- STATE = STATE0;
+ BYE_STATE = BYE_STATE0;
session->internals.may_not_write = 1;
return 0;
@@ -732,13 +731,14 @@ record_add_to_buffers(gnutls_session_t session,
type == GNUTLS_CHANGE_CIPHER_SPEC ||
type == GNUTLS_HANDSHAKE)) {
_gnutls_record_buffer_put(session, type, seq, bufel);
-
+gnutls_assert();
/* if we received application data as expected then we
* deactivate the async timer */
_dtls_async_timer_delete(session);
} else {
/* if the expected type is different than the received
*/
+gnutls_assert();
switch (recv->type) {
case GNUTLS_ALERT:
if (bufel->msg.size < 2) {
@@ -1381,6 +1381,30 @@ check_session_status(gnutls_session_t session)
}
switch (session->internals.recv_state) {
+ case RECV_STATE_FALSE_START_HANDLING:
+ return 1;
+ case RECV_STATE_FALSE_START:
+ /* if false start is not complete we always expect for handshake packets
+ * prior to anything else. */
+ if (session->security_parameters.entity == GNUTLS_CLIENT &&
+ session->internals.enable_false_start != 0) {
+ /* Attempt to complete handshake */
+
+ session->internals.recv_state = RECV_STATE_FALSE_START_HANDLING;
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ /* a temp or fatal error, make sure we reset the state
+ * so we can resume or temp errors */
+ session->internals.recv_state = RECV_STATE_FALSE_START;
+ gnutls_assert();
+ return ret;
+ }
+
+ session->internals.recv_state = RECV_STATE_0;
+ return 1;
+ } else {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
case RECV_STATE_DTLS_RETRANSMIT:
ret = _dtls_retransmit(session);
if (ret < 0)
diff --git a/lib/state.c b/lib/state.c
index b4834a570d..312cbe1972 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -1,6 +1,7 @@
/*
- * Copyright (C) 2002-2015 Free Software Foundation, Inc.
- * Copyright (C) 2014-2015 Nikos Mavrogiannopoulos
+ * Copyright (C) 2002-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2015-2016 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
@@ -450,6 +451,9 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
if (flags & GNUTLS_ALLOW_CERT_CHANGE)
(*session)->internals.allow_cert_change = 1;
+ if (flags & GNUTLS_ENABLE_FALSE_START)
+ (*session)->internals.enable_false_start = 1;
+
return 0;
}