Poor man's NCP for 2.3 clients.
PATCH 1/1 Poor man's NCP for 2.3 clients.
Proper cipher negotiation needs clients that announce IV_NCP=1 and can handle pushing of ciphers.
2.3 clients can not do that. To work around that, here's a hacky patch to make it work.
On the client side, set
cipher AES-128-CBC
push-peer-info
setenv UV_CIPHER AES-128-CBC
(same cipher!). On the server side, run a client-connect script that will create a per-instance "cipher $UV_CIPHER" config line if UV_CIPHER is set.
To make this work, make "cipher" settable per-instance (which it effectively already is, options.c just did not learn about it) AND delay cipher initialization even if IV_NCP is not set if the clients sends UV_CIPHER.
Use with care, without the proper server-side script (or plugin), it will just cause non-working setups.
--- src/openvpn/options.c | 2 +- src/openvpn/ssl.c | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-)
src/openvpn/options.c +1 -1
@@ -6677,7 +6677,7 @@ add_option (struct options *options, }else if (streq (p[0 ],"cipher" ) && p[1 ] && !p[2 ]) { -VERIFY_PERMISSION (OPT_P_NCP ); +VERIFY_PERMISSION (OPT_P_NCP |OPT_P_INSTANCE ); options->ciphername = p[1 ]; }else if (streq (p[0 ],"ncp-ciphers" ) && p[1 ] && !p[2 ])
src/openvpn/ssl.c +8 -1
@@ -2258,7 +2258,14 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessiif (tls_peer_info_ncp_ver (multi->peer_info) <2 ) {/* Peer does not support NCP */ - session->opt->ncp_enabled = false; +if (strstr (multi->peer_info,"UV_CIPHER=" ) !=NULL ) + { +msg (M_INFO ,"peer doesn't do NCP, but sends UV_CIPHER, leaving NCP enabled" ); + } +else + { + session->opt->ncp_enabled = false; + } }#endif
Signature · 1 line
2.7.3
PATCH 1/1 Poor man's NCP for non-NCP clients
Allows non-NCP clients (<= 2.3, or 2.4+ with --ncp-disable) to specify a --cipher that is different from the one in the server config, as long as the new cipher value is allowed (i.e. in --ncp-ciphers at the server side).
This patch was inspired by Gert's "Poor man's NCP for 2.3 clients" patch, but takes a different approach to avoid the need for server-side scripts or client-side 'setenv UV_*' tricks.
--- src/openvpn/options.c | 32 ++++++++++++++++++++++++++++++++ src/openvpn/options.h | 14 ++++++++++++++ src/openvpn/ssl.c | 24 +++++++++++++++++++++--- src/openvpn/ssl.h | 7 +++++++ 4 files changed, 74 insertions(+), 3 deletions(-)
src/openvpn/options.c +32 -0
@@ -3431,6 +3431,38 @@ options_string_version (const char* s, struct gc_arena *gc)return BSTR (&out); } +char * +options_string_extract_option (const char *options_string, +size_t options_string_len,const char *opt_name,struct gc_arena *gc) +{ +char *ret =NULL ; + +const char *p = options_string; +while (p) + { +if (p ==strstr (p, opt_name)) + { +const size_t opt_name_len =strlen (opt_name); +if (strlen (p) > opt_name_len+1 && p[opt_name_len] ==' ' ) + { +/* option found, extract value */ +const char *start = &p[opt_name_len+1 ]; +const char *end =strchr (p,',' ); +size_t val_len = end ? end - start :strlen (start); + ret =gc_malloc (val_len+1 , true, gc); +memcpy (ret, start, val_len); +break ; + } + } + p =strchr (p,',' ); +if (p) + { + p++;/* skip delimiter */ + } + } +return ret; +} +#endif /* ENABLE_OCC */ static void
src/openvpn/options.h +14 -0
@@ -724,6 +724,20 @@ void options_warning_safe (char *actual, const char *expected, size_t actual_n);bool options_cmp_equal (char *actual,const char *expected);void options_warning (char *actual,const char *expected); +/** +* Given an OpenVPN options string, extract the value of an option. +* +* @param options_string Zero-terminated, comma-separated options string +* @param opt_name The name of the option to extract +* @param gc The gc to allocate the return value +* +* @return gc-allocated value of option with name opt_name if option was found, +* or NULL otherwise. +*/ +char *options_string_extract_option (const char *options_string, +size_t options_string_len,const char *opt_name,struct gc_arena *gc); + +#endif void options_postprocess (struct options *options);
src/openvpn/ssl.c +21 -3
@@ -1723,8 +1723,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) { } } -static bool -item_in_list (const char *item,const char *list) +bool +tls_item_in_cipher_list (const char *item,const char *list) {char *tmp_ciphers =string_alloc (list,NULL );char *tmp_ciphers_orig = tmp_ciphers;
@@ -1752,7 +1752,7 @@ tls_session_update_crypto_params(struct tls_session *session,if (!session->opt->server &&0 !=strcmp (options->ciphername, session->opt->config_ciphername) && - !item_in_list (options->ciphername, options->ncp_ciphers)) + !tls_item_in_cipher_list (options->ciphername, options->ncp_ciphers)) { msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s", options->ciphername, session->opt->config_ciphername,
@@ -2318,6 +2318,24 @@ key_method_2_read (struct buffer *buf, struct context *c, struct tls_session *se/* Peer does not support NCP */ session->opt->ncp_enabled = false; } + +/* "Poor man's NCP": Use client-cipher if it is an allowed (NCP) cipher. +* Allows non-NCP client to upgrade their cipher individually. */ +char *options_string_remote_cipher =options_string_extract_option (options, +TLS_OPTIONS_LEN ,"cipher" , &gc); +if (!session->opt->ncp_enabled && options_string_remote_cipher && +0 !=strcmp (c->options.ciphername, options_string_remote_cipher)) + { +if (tls_item_in_cipher_list (options_string_remote_cipher, + c->options.ncp_ciphers)) + { + c->options.ciphername =string_alloc (options_string_remote_cipher, + &c->options.gc); + session->opt->ncp_enabled = true; +msg (D_TLS_DEBUG_LOW ,"Using client cipher '%s'" , c->options.ciphername); + } + } +#endif if (tls_session_user_pass_enabled (session))
src/openvpn/ssl.h +7 -0
@@ -508,6 +508,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);*/ bool tls_check_ncp_cipher_list (const char *list); +/** +* Return true iff item is present in the colon-separated zero-terminated +* cipher list. +*/ +bool tls_item_in_cipher_list (const char *item,const char *list); + +/* * inline functions */
Signature · 1 line
2.7.4
PATCH v2 1/1 Poor man's NCP for non-NCP clients
Allows non-NCP clients (<= 2.3, or 2.4+ with --ncp-disable) to specify a --cipher that is different from the one in the server config, as long as the new cipher value is allowed (i.e. in --ncp-ciphers at the server side).
This patch was inspired by Gert's "Poor man's NCP for 2.3 clients" patch, but takes a different approach to avoid the need for server-side scripts or client-side 'setenv UV_*' tricks.
---
v2: remove unused parameter from options_string_extract_option()
prototype.
src/openvpn/options.c | 32 ++++++++++++++++++++++++++++++++ src/openvpn/options.h | 14 ++++++++++++++ src/openvpn/ssl.c | 24 +++++++++++++++++++++--- src/openvpn/ssl.h | 7 +++++++ 4 files changed, 74 insertions(+), 3 deletions(-)
src/openvpn/options.c +32 -0
@@ -3431,6 +3431,38 @@ options_string_version (const char* s, struct gc_arena *gc)return BSTR (&out); } +char * +options_string_extract_option (const char *options_string, +size_t options_string_len,const char *opt_name,struct gc_arena *gc) +{ +char *ret =NULL ; + +const char *p = options_string; +while (p) + { +if (p ==strstr (p, opt_name)) + { +const size_t opt_name_len =strlen (opt_name); +if (strlen (p) > opt_name_len+1 && p[opt_name_len] ==' ' ) + { +/* option found, extract value */ +const char *start = &p[opt_name_len+1 ]; +const char *end =strchr (p,',' ); +size_t val_len = end ? end - start :strlen (start); + ret =gc_malloc (val_len+1 , true, gc); +memcpy (ret, start, val_len); +break ; + } + } + p =strchr (p,',' ); +if (p) + { + p++;/* skip delimiter */ + } + } +return ret; +} +#endif /* ENABLE_OCC */ static void
src/openvpn/options.h +14 -0
@@ -724,6 +724,20 @@ void options_warning_safe (char *actual, const char *expected, size_t actual_n);bool options_cmp_equal (char *actual,const char *expected);void options_warning (char *actual,const char *expected); +/** +* Given an OpenVPN options string, extract the value of an option. +* +* @param options_string Zero-terminated, comma-separated options string +* @param opt_name The name of the option to extract +* @param gc The gc to allocate the return value +* +* @return gc-allocated value of option with name opt_name if option was found, +* or NULL otherwise. +*/ +char *options_string_extract_option (const char *options_string, +size_t options_string_len,const char *opt_name,struct gc_arena *gc); + +#endif void options_postprocess (struct options *options);
src/openvpn/ssl.c +21 -3
@@ -1723,8 +1723,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) { } } -static bool -item_in_list (const char *item,const char *list) +bool +tls_item_in_cipher_list (const char *item,const char *list) {char *tmp_ciphers =string_alloc (list,NULL );char *tmp_ciphers_orig = tmp_ciphers;
@@ -1752,7 +1752,7 @@ tls_session_update_crypto_params(struct tls_session *session,if (!session->opt->server &&0 !=strcmp (options->ciphername, session->opt->config_ciphername) && - !item_in_list (options->ciphername, options->ncp_ciphers)) + !tls_item_in_cipher_list (options->ciphername, options->ncp_ciphers)) { msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s", options->ciphername, session->opt->config_ciphername,
@@ -2318,6 +2318,24 @@ key_method_2_read (struct buffer *buf, struct context *c, struct tls_session *se/* Peer does not support NCP */ session->opt->ncp_enabled = false; } + +/* "Poor man's NCP": Use client-cipher if it is an allowed (NCP) cipher. +* Allows non-NCP client to upgrade their cipher individually. */ +char *options_string_remote_cipher =options_string_extract_option (options, +TLS_OPTIONS_LEN ,"cipher" , &gc); +if (!session->opt->ncp_enabled && options_string_remote_cipher && +0 !=strcmp (c->options.ciphername, options_string_remote_cipher)) + { +if (tls_item_in_cipher_list (options_string_remote_cipher, + c->options.ncp_ciphers)) + { + c->options.ciphername =string_alloc (options_string_remote_cipher, + &c->options.gc); + session->opt->ncp_enabled = true; +msg (D_TLS_DEBUG_LOW ,"Using client cipher '%s'" , c->options.ciphername); + } + } +#endif if (tls_session_user_pass_enabled (session))
src/openvpn/ssl.h +7 -0
@@ -508,6 +508,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);*/ bool tls_check_ncp_cipher_list (const char *list); +/** +* Return true iff item is present in the colon-separated zero-terminated +* cipher list. +*/ +bool tls_item_in_cipher_list (const char *item,const char *list); + +/* * inline functions */
Signature · 1 line
2.7.4
PATCH v3 1/1 Poor man's NCP for non-NCP clients
Allows non-NCP clients (<= 2.3, or 2.4+ with --ncp-disable) to specify a --cipher that is different from the one in the server config, as long as the new cipher value is allowed (i.e. in --ncp-ciphers at the server side).
This patch was inspired by Gert's "Poor man's NCP for 2.3 clients" patch, but takes a different approach to avoid the need for server-side scripts or client-side 'setenv UV_*' tricks.
To achieve this cleanly, tls_session_update_crypto_parameters() had to be split into 'update' and 'generate keys' parts. I used the opportunity to introduce a simpler API that works for the other places where we used 'generate_key_expansion()' too.
---
v2: remove unused argument from options_string_extract_option()
v3: don't do poor-man's NCP if NCP is disabled
src/openvpn/init.c | 4 ++- src/openvpn/options.c | 32 ++++++++++++++++++ src/openvpn/options.h | 14 ++++++++ src/openvpn/push.c | 7 ++-- src/openvpn/ssl.c | 94 ++++++++++++++++++++++++++++----------------------- src/openvpn/ssl.h | 19 +++++++++-- 6 files changed, 122 insertions(+), 48 deletions(-)
src/openvpn/init.c +3 -1
@@ -1928,7 +1928,9 @@ do_deferred_options (struct context *c, const unsigned int found)msg (D_PUSH ,"OPTIONS IMPORT: data channel crypto options modified" );/* Do not regenerate keys if server sends an extra push request */ if (!session->key[KS_PRIMARY ].crypto_options.key_ctx_bi.initialized && - !tls_session_update_crypto_params (session, &c->options, &c->c2.frame)) + (!tls_session_update_crypto_params (session, &c->options, + &c->c2.frame) || + !tls_session_generate_data_channel_keys (session))) {msg (D_TLS_ERRORS ,"OPTIONS ERROR: failed to import crypto options" );return false;
src/openvpn/options.c +32 -0
@@ -3431,6 +3431,38 @@ options_string_version (const char* s, struct gc_arena *gc)return BSTR (&out); } +char * +options_string_extract_option (const char *options_string,const char *opt_name, +struct gc_arena *gc) +{ +char *ret =NULL ; + +const char *p = options_string; +while (p) + { +if (p ==strstr (p, opt_name)) + { +const size_t opt_name_len =strlen (opt_name); +if (strlen (p) > opt_name_len+1 && p[opt_name_len] ==' ' ) + { +/* option found, extract value */ +const char *start = &p[opt_name_len+1 ]; +const char *end =strchr (p,',' ); +size_t val_len = end ? end - start :strlen (start); + ret =gc_malloc (val_len+1 , true, gc); +memcpy (ret, start, val_len); +break ; + } + } + p =strchr (p,',' ); +if (p) + { + p++;/* skip delimiter */ + } + } +return ret; +} +#endif /* ENABLE_OCC */ static void
src/openvpn/options.h +14 -0
@@ -724,6 +724,20 @@ void options_warning_safe (char *actual, const char *expected, size_t actual_n);bool options_cmp_equal (char *actual,const char *expected);void options_warning (char *actual,const char *expected); +/** +* Given an OpenVPN options string, extract the value of an option. +* +* @param options_string Zero-terminated, comma-separated options string +* @param opt_name The name of the option to extract +* @param gc The gc to allocate the return value +* +* @return gc-allocated value of option with name opt_name if option was found, +* or NULL otherwise. +*/ +char *options_string_extract_option (const char *options_string, +const char *opt_name,struct gc_arena *gc); + +#endif void options_postprocess (struct options *options);
src/openvpn/push.c +4 -3
@@ -259,10 +259,11 @@ incoming_push_message (struct context *c, const struct buffer *buffer)struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE ];/* Do not regenerate keys if client send a second push request */ if (!session->key[KS_PRIMARY ].crypto_options.key_ctx_bi.initialized && - !tls_session_update_crypto_params (session, &c->options, - &c->c2.frame)) + (!tls_session_update_crypto_params (session, &c->options, + &c->c2.frame) || + !tls_session_generate_data_channel_keys (session))) { -msg (D_TLS_ERRORS ,"TLS Error: server generate_key_expansion failed" ); +msg (D_TLS_ERRORS ,"TLS Error: initializing data channel failed" ); goto error; } }
src/openvpn/ssl.c +52 -42
147 lines not on this page · raw message
src/openvpn/ssl.h +17 -2
@@ -472,8 +472,16 @@ void tls_update_remote_addr (struct tls_multi *multi, const struct link_socket_actual *addr);/** -* Update TLS session crypto parameters (cipher and auth) and derive data -* channel keys based on the supplied options. +* Generate data channel keys for the supplied TLS session. +* +* This erases the source material used to generate the data channel keys, and +* can thus be called only once per session. +*/ +bool tls_session_generate_data_channel_keys (struct tls_session *session); + +/** +* Update TLS session crypto parameters (cipher and auth) and recalculate +* resulting frame sizes. * * @param session The TLS session to update. * @param options The options to use when updating session.
@@ -508,6 +516,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);*/ bool tls_check_ncp_cipher_list (const char *list); +/** +* Return true iff item is present in the colon-separated zero-terminated +* cipher list. +*/ +bool tls_item_in_cipher_list (const char *item,const char *list); + +/* * inline functions */
Signature · 1 line
2.7.4
PATCH v4 1/1 Poor man's NCP for non-NCP peers
Allows non-NCP peers (<= 2.3, or 2.4+ with --ncp-disable) to specify a --cipher that is different from the one in our config, as long as the new cipher value is allowed (i.e. in --ncp-ciphers at our side).
This works both client-to-server and server-to-client. I.e. a 2.4 client with "cipher BF-CBC" and "ncp-ciphers AES-256-GCM:AES-256-CBC" can connect to both a 2.3 server with "cipher BF-CBC" as well as a server with "cipher AES-256-CBC" in its config. The other way around, a 2.3 client with either "cipher BF-CBC" or "cipher AES-256-CBC" can connect to a 2.4 server with e.g. "cipher BF-CBC" and "ncp-ciphers AES-256-GCM:AES-256-CBC" in its config.
This patch was inspired by Gert's "Poor man's NCP for 2.3 clients" patch, but takes a different approach to avoid the need for server-side scripts or client-side 'setenv UV_*' tricks.
To achieve this cleanly, tls_session_update_crypto_parameters() had to be split into 'update' and 'generate keys' parts. I used the opportunity to introduce a simpler API that works for the other places where we used 'generate_key_expansion()' too.
---
v2: remove unused argument from options_string_extract_option()
v3: don't do poor-man's NCP if NCP is disabled
v4: use strncmp instead of strstr to match prefix in
options_string_extract_option, add man page text and make clear that this feature works both client-to-server and server-to-client.
doc/openvpn.8 | 14 ++++++-- src/openvpn/init.c | 4 ++- src/openvpn/options.c | 32 ++++++++++++++++++ src/openvpn/options.h | 14 ++++++++ src/openvpn/push.c | 7 ++-- src/openvpn/ssl.c | 93 ++++++++++++++++++++++++++++----------------------- src/openvpn/ssl.h | 19 +++++++++-- 7 files changed, 133 insertions(+), 50 deletions(-)
doc/openvpn.8 +12 -2
@@ -4147,9 +4147,9 @@ to disable encryption. As of OpenVPN 2.4, cipher negotiation (NCP) can override the cipher specified by .B \-\-cipher\fR. See -.B \-\-ncp-ciphers +.B \-\-ncp\-ciphers and -.B \-\-ncp-disable +.B \-\-ncp\-disable for more on NCP. .\"*********************************************************
@@ -4177,6 +4177,16 @@ If both peers support and do not disable NCP, the negotiated cipher will override the cipher specified by .B \-\-cipher\fR. +Additionally, to allow for more smooth transition, if NCP is enabled, OpenVPN +will inherit the cipher of the peer if that cipher is different from the local +.B \-\-cipher +setting, but the peer cipher is one of the ciphers specified in +.B \-\-ncp\-ciphers\fR. +E.g. a non-NCP client (<=2.3, or with \-\-ncp\-disabled set) connecting to a +NCP server (2.4+) with "\-\-cipher BF-CBC" and "\-\-ncp-ciphers +AES-256-GCM:AES-256-CBC" set can either specify "\-\-cipher BF-CBC" or +"\-\-cipher AES-256-CBC" and both will work. + .\"********************************************************* .TP .B \-\-ncp\-disable
src/openvpn/init.c +3 -1
@@ -1928,7 +1928,9 @@ do_deferred_options (struct context *c, const unsigned int found)msg (D_PUSH ,"OPTIONS IMPORT: data channel crypto options modified" );/* Do not regenerate keys if server sends an extra push request */ if (!session->key[KS_PRIMARY ].crypto_options.key_ctx_bi.initialized && - !tls_session_update_crypto_params (session, &c->options, &c->c2.frame)) + (!tls_session_update_crypto_params (session, &c->options, + &c->c2.frame) || + !tls_session_generate_data_channel_keys (session))) {msg (D_TLS_ERRORS ,"OPTIONS ERROR: failed to import crypto options" );return false;
src/openvpn/options.c +32 -0
@@ -1642,6 +1642,8 @@ show_settings (const struct options *o)SHOW_STR (shared_secret_file);SHOW_INT (key_direction);SHOW_STR (ciphername); +SHOW_BOOL (ncp_enabled); +SHOW_STR (ncp_ciphers);SHOW_STR (authname);SHOW_STR (prng_hash);SHOW_INT (prng_nonce_secret_len);
@@ -3431,6 +3433,36 @@ options_string_version (const char* s, struct gc_arena *gc)return BSTR (&out); } +char * +options_string_extract_option (const char *options_string,const char *opt_name, +struct gc_arena *gc) +{ +char *ret =NULL ; +const size_t opt_name_len =strlen (opt_name); + +const char *p = options_string; +while (p) + { +if (0 ==strncmp (p, opt_name, opt_name_len) && +strlen (p) > (opt_name_len+1 ) && p[opt_name_len] ==' ' ) + { +/* option found, extract value */ +const char *start = &p[opt_name_len+1 ]; +const char *end =strchr (p,',' ); +size_t val_len = end ? end - start :strlen (start); + ret =gc_malloc (val_len+1 , true, gc); +memcpy (ret, start, val_len); +break ; + } + p =strchr (p,',' ); +if (p) + { + p++;/* skip delimiter */ + } + } +return ret; +} +#endif /* ENABLE_OCC */ static void
src/openvpn/options.h +14 -0
@@ -724,6 +724,20 @@ void options_warning_safe (char *actual, const char *expected, size_t actual_n);bool options_cmp_equal (char *actual,const char *expected);void options_warning (char *actual,const char *expected); +/** +* Given an OpenVPN options string, extract the value of an option. +* +* @param options_string Zero-terminated, comma-separated options string +* @param opt_name The name of the option to extract +* @param gc The gc to allocate the return value +* +* @return gc-allocated value of option with name opt_name if option was found, +* or NULL otherwise. +*/ +char *options_string_extract_option (const char *options_string, +const char *opt_name,struct gc_arena *gc); + +#endif void options_postprocess (struct options *options);
src/openvpn/push.c +4 -3
@@ -259,10 +259,11 @@ incoming_push_message (struct context *c, const struct buffer *buffer)struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE ];/* Do not regenerate keys if client send a second push request */ if (!session->key[KS_PRIMARY ].crypto_options.key_ctx_bi.initialized && - !tls_session_update_crypto_params (session, &c->options, - &c->c2.frame)) + (!tls_session_update_crypto_params (session, &c->options, + &c->c2.frame) || + !tls_session_generate_data_channel_keys (session))) { -msg (D_TLS_ERRORS ,"TLS Error: server generate_key_expansion failed" ); +msg (D_TLS_ERRORS ,"TLS Error: initializing data channel failed" ); goto error; } }
src/openvpn/ssl.c +51 -42
146 lines not on this page · raw message
src/openvpn/ssl.h +17 -2
@@ -472,8 +472,16 @@ void tls_update_remote_addr (struct tls_multi *multi, const struct link_socket_actual *addr);/** -* Update TLS session crypto parameters (cipher and auth) and derive data -* channel keys based on the supplied options. +* Generate data channel keys for the supplied TLS session. +* +* This erases the source material used to generate the data channel keys, and +* can thus be called only once per session. +*/ +bool tls_session_generate_data_channel_keys (struct tls_session *session); + +/** +* Update TLS session crypto parameters (cipher and auth) and recalculate +* resulting frame sizes. * * @param session The TLS session to update. * @param options The options to use when updating session.
@@ -508,6 +516,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);*/ bool tls_check_ncp_cipher_list (const char *list); +/** +* Return true iff item is present in the colon-separated zero-terminated +* cipher list. +*/ +bool tls_item_in_cipher_list (const char *item,const char *list); + +/* * inline functions */
Signature · 1 line
2.7.4
Re: [Openvpn-devel] [PATCH v4] Poor man's NCP for non-NCP peers
Hi,
I totally want to have this, and overall the changes all make sense (so ACK for the refactoring part, needs a few windows held side by side in addition to tkdiff... :-) ), but something puzzles me...
On Tue, Nov 22, 2016 at 08:57:11PM +0100, Steffan Karger wrote:
[..]
src/openvpn/ssl.c key_method_2_read (struct buffer *buf, struct context *c, struct tls_session *se · line 2327
Quoted 11 lines
src/openvpn/ssl.c +7
-0@@ -2320,6 +2318,26 @@ key_method_2_read (struct buffer *buf, struct context *c, struct tls_session *se/* Peer does not support NCP */ session->opt->ncp_enabled = false; } + +/* "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. +* Allows non-NCP client to upgrade their cipher individually. */ +char *remote_cipher =options_string_extract_option (options,"cipher" , &gc); +if (c->options.ncp_enabled && !session->opt->ncp_enabled && remote_cipher && +0 !=strcmp (c->options.ciphername, remote_cipher)) + {
This looks totally logical and all, until I try to compile it - and then the compiler ruins the day for me...
../../../openvpn/src/openvpn/ssl.c: In function 'key_method_2_read':../../../openvpn/src/openvpn/ssl.c:2313:7: error: 'c' undeclared (first use in this function)
if (c->options.ncp_enabled && !session->opt->ncp_enabled && remote_cipher &&
^
... and I can not seriously argue with it here... - there is no "c".
(David pointed out that *his* patch brings in the "c" here, but that one didn't receive enough love yet, so is not in *my* tree...)
So - this needs to be sorted out.
There is another catch here - options_string_extract_option() is wrapped in an #ifdef ENABLE_OCC, which is turned off if you configure --enable-small (so linking breaks). That second catch can be easily fixed by wrapping this whole code new code block in #ifdef ENABLE_OCC as well (if the data is just not enable in this case) or by moving the "extract" function after the corresponding #endif...
Finally, moving over tls_limit_reneg_bytes() broke the whitespace in weird ways - it got mangled to
tls_limit_reneg_bytes (session->opt->key_type.cipher,
&session->opt->renegotiate_bytes);
while it was
tls_limit_reneg_bytes (session->opt->key_type.cipher,
&session->opt->renegotiate_bytes);
(I could fix that on the fly, and also the #ifdef ENABLE_OCC, but not the "c" one)
"Can I have a v5, please...?"
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...PATCH v5 1/2 Poor man's NCP for non-NCP peers
Allows non-NCP peers (<= 2.3, or 2.4+ with --ncp-disable) to specify a --cipher that is different from the one in our config, as long as the new cipher value is allowed (i.e. in --ncp-ciphers at our side).
This works both client-to-server and server-to-client. I.e. a 2.4 client with "cipher BF-CBC" and "ncp-ciphers AES-256-GCM:AES-256-CBC" can connect to both a 2.3 server with "cipher BF-CBC" as well as a server with "cipher AES-256-CBC" in its config. The other way around, a 2.3 client with either "cipher BF-CBC" or "cipher AES-256-CBC" can connect to a 2.4 server with e.g. "cipher BF-CBC" and "ncp-ciphers AES-256-GCM:AES-256-CBC" in its config.
This patch was inspired by Gert's "Poor man's NCP for 2.3 clients" patch, but takes a different approach to avoid the need for server-side scripts or client-side 'setenv UV_*' tricks.
---
v2: remove unused argument from options_string_extract_option()
v3: don't do poor-man's NCP if NCP is disabled
v4: use strncmp instead of strstr to match prefix in
options_string_extract_option, add man page text and make clear that this feature works both client-to-server and server-to-client.
v5: move poor-mans-ncp logic from key_method_2_read() to the place
where we do regular NCP too. This makes that we no longer need access to the struct context in key_method_2_read, and is cleaner in general, and makes the patch a bit smaller.
doc/openvpn.8 | 14 ++++++++++++-- src/openvpn/init.c | 8 +++++++- src/openvpn/options.c | 32 ++++++++++++++++++++++++++++++++ src/openvpn/options.h | 14 ++++++++++++++ src/openvpn/push.c | 6 +++++- src/openvpn/ssl.c | 39 ++++++++++++++++++++++++++------------- src/openvpn/ssl.h | 16 ++++++++++++++++ src/openvpn/ssl_common.h | 2 ++ 8 files changed, 114 insertions(+), 17 deletions(-)
doc/openvpn.8 +12 -2
@@ -4147,9 +4147,9 @@ to disable encryption. As of OpenVPN 2.4, cipher negotiation (NCP) can override the cipher specified by .B \-\-cipher\fR. See -.B \-\-ncp-ciphers +.B \-\-ncp\-ciphers and -.B \-\-ncp-disable +.B \-\-ncp\-disable for more on NCP. .\"*********************************************************
@@ -4177,6 +4177,16 @@ If both peers support and do not disable NCP, the negotiated cipher will override the cipher specified by .B \-\-cipher\fR. +Additionally, to allow for more smooth transition, if NCP is enabled, OpenVPN +will inherit the cipher of the peer if that cipher is different from the local +.B \-\-cipher +setting, but the peer cipher is one of the ciphers specified in +.B \-\-ncp\-ciphers\fR. +E.g. a non-NCP client (<=2.3, or with \-\-ncp\-disabled set) connecting to a +NCP server (2.4+) with "\-\-cipher BF-CBC" and "\-\-ncp-ciphers +AES-256-GCM:AES-256-CBC" set can either specify "\-\-cipher BF-CBC" or +"\-\-cipher AES-256-CBC" and both will work. + .\"********************************************************* .TP .B \-\-ncp\-disable
src/openvpn/init.c +7 -1
@@ -1925,7 +1925,13 @@ do_deferred_options (struct context *c, const unsigned int found) {struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE ];if (found &OPT_P_NCP ) -msg (D_PUSH ,"OPTIONS IMPORT: data channel crypto options modified" ); + { +msg (D_PUSH ,"OPTIONS IMPORT: data channel crypto options modified" ); + } +else if (c->options.ncp_enabled) + { +tls_poor_mans_ncp (&c->options, c->c2.tls_multi->remote_ciphername); + }/* Do not regenerate keys if server sends an extra push request */ if (!session->key[KS_PRIMARY ].crypto_options.key_ctx_bi.initialized && !tls_session_update_crypto_params (session, &c->options, &c->c2.frame))
src/openvpn/options.c +32 -0
@@ -1643,6 +1643,8 @@ show_settings (const struct options *o)SHOW_STR (shared_secret_file);SHOW_INT (key_direction);SHOW_STR (ciphername); +SHOW_BOOL (ncp_enabled); +SHOW_STR (ncp_ciphers);SHOW_STR (authname);SHOW_STR (prng_hash);SHOW_INT (prng_nonce_secret_len);
@@ -3434,6 +3436,36 @@ options_string_version (const char* s, struct gc_arena *gc)#endif /* ENABLE_OCC */ +char * +options_string_extract_option (const char *options_string,const char *opt_name, +struct gc_arena *gc) +{ +char *ret =NULL ; +const size_t opt_name_len =strlen (opt_name); + +const char *p = options_string; +while (p) + { +if (0 ==strncmp (p, opt_name, opt_name_len) && +strlen (p) > (opt_name_len+1 ) && p[opt_name_len] ==' ' ) + { +/* option found, extract value */ +const char *start = &p[opt_name_len+1 ]; +const char *end =strchr (p,',' ); +size_t val_len = end ? end - start :strlen (start); + ret =gc_malloc (val_len+1 , true, gc); +memcpy (ret, start, val_len); +break ; + } + p =strchr (p,',' ); +if (p) + { + p++;/* skip delimiter */ + } + } +return ret; +} +static void foreign_option (struct options *o,char *argv[],int len,struct env_set *es) {
src/openvpn/options.h +14 -0
@@ -724,6 +724,20 @@ void options_warning_safe (char *actual, const char *expected, size_t actual_n);bool options_cmp_equal (char *actual,const char *expected);void options_warning (char *actual,const char *expected); +/** +* Given an OpenVPN options string, extract the value of an option. +* +* @param options_string Zero-terminated, comma-separated options string +* @param opt_name The name of the option to extract +* @param gc The gc to allocate the return value +* +* @return gc-allocated value of option with name opt_name if option was found, +* or NULL otherwise. +*/ +char *options_string_extract_option (const char *options_string, +const char *opt_name,struct gc_arena *gc); + +#endif void options_postprocess (struct options *options);
src/openvpn/push.c +5 -1
@@ -262,7 +262,7 @@ incoming_push_message (struct context *c, const struct buffer *buffer) !tls_session_update_crypto_params (session, &c->options, &c->c2.frame)) { -msg (D_TLS_ERRORS ,"TLS Error: server generate_key_expansion failed" ); +msg (D_TLS_ERRORS ,"TLS Error: initializing data channel failed" ); goto error; } }
@@ -370,6 +370,10 @@ prepare_push_reply (struct context *c, struct gc_arena *gc,push_option_fmt (gc, push_list,M_USAGE ,"cipher %s" , o->ciphername); } } +else if (o->ncp_enabled) + { +tls_poor_mans_ncp (o, tls_multi->remote_ciphername); + }/* If server uses --auth-gen-token and we have an auth token * to send to the client
src/openvpn/ssl.c +26 -13
@@ -1216,6 +1216,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)free (multi->auth_token); } +free (multi->remote_ciphername); +for (i =0 ; i <TM_SIZE ; ++i)tls_session_free (&multi->session[i], false);
@@ -1723,8 +1725,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) { } } -static bool -item_in_list (const char *item,const char *list) +bool +tls_item_in_cipher_list (const char *item,const char *list) {char *tmp_ciphers =string_alloc (list,NULL );char *tmp_ciphers_orig = tmp_ciphers;
@@ -1741,6 +1743,20 @@ item_in_list(const char *item, const char *list)return token !=NULL ; } +void +tls_poor_mans_ncp (struct options *o,const char *remote_ciphername) +{ +if (o->ncp_enabled && remote_ciphername && +0 !=strcmp (o->ciphername, remote_ciphername)) + { +if (tls_item_in_cipher_list (remote_ciphername, o->ncp_ciphers)) + { + o->ciphername =string_alloc (remote_ciphername, &o->gc); +msg (D_TLS_DEBUG_LOW ,"Using peer cipher '%s'" , o->ciphername); + } + } +} +bool tls_session_update_crypto_params (struct tls_session *session,const struct options *options,struct frame *frame)
@@ -1752,7 +1768,7 @@ tls_session_update_crypto_params(struct tls_session *session,if (!session->opt->server &&0 !=strcmp (options->ciphername, session->opt->config_ciphername) && - !item_in_list (options->ciphername, options->ncp_ciphers)) + !tls_item_in_cipher_list (options->ciphername, options->ncp_ciphers)) { msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s", options->ciphername, session->opt->config_ciphername,
@@ -2152,12 +2168,11 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) goto error;/* Generate tunnel keys if we're a TLS server. -* If we're a p2mp server and IV_NCP >= 2 is negotiated, the first key -* generation is postponed until after the pull/push, so we can process pushed -* cipher directives. +* If we're a p2mp server, the first key generation is postponed until after +* the pull/push, so we can process pushed cipher directives. */ -if (session->opt->server && !(session->opt->ncp_enabled && - session->opt->mode ==MODE_SERVER && ks->key_id <=0 )) +if (session->opt->server && + !(session->opt->mode ==MODE_SERVER && ks->key_id <=0 )) {if (ks->authenticated) {
@@ -2312,11 +2327,9 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessiif ( multi->peer_info )output_peer_info_env (session->opt->es, multi->peer_info); -if (tls_peer_info_ncp_ver (multi->peer_info) <2 ) - { -/* Peer does not support NCP */ - session->opt->ncp_enabled = false; - } +free (multi->remote_ciphername); + multi->remote_ciphername = +options_string_extract_option (options,"cipher" ,NULL );#endif if (tls_session_user_pass_enabled (session))
src/openvpn/ssl.h +16 -0
@@ -489,6 +489,15 @@ void tls_update_remote_addr (struct tls_multi *multi,bool tls_session_update_crypto_params (struct tls_session *session,const struct options *options,struct frame *frame); +/** +* "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. +* Allows non-NCP peers to upgrade their cipher individually. +* +* Make sure to call tls_session_update_crypto_params() after calling this +* function. +*/ +void tls_poor_mans_ncp (struct options *o,const char *remote_ciphername); +#ifdef MANAGEMENT_DEF_AUTH static inline char *tls_get_peer_info (const struct tls_multi *multi)
@@ -512,6 +521,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);*/ bool tls_check_ncp_cipher_list (const char *list); +/** +* Return true iff item is present in the colon-separated zero-terminated +* cipher list. +*/ +bool tls_item_in_cipher_list (const char *item,const char *list); + +/* * inline functions */
src/openvpn/ssl_common.h +2 -0
@@ -540,6 +540,8 @@ struct tls_multiuint32_t peer_id;bool use_peer_id; +char *remote_ciphername;/**< cipher specified in peer's config file */ +char *auth_token;/**< If server sends a generated auth-token, * this is the token to use for future * user/pass authentications in this session.
Signature · 1 line
2.7.4
PATCH 2/2 Refactor data channel key generation API
Originally for "poor man's NPC", I introduced a simpler API for generating data channel keys. That refactoring is no longer needed for that patch, but I believe still worth a patch on it's own.
This patch should not change any functionality.
--- src/openvpn/ssl.c | 75 ++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 40 deletions(-)
src/openvpn/ssl.c +35 -40
@@ -1757,15 +1757,44 @@ tls_poor_mans_ncp(struct options *o, const char *remote_ciphername) } } -bool -tls_session_update_crypto_params (struct tls_session *session, -const struct options *options,struct frame *frame) +/** +* Generate data channel keys for the supplied TLS session. +* +* This erases the source material used to generate the data channel keys, and +* can thus be called only once per session. +*/ +static bool +tls_session_generate_data_channel_keys (struct tls_session *session) {bool ret = false;struct key_state *ks = &session->key[KS_PRIMARY ];/* primary key */ +const struct session_id *client_sid = session->opt->server ? + &ks->session_id_remote : &session->session_id; +const struct session_id *server_sid = !session->opt->server ? + &ks->session_id_remote : &session->session_id;ASSERT (ks->authenticated); +if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, + &session->opt->key_type, ks->key_src, client_sid, server_sid, + session->opt->server)) + { +msg (D_TLS_ERRORS ,"TLS Error: generate_key_expansion failed" ); + goto cleanup; + } +tls_limit_reneg_bytes (session->opt->key_type.cipher, + &session->opt->renegotiate_bytes); + + ret = true; +cleanup: +CLEAR (*ks->key_src); +return ret; +} + +bool +tls_session_update_crypto_params (struct tls_session *session, +const struct options *options,struct frame *frame) +{if (!session->opt->server &&0 !=strcmp (options->ciphername, session->opt->config_ciphername) && !tls_item_in_cipher_list (options->ciphername, options->ncp_ciphers))
@@ -1793,23 +1822,7 @@ tls_session_update_crypto_params(struct tls_session *session,frame_init_mssfix (frame, options);frame_print (frame,D_MTU_INFO ,"Data Channel MTU parms" ); -const struct session_id *client_sid = session->opt->server ? - &ks->session_id_remote : &session->session_id; -const struct session_id *server_sid = !session->opt->server ? - &ks->session_id_remote : &session->session_id; -if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, - &session->opt->key_type, ks->key_src, client_sid, server_sid, - session->opt->server)) - { -msg (D_TLS_ERRORS ,"TLS Error: server generate_key_expansion failed" ); - goto cleanup; - } -tls_limit_reneg_bytes (session->opt->key_type.cipher, - &session->opt->renegotiate_bytes); - ret = true; -cleanup: -CLEAR (*ks->key_src); -return ret; +return tls_session_generate_data_channel_keys (session); } static bool
@@ -2176,21 +2189,12 @@ key_method_2_write (struct buffer *buf, struct tls_session *session) {if (ks->authenticated) { -if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, - &session->opt->key_type, - ks->key_src, - &ks->session_id_remote, - &session->session_id, - true)) +if (!tls_session_generate_data_channel_keys (session)) {msg (D_TLS_ERRORS ,"TLS Error: server generate_key_expansion failed" ); goto error; } } - -CLEAR (*ks->key_src); -tls_limit_reneg_bytes (session->opt->key_type.cipher, - &session->opt->renegotiate_bytes); }return true;
@@ -2405,20 +2409,11 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi*/ if (!session->opt->server && (!session->opt->pull || ks->key_id >0 )) { -if (!generate_key_expansion (&ks->crypto_options.key_ctx_bi, - &session->opt->key_type, - ks->key_src, - &session->session_id, - &ks->session_id_remote, - false)) +if (!tls_session_generate_data_channel_keys (session)) {msg (D_TLS_ERRORS ,"TLS Error: client generate_key_expansion failed" ); goto error; } - -CLEAR (*ks->key_src); -tls_limit_reneg_bytes (session->opt->key_type.cipher, - &session->opt->renegotiate_bytes); }gc_free (&gc);
Signature · 1 line
2.7.4
[Openvpn-devel] [PATCH applied] Re: Refactor data channel key generation API
ACK. This is slightly different from the refactoring in "v4" (as tls_session_generate_data_channel_keys() is now "static" and no longer needs to be called externally, so the actual change is much smaller). The change to ssl.c is mostly identical, though, so this was now reviewed and tested twice :-) - "just code being moved around". Passes all tests on server and client side.
Your patch has been applied to the master branch.
commit e2ffdb7c83faaee6541c248e8d83eb3dfb5a32f1
Author: Steffan Karger Date: Wed Nov 23 21:02:05 2016 +0100
Refactor data channel key generation API
Signed-off-by: Steffan Karger <[email protected]...>
Acked-by: Gert Doering <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/[email protected]/msg13216.html
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering
PATCH v6 1/2 Poor man's NCP for non-NCP peers
In reply to Steffan KargerAllows non-NCP peers (<= 2.3, or 2.4+ with --ncp-disable) to specify a --cipher that is different from the one in our config, as long as the new cipher value is allowed (i.e. in --ncp-ciphers at our side).
This works both client-to-server and server-to-client. I.e. a 2.4 client with "cipher BF-CBC" and "ncp-ciphers AES-256-GCM:AES-256-CBC" can connect to both a 2.3 server with "cipher BF-CBC" as well as a server with "cipher AES-256-CBC" in its config. The other way around, a 2.3 client with either "cipher BF-CBC" or "cipher AES-256-CBC" can connect to a 2.4 server with e.g. "cipher BF-CBC" and "ncp-ciphers AES-256-GCM:AES-256-CBC" in its config.
This patch was inspired by Gert's "Poor man's NCP for 2.3 clients" patch, but takes a different approach to avoid the need for server-side scripts or client-side 'setenv UV_*' tricks.
---
v2: remove unused argument from options_string_extract_option()
v3: don't do poor-man's NCP if NCP is disabled
v4: use strncmp instead of strstr to match prefix in
options_string_extract_option, add man page text and make clear that this feature works both client-to-server and server-to-client.
v5: move poor-mans-ncp logic from key_method_2_read() to the place
where we do regular NCP too. This makes that we no longer need access to the struct context in key_method_2_read, and is cleaner in general, and makes the patch a bit smaller.
v6: don't break p2mp-to-no-pull-client setups
doc/openvpn.8 | 14 ++++++++++++-- src/openvpn/init.c | 10 ++++++++-- src/openvpn/options.c | 32 ++++++++++++++++++++++++++++++++ src/openvpn/options.h | 14 ++++++++++++++ src/openvpn/push.c | 6 +++++- src/openvpn/ssl.c | 36 ++++++++++++++++++++++++++++++------ src/openvpn/ssl.h | 16 ++++++++++++++++ src/openvpn/ssl_common.h | 2 ++ 8 files changed, 119 insertions(+), 11 deletions(-)
doc/openvpn.8 +12 -2
@@ -4147,9 +4147,9 @@ to disable encryption. As of OpenVPN 2.4, cipher negotiation (NCP) can override the cipher specified by .B \-\-cipher\fR. See -.B \-\-ncp-ciphers +.B \-\-ncp\-ciphers and -.B \-\-ncp-disable +.B \-\-ncp\-disable for more on NCP. .\"*********************************************************
@@ -4177,6 +4177,16 @@ If both peers support and do not disable NCP, the negotiated cipher will override the cipher specified by .B \-\-cipher\fR. +Additionally, to allow for more smooth transition, if NCP is enabled, OpenVPN +will inherit the cipher of the peer if that cipher is different from the local +.B \-\-cipher +setting, but the peer cipher is one of the ciphers specified in +.B \-\-ncp\-ciphers\fR. +E.g. a non-NCP client (<=2.3, or with \-\-ncp\-disabled set) connecting to a +NCP server (2.4+) with "\-\-cipher BF-CBC" and "\-\-ncp-ciphers +AES-256-GCM:AES-256-CBC" set can either specify "\-\-cipher BF-CBC" or +"\-\-cipher AES-256-CBC" and both will work. + .\"********************************************************* .TP .B \-\-ncp\-disable
src/openvpn/init.c +8 -2
@@ -1925,8 +1925,14 @@ do_deferred_options (struct context *c, const unsigned int found) {struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE ];if (found &OPT_P_NCP ) -msg (D_PUSH ,"OPTIONS IMPORT: data channel crypto options modified" ); -/* Do not regenerate keys if server sends an extra push request */ + { +msg (D_PUSH ,"OPTIONS IMPORT: data channel crypto options modified" ); + } +else if (c->options.ncp_enabled) + { +tls_poor_mans_ncp (&c->options, c->c2.tls_multi->remote_ciphername); + } +/* Do not regenerate keys if server sends an extra push reply */ if (!session->key[KS_PRIMARY ].crypto_options.key_ctx_bi.initialized && !tls_session_update_crypto_params (session, &c->options, &c->c2.frame)) {
src/openvpn/options.c +32 -0
@@ -1643,6 +1643,8 @@ show_settings (const struct options *o)SHOW_STR (shared_secret_file);SHOW_INT (key_direction);SHOW_STR (ciphername); +SHOW_BOOL (ncp_enabled); +SHOW_STR (ncp_ciphers);SHOW_STR (authname);SHOW_STR (prng_hash);SHOW_INT (prng_nonce_secret_len);
@@ -3434,6 +3436,36 @@ options_string_version (const char* s, struct gc_arena *gc)#endif /* ENABLE_OCC */ +char * +options_string_extract_option (const char *options_string,const char *opt_name, +struct gc_arena *gc) +{ +char *ret =NULL ; +const size_t opt_name_len =strlen (opt_name); + +const char *p = options_string; +while (p) + { +if (0 ==strncmp (p, opt_name, opt_name_len) && +strlen (p) > (opt_name_len+1 ) && p[opt_name_len] ==' ' ) + { +/* option found, extract value */ +const char *start = &p[opt_name_len+1 ]; +const char *end =strchr (p,',' ); +size_t val_len = end ? end - start :strlen (start); + ret =gc_malloc (val_len+1 , true, gc); +memcpy (ret, start, val_len); +break ; + } + p =strchr (p,',' ); +if (p) + { + p++;/* skip delimiter */ + } + } +return ret; +} +static void foreign_option (struct options *o,char *argv[],int len,struct env_set *es) {
src/openvpn/options.h +14 -0
@@ -724,6 +724,20 @@ void options_warning_safe (char *actual, const char *expected, size_t actual_n);bool options_cmp_equal (char *actual,const char *expected);void options_warning (char *actual,const char *expected); +/** +* Given an OpenVPN options string, extract the value of an option. +* +* @param options_string Zero-terminated, comma-separated options string +* @param opt_name The name of the option to extract +* @param gc The gc to allocate the return value +* +* @return gc-allocated value of option with name opt_name if option was found, +* or NULL otherwise. +*/ +char *options_string_extract_option (const char *options_string, +const char *opt_name,struct gc_arena *gc); + +#endif void options_postprocess (struct options *options);
src/openvpn/push.c +5 -1
@@ -262,7 +262,7 @@ incoming_push_message (struct context *c, const struct buffer *buffer) !tls_session_update_crypto_params (session, &c->options, &c->c2.frame)) { -msg (D_TLS_ERRORS ,"TLS Error: server generate_key_expansion failed" ); +msg (D_TLS_ERRORS ,"TLS Error: initializing data channel failed" ); goto error; } }
@@ -370,6 +370,10 @@ prepare_push_reply (struct context *c, struct gc_arena *gc,push_option_fmt (gc, push_list,M_USAGE ,"cipher %s" , o->ciphername); } } +else if (o->ncp_enabled) + { +tls_poor_mans_ncp (o, tls_multi->remote_ciphername); + }/* If server uses --auth-gen-token and we have an auth token * to send to the client
src/openvpn/ssl.c +30 -6
@@ -1216,6 +1216,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)free (multi->auth_token); } +free (multi->remote_ciphername); +for (i =0 ; i <TM_SIZE ; ++i)tls_session_free (&multi->session[i], false);
@@ -1723,8 +1725,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len) { } } -static bool -item_in_list (const char *item,const char *list) +bool +tls_item_in_cipher_list (const char *item,const char *list) {char *tmp_ciphers =string_alloc (list,NULL );char *tmp_ciphers_orig = tmp_ciphers;
@@ -1741,6 +1743,20 @@ item_in_list(const char *item, const char *list)return token !=NULL ; } +void +tls_poor_mans_ncp (struct options *o,const char *remote_ciphername) +{ +if (o->ncp_enabled && remote_ciphername && +0 !=strcmp (o->ciphername, remote_ciphername)) + { +if (tls_item_in_cipher_list (remote_ciphername, o->ncp_ciphers)) + { + o->ciphername =string_alloc (remote_ciphername, &o->gc); +msg (D_TLS_DEBUG_LOW ,"Using peer cipher '%s'" , o->ciphername); + } + } +} +bool tls_session_update_crypto_params (struct tls_session *session,const struct options *options,struct frame *frame)
@@ -1752,7 +1768,7 @@ tls_session_update_crypto_params(struct tls_session *session,if (!session->opt->server &&0 !=strcmp (options->ciphername, session->opt->config_ciphername) && - !item_in_list (options->ciphername, options->ncp_ciphers)) + !tls_item_in_cipher_list (options->ciphername, options->ncp_ciphers)) { msg (D_TLS_ERRORS, "Error: pushed cipher not allowed - %s not in %s or %s", options->ciphername, session->opt->config_ciphername,
@@ -2311,13 +2327,21 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi multi->peer_info =read_string_alloc (buf);if ( multi->peer_info )output_peer_info_env (session->opt->es, multi->peer_info); +#endif + +free (multi->remote_ciphername); + multi->remote_ciphername = +options_string_extract_option (options,"cipher" ,NULL );if (tls_peer_info_ncp_ver (multi->peer_info) <2 ) { -/* Peer does not support NCP */ - session->opt->ncp_enabled = false; +/* Peer does not support NCP, but leave NCP enabled if the local and +* remote cipher do not match to attempt 'poor-man's NCP'. */ +if (0 ==strcmp (multi->remote_ciphername, multi->opt.config_ciphername)) + { + session->opt->ncp_enabled = false; + } } -#endif if (tls_session_user_pass_enabled (session)) {
src/openvpn/ssl.h +16 -0
@@ -489,6 +489,15 @@ void tls_update_remote_addr (struct tls_multi *multi,bool tls_session_update_crypto_params (struct tls_session *session,const struct options *options,struct frame *frame); +/** +* "Poor man's NCP": Use peer cipher if it is an allowed (NCP) cipher. +* Allows non-NCP peers to upgrade their cipher individually. +* +* Make sure to call tls_session_update_crypto_params() after calling this +* function. +*/ +void tls_poor_mans_ncp (struct options *o,const char *remote_ciphername); +#ifdef MANAGEMENT_DEF_AUTH static inline char *tls_get_peer_info (const struct tls_multi *multi)
@@ -512,6 +521,13 @@ int tls_peer_info_ncp_ver(const char *peer_info);*/ bool tls_check_ncp_cipher_list (const char *list); +/** +* Return true iff item is present in the colon-separated zero-terminated +* cipher list. +*/ +bool tls_item_in_cipher_list (const char *item,const char *list); + +/* * inline functions */
src/openvpn/ssl_common.h +2 -0
@@ -540,6 +540,8 @@ struct tls_multiuint32_t peer_id;bool use_peer_id; +char *remote_ciphername;/**< cipher specified in peer's config file */ +char *auth_token;/**< If server sends a generated auth-token, * this is the token to use for future * user/pass authentications in this session.
Signature · 1 line
2.7.4
[Openvpn-devel] [PATCH applied] Re: Poor man's NCP for non-NCP peers
ACK. We made it :-)
I've made three changes (as discussed on IRC) to avoid another round for "minor nuisances"
- moved prototype in options.h outside #ifdef ENABLE_OCC (warning if --disable-small)
- undo the moving of #if P2MP_SERVER in key_method_2_read() (would break --disable-server)
- add an extra "if (multi->remote_ciphername == NULL || ...) clause to the reasons for setting opt->ncp_enabled = false (remote_ciphername will be NULL if talking to a peer that has been
built without ENABLE_OCC aka --enable-small)
I've tested this on the "t_server" testbed (2.2, 2.3, master clients, plus one extra with --enable-small), client-with-patch against an older server (falling back to BF-CBC, complaining :-) ) and of course full t_client tests (though nothing in the change suspects a possible client failure in that test set).
Your patch has been applied to the master branch. Thanks!
commit 6e5ad2fa0b1e7ca2e05e0a38f47ed5561cda63b0
Author: Steffan Karger Date: Wed Nov 23 22:21:44 2016 +0100
Poor man's NCP for non-NCP peers
Signed-off-by: Steffan Karger <[email protected]...>
Acked-by: Gert Doering <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/[email protected]/msg13218.html
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering
Re: [Openvpn-devel] [PATCH applied] Re: Poor man's NCP for non-NCP peers
Hi,
Quoted 1 line · Gert Doering
ACK. We made it :-)
I *did* find a way to break it, I think - haven't tested, need to sleep - just copying what I wrote to IRC just now since everyone is asleep already...
23:30 <@cron2> syzzer: I *do* have a potential way to use this to break things 23:30 <@cron2> server with NCP enabled, --cipher foo
23:30 <@cron2> client with NCP disabled, --cipher bar
23:30 <@cron2> server will now use "cipher bar", while client will do "cipher
foo"...
23:31 <@cron2> so I think we should do a "v6a amendment" which disables this on
the client if --ncp-disable is set (so 2.4 to 2.4 will either do *real* NCP, or *no* NCP, but no half-assed two-way poorman)
23:31 <@cron2> this makes testing more annoying because you can't talk to a 2.4
server to test the client side :-) - but it's *meant* to be a 2.3<->2.4 migration feature
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...Re: [Openvpn-devel] [PATCH applied] Re: Poor man's NCP for non-NCP peers
Hi,
Quoted 11 lines · Gert Doering
Quoted 1 line · Gert Doering
ACK. We made it :-)
I *did* find a way to break it, I think - haven't tested, need to sleep - just copying what I wrote to IRC just now since everyone is asleep already...
23:30 <@cron2> syzzer: I *do* have a potential way to use this to break things 23:30 <@cron2> server with NCP enabled, --cipher foo
23:30 <@cron2> client with NCP disabled, --cipher bar
23:30 <@cron2> server will now use "cipher bar", while client will do "cipher
Quoted 7 lines
foo"...23:31 <@cron2> so I think we should do a "v6a amendment" which disables this on
the client if --ncp-disable is set (so 2.4 to 2.4 will either do *real* NCP, or *no* NCP, but no half-assed two-way poorman)
23:31 <@cron2> this makes testing more annoying because you can't talk to a 2.4
server to test the client side :-) - but it's *meant* to be a 2.3<->2.4 migration feature
This will end up with the server and client using cipher bar (if the server has --ncp-ciphers <somehting>:bar). The client-side poor-man's NCP is already guarded by "if (c->options.ncp_enabled)" (in do_deferred_options()).
-Steffan
Re: [Openvpn-devel] [PATCH applied] Re: Poor man's NCP for non-NCP peers
Hi,
Quoted 4 lines · Steffan Karger
This will end up with the server and client using cipher bar (if the server has --ncp-ciphers <somehting>:bar). The client-side poor-man's NCP is already guarded by "if (
c->options.ncp_enabled)" (indo_deferred_options()).
Good point. Seems I was not very much awake anymore :-)
/* process (potentially pushed) crypto options */
if (c->options.pull)
{
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
if (found & OPT_P_NCP)
{
msg (D_PUSH, "OPTIONS IMPORT: data channel crypto options modified");
}
else if (c->options.ncp_enabled)
{
tls_poor_mans_ncp(&c->options, c->c2.tls_multi->remote_ciphername);
}... all fine, then.
thanks,
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...