From 74abd0b916f5a446e20a7c628494d028c2f7ba80 Mon Sep 17 00:00:00 2001
From: Steffan Karger <steffan@karger.me>
Date: Thu, 20 Jul 2017 22:22:09 +0200
Subject: [PATCH 2/2] Calculate max crypto overhead more accurately
Instead of taking the overall worst case, calculate the worst case based on
current --cipher, --auth and --ncp-ciphers.
Trac: #911
Signed-off-by: Steffan Karger <steffan@karger.me>
---
src/openvpn/crypto.c | 8 --------
src/openvpn/crypto.h | 3 ---
src/openvpn/init.c | 8 ++++++--
src/openvpn/multi.c | 3 ++-
src/openvpn/openvpn.h | 1 +
src/openvpn/options.c | 3 ++-
src/openvpn/push.c | 3 ++-
src/openvpn/ssl.c | 37 +++++++++++++++++++++++++++++++++++--
src/openvpn/ssl.h | 7 ++++++-
9 files changed, 54 insertions(+), 19 deletions(-)
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index 1318c1f..fe343d1 100644
a
|
b
|
crypto_adjust_frame_parameters(struct frame *frame, |
739 | 739 | __func__, (unsigned int) crypto_overhead); |
740 | 740 | } |
741 | 741 | |
742 | | size_t |
743 | | crypto_max_overhead(void) |
744 | | { |
745 | | return packet_id_size(true) + OPENVPN_MAX_IV_LENGTH |
746 | | + OPENVPN_MAX_CIPHER_BLOCK_SIZE |
747 | | + max_int(OPENVPN_MAX_HMAC_SIZE, OPENVPN_AEAD_TAG_LENGTH); |
748 | | } |
749 | | |
750 | 742 | /* |
751 | 743 | * Build a struct key_type. |
752 | 744 | */ |
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index db79fb7..fb7ab13 100644
a
|
b
|
void crypto_adjust_frame_parameters(struct frame *frame, |
423 | 423 | bool packet_id, |
424 | 424 | bool packet_id_long_form); |
425 | 425 | |
426 | | /** Return the worst-case OpenVPN crypto overhead (in bytes) */ |
427 | | size_t crypto_max_overhead(void); |
428 | | |
429 | 426 | /* Minimum length of the nonce used by the PRNG */ |
430 | 427 | #define NONCE_SECRET_LEN_MIN 16 |
431 | 428 | |
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index b2dbca0..17075fc 100644
a
|
b
|
do_deferred_options(struct context *c, const unsigned int found) |
2156 | 2156 | } |
2157 | 2157 | /* Do not regenerate keys if server sends an extra push reply */ |
2158 | 2158 | if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized |
2159 | | && !tls_session_update_crypto_params(session, &c->options, &c->c2.frame)) |
| 2159 | && !tls_session_update_crypto_params(session, &c->options, |
| 2160 | &c->c2.frame, |
| 2161 | c->c2.pre_ncp_crypto_overhead)) |
2160 | 2162 | { |
2161 | 2163 | msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options"); |
2162 | 2164 | return false; |
… |
… |
do_init_crypto_tls(struct context *c, const unsigned int flags) |
2546 | 2548 | if (c->options.pull || c->options.mode == MODE_SERVER) |
2547 | 2549 | { |
2548 | 2550 | /* Account for worst-case crypto overhead before allocating buffers */ |
2549 | | frame_add_to_extra_frame(&c->c2.frame, crypto_max_overhead()); |
| 2551 | c->c2.pre_ncp_crypto_overhead = tls_max_crypto_overhead( |
| 2552 | &c->options, c->c1.ks.key_type.cipher); |
| 2553 | frame_add_to_extra_frame(&c->c2.frame, c->c2.pre_ncp_crypto_overhead); |
2550 | 2554 | } |
2551 | 2555 | else |
2552 | 2556 | { |
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index d6e922b..6cdbc92 100644
a
|
b
|
script_failed: |
2093 | 2093 | struct key_state *ks = &session->key[KS_PRIMARY]; |
2094 | 2094 | if (!session->opt->ncp_enabled && ks->authenticated |
2095 | 2095 | && !tls_session_update_crypto_params(session, &mi->context.options, |
2096 | | &mi->context.c2.frame)) |
| 2096 | &mi->context.c2.frame, |
| 2097 | mi->context.c2.pre_ncp_crypto_overhead)) |
2097 | 2098 | { |
2098 | 2099 | msg(D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed"); |
2099 | 2100 | cc_succeeded = false; |
diff --git a/src/openvpn/openvpn.h b/src/openvpn/openvpn.h
index 9262e68..b9209cb 100644
a
|
b
|
struct context_2 |
264 | 264 | /* MTU frame parameters */ |
265 | 265 | struct frame frame; /* Active frame parameters */ |
266 | 266 | struct frame frame_initial; /* Restored on new session */ |
| 267 | size_t pre_ncp_crypto_overhead; /* Needed to adjust for NCP */ |
267 | 268 | |
268 | 269 | #ifdef ENABLE_FRAGMENT |
269 | 270 | /* Object to handle advanced MTU negotiation and datagram fragmentation */ |
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 2c90fa7..e03663a 100644
a
|
b
|
calc_options_string_link_mtu(const struct options *o, const struct frame *frame) |
3507 | 3507 | struct key_type fake_kt; |
3508 | 3508 | init_key_type(&fake_kt, o->ciphername, o->authname, o->keysize, true, |
3509 | 3509 | false); |
3510 | | frame_add_to_extra_frame(&fake_frame, -(crypto_max_overhead())); |
| 3510 | frame_add_to_extra_frame(&fake_frame, |
| 3511 | -(tls_max_crypto_overhead(o, fake_kt.cipher))); |
3511 | 3512 | crypto_adjust_frame_parameters(&fake_frame, &fake_kt, o->replay, |
3512 | 3513 | cipher_kt_mode_ofb_cfb(fake_kt.cipher)); |
3513 | 3514 | frame_finalize(&fake_frame, o->ce.link_mtu_defined, o->ce.link_mtu, |
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 48e2a05..eb44f49 100644
a
|
b
|
incoming_push_message(struct context *c, const struct buffer *buffer) |
278 | 278 | /* Do not regenerate keys if client send a second push request */ |
279 | 279 | if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized |
280 | 280 | && !tls_session_update_crypto_params(session, &c->options, |
281 | | &c->c2.frame)) |
| 281 | &c->c2.frame, |
| 282 | c->c2.pre_ncp_crypto_overhead)) |
282 | 283 | { |
283 | 284 | msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); |
284 | 285 | goto error; |
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 7a255c7..22f8d73 100644
a
|
b
|
tls_item_in_cipher_list(const char *item, const char *list) |
1943 | 1943 | return ctx.found; |
1944 | 1944 | } |
1945 | 1945 | |
| 1946 | static bool tls_cipher_overhead_max(const char *ciphername, void *vctx){ |
| 1947 | size_t *max = vctx; |
| 1948 | const cipher_kt_t *kt = cipher_kt_get(ciphername); |
| 1949 | if (kt) |
| 1950 | { |
| 1951 | size_t overhead = cipher_kt_iv_size(kt) + cipher_kt_block_size(kt); |
| 1952 | *max = *max > overhead ? *max : overhead; |
| 1953 | } |
| 1954 | return false; |
| 1955 | } |
| 1956 | |
| 1957 | size_t |
| 1958 | tls_max_crypto_overhead(const struct options *options, |
| 1959 | const cipher_kt_t *kt) |
| 1960 | { |
| 1961 | bool long_form = cipher_kt_mode_ofb_cfb(kt) |
| 1962 | || (!options->tls_server && !options->tls_client); |
| 1963 | |
| 1964 | size_t max_ncp_overhead = 0; |
| 1965 | tls_cipher_list_for_each(options->ncp_ciphers, tls_cipher_overhead_max, |
| 1966 | &max_ncp_overhead); |
| 1967 | |
| 1968 | const md_kt_t *md = md_kt_get(options->authname); |
| 1969 | size_t auth_overhead = md ? md_kt_size(md) : 0; |
| 1970 | size_t cipher_overhead = kt |
| 1971 | ? cipher_kt_block_size(kt) + cipher_kt_iv_size(kt) : 0; |
| 1972 | |
| 1973 | return packet_id_size(long_form) + MAX(max_ncp_overhead, cipher_overhead) |
| 1974 | + MAX(auth_overhead, OPENVPN_AEAD_TAG_LENGTH); |
| 1975 | } |
| 1976 | |
| 1977 | |
1946 | 1978 | void |
1947 | 1979 | tls_poor_mans_ncp(struct options *o, const char *remote_ciphername) |
1948 | 1980 | { |
… |
… |
cleanup: |
1994 | 2026 | |
1995 | 2027 | bool |
1996 | 2028 | tls_session_update_crypto_params(struct tls_session *session, |
1997 | | struct options *options, struct frame *frame) |
| 2029 | struct options *options, struct frame *frame, |
| 2030 | size_t pre_ncp_overhead) |
1998 | 2031 | { |
1999 | 2032 | if (!session->opt->server |
2000 | 2033 | && 0 != strcmp(options->ciphername, session->opt->config_ciphername) |
… |
… |
tls_session_update_crypto_params(struct tls_session *session, |
2030 | 2063 | } |
2031 | 2064 | |
2032 | 2065 | /* Update frame parameters: undo worst-case overhead, add actual overhead */ |
2033 | | frame_add_to_extra_frame(frame, -(crypto_max_overhead())); |
| 2066 | frame_add_to_extra_frame(frame, pre_ncp_overhead); |
2034 | 2067 | crypto_adjust_frame_parameters(frame, &session->opt->key_type, |
2035 | 2068 | options->replay, packet_id_long_form); |
2036 | 2069 | frame_finalize(frame, options->ce.link_mtu_defined, options->ce.link_mtu, |
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 0e0f68f..d46b503 100644
a
|
b
|
void tls_update_remote_addr(struct tls_multi *multi, |
481 | 481 | * @return true if updating succeeded, false otherwise. |
482 | 482 | */ |
483 | 483 | bool tls_session_update_crypto_params(struct tls_session *session, |
484 | | struct options *options, struct frame *frame); |
| 484 | struct options *options, |
| 485 | struct frame *frame, |
| 486 | size_t pre_ncp_overhead); |
485 | 487 | |
486 | 488 | /** |
487 | 489 | * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. |
… |
… |
bool tls_check_ncp_cipher_list(const char *list); |
521 | 523 | */ |
522 | 524 | bool tls_item_in_cipher_list(const char *item, const char *list); |
523 | 525 | |
| 526 | /** Return the worst-case OpenVPN crypto overhead for this config (in bytes) */ |
| 527 | size_t tls_max_crypto_overhead(const struct options *options, |
| 528 | const cipher_kt_t *kt); |
524 | 529 | |
525 | 530 | /* |
526 | 531 | * inline functions |