From: Nikos Mavrogiannopoulos Date: Thu, 14 Apr 2016 17:20:36 +0000 (+0200) Subject: Updated false start support to be transparent to applications. X-Git-Tag: gnutls_3_5_0~139 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=eee2b7554f8831a8b32c8f1b68bb23ff3114fea4;p=thirdparty%2Fgnutls.git 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. --- 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; }