v4: - fix whitespaces, wrap long lines v3: - rebase on master
v2: - Also move ifconfig and ipv6-ifconfig to separate options list Move client-specific push options (currently peer-id and cipher) to separate list, which is deallocated after push_reply has been send. This makes sure that options fit into buf, not duplicated nor leak memory on renegotiation. Signed-off-by: Lev Stipakov <lstipa...@gmail.com> Fix whitespaces and wrap long lines --- src/openvpn/push.c | 206 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 118 insertions(+), 88 deletions(-) diff --git a/src/openvpn/push.c b/src/openvpn/push.c index df4f596..145fb7d 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -40,26 +40,29 @@ #if P2MP +static char push_reply_cmd[] = "PUSH_REPLY"; + /** - * Add an option to the push list by providing a format string. + * Add an option to the given push list by providing a format string. * * The string added to the push options is allocated in o->gc, so the caller * does not have to preserve anything. * - * @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 + * @param gc GC arena where options are allocated + * @param push_list Push list containing 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, ...) +static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, + int msglevel, const char *fmt, ...) #ifdef __GNUC__ #if __USE_MINGW_ANSI_STDIO - __attribute__ ((format (gnu_printf, 3, 4))) + __attribute__ ((format (gnu_printf, 4, 5))) #else - __attribute__ ((format (__printf__, 3, 4))) + __attribute__ ((format (__printf__, 4, 5))) #endif #endif ; @@ -295,16 +298,43 @@ send_push_request (struct context *c) /** * 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 + * @param context context structure storing data for VPN tunnel + * @param gc gc arena for allocating push options + * @param push_list push list to where options are added * * @return true on success, false on failure. */ static bool -prepare_push_reply (struct options *o, struct tls_multi *tls_multi) +prepare_push_reply (struct context *c, struct gc_arena *gc, + struct push_list *push_list) { const char *optstr = NULL; + const struct tls_multi *tls_multi = c->c2.tls_multi; const char * const peer_info = tls_multi->peer_info; + struct options *o = &c->options; + + /* ipv6 */ + if (c->c2.push_ifconfig_ipv6_defined && !o->push_ifconfig_ipv6_blocked) + { + push_option_fmt (gc, push_list, M_USAGE, "ifconfig-ipv6 %s/%d %s", + print_in6_addr (c->c2.push_ifconfig_ipv6_local, 0, gc), + c->c2.push_ifconfig_ipv6_netbits, + print_in6_addr (c->c2.push_ifconfig_ipv6_remote, + 0, gc)); + } + + /* ipv4 */ + if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && + c->c2.push_ifconfig_remote_netmask) + { + in_addr_t ifconfig_local = c->c2.push_ifconfig_local; + if (c->c2.push_ifconfig_local_alias) + ifconfig_local = c->c2.push_ifconfig_local_alias; + push_option_fmt (gc, push_list, M_USAGE, "ifconfig %s %s", + print_in_addr_t (ifconfig_local, 0, gc), + print_in_addr_t (c->c2.push_ifconfig_remote_netmask, + 0, gc)); + } /* Send peer-id if client supports it */ optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; @@ -314,8 +344,8 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi) int r = sscanf(optstr, "IV_PROTO=%d", &proto); if ((r == 1) && (proto >= 2)) { - push_remove_option(o, "peer-id"); - push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id); + push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", + tls_multi->peer_id); } } @@ -325,7 +355,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi) /* if we have already created our key, we cannot change our own * cipher, so disable NCP and warn = explain why */ - struct tls_session *session = &tls_multi->session[TM_ACTIVE]; + const struct tls_session *session = &tls_multi->session[TM_ACTIVE]; if ( session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized ) { msg( M_INFO, "PUSH: client wants to negotiate cipher (NCP), but " @@ -336,87 +366,79 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi) { /* Push the first cipher from --ncp-ciphers to the client. * TODO: actual negotiation, instead of server dictatorship. */ - char *push_cipher = string_alloc(o->ncp_ciphers, &o->gc); + char *push_cipher = string_alloc(o->ncp_ciphers, gc); o->ciphername = strtok (push_cipher, ":"); - push_remove_option(o, "cipher"); - push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername); + push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername); } } return true; } static bool -send_push_reply (struct context *c) +send_push_options (struct context *c, struct buffer *buf, + struct push_list *push_list, int safe_cap, + bool *push_sent, bool *multi_push) { - struct gc_arena gc = gc_new (); - struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); - struct push_entry *e = c->options.push_list.head; - bool multi_push = false; - static char cmd[] = "PUSH_REPLY"; - const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ - const int safe_cap = BCAP (&buf) - extra; - bool push_sent = false; - - msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap ); - - buf_printf (&buf, "%s", cmd); - - if ( c->c2.push_ifconfig_ipv6_defined && - !c->options.push_ifconfig_ipv6_blocked ) - { - /* IPv6 is put into buffer first, could be lengthy */ - buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s", - print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc), - c->c2.push_ifconfig_ipv6_netbits, - print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) ); - if (BLEN (&buf) >= safe_cap) - { - msg (M_WARN, "--push ifconfig-ipv6 option is too long"); - goto fail; - } - } + struct push_entry *e = push_list->head; while (e) { if (e->enable) { const int l = strlen (e->option); - if (BLEN (&buf) + l >= safe_cap) + if (BLEN (buf) + l >= safe_cap) { - buf_printf (&buf, ",push-continuation 2"); - { - const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); - if (!status) - goto fail; - push_sent = true; - multi_push = true; - buf_reset_len (&buf); - buf_printf (&buf, "%s", cmd); - } + buf_printf (buf, ",push-continuation 2"); + { + const bool status = send_control_channel_string (c, BSTR (buf), D_PUSH); + if (!status) + return false; + *push_sent = true; + *multi_push = true; + buf_reset_len (buf); + buf_printf (buf, "%s", push_reply_cmd); + } } - if (BLEN (&buf) + l >= safe_cap) + if (BLEN (buf) + l >= safe_cap) { msg (M_WARN, "--push option is too long"); - goto fail; + return false; } - buf_printf (&buf, ",%s", e->option); + buf_printf (buf, ",%s", e->option); } e = e->next; } + return true; +} + +static bool +send_push_reply (struct context *c, struct push_list *per_client_push_list) +{ + struct gc_arena gc = gc_new (); + struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); + bool multi_push = false; + const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ + const int safe_cap = BCAP (&buf) - extra; + bool push_sent = false; + + msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap ); + + buf_printf (&buf, "%s", push_reply_cmd); + + /* send options which are common to all clients */ + if (!send_push_options (c, &buf, &c->options.push_list, safe_cap, + &push_sent, &multi_push)) + goto fail; + + /* send client-specific options */ + if (!send_push_options (c, &buf, per_client_push_list, safe_cap, + &push_sent, &multi_push)) + goto fail; - if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask) - { - in_addr_t ifconfig_local = c->c2.push_ifconfig_local; - if (c->c2.push_ifconfig_local_alias) - ifconfig_local = c->c2.push_ifconfig_local_alias; - buf_printf (&buf, ",ifconfig %s %s", - print_in_addr_t (ifconfig_local, 0, &gc), - print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc)); - } if (multi_push) buf_printf (&buf, ",push-continuation 1"); - if (BLEN (&buf) > sizeof(cmd)-1) + if (BLEN (&buf) > sizeof(push_reply_cmd)-1) { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) @@ -432,7 +454,7 @@ send_push_reply (struct context *c) bool status = false; buf_reset_len (&buf); - buf_printf (&buf, "%s", cmd); + buf_printf (&buf, "%s", push_reply_cmd); status = send_control_channel_string (c, BSTR(&buf), D_PUSH); if (!status) goto fail; @@ -447,7 +469,8 @@ send_push_reply (struct context *c) } static void -push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) +push_option_ex (struct gc_arena *gc, struct push_list *push_list, + const char *opt, bool enable, int msglevel) { if (!string_class (opt, CC_ANY, CC_COMMA)) { @@ -456,20 +479,20 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) else { struct push_entry *e; - ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc); + ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc); e->enable = true; e->option = opt; - if (o->push_list.head) + if (push_list->head) { - ASSERT(o->push_list.tail); - o->push_list.tail->next = e; - o->push_list.tail = e; + ASSERT(push_list->tail); + push_list->tail->next = e; + push_list->tail = e; } else { - ASSERT(!o->push_list.tail); - o->push_list.head = e; - o->push_list.tail = e; + ASSERT(!push_list->tail); + push_list->head = e; + push_list->tail = e; } } } @@ -477,7 +500,7 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel) void push_option (struct options *o, const char *opt, int msglevel) { - push_option_ex (o, opt, true, msglevel); + push_option_ex (&o->gc, &o->push_list, opt, true, msglevel); } void @@ -489,7 +512,8 @@ clone_push_list (struct options *o) push_reset (o); while (e) { - push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL); + push_option_ex (&o->gc, &o->push_list, + string_alloc (e->option, &o->gc), true, M_FATAL); e = e->next; } } @@ -503,18 +527,18 @@ 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, ...) +static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, + int msglevel, const char *format, ...) { va_list arglist; char tmp[256] = {0}; - int len = -1; + int len; 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); + push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel); return true; } @@ -582,12 +606,18 @@ process_incoming_push_request (struct context *c) } else { - if (prepare_push_reply(&c->options, c->c2.tls_multi) && - send_push_reply (c)) + /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */ + struct push_list push_list; + struct gc_arena gc = gc_new (); + + CLEAR (push_list); + if (prepare_push_reply (c, &gc, &push_list) && + send_push_reply (c, &push_list)) { ret = PUSH_MSG_REQUEST; c->c2.sent_push_reply_expiry = now + 30; } + gc_free(&gc); } } else -- 1.9.1 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, SlashDot.org! http://sdm.link/slashdot _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel