Skip to content

Commit 20b234b

Browse files
mfilRein van Baaren
authored andcommitted
Add option to check tls-crypt-v2 key timestamps
This commit adds the option --tls-crypt-v2-max-age n. When a client key is older than n days or has no timestamp, the server rejects it. Based on work by Rein van Baaren for Sentyron. Co-authored-by: Rein van Baaren <[email protected]> Change-Id: I0579d18c784e2ac16973d5553992c28f281a0900 Signed-off-by: Max Fillinger <[email protected]> Acked-by: Arne Schwabe <[email protected]> Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1304 Message-Id: <[email protected]> URL: https://www.mail-archive.com/[email protected]/msg34545.html Signed-off-by: Gert Doering <[email protected]>
1 parent 92eaedb commit 20b234b

File tree

7 files changed

+57
-1
lines changed

7 files changed

+57
-1
lines changed

doc/man-sections/tls-options.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,10 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa
568568
The command can reject the connection by exiting with a non-zero exit
569569
code.
570570

571+
--tls-crypt-v2-max-age n
572+
Reject tls-crypt-v2 client keys that are older than n days or have
573+
no timestamp.
574+
571575
--tls-exit
572576
Exit on TLS negotiation failure. This option can be useful when you only
573577
want to make one attempt at connecting, e.g. in a test or monitoring script.

doc/tls-crypt-v2.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ When setting up the openvpn connection:
139139
The message is dropped and no error response is sent when either 3.1, 3.2 or
140140
3.3 fails (DoS protection).
141141

142-
4. Server optionally checks metadata using a --tls-crypt-v2-verify script
142+
4. The server optionally checks if the client key contains a timestamp that is
143+
below a maximum age configured with the --tls-crypt-v2-max-age option.
144+
145+
5. Server optionally checks metadata using a --tls-crypt-v2-verify script
143146

144147
This allows early abort of connection, *before* we expose any of the
145148
notoriously dangerous TLS, X.509 and ASN.1 parsers and thereby reduces the

src/openvpn/init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3418,6 +3418,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
34183418
{
34193419
to.tls_wrap.tls_crypt_v2_server_key = c->c1.ks.tls_crypt_v2_server_key;
34203420
to.tls_crypt_v2_verify_script = c->options.tls_crypt_v2_verify_script;
3421+
to.tls_crypt_v2_max_age = c->options.tls_crypt_v2_max_age;
34213422
if (options->ce.tls_crypt_v2_force_cookie)
34223423
{
34233424
to.tls_wrap.opt.flags |= CO_FORCE_TLSCRYPTV2_COOKIE;

src/openvpn/options.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,8 @@ static const char usage_message[] =
648648
" fresh tls-crypt-v2 server key, and store to keyfile\n"
649649
"--tls-crypt-v2-verify cmd : Run command cmd to verify the metadata of the\n"
650650
" client-supplied tls-crypt-v2 client key\n"
651+
"--tls-crypt-v2-max-age n : Only accept tls-crypt-v2 client keys that have a\n"
652+
" timestamp which is at most n days old.\n"
651653
"--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
652654
"--auth-nocache : Don't cache --askpass or --auth-user-pass passwords.\n"
653655
"--crl-verify crl ['dir']: Check peer certificate against a CRL.\n"
@@ -9079,6 +9081,14 @@ add_option(struct options *options, char *p[], bool is_inline, const char *file,
90799081
VERIFY_PERMISSION(OPT_P_GENERAL);
90809082
options->tls_crypt_v2_verify_script = p[1];
90819083
}
9084+
else if (streq(p[0], "tls-crypt-v2-max-age") && p[1])
9085+
{
9086+
VERIFY_PERMISSION(OPT_P_GENERAL);
9087+
if (!atoi_constrained(p[1], &options->tls_crypt_v2_max_age, "tls-crypt-v2-max-age", 1, INT_MAX, msglevel))
9088+
{
9089+
goto err;
9090+
}
9091+
}
90829092
else if (streq(p[0], "x509-track") && p[1] && !p[2])
90839093
{
90849094
VERIFY_PERMISSION(OPT_P_GENERAL);

src/openvpn/options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,8 @@ struct options
678678

679679
const char *tls_crypt_v2_verify_script;
680680

681+
int tls_crypt_v2_max_age;
682+
681683
/* Allow only one session */
682684
bool single_session;
683685

src/openvpn/ssl_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ struct tls_options
383383

384384
bool tls_crypt_v2;
385385
const char *tls_crypt_v2_verify_script;
386+
int tls_crypt_v2_max_age;
386387

387388
/** TLS handshake wrapping state */
388389
struct tls_wrap_ctx tls_wrap;

src/openvpn/tls_crypt.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "argv.h"
3030
#include "base64.h"
3131
#include "crypto.h"
32+
#include "integer.h"
3233
#include "platform.h"
3334
#include "run_command.h"
3435
#include "session_id.h"
@@ -519,6 +520,34 @@ tls_crypt_v2_unwrap_client_key(struct key2 *client_key, struct buffer *metadata,
519520
return ret;
520521
}
521522

523+
static bool
524+
tls_crypt_v2_check_client_key_age(const struct tls_wrap_ctx *ctx, int max_days)
525+
{
526+
if (ctx->tls_crypt_v2_metadata.len < 1 + sizeof(int64_t))
527+
{
528+
msg(M_WARN, "ERROR: Client key metadata is too small to contain a timestamp.");
529+
return false;
530+
}
531+
532+
const uint8_t *metadata = ctx->tls_crypt_v2_metadata.data;
533+
if (*metadata != TLS_CRYPT_METADATA_TYPE_TIMESTAMP)
534+
{
535+
msg(M_WARN, "ERROR: Client key does not have a timestamp.");
536+
return false;
537+
}
538+
539+
int64_t timestamp;
540+
memcpy(&timestamp, metadata + 1, sizeof(int64_t));
541+
timestamp = (int64_t)ntohll((uint64_t)timestamp);
542+
int64_t max_age_in_seconds = max_days * 24 * 60 * 60;
543+
if (now - timestamp > max_age_in_seconds)
544+
{
545+
msg(M_WARN, "ERROR: Client key is too old.");
546+
return false;
547+
}
548+
return true;
549+
}
550+
522551
static bool
523552
tls_crypt_v2_verify_metadata(const struct tls_wrap_ctx *ctx, const struct tls_options *opt)
524553
{
@@ -634,6 +663,12 @@ tls_crypt_v2_extract_client_key(struct buffer *buf, struct tls_wrap_ctx *ctx,
634663
return false;
635664
}
636665

666+
if (opt && opt->tls_crypt_v2_max_age > 0 && !tls_crypt_v2_check_client_key_age(ctx, opt->tls_crypt_v2_max_age))
667+
{
668+
secure_memzero(&ctx->original_wrap_keydata, sizeof(ctx->original_wrap_keydata));
669+
return false;
670+
}
671+
637672
if (opt && opt->tls_crypt_v2_verify_script && !tls_crypt_v2_verify_metadata(ctx, opt))
638673
{
639674
secure_memzero(&ctx->original_wrap_keydata, sizeof(ctx->original_wrap_keydata));

0 commit comments

Comments
 (0)