summaryrefslogtreecommitdiff
path: root/src/openvpn/push.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/push.c')
-rw-r--r--src/openvpn/push.c1148
1 files changed, 597 insertions, 551 deletions
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index 9953079..f515475 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -49,50 +49,55 @@ static char push_reply_cmd[] = "PUSH_REPLY";
* Runs on client.
*/
void
-receive_auth_failed (struct context *c, const struct buffer *buffer)
+receive_auth_failed(struct context *c, const struct buffer *buffer)
{
- msg (M_VERB0, "AUTH: Received control message: %s", BSTR(buffer));
- c->options.no_advance=true;
-
- if (c->options.pull)
- {
- switch (auth_retry_get ())
- {
- case AR_NONE:
- c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */
- break;
- case AR_INTERACT:
- ssl_purge_auth (false);
- case AR_NOINTERACT:
- c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */
- break;
- default:
- ASSERT (0);
- }
- c->sig->signal_text = "auth-failure";
+ msg(M_VERB0, "AUTH: Received control message: %s", BSTR(buffer));
+ c->options.no_advance = true;
+
+ if (c->options.pull)
+ {
+ switch (auth_retry_get())
+ {
+ case AR_NONE:
+ c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */
+ break;
+
+ case AR_INTERACT:
+ ssl_purge_auth(false);
+
+ case AR_NOINTERACT:
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ c->sig->signal_text = "auth-failure";
#ifdef ENABLE_MANAGEMENT
- if (management)
- {
- const char *reason = NULL;
- struct buffer buf = *buffer;
- if (buf_string_compare_advance (&buf, "AUTH_FAILED,") && BLEN (&buf))
- reason = BSTR (&buf);
- management_auth_failure (management, UP_TYPE_AUTH, reason);
- }
+ if (management)
+ {
+ const char *reason = NULL;
+ struct buffer buf = *buffer;
+ if (buf_string_compare_advance(&buf, "AUTH_FAILED,") && BLEN(&buf))
+ {
+ reason = BSTR(&buf);
+ }
+ management_auth_failure(management, UP_TYPE_AUTH, reason);
+ }
#endif
- /*
- * Save the dynamic-challenge text even when management is defined
- */
- {
+ /*
+ * Save the dynamic-challenge text even when management is defined
+ */
+ {
#ifdef ENABLE_CLIENT_CR
- struct buffer buf = *buffer;
- if (buf_string_match_head_str (&buf, "AUTH_FAILED,CRV1:") && BLEN (&buf))
- {
- buf_advance (&buf, 12); /* Length of "AUTH_FAILED," substring */
- ssl_put_auth_challenge (BSTR (&buf));
- }
+ struct buffer buf = *buffer;
+ if (buf_string_match_head_str(&buf, "AUTH_FAILED,CRV1:") && BLEN(&buf))
+ {
+ buf_advance(&buf, 12); /* Length of "AUTH_FAILED," substring */
+ ssl_put_auth_challenge(BSTR(&buf));
+ }
#endif
- }
+ }
}
}
@@ -100,53 +105,61 @@ receive_auth_failed (struct context *c, const struct buffer *buffer)
* Act on received restart message from server
*/
void
-server_pushed_signal (struct context *c, const struct buffer *buffer, const bool restart, const int adv)
+server_pushed_signal(struct context *c, const struct buffer *buffer, const bool restart, const int adv)
{
- if (c->options.pull)
- {
- struct buffer buf = *buffer;
- const char *m = "";
- if (buf_advance (&buf, adv) && buf_read_u8 (&buf) == ',' && BLEN (&buf))
- m = BSTR (&buf);
-
- /* preserve cached passwords? */
- /* advance to next server? */
- {
- bool purge = true;
-
- if (m[0] == '[')
- {
- int i;
- for (i = 1; m[i] != '\0' && m[i] != ']'; ++i)
- {
- if (m[i] == 'P')
- purge = false;
- else if (m[i] == 'N')
- {
- /* next server? */
- c->options.no_advance = false;
- }
- }
- }
- if (purge)
- ssl_purge_auth (true);
- }
-
- if (restart)
- {
- msg (D_STREAM_ERRORS, "Connection reset command was pushed by server ('%s')", m);
- c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
- c->sig->signal_text = "server-pushed-connection-reset";
- }
- else
- {
- msg (D_STREAM_ERRORS, "Halt command was pushed by server ('%s')", m);
- c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- server-pushed halt */
- c->sig->signal_text = "server-pushed-halt";
- }
+ if (c->options.pull)
+ {
+ struct buffer buf = *buffer;
+ const char *m = "";
+ if (buf_advance(&buf, adv) && buf_read_u8(&buf) == ',' && BLEN(&buf))
+ {
+ m = BSTR(&buf);
+ }
+
+ /* preserve cached passwords? */
+ /* advance to next server? */
+ {
+ bool purge = true;
+
+ if (m[0] == '[')
+ {
+ int i;
+ for (i = 1; m[i] != '\0' && m[i] != ']'; ++i)
+ {
+ if (m[i] == 'P')
+ {
+ purge = false;
+ }
+ else if (m[i] == 'N')
+ {
+ /* next server? */
+ c->options.no_advance = false;
+ }
+ }
+ }
+ if (purge)
+ {
+ ssl_purge_auth(true);
+ }
+ }
+
+ if (restart)
+ {
+ msg(D_STREAM_ERRORS, "Connection reset command was pushed by server ('%s')", m);
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
+ c->sig->signal_text = "server-pushed-connection-reset";
+ }
+ else
+ {
+ msg(D_STREAM_ERRORS, "Halt command was pushed by server ('%s')", m);
+ c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- server-pushed halt */
+ c->sig->signal_text = "server-pushed-halt";
+ }
#ifdef ENABLE_MANAGEMENT
- if (management)
- management_notify (management, "info", c->sig->signal_text, m);
+ if (management)
+ {
+ management_notify(management, "info", c->sig->signal_text, m);
+ }
#endif
}
}
@@ -167,128 +180,134 @@ server_pushed_signal (struct context *c, const struct buffer *buffer, const bool
* @return true on success, false on failure.
*/
static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
- int msglevel, const char *fmt, ...)
+ int msglevel, const char *fmt, ...)
#ifdef __GNUC__
#if __USE_MINGW_ANSI_STDIO
- __attribute__ ((format (gnu_printf, 4, 5)))
+__attribute__ ((format(gnu_printf, 4, 5)))
#else
- __attribute__ ((format (__printf__, 4, 5)))
+__attribute__ ((format(__printf__, 4, 5)))
#endif
#endif
- ;
+;
/*
* Send auth failed message from server to client.
*/
void
-send_auth_failed (struct context *c, const char *client_reason)
+send_auth_failed(struct context *c, const char *client_reason)
{
- struct gc_arena gc = gc_new ();
- static const char auth_failed[] = "AUTH_FAILED";
- size_t len;
+ struct gc_arena gc = gc_new();
+ static const char auth_failed[] = "AUTH_FAILED";
+ size_t len;
- schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
+ schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
- len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
- if (len > PUSH_BUNDLE_SIZE)
- len = PUSH_BUNDLE_SIZE;
+ len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
+ if (len > PUSH_BUNDLE_SIZE)
+ {
+ len = PUSH_BUNDLE_SIZE;
+ }
- {
- struct buffer buf = alloc_buf_gc (len, &gc);
- buf_printf (&buf, auth_failed);
- if (client_reason)
- buf_printf (&buf, ",%s", client_reason);
- send_control_channel_string (c, BSTR (&buf), D_PUSH);
- }
+ {
+ struct buffer buf = alloc_buf_gc(len, &gc);
+ buf_printf(&buf, auth_failed);
+ if (client_reason)
+ {
+ buf_printf(&buf, ",%s", client_reason);
+ }
+ send_control_channel_string(c, BSTR(&buf), D_PUSH);
+ }
- gc_free (&gc);
+ gc_free(&gc);
}
/*
* Send restart message from server to client.
*/
void
-send_restart (struct context *c, const char *kill_msg)
+send_restart(struct context *c, const char *kill_msg)
{
- schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
- send_control_channel_string (c, kill_msg ? kill_msg : "RESTART", D_PUSH);
+ schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
+ send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH);
}
-#endif
+#endif /* if P2MP_SERVER */
/*
* Push/Pull
*/
void
-incoming_push_message (struct context *c, const struct buffer *buffer)
+incoming_push_message(struct context *c, const struct buffer *buffer)
{
- struct gc_arena gc = gc_new ();
- unsigned int option_types_found = 0;
- int status;
-
- msg (D_PUSH, "PUSH: Received control message: '%s'", sanitize_control_message(BSTR(buffer), &gc));
-
- status = process_incoming_push_msg (c,
- buffer,
- c->options.pull,
- pull_permission_mask (c),
- &option_types_found);
-
- if (status == PUSH_MSG_ERROR)
- msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", sanitize_control_message(BSTR(buffer), &gc));
- else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION)
- {
- c->options.push_option_types_found |= option_types_found;
-
- /* delay bringing tun/tap up until --push parms received from remote */
- if (status == PUSH_MSG_REPLY)
- {
- if (!do_up (c, true, c->options.push_option_types_found))
- {
- msg (D_PUSH_ERRORS, "Failed to open tun/tap interface");
- goto error;
- }
- }
- event_timeout_clear (&c->c2.push_request_interval);
- }
- else if (status == PUSH_MSG_REQUEST)
- {
- if (c->options.mode == MODE_SERVER)
- {
- 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))
- {
- msg (D_TLS_ERRORS, "TLS Error: initializing data channel failed");
- goto error;
- }
- }
- }
-
- goto cleanup;
+ struct gc_arena gc = gc_new();
+ unsigned int option_types_found = 0;
+ int status;
+
+ msg(D_PUSH, "PUSH: Received control message: '%s'", sanitize_control_message(BSTR(buffer), &gc));
+
+ status = process_incoming_push_msg(c,
+ buffer,
+ c->options.pull,
+ pull_permission_mask(c),
+ &option_types_found);
+
+ if (status == PUSH_MSG_ERROR)
+ {
+ msg(D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", sanitize_control_message(BSTR(buffer), &gc));
+ }
+ else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION)
+ {
+ c->options.push_option_types_found |= option_types_found;
+
+ /* delay bringing tun/tap up until --push parms received from remote */
+ if (status == PUSH_MSG_REPLY)
+ {
+ if (!do_up(c, true, c->options.push_option_types_found))
+ {
+ msg(D_PUSH_ERRORS, "Failed to open tun/tap interface");
+ goto error;
+ }
+ }
+ event_timeout_clear(&c->c2.push_request_interval);
+ }
+ else if (status == PUSH_MSG_REQUEST)
+ {
+ if (c->options.mode == MODE_SERVER)
+ {
+ 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))
+ {
+ msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed");
+ goto error;
+ }
+ }
+ }
+
+ goto cleanup;
error:
- register_signal (c, SIGUSR1, "process-push-msg-failed");
+ register_signal(c, SIGUSR1, "process-push-msg-failed");
cleanup:
- gc_free (&gc);
+ gc_free(&gc);
}
bool
-send_push_request (struct context *c)
+send_push_request(struct context *c)
{
- const int max_push_requests = c->options.handshake_window / PUSH_REQUEST_INTERVAL;
- if (++c->c2.n_sent_push_requests <= max_push_requests)
+ const int max_push_requests = c->options.handshake_window / PUSH_REQUEST_INTERVAL;
+ if (++c->c2.n_sent_push_requests <= max_push_requests)
{
- return send_control_channel_string (c, "PUSH_REQUEST", D_PUSH);
+ return send_control_channel_string(c, "PUSH_REQUEST", D_PUSH);
}
- else
+ else
{
- msg (D_STREAM_ERRORS, "No reply from server after sending %d push requests", max_push_requests);
- c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
- c->sig->signal_text = "no-push-reply";
- return false;
+ msg(D_STREAM_ERRORS, "No reply from server after sending %d push requests", max_push_requests);
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
+ c->sig->signal_text = "no-push-reply";
+ return false;
}
}
@@ -297,426 +316,451 @@ send_push_request (struct context *c)
/**
* Prepare push options, based on local options and available peer info.
*
- * @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
+ * @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 context *c, struct gc_arena *gc,
- struct push_list *push_list)
+prepare_push_reply(struct context *c, struct gc_arena *gc,
+ struct push_list *push_list)
{
- const char *optstr = NULL;
- 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;
- if (optstr)
- {
- int proto = 0;
- int r = sscanf(optstr, "IV_PROTO=%d", &proto);
- if ((r == 1) && (proto >= 2))
- {
- push_option_fmt(gc, push_list, M_USAGE, "peer-id %d",
- tls_multi->peer_id);
- }
- }
-
- /* Push cipher if client supports Negotiable Crypto Parameters */
- if (tls_peer_info_ncp_ver (peer_info) >= 2 && o->ncp_enabled)
- {
- /* if we have already created our key, we cannot change our own
- * cipher, so disable NCP and warn = explain why
- */
- 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 "
- "server has already generated data channel keys, "
- "ignoring client request" );
- }
- else
- {
- /* 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);
- o->ciphername = strtok (push_cipher, ":");
- 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
- */
- if (false == tls_multi->auth_token_sent && NULL != tls_multi->auth_token)
- {
- push_option_fmt(gc, push_list, M_USAGE,
- "auth-token %s", tls_multi->auth_token);
- tls_multi->auth_token_sent = true;
- }
- return true;
+ const char *optstr = NULL;
+ 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;
+ if (optstr)
+ {
+ int proto = 0;
+ int r = sscanf(optstr, "IV_PROTO=%d", &proto);
+ if ((r == 1) && (proto >= 2))
+ {
+ push_option_fmt(gc, push_list, M_USAGE, "peer-id %d",
+ tls_multi->peer_id);
+ }
+ }
+
+ /* Push cipher if client supports Negotiable Crypto Parameters */
+ if (tls_peer_info_ncp_ver(peer_info) >= 2 && o->ncp_enabled)
+ {
+ /* if we have already created our key, we cannot change our own
+ * cipher, so disable NCP and warn = explain why
+ */
+ 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 "
+ "server has already generated data channel keys, "
+ "ignoring client request" );
+ }
+ else
+ {
+ /* 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);
+ o->ciphername = strtok(push_cipher, ":");
+ 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
+ */
+ if (false == tls_multi->auth_token_sent && NULL != tls_multi->auth_token)
+ {
+ push_option_fmt(gc, push_list, M_USAGE,
+ "auth-token %s", tls_multi->auth_token);
+ tls_multi->auth_token_sent = true;
+ }
+ return true;
}
static bool
-send_push_options (struct context *c, struct buffer *buf,
- struct push_list *push_list, int safe_cap,
- bool *push_sent, bool *multi_push)
+send_push_options(struct context *c, struct buffer *buf,
+ struct push_list *push_list, int safe_cap,
+ bool *push_sent, bool *multi_push)
{
- struct push_entry *e = push_list->head;
-
- while (e)
- {
- if (e->enable)
- {
- const int l = strlen (e->option);
- 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)
- 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)
- {
- msg (M_WARN, "--push option is too long");
- return false;
- }
- buf_printf (buf, ",%s", e->option);
- }
- e = e->next;
- }
- return true;
+ struct push_entry *e = push_list->head;
+
+ while (e)
+ {
+ if (e->enable)
+ {
+ const int l = strlen(e->option);
+ 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)
+ {
+ 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)
+ {
+ msg(M_WARN, "--push option is too long");
+ return false;
+ }
+ 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)
+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;
-
- 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 (multi_push)
- buf_printf (&buf, ",push-continuation 1");
+ 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;
+
+ 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;
+ }
- if (BLEN (&buf) > sizeof(push_reply_cmd)-1)
+ /* send client-specific options */
+ if (!send_push_options(c, &buf, per_client_push_list, safe_cap,
+ &push_sent, &multi_push))
{
- const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
- if (!status)
goto fail;
- push_sent = true;
}
- /* If nothing have been pushed, send an empty push,
- * as the client is expecting a response
- */
- if (!push_sent)
+ if (multi_push)
{
- bool status = false;
+ buf_printf(&buf, ",push-continuation 1");
+ }
- buf_reset_len (&buf);
- buf_printf (&buf, "%s", push_reply_cmd);
- status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
- if (!status)
- goto fail;
+ if (BLEN(&buf) > sizeof(push_reply_cmd)-1)
+ {
+ const bool status = send_control_channel_string(c, BSTR(&buf), D_PUSH);
+ if (!status)
+ {
+ goto fail;
+ }
+ push_sent = true;
}
- gc_free (&gc);
- return true;
+ /* If nothing have been pushed, send an empty push,
+ * as the client is expecting a response
+ */
+ if (!push_sent)
+ {
+ bool status = false;
+
+ buf_reset_len(&buf);
+ buf_printf(&buf, "%s", push_reply_cmd);
+ status = send_control_channel_string(c, BSTR(&buf), D_PUSH);
+ if (!status)
+ {
+ goto fail;
+ }
+ }
- fail:
- gc_free (&gc);
- return false;
+ gc_free(&gc);
+ return true;
+
+fail:
+ gc_free(&gc);
+ return false;
}
static void
-push_option_ex (struct gc_arena *gc, struct push_list *push_list,
- 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))
- {
- msg (msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt);
- }
- else
- {
- struct push_entry *e;
- ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc);
- e->enable = true;
- e->option = opt;
- if (push_list->head)
- {
- ASSERT(push_list->tail);
- push_list->tail->next = e;
- push_list->tail = e;
- }
- else
- {
- ASSERT(!push_list->tail);
- push_list->head = e;
- push_list->tail = e;
- }
+ if (!string_class(opt, CC_ANY, CC_COMMA))
+ {
+ msg(msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt);
+ }
+ else
+ {
+ struct push_entry *e;
+ ALLOC_OBJ_CLEAR_GC(e, struct push_entry, gc);
+ e->enable = true;
+ e->option = opt;
+ if (push_list->head)
+ {
+ ASSERT(push_list->tail);
+ push_list->tail->next = e;
+ push_list->tail = e;
+ }
+ else
+ {
+ ASSERT(!push_list->tail);
+ push_list->head = e;
+ push_list->tail = e;
+ }
}
}
void
-push_option (struct options *o, const char *opt, int msglevel)
+push_option(struct options *o, const char *opt, int msglevel)
{
- push_option_ex (&o->gc, &o->push_list, opt, true, msglevel);
+ push_option_ex(&o->gc, &o->push_list, opt, true, msglevel);
}
void
-clone_push_list (struct options *o)
+clone_push_list(struct options *o)
{
- if (o->push_list.head)
+ if (o->push_list.head)
{
- const struct push_entry *e = o->push_list.head;
- push_reset (o);
- while (e)
- {
- push_option_ex (&o->gc, &o->push_list,
- string_alloc (e->option, &o->gc), true, M_FATAL);
- e = e->next;
- }
+ const struct push_entry *e = o->push_list.head;
+ push_reset(o);
+ while (e)
+ {
+ push_option_ex(&o->gc, &o->push_list,
+ string_alloc(e->option, &o->gc), true, M_FATAL);
+ e = e->next;
+ }
}
}
void
-push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
+push_options(struct options *o, char **p, int msglevel, struct gc_arena *gc)
{
- const char **argv = make_extended_arg_array (p, gc);
- char *opt = print_argv (argv, gc, 0);
- push_option (o, opt, msglevel);
+ const char **argv = make_extended_arg_array(p, gc);
+ char *opt = print_argv(argv, gc, 0);
+ push_option(o, opt, msglevel);
}
-static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
- 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;
- va_start (arglist, format);
- len = vsnprintf (tmp, sizeof(tmp), format, arglist);
- va_end (arglist);
- if (len > sizeof(tmp)-1)
- return false;
- push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel);
- return true;
+ va_list arglist;
+ char tmp[256] = {0};
+ 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_ex(gc, push_list, string_alloc(tmp, gc), true, msglevel);
+ return true;
}
void
-push_reset (struct options *o)
+push_reset(struct options *o)
{
- CLEAR (o->push_list);
+ CLEAR(o->push_list);
}
void
-push_remove_option (struct options *o, const char *p)
+push_remove_option(struct options *o, const char *p)
{
- msg (D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p);
+ msg(D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p);
- /* ifconfig-ipv6 is special, as not part of the push list */
- if ( streq( p, "ifconfig-ipv6" ))
+ /* ifconfig-ipv6 is special, as not part of the push list */
+ if (streq( p, "ifconfig-ipv6" ))
{
- o->push_ifconfig_ipv6_blocked = true;
- return;
+ o->push_ifconfig_ipv6_blocked = true;
+ return;
}
- if (o && o->push_list.head )
+ if (o && o->push_list.head)
{
- struct push_entry *e = o->push_list.head;
-
- /* cycle through the push list */
- while (e)
- {
- if ( e->enable &&
- strncmp( e->option, p, strlen(p) ) == 0 )
- {
- msg (D_PUSH_DEBUG, "PUSH_REMOVE removing: '%s'", e->option);
- e->enable = false;
- }
-
- e = e->next;
- }
+ struct push_entry *e = o->push_list.head;
+
+ /* cycle through the push list */
+ while (e)
+ {
+ if (e->enable
+ && strncmp( e->option, p, strlen(p) ) == 0)
+ {
+ msg(D_PUSH_DEBUG, "PUSH_REMOVE removing: '%s'", e->option);
+ e->enable = false;
+ }
+
+ e = e->next;
+ }
}
}
-#endif
+#endif /* if P2MP_SERVER */
#if P2MP_SERVER
int
-process_incoming_push_request (struct context *c)
+process_incoming_push_request(struct context *c)
{
- int ret = PUSH_MSG_ERROR;
+ int ret = PUSH_MSG_ERROR;
#ifdef ENABLE_ASYNC_PUSH
- c->c2.push_request_received = true;
+ c->c2.push_request_received = true;
#endif
- if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
+ if (tls_authentication_status(c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
{
- const char *client_reason = tls_client_reason (c->c2.tls_multi);
- send_auth_failed (c, client_reason);
- ret = PUSH_MSG_AUTH_FAILURE;
+ const char *client_reason = tls_client_reason(c->c2.tls_multi);
+ send_auth_failed(c, client_reason);
+ ret = PUSH_MSG_AUTH_FAILURE;
}
- else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
+ else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
{
- time_t now;
-
- openvpn_time (&now);
- if (c->c2.sent_push_reply_expiry > now)
- {
- ret = PUSH_MSG_ALREADY_REPLIED;
- }
- else
- {
- /* 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);
- }
+ time_t now;
+
+ openvpn_time(&now);
+ if (c->c2.sent_push_reply_expiry > now)
+ {
+ ret = PUSH_MSG_ALREADY_REPLIED;
+ }
+ else
+ {
+ /* 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
+ else
{
- ret = PUSH_MSG_REQUEST_DEFERRED;
+ ret = PUSH_MSG_REQUEST_DEFERRED;
}
- return ret;
+ return ret;
}
-#endif
+#endif /* if P2MP_SERVER */
static void
-push_update_digest(md_ctx_t *ctx, struct buffer *buf)
+push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt)
{
- char line[OPTION_PARM_SIZE];
- while (buf_parse (buf, ',', line, sizeof (line)))
+ char line[OPTION_PARM_SIZE];
+ while (buf_parse(buf, ',', line, sizeof(line)))
{
- /* peer-id might change on restart and this should not trigger reopening tun */
- if (strstr (line, "peer-id ") != line)
- {
- md_ctx_update (ctx, (const uint8_t *) line, strlen(line));
- }
+ /* peer-id might change on restart and this should not trigger reopening tun */
+ if (strprefix(line, "peer-id "))
+ {
+ continue;
+ }
+ /* tun reopen only needed if cipher change can change tun MTU */
+ if (strprefix(line, "cipher ") && !opt->ce.tun_mtu_defined)
+ {
+ continue;
+ }
}
+ md_ctx_update(ctx, (const uint8_t *) line, strlen(line)+1);
}
int
-process_incoming_push_msg (struct context *c,
- const struct buffer *buffer,
- bool honor_received_options,
- unsigned int permission_mask,
- unsigned int *option_types_found)
+process_incoming_push_msg(struct context *c,
+ const struct buffer *buffer,
+ bool honor_received_options,
+ unsigned int permission_mask,
+ unsigned int *option_types_found)
{
- int ret = PUSH_MSG_ERROR;
- struct buffer buf = *buffer;
+ int ret = PUSH_MSG_ERROR;
+ struct buffer buf = *buffer;
#if P2MP_SERVER
- if (buf_string_compare_advance (&buf, "PUSH_REQUEST"))
+ if (buf_string_compare_advance(&buf, "PUSH_REQUEST"))
{
- ret = process_incoming_push_request(c);
+ ret = process_incoming_push_request(c);
}
- else
+ else
#endif
- if (honor_received_options && buf_string_compare_advance (&buf, "PUSH_REPLY"))
- {
- const uint8_t ch = buf_read_u8 (&buf);
- if (ch == ',')
- {
- struct buffer buf_orig = buf;
- if (!c->c2.pulled_options_md5_init_done)
- {
- md_ctx_init(&c->c2.pulled_options_state, md_kt_get("MD5"));
- c->c2.pulled_options_md5_init_done = true;
- }
- if (!c->c2.did_pre_pull_restore)
- {
- pre_pull_restore (&c->options, &c->c2.gc);
- c->c2.did_pre_pull_restore = true;
- }
- if (apply_push_options (&c->options,
- &buf,
- permission_mask,
- option_types_found,
- c->c2.es))
- {
- push_update_digest (&c->c2.pulled_options_state, &buf_orig);
- switch (c->options.push_continuation)
- {
- case 0:
- case 1:
- md_ctx_final (&c->c2.pulled_options_state, c->c2.pulled_options_digest.digest);
- md_ctx_cleanup (&c->c2.pulled_options_state);
- c->c2.pulled_options_md5_init_done = false;
- ret = PUSH_MSG_REPLY;
- break;
- case 2:
- ret = PUSH_MSG_CONTINUATION;
- break;
- }
- }
- }
- else if (ch == '\0')
- {
- ret = PUSH_MSG_REPLY;
- }
- /* show_settings (&c->options); */
- }
- return ret;
+ if (honor_received_options && buf_string_compare_advance(&buf, "PUSH_REPLY"))
+ {
+ const uint8_t ch = buf_read_u8(&buf);
+ if (ch == ',')
+ {
+ struct buffer buf_orig = buf;
+ if (!c->c2.pulled_options_md5_init_done)
+ {
+ md_ctx_init(&c->c2.pulled_options_state, md_kt_get("MD5"));
+ c->c2.pulled_options_md5_init_done = true;
+ }
+ if (!c->c2.did_pre_pull_restore)
+ {
+ pre_pull_restore(&c->options, &c->c2.gc);
+ c->c2.did_pre_pull_restore = true;
+ }
+ if (apply_push_options(&c->options,
+ &buf,
+ permission_mask,
+ option_types_found,
+ c->c2.es))
+ {
+ push_update_digest(&c->c2.pulled_options_state, &buf_orig,
+ &c->options);
+ switch (c->options.push_continuation)
+ {
+ case 0:
+ case 1:
+ md_ctx_final(&c->c2.pulled_options_state, c->c2.pulled_options_digest.digest);
+ md_ctx_cleanup(&c->c2.pulled_options_state);
+ c->c2.pulled_options_md5_init_done = false;
+ ret = PUSH_MSG_REPLY;
+ break;
+
+ case 2:
+ ret = PUSH_MSG_CONTINUATION;
+ break;
+ }
+ }
+ }
+ else if (ch == '\0')
+ {
+ ret = PUSH_MSG_REPLY;
+ }
+ /* show_settings (&c->options); */
+ }
+ return ret;
}
#if P2MP_SERVER
@@ -725,62 +769,64 @@ process_incoming_push_msg (struct context *c,
* Remove iroutes from the push_list.
*/
void
-remove_iroutes_from_push_route_list (struct options *o)
+remove_iroutes_from_push_route_list(struct options *o)
{
- if (o && o->push_list.head && o->iroutes)
- {
- struct gc_arena gc = gc_new ();
- struct push_entry *e = o->push_list.head;
-
- /* cycle through the push list */
- while (e)
- {
- char *p[MAX_PARMS];
- bool enable = true;
-
- /* parse the push item */
- CLEAR (p);
- if ( e->enable &&
- parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
- {
- /* is the push item a route directive? */
- if (p[0] && !strcmp (p[0], "route") && !p[3])
- {
- /* get route parameters */
- bool status1, status2;
- const in_addr_t network = getaddr (GETADDR_HOST_ORDER, p[1], 0, &status1, NULL);
- const in_addr_t netmask = getaddr (GETADDR_HOST_ORDER, p[2] ? p[2] : "255.255.255.255", 0, &status2, NULL);
-
- /* did route parameters parse correctly? */
- if (status1 && status2)
- {
- const struct iroute *ir;
-
- /* does route match an iroute? */
- for (ir = o->iroutes; ir != NULL; ir = ir->next)
- {
- if (network == ir->network && netmask == netbits_to_netmask (ir->netbits >= 0 ? ir->netbits : 32))
- {
- enable = false;
- break;
- }
- }
- }
- }
-
- /* should we copy the push item? */
- e->enable = enable;
- if (!enable)
- msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
- }
-
- e = e->next;
- }
-
- gc_free (&gc);
+ if (o && o->push_list.head && o->iroutes)
+ {
+ struct gc_arena gc = gc_new();
+ struct push_entry *e = o->push_list.head;
+
+ /* cycle through the push list */
+ while (e)
+ {
+ char *p[MAX_PARMS];
+ bool enable = true;
+
+ /* parse the push item */
+ CLEAR(p);
+ if (e->enable
+ && parse_line(e->option, p, SIZE(p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
+ {
+ /* is the push item a route directive? */
+ if (p[0] && !strcmp(p[0], "route") && !p[3])
+ {
+ /* get route parameters */
+ bool status1, status2;
+ const in_addr_t network = getaddr(GETADDR_HOST_ORDER, p[1], 0, &status1, NULL);
+ const in_addr_t netmask = getaddr(GETADDR_HOST_ORDER, p[2] ? p[2] : "255.255.255.255", 0, &status2, NULL);
+
+ /* did route parameters parse correctly? */
+ if (status1 && status2)
+ {
+ const struct iroute *ir;
+
+ /* does route match an iroute? */
+ for (ir = o->iroutes; ir != NULL; ir = ir->next)
+ {
+ if (network == ir->network && netmask == netbits_to_netmask(ir->netbits >= 0 ? ir->netbits : 32))
+ {
+ enable = false;
+ break;
+ }
+ }
+ }
+ }
+
+ /* should we copy the push item? */
+ e->enable = enable;
+ if (!enable)
+ {
+ msg(D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
+ }
+ }
+
+ e = e->next;
+ }
+
+ gc_free(&gc);
}
}
-#endif
+#endif /* if P2MP_SERVER */
-#endif
+#endif /* if P2MP */