diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-12-27 18:25:47 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-12-27 18:25:47 +0100 |
commit | 79f3537f69e125f19f59c36aa090120a63186a54 (patch) | |
tree | 2089a3b7dac990841dbc2e4d9b2f535b82dbb0af /src/openvpn/push.c | |
parent | f2137fedb30cb87448eb03b2f288920df6187571 (diff) | |
parent | 3a2bbdb05ca6a6996e424c9fb225cb0d53804125 (diff) |
Merge tag 'upstream/2.4.0'
Upstream version 2.4.0
Diffstat (limited to 'src/openvpn/push.c')
-rw-r--r-- | src/openvpn/push.c | 1148 |
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 */ |