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}
};
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"
* 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
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().
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)
{
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,
}
/* 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;
}
session_id_size, buf,
sizeof(buf), NULL));
#endif
-
switch (STATE) {
case STATE0:
case STATE1:
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);
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;
}
* @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
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;
/**
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);
#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
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;
{
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 =
return ret;
}
}
- STATE = STATE62;
+ BYE_STATE = BYE_STATE2;
break;
default:
return GNUTLS_E_INTERNAL_ERROR;
}
- STATE = STATE0;
+ BYE_STATE = BYE_STATE0;
session->internals.may_not_write = 1;
return 0;
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) {
}
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)
/*
- * 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
*
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;
}