enum handshake_state {
TLS_HS_NOT_STARTED = 0,
TLS_HS_EXPECTED,
+ TLS_HS_REAUTH_EXPECTED,
TLS_HS_IN_PROGRESS,
TLS_HS_DONE,
TLS_HS_CLOSING,
static int gnutls_references = 0;
-static const char * const priorities =
+static const char * const tlsv12_priorities =
"NORMAL:" /* GnuTLS defaults */
- "-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3:" /* TLS 1.2 only */
+ "-VERS-TLS1.0:-VERS-TLS1.1:+VERS-TLS1.2:-VERS-TLS1.3:" /* TLS 1.2 only */
"-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
+static const char * const tlsv13_priorities =
+ "NORMAL:" /* GnuTLS defaults */
+ "-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:+VERS-TLS1.3:" /* TLS 1.3 only */
+ "-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
static struct tls_proxy_ctx *get_proxy(struct peer *peer)
{
}
int len = strlen(str);
str[len] = '#';
- snprintf(&str[len + 1], 6, "%uh", ip_addr_port(addr));
+ snprintf(&str[len + 1], 6, "%hu", ip_addr_port(addr));
len += 6;
str[len] = 0;
if (len >= *buflen) {
client->tls = tls;
const char *errpos = NULL;
- err = gnutls_init(&tls->session, GNUTLS_SERVER | GNUTLS_NONBLOCK);
+ unsigned int gnutls_flags = GNUTLS_SERVER | GNUTLS_NONBLOCK;
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+ if (proxy->a->tls_13) {
+ gnutls_flags |= GNUTLS_POST_HANDSHAKE_AUTH;
+ }
+#endif
+ err = gnutls_init(&tls->session, gnutls_flags);
if (err != GNUTLS_E_SUCCESS) {
fprintf(stdout, "[client] gnutls_init() failed: (%d) %s\n",
err, gnutls_strerror_name(err));
fprintf(stdout, "[client] gnutls_priority_set() failed: (%d) %s\n",
err, gnutls_strerror_name(err));
}
- err = gnutls_priority_set_direct(tls->session, priorities, &errpos);
+
+ const char *direct_priorities = proxy->a->tls_13 ? tlsv13_priorities : tlsv12_priorities;
+ err = gnutls_priority_set_direct(tls->session, direct_priorities, &errpos);
if (err != GNUTLS_E_SUCCESS) {
fprintf(stdout, "[client] setting priority '%s' failed at character %zd (...'%s') with %s (%d)\n",
- priorities, errpos - priorities, errpos, gnutls_strerror_name(err), err);
+ direct_priorities, errpos - direct_priorities, errpos,
+ gnutls_strerror_name(err), err);
}
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, proxy->tls_credentials);
if (err != GNUTLS_E_SUCCESS) {
fprintf(stdout, "[client] gnutls_credentials_set() failed: (%d) %s\n",
err, gnutls_strerror_name(err));
}
- gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_IGNORE);
+ if (proxy->a->tls_13) {
+ gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_REQUEST);
+ } else {
+ gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_IGNORE);
+ }
gnutls_handshake_set_timeout(tls->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
gnutls_transport_set_pull_function(tls->session, proxy_gnutls_pull);
gnutls_transport_set_push_function(tls->session, proxy_gnutls_push);
fprintf(stdout, "[client] submitted %zd bytes\n", submitted);
if (proxy->a->rehandshake) {
+ int err = GNUTLS_E_SUCCESS;
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+ if (proxy->a->tls_13) {
+ int flags = gnutls_session_get_flags(tls_session);
+ if ((flags & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH) == 0) {
+ /* Client doesn't support post-handshake re-authentication,
+ * nothing to test here */
+ fprintf(stdout, "[client] GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH flag not detected\n");
+ assert(false);
+ }
+ err = gnutls_reauth(tls_session, 0);
+ if (err != GNUTLS_E_INTERRUPTED &&
+ err != GNUTLS_E_AGAIN &&
+ err != GNUTLS_E_GOT_APPLICATION_DATA) {
+ fprintf(stdout, "[client] gnutls_reauth() failed: %s (%i)\n",
+ gnutls_strerror_name(err), err);
+ } else {
+ fprintf(stdout, "[client] post-handshake authentication initiated\n");
+ }
+ client->tls->handshake_state = TLS_HS_REAUTH_EXPECTED;
+ } else {
+ assert (gnutls_safe_renegotiation_status(tls_session) != 0);
+ err = gnutls_rehandshake(tls_session);
+ if (err != GNUTLS_E_SUCCESS) {
+ fprintf(stdout, "[client] gnutls_rehandshake() failed: %s (%i)\n",
+ gnutls_strerror_name(err), err);
+ assert(false);
+ } else {
+ fprintf(stdout, "[client] rehandshake started\n");
+ }
+ client->tls->handshake_state = TLS_HS_EXPECTED;
+ }
+#else
assert (gnutls_safe_renegotiation_status(tls_session) != 0);
- assert (gnutls_rehandshake(tls_session) == GNUTLS_E_SUCCESS);
+ err = gnutls_rehandshake(tls_session);
+ if (err != GNUTLS_E_SUCCESS) {
+ fprintf(stdout, "[client] gnutls_rehandshake() failed: %s (%i)\n",
+ gnutls_strerror_name(err), err);
+ assert(false);
+ } else {
+ fprintf(stdout, "[client] rehandshake started\n");
+ }
/* Prevent write-to-client callback from sending next pending chunk.
* At the same time tls_process_from_client() must not call gnutls_handshake()
* as there can be application data in this direction. */
client->tls->handshake_state = TLS_HS_EXPECTED;
- fprintf(stdout, "[client] rehandshake started\n");
+#endif
}
return submitted;
}
return ret;
}
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+int tls_process_reauth(struct peer *peer)
+{
+ struct tls_ctx *tls = peer->tls;
+ int ret = 1;
+ while (tls->handshake_state == TLS_HS_REAUTH_EXPECTED) {
+ fprintf(stdout, "[tls] TLS re-authentication in progress...\n");
+ int err = gnutls_reauth(tls->session, 0);
+ if (err == GNUTLS_E_SUCCESS) {
+ tls->handshake_state = TLS_HS_DONE;
+ fprintf(stdout, "[tls] TLS re-authentication has completed\n");
+ ret = 1;
+ if (peer->pending_buf.len != 0) {
+ write_to_client_pending(peer);
+ }
+ } else if (err != GNUTLS_E_INTERRUPTED &&
+ err != GNUTLS_E_AGAIN &&
+ err != GNUTLS_E_GOT_APPLICATION_DATA) {
+ /* these are listed as nonfatal errors there
+ * https://www.gnutls.org/manual/gnutls.html#gnutls_005freauth */
+ fprintf(stdout, "[tls] gnutls_reauth failed: %s (%d)\n",
+ gnutls_strerror_name(err), err);
+ ret = -1;
+ break;
+ } else {
+ fprintf(stdout, "[tls] gnutls_reauth nonfatal error: %s (%d)\n",
+ gnutls_strerror_name(err), err);
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+#endif
+
int tls_process_from_client(struct peer *client, const uint8_t *buf, ssize_t nread)
{
struct tls_ctx *tls = client->tls;
fprintf(stdout, "[client] tls_process: reading %zd bytes from client\n", nread);
- int ret = tls_process_handshake(client);
+ int ret = 0;
+ if (tls->handshake_state == TLS_HS_REAUTH_EXPECTED) {
+ ret = tls_process_reauth(client);
+ } else {
+ ret = tls_process_handshake(client);
+ }
if (ret <= 0) {
return ret;
}
break;
}
continue;
- } else if (count < 0) {
+ }
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+ else if (count == GNUTLS_E_REAUTH_REQUEST) {
+ assert(false);
+ tls->handshake_state = TLS_HS_IN_PROGRESS;
+ ret = tls_process_reauth(client);
+ if (ret < 0) { /* Critical error */
+ return ret;
+ }
+ if (ret == 0) { /* Non fatal, most likely GNUTLS_E_AGAIN */
+ break;
+ }
+ continue;
+ }
+#endif
+ else if (count < 0) {
fprintf(stdout, "[client] gnutls_record_recv failed: %s (%zd)\n",
gnutls_strerror_name(count), count);
+ assert(false);
return -1;
} else if (count == 0) {
break;
" sent to the client (default: no).\n"
" -a, --acceptonly Accept incoming connections, but don't\n"
" connect to upstream (default: no).\n"
+ " -v, --tls13 Force use of TLSv1.3. If not turned on,\n"
+ " TLSv1.2 will be used (default: no).\n"
,
a->local_addr, a->local_port,
a->upstream, a->upstream_port,
a->key_file = default_key_path;
a->rehandshake = false;
a->accept_only = false;
+ a->tls_13 = false;
a->close_connection = false;
a->close_timeout = 1000;
a->max_conn_sequence = 0; /* disabled */
{"fail", required_argument, 0, 'f'},
{"rehandshake", no_argument, 0, 'r'},
{"acceptonly", no_argument, 0, 'a'},
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+ {"tls13", no_argument, 0, 'v'},
+#endif
{0, 0, 0, 0}
};
struct args args;
init_args(&args);
- while ((c = getopt_long(argc, argv, "l:p:u:d:t:k:c:f:ra", opts, &li)) != -1) {
+ while ((c = getopt_long(argc, argv, "l:p:u:d:t:k:c:f:rav", opts, &li)) != -1) {
switch (c)
{
case 'l':
case 'a':
args.accept_only = true;
break;
+ case 'v':
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+ args.tls_13 = true;
+#endif
+ break;
default:
init_args(&args);
help(argv, &args);
}
fprintf(stdout, "Listen on %s#%u\n"
"Upstream is expected on %s#%u\n"
+ "Certificate file %s\n"
+ "Key file %s\n"
"Rehandshake %s\n"
"Close %s\n"
"Refuse incoming connections every %ith%s\n"
- "Only accept, don't forward %s\n",
+ "Only accept, don't forward %s\n"
+ "Force TLSv1.3 %s\n"
+ ,
args.local_addr, args.local_port,
args.upstream, args.upstream_port,
+ args.cert_file, args.key_file,
args.rehandshake ? "yes" : "no",
args.close_connection ? "yes" : "no",
args.max_conn_sequence, args.max_conn_sequence ? "" : " (disabled)",
- args.accept_only ? "yes" : "no"
+ args.accept_only ? "yes" : "no",
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+ args.tls_13 ? "yes" : "no"
+#else
+ "Not supported"
+#endif
);
res = tls_proxy_run(proxy);
tls_proxy_free(proxy);