summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/constate.c25
-rw-r--r--lib/gnutls_int.h7
-rw-r--r--lib/handshake-tls13.c9
-rw-r--r--lib/handshake.c11
-rw-r--r--lib/record.c25
-rw-r--r--lib/state.c23
-rw-r--r--lib/system_override.c3
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)
{