summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2018-10-01 11:58:15 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-10-16 09:02:27 +0200
commit8dd5b32b5a1a709d90bced2959b80a16af66389f (patch)
treebe0b1fca2f8d3bbea230d41ecdd8cd284eba9d41
parent20abfc36bec4de2ad9f2e4682be7f93f61f6419d (diff)
downloadgnutls-8dd5b32b5a1a709d90bced2959b80a16af66389f.tar.gz
gnutls_init: added flag for automatic re-authentication
This introduces the GNUTLS_AUTO_REAUTH gnutls_init() flag and makes re-authentication under TLS simpler to enable and use. Resolves #571 Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--NEWS9
-rw-r--r--doc/cha-gtls-app.texi12
-rw-r--r--lib/gnutls_int.h4
-rw-r--r--lib/handshake-tls13.c22
-rw-r--r--lib/handshake.c24
-rw-r--r--lib/handshake.h2
-rw-r--r--lib/includes/gnutls/gnutls.h.in9
-rw-r--r--lib/record.c63
-rw-r--r--lib/tls13/post_handshake.c4
-rw-r--r--tests/Makefile.am4
-rw-r--r--tests/eagain-auto-auth.c235
-rw-r--r--tests/tls12-rehandshake-cert-auto.c278
-rw-r--r--tests/tls13/post-handshake-with-cert-auto.c366
13 files changed, 992 insertions, 40 deletions
diff --git a/NEWS b/NEWS
index c5246bf334..3b5e914886 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,15 @@ Copyright (C) 2000-2016 Free Software Foundation, Inc.
Copyright (C) 2013-2017 Nikos Mavrogiannopoulos
See the end for copying conditions.
+* Version 3.6.5 (unreleased)
+
+** libgnutls: Provide the option of transparent re-handshake/reauthentication
+ when the GNUTLS_AUTO_REAUTH flag is specified in gnutls_init().
+
+** API and ABI modifications:
+GNUTLS_AUTO_REAUTH: Added
+
+
* Version 3.6.4 (released 2018-09-24)
** libgnutls: Added the final (RFC8446) version numbering of the TLS1.3 protocol.
diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi
index 0288543482..1575c8fa52 100644
--- a/doc/cha-gtls-app.texi
+++ b/doc/cha-gtls-app.texi
@@ -1835,6 +1835,12 @@ Due to limitations of early protocol versions, it is required to check whether
safe renegotiation is in place, i.e., using @funcref{gnutls_safe_renegotiation_status},
which ensures that the server remains the same as the initial.
+To make re-authentication transparent to the application when requested
+by the server, use the @code{GNUTLS_AUTO_REAUTH} flag on the
+@funcref{gnutls_init} call. In that case the re-authentication will happen
+in the call of @funcref{gnutls_record_recv} that received the
+reauthentication request.
+
@showfuncdesc{gnutls_safe_renegotiation_status}
@subsubsection Server side
@@ -1877,6 +1883,12 @@ A client receiving a re-authentication request will "see" the error code
@code{GNUTLS_E_REAUTH_REQUEST} at @funcref{gnutls_record_recv}. At this
point, it should also call @funcref{gnutls_reauth}.
+To make re-authentication transparent to the application when requested
+by the server, use the @code{GNUTLS_AUTO_REAUTH} and @code{GNUTLS_POST_HANDSHAKE_AUTH}
+flags on the @funcref{gnutls_init} call. In that case the re-authentication will happen
+in the call of @funcref{gnutls_record_recv} that received the
+reauthentication request.
+
@node Parameter generation
@subsection Parameter generation
@cindex parameter generation
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 2574f4e420..4a514ccc71 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -314,7 +314,9 @@ typedef enum recv_state_t {
RECV_STATE_ASYNC_HANDSHAKE, /* an incomplete async handshake message was seen */
/* server-side early start under TLS1.3; enabled when no client cert is received */
RECV_STATE_EARLY_START_HANDLING, /* we are calling gnutls_handshake() within record_recv() */
- RECV_STATE_EARLY_START /* gnutls_record_recv() should complete the handshake */
+ RECV_STATE_EARLY_START, /* gnutls_record_recv() should complete the handshake */
+ RECV_STATE_REHANDSHAKE, /* gnutls_record_recv() should complete any incoming re-handshake requests */
+ RECV_STATE_REAUTH /* gnutls_record_recv() should complete any incoming reauthentication requests */
} recv_state_t;
#include "str.h"
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index 06c7c01d29..5fed553310 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -527,6 +527,7 @@ _gnutls13_recv_async_handshake(gnutls_session_t session)
{
int ret;
handshake_buffer_st hsk;
+ recv_state_t next_state = RECV_STATE_0;
/* The following messages are expected asynchronously after
* the handshake process is complete */
@@ -576,9 +577,20 @@ _gnutls13_recv_async_handshake(gnutls_session_t session)
goto cleanup;
}
- /* Application is expected to handle re-authentication
- * explicitly. */
- ret = GNUTLS_E_REAUTH_REQUEST;
+ if (session->internals.flags & GNUTLS_AUTO_REAUTH) {
+ ret = gnutls_reauth(session, 0);
+ if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
+ next_state = RECV_STATE_REAUTH;
+ } else if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ /* Application is expected to handle re-authentication
+ * explicitly. */
+ ret = GNUTLS_E_REAUTH_REQUEST;
+ }
+
goto cleanup;
case GNUTLS_HANDSHAKE_KEY_UPDATE:
@@ -630,7 +642,7 @@ _gnutls13_recv_async_handshake(gnutls_session_t session)
} while (_gnutls_record_buffer_get_size(session) > 0);
- session->internals.recv_state = RECV_STATE_0;
+ session->internals.recv_state = next_state;
return 0;
@@ -640,7 +652,7 @@ _gnutls13_recv_async_handshake(gnutls_session_t session)
if (_gnutls_record_buffer_get_size(session) > 0)
session->internals.recv_state = RECV_STATE_ASYNC_HANDSHAKE;
else
- session->internals.recv_state = RECV_STATE_0;
+ session->internals.recv_state = next_state;
_gnutls_handshake_buffer_clear(&hsk);
return ret;
diff --git a/lib/handshake.c b/lib/handshake.c
index b513872ec3..7db134a638 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -3468,30 +3468,6 @@ int _gnutls_generate_session_id(uint8_t * session_id, uint8_t *len)
return 0;
}
-int
-_gnutls_recv_hello_request(gnutls_session_t session, void *data,
- uint32_t data_size)
-{
- uint8_t type;
-
- if (session->security_parameters.entity == GNUTLS_SERVER) {
- gnutls_assert();
- return GNUTLS_E_UNEXPECTED_PACKET;
- }
- if (data_size < 1) {
- gnutls_assert();
- return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
- }
- type = ((uint8_t *) data)[0];
- if (type == GNUTLS_HANDSHAKE_HELLO_REQUEST) {
- if (IS_DTLS(session))
- session->internals.dtls.hsk_read_seq++;
- return GNUTLS_E_REHANDSHAKE;
- } else {
- gnutls_assert();
- return GNUTLS_E_UNEXPECTED_PACKET;
- }
-}
/**
* gnutls_handshake_set_max_packet_length:
diff --git a/lib/handshake.h b/lib/handshake.h
index 184a349a91..38ef848784 100644
--- a/lib/handshake.h
+++ b/lib/handshake.h
@@ -71,8 +71,6 @@
int _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
gnutls_handshake_description_t type);
-int _gnutls_recv_hello_request(gnutls_session_t session, void *data,
- uint32_t data_size);
int _gnutls_recv_handshake(gnutls_session_t session,
gnutls_handshake_description_t type,
unsigned int optional, gnutls_buffer_st * buf);
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index ec8d6f4efc..01cf9a880e 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -411,6 +411,12 @@ typedef enum {
* are already taking steps to hide the data processing time. This comes at a performance
* penalty.
* @GNUTLS_ENABLE_CERT_TYPE_NEG: Enable certificate type negotiation extensions (RFC7250).
+ * @GNUTLS_AUTO_REAUTH: Enable transparent re-authentication in client side when the server
+ * requests to. That is, reauthentication is handled within gnutls_record_recv(), and
+ * the %GNUTLS_E_REHANDSHAKE or %GNUTLS_E_REAUTH_REQUEST are not returned. This must be
+ * enabled with %GNUTLS_POST_HANDSHAKE_AUTH for TLS1.3, and it requires to restore interrupted
+ * calls to gnutls_record_recv() based on the output of gnutls_record_get_direction(), i.e.,
+ * gnutls_record_recv() could also be interrupted when sending when this flag is enabled.
*
* Enumeration of different flags for gnutls_init() function. All the flags
* can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually
@@ -439,7 +445,8 @@ typedef enum {
GNUTLS_NO_AUTO_REKEY = (1<<15),
GNUTLS_SAFE_PADDING_CHECK = (1<<16),
GNUTLS_ENABLE_EARLY_START = (1<<17),
- GNUTLS_ENABLE_CERT_TYPE_NEG = (1<<18)
+ GNUTLS_ENABLE_CERT_TYPE_NEG = (1<<18),
+ GNUTLS_AUTO_REAUTH = (1<<19)
} gnutls_init_flags_t;
/* compatibility defines (previous versions of gnutls
diff --git a/lib/record.c b/lib/record.c
index 745969a732..87b9dee304 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -756,6 +756,37 @@ record_check_version(gnutls_session_t session,
return 0;
}
+static int
+recv_hello_request(gnutls_session_t session, void *data,
+ uint32_t data_size)
+{
+ uint8_t type;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+ if (data_size < 1)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ if (session->internals.handshake_in_progress)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+ type = ((uint8_t *) data)[0];
+ if (type == GNUTLS_HANDSHAKE_HELLO_REQUEST) {
+ if (IS_DTLS(session))
+ session->internals.dtls.hsk_read_seq++;
+ if (session->internals.flags & GNUTLS_AUTO_REAUTH) {
+ session->internals.recv_state = RECV_STATE_REHANDSHAKE;
+ return GNUTLS_E_AGAIN;
+ } else {
+ return GNUTLS_E_REHANDSHAKE;
+ }
+ } else {
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET;
+ }
+}
+
/* This function will check if the received record type is
* the one we actually expect and adds it to the proper
* buffer. The bufel will be deinitialized after calling
@@ -995,12 +1026,10 @@ record_add_to_buffers(gnutls_session_t session,
/* So we accept it, if it is a Hello. If not, this will
* fail and trigger flight retransmissions after some time. */
ret =
- _gnutls_recv_hello_request(session,
- bufel->msg.data,
- bufel->msg.size);
+ recv_hello_request(session,
+ bufel->msg.data,
+ bufel->msg.size);
goto unexpected_packet;
-
- break;
default:
_gnutls_record_log
@@ -1555,6 +1584,30 @@ check_session_status(gnutls_session_t session, unsigned ms)
}
switch (session->internals.recv_state) {
+ case RECV_STATE_REAUTH:
+ session->internals.recv_state = RECV_STATE_0;
+
+ ret = gnutls_reauth(session, 0);
+ if (ret < 0) {
+ /* a temp or fatal error, make sure we reset the state
+ * so we can resume on temp errors */
+ session->internals.recv_state = RECV_STATE_REAUTH;
+ return gnutls_assert_val(ret);
+ }
+
+ return 1;
+ case RECV_STATE_REHANDSHAKE:
+ session->internals.recv_state = RECV_STATE_0;
+
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ /* a temp or fatal error, make sure we reset the state
+ * so we can resume on temp errors */
+ session->internals.recv_state = RECV_STATE_REHANDSHAKE;
+ return gnutls_assert_val(ret);
+ }
+
+ return 1;
case RECV_STATE_ASYNC_HANDSHAKE:
ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, -1, ms);
if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
diff --git a/lib/tls13/post_handshake.c b/lib/tls13/post_handshake.c
index ca0ed53b0a..c786d8cae3 100644
--- a/lib/tls13/post_handshake.c
+++ b/lib/tls13/post_handshake.c
@@ -145,8 +145,10 @@ int _gnutls13_reauth_server(gnutls_session_t session)
(!(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)))
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
- if (session->internals.send_cert_req == 0)
+ if (session->internals.send_cert_req == 0) {
+ _gnutls_debug_log("You need to call gnutls_certificate_server_set_request to enable post handshake auth\n");
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
switch (REAUTH_STATE) {
case REAUTH_STATE0:
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 71d00bcd32..25c2bf624d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -109,7 +109,7 @@ ctests = tls13/supported_versions tls13/tls12-no-tls13-exts \
tls13/cookie tls13/key_share tls13/prf tls13/post-handshake-with-cert-ticket \
tls12-rollback-detection tls11-rollback-detection \
tls12-check-rollback-val tls11-check-rollback-val tls13/hello_random_value \
- tls13/post-handshake-with-psk
+ tls13/post-handshake-with-psk tls13/post-handshake-with-cert-auto
ctests += tls13/hello_retry_request
@@ -157,6 +157,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
long-session-id mini-x509-callbacks-intr mini-dtls-lowmtu set_x509_key_file-late \
crlverify mini-dtls-discard init_fds mini-record-failure \
tls12-rehandshake-cert-2 custom-urls set_x509_key_mem set_x509_key_file \
+ tls12-rehandshake-cert-auto eagain-auto-auth \
mini-chain-unsorted x509-verify-with-crl mini-dtls-mtu privkey-verify-broken \
mini-dtls-record-asym key-import-export priority-set priority-set2 \
pubkey-import-export sign-is-secure spki spki-abstract rsa-rsa-pss \
@@ -226,6 +227,7 @@ str_idna_LDADD = $(CMOCKA_LDADD)
tls10_prf_LDADD = $(CMOCKA_LDADD)
tls12_prf_LDADD = $(CMOCKA_LDADD)
eagain_LDADD = $(CMOCKA_LDADD)
+eagain_auto_auth_LDADD = $(CMOCKA_LDADD)
tls12_rehandshake_cert_LDADD = $(CMOCKA_LDADD)
gnutls_record_overhead_CPPFLAGS = $(AM_CPPFLAGS) \
diff --git a/tests/eagain-auto-auth.c b/tests/eagain-auto-auth.c
new file mode 100644
index 0000000000..441e7c02e4
--- /dev/null
+++ b/tests/eagain-auto-auth.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2008-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
+
+#define RANDOMIZE
+#include "cert-common.h"
+#include "cmocka-common.h"
+
+/* This tests operation under non-blocking mode in TLS1.2/TLS1.3
+ * rekey/rehandshake.
+ */
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
+
+static unsigned int cert_asked = 0;
+
+static int cert_callback(gnutls_session_t session,
+ const gnutls_datum_t * req_ca_rdn, int nreqs,
+ const gnutls_pk_algorithm_t * sign_algos,
+ int sign_algos_length, gnutls_pcert_st ** pcert,
+ unsigned int *pcert_length, gnutls_privkey_t * pkey)
+{
+ cert_asked = 1;
+ *pcert_length = 0;
+ *pcert = NULL;
+ *pkey = NULL;
+
+ return 0;
+}
+
+static void async_handshake(void **glob_state, const char *prio, unsigned rehsk)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret, cret;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ /* Need to enable anonymous KX specifically. */
+ char buffer[MAX_BUF + 1];
+ int ret, transferred = 0, msglen;
+
+ /* General init. */
+ reset_buffers();
+ cert_asked = 0;
+ gnutls_global_init();
+ gnutls_global_set_log_function(tls_log_func);
+
+ /* Init server */
+ assert_return_code(gnutls_certificate_allocate_credentials(&serverx509cred), 0);
+ assert_return_code(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM), 0);
+ ret = gnutls_init(&server, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH);
+ assert_return_code(ret, 0);
+
+ ret =
+ gnutls_priority_set_direct(server,
+ prio,
+ NULL);
+ assert_return_code(ret, 0);
+
+
+ ret = gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, serverx509cred);
+ assert_return_code(ret, 0);
+
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ assert_return_code(ret, 0);
+
+ gnutls_certificate_set_retrieve_function2(clientx509cred, cert_callback);
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_AUTO_REAUTH|GNUTLS_POST_HANDSHAKE_AUTH);
+ ret =
+ gnutls_priority_set_direct(client,
+ prio,
+ NULL);
+ assert_return_code(ret, 0);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, clientx509cred);
+ assert_return_code(ret, 0);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE(client, server);
+
+ if (rehsk == 1) {
+ char b[1];
+ unsigned hstarted = 0;
+
+ do {
+ sret = gnutls_rehandshake(server);
+ } while (sret == GNUTLS_E_AGAIN || sret == GNUTLS_E_INTERRUPTED);
+ assert_true(sret == 0);
+ assert_true(gnutls_record_get_direction(server)==1);
+
+ sret = cret = GNUTLS_E_AGAIN;
+ do {
+ if (!hstarted) {
+ sret = gnutls_record_recv(server, b, 1);
+ if (sret == GNUTLS_E_INTERRUPTED) sret = GNUTLS_E_AGAIN;
+
+ if (sret == GNUTLS_E_REHANDSHAKE) {
+ hstarted = 1;
+ sret = GNUTLS_E_AGAIN;
+ }
+ assert_true(sret == GNUTLS_E_AGAIN);
+ }
+
+ if (sret == GNUTLS_E_AGAIN && hstarted) {
+ sret = gnutls_handshake (server);
+ if (sret == GNUTLS_E_INTERRUPTED) sret = GNUTLS_E_AGAIN;
+ assert_true(sret == GNUTLS_E_AGAIN || sret == 0);
+ }
+
+ /* we are done in client side */
+ if (hstarted && gnutls_record_get_direction(client) == 0 && to_client_len == 0)
+ cret = 0;
+
+ if (cret == GNUTLS_E_AGAIN) {
+ cret = gnutls_record_recv(client, b, 1);
+ if (cret == GNUTLS_E_INTERRUPTED) cret = GNUTLS_E_AGAIN;
+ }
+ assert_true(cret == GNUTLS_E_AGAIN || cret >= 0);
+
+ } while (cret == GNUTLS_E_AGAIN || sret == GNUTLS_E_AGAIN);
+ assert_true(hstarted != 0);
+ } else {
+ char b[1];
+
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUEST);
+
+ do {
+ sret = gnutls_reauth(server, 0);
+ } while (sret == GNUTLS_E_INTERRUPTED);
+
+ assert_true(sret == GNUTLS_E_AGAIN || sret >= 0);
+
+ cret = GNUTLS_E_AGAIN;
+ do {
+ if (cret == GNUTLS_E_AGAIN) {
+ cret = gnutls_record_recv(client, b, 1);
+ if (cret == GNUTLS_E_INTERRUPTED) cret = GNUTLS_E_AGAIN;
+ }
+
+ if (sret == GNUTLS_E_AGAIN) {
+ sret = gnutls_reauth(server, 0);
+ if (sret == GNUTLS_E_INTERRUPTED) sret = GNUTLS_E_AGAIN;
+ }
+
+ /* we are done in client side */
+ if (gnutls_record_get_direction(client) == 0 && to_client_len == 0)
+ cret = 0;
+ } while (cret == GNUTLS_E_AGAIN || sret == GNUTLS_E_AGAIN);
+ }
+ assert_return_code(cret, 0);
+ assert_return_code(sret, 0);
+ assert_return_code(cert_asked, 1);
+
+ msglen = strlen(MSG);
+ TRANSFER(client, server, MSG, msglen, buffer, MAX_BUF);
+
+ assert_true(gnutls_bye(client, GNUTLS_SHUT_WR)>=0);
+ assert_true(gnutls_bye(server, GNUTLS_SHUT_WR)>=0);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+}
+
+static void tls12_async_handshake(void **glob_state)
+{
+ async_handshake(glob_state, "NORMAL:-VERS-ALL:+VERS-TLS1.2", 1);
+}
+
+static void tls13_async_handshake(void **glob_state)
+{
+ async_handshake(glob_state, "NORMAL:-VERS-ALL:+VERS-TLS1.3", 0);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(tls12_async_handshake),
+ cmocka_unit_test(tls13_async_handshake),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/tls12-rehandshake-cert-auto.c b/tests/tls12-rehandshake-cert-auto.c
new file mode 100644
index 0000000000..88975c9ed4
--- /dev/null
+++ b/tests/tls12-rehandshake-cert-auto.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2014 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "utils.h"
+#include "cert-common.h"
+
+/* This program tests server initiated rehandshake when
+ * handled transparently by the client.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+#define MAX_BUF 1024
+
+static void client(int fd)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_anon_client_credentials_t anoncred;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ /* Need to enable anonymous KX specifically. */
+
+ global_init();
+ memset(buffer, 2, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4);
+ }
+
+ gnutls_anon_allocate_client_credentials(&anoncred);
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_AUTO_REAUTH);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ /* Use default priorities */
+ gnutls_priority_set_direct(session, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1:+VERS-TLS1.2", NULL);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed\n");
+ gnutls_perror(ret);
+ exit(1);
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ do {
+ ret =
+ gnutls_record_recv(session, buffer,
+ MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+
+ assert(ret == 0);
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_anon_free_client_credentials(anoncred);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+/* These are global */
+pid_t child;
+
+static void server(int fd)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_anon_server_credentials_t anoncred;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key, GNUTLS_X509_FMT_PEM);
+
+ gnutls_anon_allocate_server_credentials(&anoncred);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1:+VERS-TLS1.2", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ close(fd);
+ gnutls_deinit(session);
+ fail("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (debug)
+ success("server: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ if (debug)
+ success("server: sending rehandshake request\n");
+
+ do {
+ ret = gnutls_rehandshake(session);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0) {
+ fail("Error sending %d byte packet: %s\n",
+ (int)sizeof(buffer), gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("server: starting handshake\n");
+
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ fail("server: didn't complete handshake: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("server: re-handshake is complete\n");
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_anon_free_server_credentials(anoncred);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+void doit(void)
+{
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ int fd[2];
+ int ret;
+ int status = 0;
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/post-handshake-with-cert-auto.c b/tests/tls13/post-handshake-with-cert-auto.c
new file mode 100644
index 0000000000..9d920da80c
--- /dev/null
+++ b/tests/tls13/post-handshake-with-cert-auto.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+#define MAX_AUTHS 4
+
+/* This program tests whether the Post Handshake Auth extension is
+ * present in the client hello, and whether it is missing from server
+ * hello. In addition it contains basic functionality test for
+ * post handshake authentication.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+#define MAX_APP_DATA 3
+
+static void client(int fd, unsigned send_cert, unsigned max_auths)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ char buf[64];
+ unsigned i;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH|GNUTLS_AUTO_REAUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (send_cert) {
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (debug)
+ success("client handshake completed\n");
+
+ gnutls_record_set_timeout(session, 20 * 1000);
+
+ for (i=0;i<max_auths;i++) {
+ if (debug)
+ success("waiting for auth nr %d\n", i);
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0)
+ fail("client: gnutls_record_recv did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+ assert(ret == 0);
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_WR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_POST_HANDSHAKE 49
+
+static void parse_ext(void *priv, gnutls_datum_t *msg)
+{
+ if (msg->size != 0) {
+ fail("error in extension length: %d\n", (int)msg->size);
+ }
+}
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Post handshake extension seen in server hello!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, parse_ext))
+ client_hello_ok = 1;
+ else
+ fail("Post handshake extension NOT seen in client hello!\n");
+
+ return 0;
+}
+
+static void server(int fd, int err, int type, unsigned max_auths)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned i, retries;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH);
+
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH)) {
+ fail("server: session flags did not contain GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH\n");
+ }
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ if (debug)
+ success("server handshake completed\n");
+
+ gnutls_certificate_server_set_request(session, type);
+
+ /* i = 0 */
+ /* ask peer for re-authentication */
+ retries = 0;
+ do {
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == GNUTLS_E_GOT_APPLICATION_DATA) {
+ int ret2;
+ do {
+ ret2 = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret2 == GNUTLS_E_AGAIN || ret2 == GNUTLS_E_INTERRUPTED);
+
+ if (ret2 < 0)
+ fail("error receiving app data: %s\n", gnutls_strerror(ret2));
+
+ /* sender memsets the message with the retry attempt */
+ assert((uint8_t)buffer[0] == retries);
+ assert(retries < MAX_APP_DATA);
+ }
+
+ retries++;
+ } while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+
+ for (i=1;i<max_auths;i++) {
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static
+void start(const char *name, int err, int type, unsigned max_auths, unsigned send_cert)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+ int status = 0;
+
+ success("testing %s\n", name);
+
+ client_hello_ok = 0;
+ server_hello_ok = 0;
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], err, type, max_auths);
+ kill(child, SIGTERM);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], send_cert, max_auths);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ start("multi-reauth", 0, GNUTLS_CERT_REQUIRE, MAX_AUTHS, 1);
+ start("reauth-require with no-cert", GNUTLS_E_NO_CERTIFICATE_FOUND, GNUTLS_CERT_REQUIRE, 1, 0);
+ start("reauth-request with no-cert", 0, GNUTLS_CERT_REQUEST, 1, 0);
+}
+#endif /* _WIN32 */