@@ -2436,6 +2436,9 @@ key_schedule_free(struct key_schedule *ks, bool free_ssl_ctx)
{
tls_ctx_free(&ks->ssl_ctx);
free_key_ctx_bi(&ks->tls_wrap_key);
+ free_key_ctx(&ks->tls_crypt_v2_server_key);
+ buf_clear(&ks->tls_crypt_v2_wkc);
+ free_buf(&ks->tls_crypt_v2_wkc);
}
CLEAR(*ks);
}
@@ -2604,6 +2607,24 @@ do_init_crypto_tls_c1(struct context *c)
options->tls_crypt_inline, options->tls_server);
}
+ /* tls-crypt with client-specific keys (--tls-crypt-v2) */
+ if (options->tls_crypt_v2_file)
+ {
+ if (options->tls_server)
+ {
+ tls_crypt_v2_init_server_key(&c->c1.ks.tls_crypt_v2_server_key,
+ true, options->tls_crypt_v2_file,
+ options->tls_crypt_v2_inline);
+ }
+ else
+ {
+ tls_crypt_v2_init_client_key(&c->c1.ks.tls_wrap_key,
+ &c->c1.ks.tls_crypt_v2_wkc,
+ options->tls_crypt_v2_file,
+ options->tls_crypt_v2_inline);
+ }
+ }
+
c->c1.ciphername = options->ciphername;
c->c1.authname = options->authname;
c->c1.keysize = options->keysize;
@@ -2823,13 +2844,28 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
}
/* TLS handshake encryption (--tls-crypt) */
- if (options->tls_crypt_file)
+ if (options->tls_crypt_file
+ || (options->tls_crypt_v2_file && options->tls_client))
{
to.tls_wrap.mode = TLS_WRAP_CRYPT;
to.tls_wrap.opt.key_ctx_bi = c->c1.ks.tls_wrap_key;
to.tls_wrap.opt.pid_persist = &c->c1.pid_persist;
to.tls_wrap.opt.flags |= CO_PACKET_ID_LONG_FORM;
tls_crypt_adjust_frame_parameters(&to.frame);
+
+ if (options->tls_crypt_v2_file)
+ {
+ to.tls_wrap.tls_crypt_v2_wkc = &c->c1.ks.tls_crypt_v2_wkc;
+ }
+ }
+
+ if (options->tls_crypt_v2_file)
+ {
+ to.tls_crypt_v2 = true;
+ if (options->tls_server)
+ {
+ to.tls_wrap.tls_crypt_v2_server_key = c->c1.ks.tls_crypt_v2_server_key;
+ }
}
/* If we are running over TCP, allow for
@@ -4359,6 +4395,7 @@ inherit_context_child(struct context *dest,
dest->c1.ks.ssl_ctx = src->c1.ks.ssl_ctx;
dest->c1.ks.tls_wrap_key = src->c1.ks.tls_wrap_key;
dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type;
+ dest->c1.ks.tls_crypt_v2_server_key = src->c1.ks.tls_crypt_v2_server_key;
/* inherit pre-NCP ciphers */
dest->c1.ciphername = src->c1.ciphername;
dest->c1.authname = src->c1.authname;
@@ -66,6 +66,8 @@ struct key_schedule
/* optional TLS control channel wrapping */
struct key_type tls_auth_key_type;
struct key_ctx_bi tls_wrap_key;
+ struct key_ctx tls_crypt_v2_server_key;
+ struct buffer tls_crypt_v2_wkc; /**< Wrapped client key */
};
/*
@@ -2739,6 +2739,11 @@ options_postprocess_verify_ce(const struct options *options, const struct connec
{
msg(M_USAGE, "--tls-auth and --tls-crypt are mutually exclusive");
}
+ if (options->client && options->tls_crypt_v2_file
+ && (options->tls_auth_file || options->tls_crypt_file))
+ {
+ msg(M_USAGE, "--tls-crypt-v2, --tls-auth and --tls-crypt are mutually exclusive in client mode");
+ }
}
else
{
@@ -1055,6 +1055,23 @@ tls_session_user_pass_enabled(struct tls_session *session)
/** @addtogroup control_processor
* @{ */
+/** Free the elements of a tls_wrap_ctx structure */
+static void tls_wrap_free(struct tls_wrap_ctx *tls_wrap)
+{
+ if (packet_id_initialized(&tls_wrap->opt.packet_id))
+ {
+ packet_id_free(&tls_wrap->opt.packet_id);
+ }
+
+ if (tls_wrap->cleanup_key_ctx)
+ {
+ free_key_ctx_bi(&tls_wrap->opt.key_ctx_bi);
+ }
+
+ free_buf(&tls_wrap->tls_crypt_v2_metadata);
+ free_buf(&tls_wrap->work);
+}
+
/** @name Functions for initialization and cleanup of tls_session structures
* @{ */
@@ -1147,16 +1164,9 @@ tls_session_init(struct tls_multi *multi, struct tls_session *session)
static void
tls_session_free(struct tls_session *session, bool clear)
{
- int i;
-
- if (packet_id_initialized(&session->tls_wrap.opt.packet_id))
- {
- packet_id_free(&session->tls_wrap.opt.packet_id);
- }
+ tls_wrap_free(&session->tls_wrap);
- free_buf(&session->tls_wrap.work);
-
- for (i = 0; i < KS_SIZE; ++i)
+ for (size_t i = 0; i < KS_SIZE; ++i)
{
key_state_free(&session->key[i], false);
}
@@ -1482,6 +1492,8 @@ write_control_auth(struct tls_session *session,
ASSERT(reliable_ack_write
(ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack));
+ msg(D_TLS_DEBUG, "%s(): %s", __func__, packet_opcode_name(opcode));
+
if (session->tls_wrap.mode == TLS_WRAP_AUTH
|| session->tls_wrap.mode == TLS_WRAP_NONE)
{
@@ -1499,17 +1511,26 @@ write_control_auth(struct tls_session *session,
ASSERT(buf_init(&session->tls_wrap.work, buf->offset));
ASSERT(buf_write(&session->tls_wrap.work, &header, sizeof(header)));
ASSERT(session_id_write(&session->session_id, &session->tls_wrap.work));
- if (tls_crypt_wrap(buf, &session->tls_wrap.work, &session->tls_wrap.opt))
- {
- /* Don't change the original data in buf, it's used by the reliability
- * layer to resend on failure. */
- *buf = session->tls_wrap.work;
- }
- else
+ if (!tls_crypt_wrap(buf, &session->tls_wrap.work, &session->tls_wrap.opt))
{
buf->len = 0;
return;
}
+
+ if (opcode == P_CONTROL_HARD_RESET_CLIENT_V3)
+ {
+ if (!buf_copy(&session->tls_wrap.work,
+ session->tls_wrap.tls_crypt_v2_wkc))
+ {
+ msg(D_TLS_ERRORS, "Could not append tls-crypt-v2 client key");
+ buf->len = 0;
+ return;
+ }
+ }
+
+ /* Don't change the original data in buf, it's used by the reliability
+ * layer to resend on failure. */
+ *buf = session->tls_wrap.work;
}
*to_link_addr = &ks->remote_addr;
}
@@ -1525,6 +1546,16 @@ read_control_auth(struct buffer *buf,
struct gc_arena gc = gc_new();
bool ret = false;
+ const uint8_t opcode = *(BPTR(buf)) >> P_OPCODE_SHIFT;
+ if (opcode == P_CONTROL_HARD_RESET_CLIENT_V3
+ && !tls_crypt_v2_extract_client_key(buf, ctx))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: can not extract tls-crypt-v2 client key from %s",
+ print_link_socket_actual(from, &gc));
+ goto cleanup;
+ }
+
if (ctx->mode == TLS_WRAP_AUTH)
{
struct buffer null = clear_buf();
@@ -1564,6 +1595,18 @@ read_control_auth(struct buffer *buf,
ASSERT(buf_copy(buf, &tmp));
buf_clear(&tmp);
}
+ else if (ctx->tls_crypt_v2_server_key.cipher)
+ {
+ /* If tls-crypt-v2 is enabled, require *some* wrapping */
+ msg(D_TLS_ERRORS, "TLS Error: could not determine wrapping from %s",
+ print_link_socket_actual(from, &gc));
+ /* TODO Do we want to support using tls-crypt-v2 and no control channel
+ * wrapping at all simultaneously? That would allow server admins to
+ * upgrade clients one-by-one without running a second instance, but we
+ * should not enable it by default because it breaks DoS-protection.
+ * So, add something like --tls-crypt-v2-allow-insecure-fallback ? */
+ goto cleanup;
+ }
if (ctx->mode == TLS_WRAP_NONE || ctx->mode == TLS_WRAP_AUTH)
{
@@ -3864,6 +3907,10 @@ tls_pre_decrypt_lite(const struct tls_auth_standalone *tas,
/* HMAC test, if --tls-auth was specified */
status = read_control_auth(&newbuf, &tls_wrap_tmp, from);
free_buf(&newbuf);
+ if (tls_wrap_tmp.cleanup_key_ctx)
+ {
+ free_key_ctx_bi(&tls_wrap_tmp.opt.key_ctx_bi);
+ }
if (!status)
{
goto error;
@@ -213,6 +213,12 @@ struct tls_wrap_ctx
} mode; /**< Control channel wrapping mode */
struct crypto_options opt; /**< Crypto state */
struct buffer work; /**< Work buffer (only for --tls-crypt) */
+ struct key_ctx tls_crypt_v2_server_key; /**< Decrypts client keys */
+ const struct buffer *tls_crypt_v2_wkc; /**< Wrapped client key,
+ sent to server */
+ struct buffer tls_crypt_v2_metadata; /**< Received from client */
+ bool cleanup_key_ctx; /**< opt.key_ctx_bi is owned by
+ this context */
};
/*
@@ -33,6 +33,7 @@
#include "crypto.h"
#include "platform.h"
#include "session_id.h"
+#include "ssl.h"
#include "tls_crypt.h"
@@ -519,6 +520,55 @@ error_exit:
return ret;
}
+bool
+tls_crypt_v2_extract_client_key(struct buffer *buf,
+ struct tls_wrap_ctx *ctx)
+{
+ static const int hard_reset_length =
+ TLS_CRYPT_OFF_CT + sizeof(uint8_t) + sizeof(packet_id_type);
+
+ if (!ctx->tls_crypt_v2_server_key.cipher)
+ {
+ msg (D_TLS_ERRORS,
+ "Client want tls-crypt-v2, but no server key present.");
+ return false;
+ }
+
+ msg (D_HANDSHAKE, "Control Channel: using tls-crypt-v2 key");
+
+ struct buffer wrapped_client_key = *buf;
+ if (!buf_advance(&wrapped_client_key, hard_reset_length))
+ {
+ msg (D_TLS_ERRORS, "Can not locate tls-crypt-v2 client key");
+ return false;
+ }
+
+ struct key2 client_key = { 0 };
+ ctx->tls_crypt_v2_metadata = alloc_buf(TLS_CRYPT_V2_MAX_METADATA_LEN);
+ if (!tls_crypt_v2_unwrap_client_key(&client_key,
+ &ctx->tls_crypt_v2_metadata,
+ wrapped_client_key,
+ &ctx->tls_crypt_v2_server_key))
+ {
+ msg (D_TLS_ERRORS, "Can not unwrap tls-crypt-v2 client key");
+ secure_memzero(&client_key, sizeof(client_key));
+ return false;
+ }
+
+ /* Load the decrypted key */
+ ctx->mode = TLS_WRAP_CRYPT;
+ ctx->cleanup_key_ctx = true;
+ ctx->opt.flags |= CO_PACKET_ID_LONG_FORM;
+ memset(&ctx->opt.key_ctx_bi, 0, sizeof(ctx->opt.key_ctx_bi));
+ tls_crypt_v2_load_client_key(&ctx->opt.key_ctx_bi, &client_key, true);
+ secure_memzero(&client_key, sizeof(client_key));
+
+ /* Remove client key from buffer so tls-crypt code can unwrap message */
+ ASSERT(buf_inc_len(buf, -(BLEN(&wrapped_client_key))));
+
+ return true;
+}
+
void
tls_crypt_v2_write_server_key_file(const char *filename)
{
@@ -83,6 +83,7 @@
#include "buffer.h"
#include "crypto.h"
#include "session_id.h"
+#include "ssl_common.h"
#define TLS_CRYPT_TAG_SIZE (256/8)
#define TLS_CRYPT_PID_SIZE (sizeof(packet_id_type) + sizeof(net_time_t))
@@ -181,6 +182,19 @@ void tls_crypt_v2_init_client_key(struct key_ctx_bi *key,
const char *key_file,
const char *key_inline);
+/**
+ * Extract a tls-crypt-v2 client key from a P_CONTROL_HARD_RESET_CLIENT_V3
+ * message, and load the key into the supplied tls wrap context.
+ *
+ * @param buf Buffer containing a received P_CONTROL_HARD_RESET_CLIENT_V3
+ * message.
+ * @param ctx tls-wrap context to be initialized with the client key.
+ *
+ * @returns true if a key was successfully extracted.
+ */
+bool tls_crypt_v2_extract_client_key(struct buffer *buf,
+ struct tls_wrap_ctx *ctx);
+
/**
* Generate a tls-crypt-v2 server key, and write to file.
*
@@ -59,7 +59,7 @@ packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c \
tls_crypt_testdriver_CFLAGS = @TEST_CFLAGS@ \
-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
- $(OPTIONAL_CRYPTO_CFLAGS)
+ $(OPTIONAL_CRYPTO_CFLAGS) $(OPTIONAL_PKCS11_HELPER_CFLAGS)
tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
$(OPTIONAL_CRYPTO_LIBS)
tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c \