From b1cd3682a8699eff2e31ea026479a5877e67a3e1 Mon Sep 17 00:00:00 2001
From: Lev Stipakov <lev@openvpn.net>
Date: Fri, 9 Nov 2018 16:17:23 +0200
Subject: [PATCH] Fix broken fragment/mssfix with NCP
The problem seems to be caused by fragment_frame holding
incorrect "extra_frame" value. Initially it is based on
max crypto overhead. After NCP, we adjust extra_frame
value in frame and use it in mss_fixup_dowork().
However resulting packet size exceeds fragment size,
which is calculated with incorrect fragment_frame->extra_frame.
Because fragment size is exceeded, and we perform
unneeded fragmentation.
As a fix, fragment_frame->frame_extra should be
also updated on NCP negotiation.
Signed-off-by: Lev Stipakov <lstipakov@gmail.com>
---
src/openvpn/init.c | 11 ++++++++++-
src/openvpn/push.c | 9 ++++++++-
src/openvpn/ssl.c | 12 +++++++++++-
src/openvpn/ssl.h | 4 +++-
4 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 39e8ca5f..a5b27589 100644
a
|
b
|
do_deferred_options(struct context *c, const unsigned int found) |
2300 | 2300 | { |
2301 | 2301 | tls_poor_mans_ncp(&c->options, c->c2.tls_multi->remote_ciphername); |
2302 | 2302 | } |
| 2303 | struct frame *frame_fragment = NULL; |
| 2304 | #ifdef ENABLE_FRAGMENT |
| 2305 | if (c->options.ce.fragment) |
| 2306 | { |
| 2307 | frame_fragment = &c->c2.frame_fragment; |
| 2308 | } |
| 2309 | #endif |
| 2310 | |
2303 | 2311 | /* Do not regenerate keys if server sends an extra push reply */ |
2304 | 2312 | if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized |
2305 | | && !tls_session_update_crypto_params(session, &c->options, &c->c2.frame)) |
| 2313 | && !tls_session_update_crypto_params(session, &c->options, &c->c2.frame, |
| 2314 | frame_fragment)) |
2306 | 2315 | { |
2307 | 2316 | msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options"); |
2308 | 2317 | return false; |
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 72f09962..a44646a5 100644
a
|
b
|
incoming_push_message(struct context *c, const struct buffer *buffer) |
274 | 274 | { |
275 | 275 | if (c->options.mode == MODE_SERVER) |
276 | 276 | { |
| 277 | struct frame *frame_fragment = NULL; |
| 278 | #ifdef ENABLE_FRAGMENT |
| 279 | if (c->options.ce.fragment) |
| 280 | { |
| 281 | frame_fragment = &c->c2.frame_fragment; |
| 282 | } |
| 283 | #endif |
277 | 284 | struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; |
278 | 285 | /* Do not regenerate keys if client send a second push request */ |
279 | 286 | if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized |
280 | 287 | && !tls_session_update_crypto_params(session, &c->options, |
281 | | &c->c2.frame)) |
| 288 | &c->c2.frame, frame_fragment)) |
282 | 289 | { |
283 | 290 | msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); |
284 | 291 | goto error; |
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 74b88ce6..4b912977 100644
a
|
b
|
cleanup: |
1986 | 1986 | |
1987 | 1987 | bool |
1988 | 1988 | tls_session_update_crypto_params(struct tls_session *session, |
1989 | | struct options *options, struct frame *frame) |
| 1989 | struct options *options, struct frame *frame, |
| 1990 | struct frame *frame_fragment) |
1990 | 1991 | { |
1991 | 1992 | if (!session->opt->server |
1992 | 1993 | && 0 != strcmp(options->ciphername, session->opt->config_ciphername) |
… |
… |
tls_session_update_crypto_params(struct tls_session *session, |
2030 | 2031 | frame_init_mssfix(frame, options); |
2031 | 2032 | frame_print(frame, D_MTU_INFO, "Data Channel MTU parms"); |
2032 | 2033 | |
| 2034 | if (frame_fragment) |
| 2035 | { |
| 2036 | frame_remove_from_extra_frame(frame_fragment, crypto_max_overhead()); |
| 2037 | crypto_adjust_frame_parameters(frame_fragment, &session->opt->key_type, |
| 2038 | options->replay, packet_id_long_form); |
| 2039 | frame_set_mtu_dynamic(frame_fragment, options->ce.fragment, SET_MTU_UPPER_BOUND); |
| 2040 | frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms"); |
| 2041 | } |
| 2042 | |
2033 | 2043 | return tls_session_generate_data_channel_keys(session); |
2034 | 2044 | } |
2035 | 2045 | |
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index ac6fef75..db9ea610 100644
a
|
b
|
void tls_update_remote_addr(struct tls_multi *multi, |
482 | 482 | * @return true if updating succeeded, false otherwise. |
483 | 483 | */ |
484 | 484 | bool tls_session_update_crypto_params(struct tls_session *session, |
485 | | struct options *options, struct frame *frame); |
| 485 | struct options *options, |
| 486 | struct frame *frame, |
| 487 | struct frame *frame_fragment); |
486 | 488 | |
487 | 489 | /** |
488 | 490 | * "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. |