summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-11-02 15:19:10 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-02-19 15:29:36 +0100
commitc0be323b29c0c31b7bc307f03df7db14816b0c48 (patch)
tree92469b39093ec6ab17f7f8c6c1227158e11f0e49
parent9ec15a01ee5982706adc92a938304eeb8a3de60a (diff)
downloadgnutls-c0be323b29c0c31b7bc307f03df7db14816b0c48.tar.gz
handshake: added support for post-handshake authentication
That is: * introduced a gnutls_init() flag for clients to enable post-handshake authentication * introduced gnutls_reauth() function, to be called by servers to request authentication, and by clients to perform authentication Resolves #562 Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/errors.c2
-rw-r--r--lib/ext/post_handshake.c13
-rw-r--r--lib/gnutls_int.h18
-rw-r--r--lib/handshake-tls13.c37
-rw-r--r--lib/handshake.c14
-rw-r--r--lib/includes/gnutls/gnutls.h.in9
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/record.c1
-rw-r--r--lib/state.c5
-rw-r--r--lib/tls13/certificate.c57
-rw-r--r--lib/tls13/certificate_request.c98
-rw-r--r--lib/tls13/certificate_request.h2
-rw-r--r--lib/tls13/post_handshake.c239
14 files changed, 448 insertions, 51 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 52686bf25d..3b4ae8bda9 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -96,7 +96,8 @@ COBJECTS += tls13/encrypted_extensions.c tls13/encrypted_extensions.h \
tls13/key_update.c tls13/key_update.h \
tls13/hello_retry.c tls13/hello_retry.h \
tls13/session_ticket.c tls13/session_ticket.h \
- tls13/certificate.c tls13/certificate.h
+ tls13/certificate.c tls13/certificate.h \
+ tls13/post_handshake.c
if ENABLE_PKCS11
COBJECTS += pkcs11.c pkcs11x.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c \
diff --git a/lib/errors.c b/lib/errors.c
index ea444c3e12..77cba34fc7 100644
--- a/lib/errors.c
+++ b/lib/errors.c
@@ -440,6 +440,8 @@ static const gnutls_error_entry non_fatal_error_entries[] = {
ERROR_ENTRY(N_("Function was interrupted."), GNUTLS_E_INTERRUPTED),
ERROR_ENTRY(N_("Rehandshake was requested by the peer."),
GNUTLS_E_REHANDSHAKE),
+ ERROR_ENTRY(N_("Re-authentication was requested by the peer."),
+ GNUTLS_E_REAUTH_REQUEST),
/* Only non fatal (for handshake) errors here */
{NULL, NULL, 0}
};
diff --git a/lib/ext/post_handshake.c b/lib/ext/post_handshake.c
index fa1e870fde..9aa67e2aed 100644
--- a/lib/ext/post_handshake.c
+++ b/lib/ext/post_handshake.c
@@ -62,7 +62,8 @@ _gnutls_post_handshake_recv_params(gnutls_session_t session,
if (unlikely(vers == NULL))
return 0;
- if (vers->post_handshake_auth)
+ if ((session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH) &&
+ vers->post_handshake_auth)
session->security_parameters.post_handshake_auth = 1;
}
@@ -75,14 +76,11 @@ static int
_gnutls_post_handshake_send_params(gnutls_session_t session,
gnutls_buffer_st * extdata)
{
- /* we don't support post-handshake authentication yet */
- return 0;
-#if 0
-
gnutls_certificate_credentials_t cred;
const version_entry_st *max;
- if (session->security_parameters.entity != GNUTLS_CLIENT) {
+ if (session->security_parameters.entity != GNUTLS_CLIENT ||
+ !(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)) {
/* not sent on server side */
return 0;
}
@@ -100,7 +98,4 @@ _gnutls_post_handshake_send_params(gnutls_session_t session,
return GNUTLS_E_INT_RET_0;
else
return 0;
-#endif
}
-
-
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index d3862d5a4f..4a02ddbae1 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -263,7 +263,13 @@ typedef enum bye_state_t {
BYE_STATE0 = 0, BYE_STATE1, BYE_STATE2
} bye_state_t;
+typedef enum reauth_state_t {
+ REAUTH_STATE0 = 0, REAUTH_STATE1, REAUTH_STATE2, REAUTH_STATE3,
+ REAUTH_STATE4, REAUTH_STATE5
+} reauth_state_t;
+
#define BYE_STATE session->internals.bye_state
+#define REAUTH_STATE session->internals.reauth_state
typedef enum heartbeat_state_t {
SHB_SEND1 = 0,
@@ -921,6 +927,8 @@ typedef struct {
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() */
+ reauth_state_t reauth_state; /* used by gnutls_reauth() */
+
handshake_state_t handshake_final_state;
handshake_state_t handshake_state; /* holds
* a number which indicates where
@@ -997,6 +1005,9 @@ typedef struct {
* function.
*/
+ /* buffer used temporarily during TLS1.3 reauthentication */
+ gnutls_buffer_st reauth_buffer;
+
time_t expire_time; /* after expire_time seconds this session will expire */
const struct mod_auth_st_int *auth_struct; /* used in handshake packets and KX algorithms */
@@ -1119,6 +1130,13 @@ typedef struct {
unsigned int handshake_timeout_ms; /* timeout in milliseconds */
unsigned int record_timeout_ms; /* timeout in milliseconds */
+ /* saved context of post handshake certificate request. In
+ * client side is what we received in server's certificate request;
+ * in server side is what we sent to client. */
+ gnutls_datum_t post_handshake_cr_context;
+ /* it is a copy of the handshake hash buffer if post handshake is used */
+ gnutls_buffer_st post_handshake_hash_buffer;
+
#define HSK_CRT_VRFY_EXPECTED 1
#define HSK_CRT_SENT (1<<1)
#define HSK_CRT_ASKED (1<<2)
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index d7e6d168d1..03b08285da 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -59,6 +59,16 @@
static int generate_hs_traffic_keys(gnutls_session_t session);
static int generate_ap_traffic_keys(gnutls_session_t session);
+#define SAVE_TRANSCRIPT \
+ if (session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH) { \
+ /* If post-handshake auth is in use we need a copy of the original \
+ * handshake transcript */ \
+ memcpy( &session->internals.post_handshake_hash_buffer, \
+ &session->internals.handshake_hash_buffer, \
+ sizeof(session->internals.handshake_hash_buffer)); \
+ _gnutls_buffer_init(&session->internals.handshake_hash_buffer); \
+ }
+
/*
* _gnutls13_handshake_client
* This function performs the client side of the handshake of the TLS/SSL protocol.
@@ -136,6 +146,8 @@ int _gnutls13_handshake_client(gnutls_session_t session)
session->internals.recv_state = RECV_STATE_0;
session->internals.initial_negotiation_completed = 1;
+ SAVE_TRANSCRIPT;
+
return 0;
}
@@ -311,9 +323,16 @@ int _gnutls13_handshake_server(gnutls_session_t session)
session->internals.recv_state = RECV_STATE_0;
session->internals.initial_negotiation_completed = 1;
+ SAVE_TRANSCRIPT;
+
return 0;
}
+/* Processes handshake messages received asynchronously after initial handshake.
+ *
+ * It is called once per message, with a read-only buffer in @buf,
+ * and should return success, or a fatal error code.
+ */
int
_gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
{
@@ -341,6 +360,24 @@ _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
return gnutls_assert_val(ret);
switch(type) {
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
+ if (!(session->security_parameters.entity == GNUTLS_CLIENT) ||
+ !(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)) {
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ }
+
+ _gnutls_buffer_reset(&session->internals.reauth_buffer);
+
+ /* include the handshake headers in reauth buffer */
+ ret = _gnutls_buffer_append_data(&session->internals.reauth_buffer,
+ buf->data-4, buf->length+4);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Application is expected to handle re-authentication
+ * explicitly. */
+ return GNUTLS_E_REAUTH_REQUEST;
+
case GNUTLS_HANDSHAKE_KEY_UPDATE:
ret = _gnutls13_recv_key_update(session, buf);
if (ret < 0)
diff --git a/lib/handshake.c b/lib/handshake.c
index afd2d97045..f7c6853416 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -1088,6 +1088,7 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
uint8_t *data;
uint32_t datasize, i_datasize;
int pos = 0;
+ const version_entry_st *vers = get_version(session);
if (bufel == NULL) {
/* we are resuming a previously interrupted
@@ -1164,6 +1165,13 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
return ret;
}
+ if (vers && vers->tls13_sem &&
+ session->internals.initial_negotiation_completed) {
+ /* we are under TLS1.3 in a re-authentication phase.
+ * we don't attempt to cache any messages */
+ goto force_send;
+ }
+
/* The messages which are followed by another are not sent by default
* but are cached instead */
switch (type) {
@@ -1185,11 +1193,13 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
break;
default:
/* send cached messages */
- ret = _gnutls_handshake_io_write_flush(session);
- break;
+ goto force_send;
}
return ret;
+
+ force_send:
+ return _gnutls_handshake_io_write_flush(session);
}
#define CHECK_SIZE(ll) \
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index ea231550b0..cddc3d4348 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -372,6 +372,9 @@ typedef enum {
* result to more roundtrips needed to discover the server's choice.
* @GNUTLS_NO_AUTO_REKEY: Disable auto-rekeying under TLS1.3. If this option is not specified
* gnutls will force a rekey after 2^24 records have been sent.
+ * @GNUTLS_POST_HANDSHAKE_AUTH: Enable post handshake authentication for server and client. When set and
+ * a server requests authentication after handshake %GNUTLS_E_REAUTH_REQUEST will be returned
+ * by gnutls_record_recv(). A client should then call gnutls_reauth() to re-authenticate.
*
* Enumeration of different flags for gnutls_init() function. All the flags
* can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually
@@ -396,6 +399,7 @@ typedef enum {
GNUTLS_KEY_SHARE_TOP = (1<<11),
GNUTLS_KEY_SHARE_TOP2 = (1<<12),
GNUTLS_KEY_SHARE_TOP3 = (1<<13),
+ GNUTLS_POST_HANDSHAKE_AUTH = (1<<14),
GNUTLS_NO_AUTO_REKEY = (1<<15)
} gnutls_init_flags_t;
@@ -1004,6 +1008,8 @@ int gnutls_bye(gnutls_session_t session, gnutls_close_request_t how);
int gnutls_handshake(gnutls_session_t session);
+int gnutls_reauth(gnutls_session_t session, unsigned int flags);
+
#define GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT ((unsigned int)-1)
#define GNUTLS_INDEFINITE_TIMEOUT ((unsigned int)-2)
void gnutls_handshake_set_timeout(gnutls_session_t session,
@@ -3011,7 +3017,8 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags);
#define GNUTLS_E_PK_INVALID_PUBKEY_PARAMS -420
#define GNUTLS_E_PK_NO_VALIDATION_PARAMS -421
-#define GNUTLS_E_NO_COMMON_KEY_SHARE -420
+#define GNUTLS_E_NO_COMMON_KEY_SHARE -423
+#define GNUTLS_E_REAUTH_REQUEST -424
#define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 8e5444745a..0641a09bbb 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1209,6 +1209,7 @@ GNUTLS_3_6_xx
global:
gnutls_session_key_update;
gnutls_ext_get_current_msg;
+ gnutls_reauth;
} GNUTLS_3_6_2;
GNUTLS_FIPS140_3_4 {
diff --git a/lib/record.c b/lib/record.c
index cee139d80c..b3ae667681 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -940,7 +940,6 @@ record_add_to_buffers(gnutls_session_t session,
} else {
ret = GNUTLS_E_AGAIN;
}
-
goto cleanup;
}
diff --git a/lib/state.c b/lib/state.c
index c90adbb551..889b3190c0 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -291,10 +291,12 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
/* Initialize buffers */
_gnutls_buffer_init(&(*session)->internals.handshake_hash_buffer);
+ _gnutls_buffer_init(&(*session)->internals.post_handshake_hash_buffer);
_gnutls_buffer_init(&(*session)->internals.hb_remote_data);
_gnutls_buffer_init(&(*session)->internals.hb_local_data);
_gnutls_buffer_init(&(*session)->internals.record_presend_buffer);
_gnutls_buffer_init(&(*session)->internals.record_key_update_buffer);
+ _gnutls_buffer_init(&(*session)->internals.reauth_buffer);
_mbuffer_head_init(&(*session)->internals.record_buffer);
_mbuffer_head_init(&(*session)->internals.record_send_buffer);
@@ -410,10 +412,12 @@ void gnutls_deinit(gnutls_session_t session)
}
_gnutls_buffer_clear(&session->internals.handshake_hash_buffer);
+ _gnutls_buffer_clear(&session->internals.post_handshake_hash_buffer);
_gnutls_buffer_clear(&session->internals.hb_remote_data);
_gnutls_buffer_clear(&session->internals.hb_local_data);
_gnutls_buffer_clear(&session->internals.record_presend_buffer);
_gnutls_buffer_clear(&session->internals.record_key_update_buffer);
+ _gnutls_buffer_clear(&session->internals.reauth_buffer);
_mbuffer_head_clear(&session->internals.record_buffer);
_mbuffer_head_clear(&session->internals.record_recv_buffer);
@@ -423,6 +427,7 @@ void gnutls_deinit(gnutls_session_t session)
_gnutls_free_datum(&session->internals.dtls.dcookie);
gnutls_free(session->internals.rexts);
+ gnutls_free(session->internals.post_handshake_cr_context.data);
gnutls_free(session->internals.rsup);
diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c
index c4aadedc20..147100be88 100644
--- a/lib/tls13/certificate.c
+++ b/lib/tls13/certificate.c
@@ -58,16 +58,36 @@ int _gnutls13_recv_certificate(gnutls_session_t session)
return 0;
}
- if (buf.data[0] != 0) {
- /* The context field must be empty during handshake */
- gnutls_assert();
- ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
- goto cleanup;
- }
+ if (session->internals.initial_negotiation_completed &&
+ session->internals.post_handshake_cr_context.size > 0) {
+ gnutls_datum_t context;
- /* buf.length is positive */
- buf.data++;
- buf.length--;
+ /* verify whether the context matches */
+ ret = _gnutls_buffer_pop_datum_prefix8(&buf, &context);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (context.size != session->internals.post_handshake_cr_context.size ||
+ memcmp(context.data, session->internals.post_handshake_cr_context.data,
+ context.size) != 0) {
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ if (buf.data[0] != 0) {
+ /* The context field must be empty during handshake */
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+
+ /* buf.length is positive */
+ buf.data++;
+ buf.length--;
+ }
_gnutls_handshake_log("HSK[%p]: parsing certificate message\n", session);
@@ -114,10 +134,21 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8,
+ session->internals.post_handshake_cr_context.data,
+ session->internals.post_handshake_cr_context.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ } else {
+ ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
}
/* mark total size */
diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c
index 3e5b831a4f..42ba3c4055 100644
--- a/lib/tls13/certificate_request.c
+++ b/lib/tls13/certificate_request.c
@@ -112,42 +112,45 @@ int parse_cert_extension(void *_ctx, uint16_t tls_id, const uint8_t *data, int d
return 0;
}
-int _gnutls13_recv_certificate_request(gnutls_session_t session)
+int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buffer_st *buf)
{
int ret;
- gnutls_buffer_st buf;
crt_req_ctx_st ctx;
- if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ _gnutls_handshake_log("HSK[%p]: parsing certificate request\n", session);
- ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ /* if initial negotiation is complete, this is a post-handshake auth */
+ if (!session->internals.initial_negotiation_completed ||
+ session->security_parameters.entity == GNUTLS_SERVER) {
+ if (buf->data[0] != 0) {
+ /* The context field must be empty during handshake */
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ gnutls_assert();
+ goto cleanup;
+ }
- /* if not received */
- if (buf.length == 0) {
- _gnutls_buffer_clear(&buf);
- return 0;
- }
+ /* buf->length is positive */
+ buf->data++;
+ buf->length--;
+ } else {
+ gnutls_datum_t context;
- _gnutls_handshake_log("HSK[%p]: parsing certificate request\n", session);
+ ret = _gnutls_buffer_pop_datum_prefix8(buf, &context);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- if (buf.data[0] != 0) {
- /* The context field must be empty during handshake */
- ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
- gnutls_assert();
- goto cleanup;
+ gnutls_free(session->internals.post_handshake_cr_context.data);
+ session->internals.post_handshake_cr_context.data = NULL;
+ ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
+ context.data, context.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
}
- /* buf.length is positive */
- buf.data++;
- buf.length--;
-
memset(&ctx, 0, sizeof(ctx));
ctx.session = session;
- ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf.data, buf.length);
+ ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data, buf->length);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -167,6 +170,29 @@ int _gnutls13_recv_certificate_request(gnutls_session_t session)
ret = 0;
cleanup:
+ return ret;
+}
+
+int _gnutls13_recv_certificate_request(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+
+ if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* if not received */
+ if (buf.length == 0) {
+ _gnutls_buffer_clear(&buf);
+ return 0;
+ }
+
+ ret = _gnutls13_recv_certificate_request_int(session, &buf);
+
_gnutls_buffer_clear(&buf);
return ret;
}
@@ -209,6 +235,8 @@ int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
unsigned init_pos;
if (again == 0) {
+ unsigned char rnd[12];
+
if (session->internals.send_cert_req == 0)
return 0;
@@ -221,7 +249,29 @@ int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
if (ret < 0)
return gnutls_assert_val(ret);
- ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ if (session->internals.initial_negotiation_completed) { /* reauth */
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, rnd, sizeof(rnd));
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ gnutls_free(session->internals.post_handshake_cr_context.data);
+ session->internals.post_handshake_cr_context.data = NULL;
+ ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
+ rnd, sizeof(rnd));
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8,
+ session->internals.post_handshake_cr_context.data,
+ session->internals.post_handshake_cr_context.size);
+ } else {
+ ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ }
+
if (ret < 0) {
gnutls_assert();
goto cleanup;
diff --git a/lib/tls13/certificate_request.h b/lib/tls13/certificate_request.h
index 78b4b4eb41..d00e890e87 100644
--- a/lib/tls13/certificate_request.h
+++ b/lib/tls13/certificate_request.h
@@ -21,4 +21,6 @@
*/
int _gnutls13_recv_certificate_request(gnutls_session_t session);
+int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buffer_st *buf);
+
int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again);
diff --git a/lib/tls13/post_handshake.c b/lib/tls13/post_handshake.c
new file mode 100644
index 0000000000..39ae680ab9
--- /dev/null
+++ b/lib/tls13/post_handshake.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate to the TLS handshake procedure.
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "dh.h"
+#include "debug.h"
+#include "algorithms.h"
+#include "cipher.h"
+#include "buffers.h"
+#include "mbuffers.h"
+#include "kx.h"
+#include "handshake.h"
+#include "num.h"
+#include "hash_int.h"
+#include "db.h"
+#include "hello_ext.h"
+#include "supplemental.h"
+#include "auth.h"
+#include "sslv2_compat.h"
+#include <auth/cert.h>
+#include "constate.h"
+#include <record.h>
+#include <state.h>
+#include <random.h>
+#include <dtls.h>
+#include "tls13/certificate_request.h"
+#include "tls13/certificate_verify.h"
+#include "tls13/certificate.h"
+#include "tls13/finished.h"
+
+#undef AGAIN
+#define AGAIN(x) ((x)==(REAUTH_STATE))
+
+/*
+ * _gnutls13_reauth_client
+ * This function performs the client side of the post-handshake authentication
+ */
+static
+int _gnutls13_reauth_client(gnutls_session_t session)
+{
+ int ret = 0;
+ size_t tmp;
+
+ if (!session->internals.initial_negotiation_completed)
+ return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
+
+ if (!(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->internals.reauth_buffer.length == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ switch (REAUTH_STATE) {
+ case REAUTH_STATE0:
+
+ /* restore handshake transcript */
+ _gnutls_buffer_reset(&session->internals.handshake_hash_buffer);
+ ret = gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+ session->internals.post_handshake_hash_buffer.data,
+ session->internals.post_handshake_hash_buffer.length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* append the previously received certificate request message, to the
+ * transcript. */
+ ret = gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+ session->internals.reauth_buffer.data,
+ session->internals.reauth_buffer.length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length;
+
+ /* skip the reauth buffer handshake message headers */
+ ret = _gnutls_buffer_pop_prefix32(&session->internals.reauth_buffer, &tmp, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* fall through */
+ case REAUTH_STATE1:
+ ret = _gnutls13_recv_certificate_request_int(session,
+ &session->internals.reauth_buffer);
+ REAUTH_STATE = REAUTH_STATE1;
+ IMED_RET("recv certificate request", ret, 0);
+ /* fall through */
+ case REAUTH_STATE2:
+ ret = _gnutls13_send_certificate(session, AGAIN(REAUTH_STATE2));
+ REAUTH_STATE = REAUTH_STATE2;
+ IMED_RET("send certificate", ret, 0);
+ /* fall through */
+ case REAUTH_STATE3:
+ ret = _gnutls13_send_certificate_verify(session, AGAIN(REAUTH_STATE3));
+ REAUTH_STATE = REAUTH_STATE3;
+ IMED_RET("send certificate verify", ret, 0);
+ /* fall through */
+ case REAUTH_STATE4:
+ ret = _gnutls13_send_finished(session, AGAIN(REAUTH_STATE4));
+ REAUTH_STATE = REAUTH_STATE4;
+ IMED_RET("send finished", ret, 0);
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ _gnutls_handshake_hash_buffers_clear(session);
+ _gnutls_buffer_reset(&session->internals.reauth_buffer);
+ REAUTH_STATE = REAUTH_STATE0;
+
+ return 0;
+}
+
+/*
+ * _gnutls13_reauth_server
+ * This function does the server stuff of the post-handshake authentication.
+ */
+static
+int _gnutls13_reauth_server(gnutls_session_t session)
+{
+ int ret = 0;
+
+ if (session->security_parameters.post_handshake_auth == 0 ||
+ (!(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->internals.send_cert_req == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ switch (REAUTH_STATE) {
+ case REAUTH_STATE0:
+ /* restore handshake transcript */
+ _gnutls_buffer_reset(&session->internals.handshake_hash_buffer);
+ ret = gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+ session->internals.post_handshake_hash_buffer.data,
+ session->internals.post_handshake_hash_buffer.length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length;
+
+ /* fall through */
+ case REAUTH_STATE1:
+ ret = _gnutls13_send_certificate_request(session, AGAIN(REAUTH_STATE1));
+ REAUTH_STATE = REAUTH_STATE1;
+ IMED_RET("send certificate request", ret, 0);
+ /* fall through */
+ case REAUTH_STATE2:
+ /* here we should tolerate application data */
+ ret = _gnutls13_recv_certificate(session);
+ REAUTH_STATE = REAUTH_STATE2;
+ IMED_RET("recv certificate", ret, 0);
+ /* fall through */
+ case REAUTH_STATE3:
+ ret = _gnutls13_recv_certificate_verify(session);
+ REAUTH_STATE = REAUTH_STATE3;
+ IMED_RET("recv certificate verify", ret, 0);
+ /* fall through */
+ case REAUTH_STATE4:
+ ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
+ REAUTH_STATE = REAUTH_STATE4;
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ /* fall through */
+ case REAUTH_STATE5:
+ ret = _gnutls13_recv_finished(session);
+ REAUTH_STATE = REAUTH_STATE5;
+ IMED_RET("recv finished", ret, 0);
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ _gnutls_handshake_hash_buffers_clear(session);
+ REAUTH_STATE = REAUTH_STATE0;
+
+ return 0;
+}
+
+/**
+ * gnutls_reauth:
+ * @session: is a #gnutls_session_t type.
+ * @flags: must be zero
+ *
+ * This function performs the post-handshake authentication
+ * for TLS 1.3.
+ *
+ * The non-fatal errors expected by this function are:
+ * %GNUTLS_E_INTERRUPTED, %GNUTLS_E_AGAIN, as well as
+ * %GNUTLS_E_GOT_APPLICATION_DATA when called on server side.
+ *
+ * The former two interrupt the authentication procedure due to the transport
+ * layer being interrupted, and the latter because there were pending data prior
+ * to peer initiating the re-authentication.
+ *
+ * When this function is called under TLS1.2 or earlier or the peer didn't
+ * advertise post-handshake auth, it always fails with
+ * %GNUTLS_E_INVALID_REQUEST. The verification of the received peers certificate
+ * is delegated to the session or credentials verification callbacks.
+ *
+ * Prior to calling this function in server side, the function
+ * gnutls_certificate_server_set_request() must be called setting expectations
+ * for the received certificate (request or require).
+ *
+ * Returns: %GNUTLS_E_SUCCESS on a successful authentication, otherwise a negative error code.
+ **/
+int gnutls_reauth(gnutls_session_t session, unsigned int flags)
+{
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(!vers->tls13_sem))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ return _gnutls13_reauth_server(session);
+ else
+ return _gnutls13_reauth_client(session);
+}