Add preliminary support for Negotiable Crypto Parameters 'level 2' (IV_NCP=2), as proposed by James Yonan on the openvpn-devel mailinglist: http://comments.gmane.org/gmane.network.openvpn.devel/9385
This patch: * Makes the server advertise "IV_NCP=2", if --push-peer-info 2 is enabled. * Pushes a 'cipher XXX' directive to the client, if the client advertises "IV_NCP=2", where XXX is the cipher set in the server config file. This enables clients that have support for IV_NCP to connect to a server, even when the client does not have the correct cipher specified in it's config file. Since pushing the cipher directive is quite similar to pushing peer-id, I moved peer-id pushing to same prepare_push_reply() function I created for pushing cipher. Adding these directives as regular push options allows us to use the existing 'push-continuation' infrastructure. Note that we should not reduce safe_cap in send_push_reply, because it was never increased to account for peer-id. This is a preliminary patch, which will be followed by more patches to add client support, and configurability. Signed-off-by: Steffan Karger <stef...@karger.me> --- src/openvpn/push.c | 97 +++++++++++++++++++++++++++++++++++++++++++++--------- src/openvpn/push.h | 2 -- src/openvpn/ssl.c | 4 +++ 3 files changed, 86 insertions(+), 17 deletions(-) diff --git a/src/openvpn/push.c b/src/openvpn/push.c index d4f3cb6..051aef6 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -40,6 +40,30 @@ #if P2MP +/** + * Add an option to the push list by providing a format string. + * + * The string added to the push options is allocated in o->gc, so fmt may be + * free'd by the caller before the option is sent to the peer. + * + * @param o The current connection's options + * @param msglevel The message level to use when printing errors + * @param fmt Format string for the option + * @param ... Format string arguments + * + * @return true on success, false on failure. + */ +static bool push_option_fmt(struct options *o, int msglevel, + const char *fmt, ...) +#ifdef __GNUC__ +#if __USE_MINGW_ANSI_STDIO + __attribute__ ((format (gnu_printf, 3, 4))) +#else + __attribute__ ((format (__printf__, 3, 4))) +#endif +#endif + ; + /* * Auth username/password * @@ -239,7 +263,47 @@ send_push_request (struct context *c) #if P2MP_SERVER -bool +/** + * Prepare push options, based on local options and available peer info. + * + * @param options Connection options + * @param tls_multi TLS state structure for the current tunnel + * + * @return true on success, false on failure. + */ +static bool +prepare_push_reply (struct options *o, struct tls_multi *tls_multi) +{ + const char *optstr = NULL; + const char * const peer_info = tls_multi->peer_info; + + /* Send peer-id if client supports it */ + optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; + if (optstr) + { + int proto = 0; + int r = sscanf(optstr, "IV_PROTO=%d", &proto); + if ((r == 1) && (proto >= 2)) + { + push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id); + } + } + + /* Push cipher if client supports Negotiable Crypto Parameters */ + optstr = peer_info ? strstr(peer_info, "IV_NCP=") : NULL; + if (optstr) + { + int ncp = 0; + int r = sscanf(optstr, "IV_NCP=%d", &ncp); + if ((r == 1) && (ncp == 2)) + { + push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername); + } + } + return true; +} + +static bool send_push_reply (struct context *c) { struct gc_arena gc = gc_new (); @@ -309,19 +373,6 @@ send_push_reply (struct context *c) if (multi_push) buf_printf (&buf, ",push-continuation 1"); - /* Send peer-id if client supports it */ - if (c->c2.tls_multi->peer_info) - { - const char* proto_str = strstr(c->c2.tls_multi->peer_info, "IV_PROTO="); - if (proto_str) - { - int proto = 0; - int r = sscanf(proto_str, "IV_PROTO=%d", &proto); - if ((r == 1) && (proto >= 2)) - buf_printf(&buf, ",peer-id %d", c->c2.tls_multi->peer_id); - } - } - if (BLEN (&buf) > sizeof(cmd)-1) { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); @@ -409,6 +460,21 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc) push_option (o, opt, msglevel); } +static bool push_option_fmt(struct options *o, int msglevel, + const char *format, ...) +{ + va_list arglist; + char tmp[256] = {0}; + int len = -1; + va_start (arglist, format); + len = vsnprintf (tmp, sizeof(tmp), format, arglist); + va_end (arglist); + if (len > sizeof(tmp)-1) + return false; + push_option (o, string_alloc (tmp, &o->gc), msglevel); + return true; +} + void push_reset (struct options *o) { @@ -442,7 +508,8 @@ process_incoming_push_request (struct context *c) } else { - if (send_push_reply (c)) + if (prepare_push_reply(&c->options, c->c2.tls_multi) && + send_push_reply (c)) { ret = PUSH_MSG_REQUEST; c->c2.sent_push_reply_expiry = now + 30; diff --git a/src/openvpn/push.h b/src/openvpn/push.h index fa06e08..19bbf5e 100644 --- a/src/openvpn/push.h +++ b/src/openvpn/push.h @@ -62,8 +62,6 @@ void push_options (struct options *o, char **p, int msglevel, struct gc_arena *g void push_reset (struct options *o); -bool send_push_reply (struct context *c); - void remove_iroutes_from_push_route_list (struct options *o); void send_auth_failed (struct context *c, const char *client_reason); diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 7156030..1d811b1 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -1883,6 +1883,10 @@ push_peer_info(struct buffer *buf, struct tls_session *session) /* support for P_DATA_V2 */ buf_printf(&out, "IV_PROTO=2\n"); + /* support for Negotiable Crypto Paramters */ + if (session->opt->server) + buf_printf(&out, "IV_NCP=2\n"); + /* push compression status */ #ifdef USE_COMP comp_generate_peer_info_string(&session->opt->comp_options, &out); -- 2.5.0