diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/constate.c | 25 | ||||
-rw-r--r-- | lib/gnutls_int.h | 7 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 9 | ||||
-rw-r--r-- | lib/handshake.c | 11 | ||||
-rw-r--r-- | lib/record.c | 25 | ||||
-rw-r--r-- | lib/state.c | 23 | ||||
-rw-r--r-- | lib/system_override.c | 3 |
7 files changed, 84 insertions, 19 deletions
diff --git a/lib/constate.c b/lib/constate.c index e6ed8a3532..f2c9b839c1 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -39,6 +39,7 @@ #include "secrets.h" #include "handshake.h" #include "crypto-api.h" +#include "locks.h" static const char keyexp[] = "key expansion"; static const int keyexp_length = sizeof(keyexp) - 1; @@ -906,18 +907,28 @@ _gnutls_epoch_get(gnutls_session_t session, unsigned int epoch_rel, record_parameters_st **params; int ret; + gnutls_mutex_lock(&session->internals.epoch_lock); + ret = epoch_resolve(session, epoch_rel, &epoch); - if (ret < 0) - return gnutls_assert_val(ret); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } params = epoch_get_slot(session, epoch); - if (params == NULL || *params == NULL) - return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + if (params == NULL || *params == NULL) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } if (params_out) *params_out = *params; - return 0; + ret = 0; + +cleanup: + gnutls_mutex_unlock(&session->internals.epoch_lock); + return ret; } /* Ensures that the next epoch is setup. When an epoch will null ciphers @@ -1008,6 +1019,8 @@ void _gnutls_epoch_gc(gnutls_session_t session) _gnutls_record_log("REC[%p]: Start of epoch cleanup\n", session); + gnutls_mutex_lock(&session->internals.epoch_lock); + /* Free all dead cipher state */ for (i = 0; i < MAX_EPOCH_INDEX; i++) { if (session->record_parameters[i] != NULL) { @@ -1050,6 +1063,8 @@ void _gnutls_epoch_gc(gnutls_session_t session) session->security_parameters.epoch_min = session->record_parameters[0]->epoch; + gnutls_mutex_unlock(&session->internals.epoch_lock); + _gnutls_record_log("REC[%p]: End of epoch cleanup\n", session); } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 2352299cd8..4547ead0a4 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1281,6 +1281,9 @@ typedef struct { /* A handshake process has been completed */ bool initial_negotiation_completed; + void *post_negotiation_lock; /* protects access to the variable above + * in the cases where negotiation is incomplete + * after gnutls_handshake() - early/false start */ /* The type of transport protocol; stream or datagram */ transport_t transport; @@ -1466,6 +1469,10 @@ typedef struct { /* anti-replay measure for 0-RTT mode */ gnutls_anti_replay_t anti_replay; + /* Protects _gnutls_epoch_gc() from _gnutls_epoch_get(); these may be + * called in parallel when false start is used and false start is used. */ + void *epoch_lock; + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ } internals_st; diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index fedef6cbbc..8f9ec7842c 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -56,6 +56,7 @@ #include "tls13/finished.h" #include "tls13/key_update.h" #include "ext/pre_shared_key.h" +#include "locks.h" static int generate_rms_keys(gnutls_session_t session); static int generate_hs_traffic_keys(gnutls_session_t session); @@ -202,8 +203,8 @@ int _gnutls13_handshake_client(gnutls_session_t session) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } - - /* explicitly reset any false start flags */ + /* no lock of post_negotiation_lock is required here as this is not run + * after handshake */ session->internals.recv_state = RECV_STATE_0; session->internals.initial_negotiation_completed = 1; @@ -577,9 +578,11 @@ int _gnutls13_handshake_server(gnutls_session_t session) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } - /* explicitly reset any false start flags */ + /* explicitly reset any early start flags */ + gnutls_mutex_lock(&session->internals.post_negotiation_lock); session->internals.recv_state = RECV_STATE_0; session->internals.initial_negotiation_completed = 1; + gnutls_mutex_unlock(&session->internals.post_negotiation_lock); SAVE_TRANSCRIPT; diff --git a/lib/handshake.c b/lib/handshake.c index 481210ebc0..c6762076aa 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -55,6 +55,7 @@ #include <dtls.h> #include "secrets.h" #include "tls13/session_ticket.h" +#include "locks.h" #define TRUE 1 #define FALSE 0 @@ -1008,8 +1009,6 @@ int _gnutls_recv_finished(gnutls_session_t session) } - session->internals.initial_negotiation_completed = 1; - cleanup: _gnutls_buffer_clear(&buf); @@ -3144,7 +3143,10 @@ static int handshake_client(gnutls_session_t session) } /* explicitly reset any false start flags */ + gnutls_mutex_lock(&session->internals.post_negotiation_lock); + session->internals.initial_negotiation_completed = 1; session->internals.recv_state = RECV_STATE_0; + gnutls_mutex_unlock(&session->internals.post_negotiation_lock); return 0; } @@ -3363,7 +3365,6 @@ static int recv_handshake_final(gnutls_session_t session, int init) break; } - return 0; } @@ -3560,6 +3561,10 @@ static int handshake_server(gnutls_session_t session) break; } + /* no lock of post_negotiation_lock is required here as this is not run + * after handshake */ + session->internals.initial_negotiation_completed = 1; + return _gnutls_check_id_for_change(session); } diff --git a/lib/record.c b/lib/record.c index 08aad540db..e7f8e4f01a 100644 --- a/lib/record.c +++ b/lib/record.c @@ -53,6 +53,7 @@ #include <dh.h> #include <random.h> #include <xsize.h> +#include "locks.h" struct tls_record_st { uint16_t header_size; @@ -1694,8 +1695,7 @@ check_session_status(gnutls_session_t session, unsigned ms) !(session->internals.flags & GNUTLS_ENABLE_FALSE_START)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); - /* Attempt to complete handshake */ - + /* Attempt to complete handshake - we only need to receive */ session->internals.recv_state = RECV_STATE_FALSE_START_HANDLING; ret = gnutls_handshake(session); if (ret < 0) { @@ -1714,7 +1714,7 @@ check_session_status(gnutls_session_t session, unsigned ms) !(session->internals.flags & GNUTLS_ENABLE_EARLY_START)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); - /* Attempt to complete handshake */ + /* Attempt to complete handshake - we only need to receive */ session->internals.recv_state = RECV_STATE_EARLY_START_HANDLING; ret = gnutls_handshake(session); if (ret < 0) { @@ -1987,12 +1987,23 @@ gnutls_record_send2(gnutls_session_t session, const void *data, if (unlikely(!session->internals.initial_negotiation_completed)) { /* this is to protect buggy applications from sending unencrypted - * data. We allow sending however, if we are in false start handshake - * state. */ - if (session->internals.recv_state != RECV_STATE_FALSE_START && + * data. We allow sending however, if we are in false or early start + * handshake state. */ + gnutls_mutex_lock(&session->internals.post_negotiation_lock); + + /* we intentionally re-check the initial_negotation_completed variable + * to avoid locking during normal operation of gnutls_record_send2() */ + if (!session->internals.initial_negotiation_completed && + session->internals.recv_state != RECV_STATE_FALSE_START && + session->internals.recv_state != RECV_STATE_FALSE_START_HANDLING && session->internals.recv_state != RECV_STATE_EARLY_START && - !(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) + session->internals.recv_state != RECV_STATE_EARLY_START_HANDLING && + !(session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT)) { + + gnutls_mutex_unlock(&session->internals.post_negotiation_lock); return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE); + } + gnutls_mutex_unlock(&session->internals.post_negotiation_lock); } if (unlikely(!vers)) diff --git a/lib/state.c b/lib/state.c index 540a83c7b8..a8ff0d81f0 100644 --- a/lib/state.c +++ b/lib/state.c @@ -53,6 +53,7 @@ #include "dtls.h" #include "tls13/session_ticket.h" #include "ext/cert_types.h" +#include "locks.h" /* to be used by supplemental data support to disable TLS1.3 * when supplemental data have been globally registered */ @@ -451,8 +452,25 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags) if (*session == NULL) return GNUTLS_E_MEMORY_ERROR; + ret = gnutls_mutex_init(&(*session)->internals.post_negotiation_lock); + if (ret < 0) { + gnutls_assert(); + gnutls_free(*session); + return ret; + } + + ret = gnutls_mutex_init(&(*session)->internals.epoch_lock); + if (ret < 0) { + gnutls_assert(); + gnutls_mutex_deinit(&(*session)->internals.post_negotiation_lock); + gnutls_free(*session); + return ret; + } + ret = _gnutls_epoch_setup_next(*session, 1, NULL); if (ret < 0) { + gnutls_mutex_deinit(&(*session)->internals.post_negotiation_lock); + gnutls_mutex_deinit(&(*session)->internals.epoch_lock); gnutls_free(*session); return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); } @@ -640,6 +658,9 @@ void gnutls_deinit(gnutls_session_t session) /* overwrite any temp TLS1.3 keys */ gnutls_memset(&session->key.proto, 0, sizeof(session->key.proto)); + gnutls_mutex_deinit(&session->internals.post_negotiation_lock); + gnutls_mutex_deinit(&session->internals.epoch_lock); + gnutls_free(session); } @@ -1058,7 +1079,7 @@ void * interrupted GnuTLS function. * * This function's output is unreliable if you are using the same - * @session in different threads, for sending and receiving. + * @session in different threads for sending and receiving. * * Returns: 0 if interrupted while trying to read data, or 1 while trying to write data. **/ diff --git a/lib/system_override.c b/lib/system_override.c index fdf766509c..3c4805bc56 100644 --- a/lib/system_override.c +++ b/lib/system_override.c @@ -55,6 +55,9 @@ * variable that is used by GnuTLS (e.g., the application is linked to * msvcr71.dll and gnutls is linked to msvcrt.dll). * + * This function is unreliable if you are using the same + * @session in different threads for sending and receiving. + * **/ void gnutls_transport_set_errno(gnutls_session_t session, int err) { |