]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Updated false start support to be transparent to applications.
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Thu, 14 Apr 2016 17:20:36 +0000 (19:20 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 15 Apr 2016 08:00:45 +0000 (10:00 +0200)
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.

lib/errors.c
lib/gnutls_int.h
lib/handshake.c
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/record.c
lib/state.c

index 9dc4d658a1225dff3ccfc8a8f1c7f7ad8b673f17..34fcf2115342a016feedaf5ef0159a628eb5e02f 100644 (file)
@@ -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}
 };
 
index 484bea6edd7e796ccb91c2cb5fd63a601084d182..cbd5edf082f9aa405b97248496067fba90b503b8 100644 (file)
@@ -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().
index efd0a316fca5bfc2e216194598ccb811499401f4..0b69a52fc6342fdb8dc4a28db4d43ca79763f581 100644 (file)
@@ -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;
 }
 
index 8f2df57049ac102dbed0bcf55ba018bfc90f1253..98014aa11bedf972824eacdb407300565f1b56bd 100644 (file)
@@ -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
 
index b1787f342d981ba826ee36f998aa9e24132cddc6..b1bef2805c4f0b43390d248f2ee31447063ccf97 100644 (file)
@@ -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;
index ca181622c1e7542febf1fd71e6484595079359b1..52b22393fa4d703e99b8499d1906a706338ad4fb 100644 (file)
@@ -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)
index b4834a570db9ad2e83387b47bd72ee5b39e99761..312cbe19727e39d32bfd00fd9e1486c517a245bc 100644 (file)
@@ -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;
 }