diff options
author | Bernhard Schmidt <berni@debian.org> | 2020-09-01 16:52:17 +0200 |
---|---|---|
committer | Bernhard Schmidt <berni@debian.org> | 2020-09-01 16:52:17 +0200 |
commit | 9fc3b98112217f2d92a67977dbde0987cc7a1803 (patch) | |
tree | 29fcc8654ee65d9dd89ade797bea2f3d9dfd9cfd /src/openvpn/push.c | |
parent | a8758c0e03eed188dcb9da0e4fd781a67c25bf1e (diff) | |
parent | 69b02b1f7fd609d84ace13ab04697158de2418a9 (diff) |
Merge branch 'debian/experimental-2.5'
Diffstat (limited to 'src/openvpn/push.c')
-rw-r--r-- | src/openvpn/push.c | 403 |
1 files changed, 244 insertions, 159 deletions
diff --git a/src/openvpn/push.c b/src/openvpn/push.c index 002be23..e0d2eea 100644 --- a/src/openvpn/push.c +++ b/src/openvpn/push.c @@ -33,6 +33,7 @@ #include "options.h" #include "ssl.h" #include "ssl_verify.h" +#include "ssl_ncp.h" #include "manage.h" #include "memdbg.h" @@ -69,19 +70,19 @@ receive_auth_failed(struct context *c, const struct buffer *buffer) { switch (auth_retry_get()) { - case AR_NONE: - c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */ - break; + case AR_NONE: + c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */ + break; - case AR_INTERACT: - ssl_purge_auth(false); + case AR_INTERACT: + ssl_purge_auth(false); - case AR_NOINTERACT: - c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ - break; + case AR_NOINTERACT: + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ + break; - default: - ASSERT(0); + default: + ASSERT(0); } c->sig->signal_text = "auth-failure"; } @@ -101,7 +102,7 @@ receive_auth_failed(struct context *c, const struct buffer *buffer) * Save the dynamic-challenge text even when management is defined */ { -#ifdef ENABLE_CLIENT_CR +#ifdef ENABLE_MANAGEMENT struct buffer buf = *buffer; if (buf_string_match_head_str(&buf, "AUTH_FAILED,CRV1:") && BLEN(&buf)) { @@ -176,7 +177,60 @@ server_pushed_signal(struct context *c, const struct buffer *buffer, const bool } } -#if P2MP_SERVER +void +server_pushed_info(struct context *c, const struct buffer *buffer, + const int adv) +{ + const char *m = ""; + struct buffer buf = *buffer; + + if (buf_advance(&buf, adv) && buf_read_u8(&buf) == ',' && BLEN(&buf)) + { + m = BSTR(&buf); + } + +#ifdef ENABLE_MANAGEMENT + struct gc_arena gc; + if (management) + { + gc = gc_new(); + + /* + * We use >INFOMSG here instead of plain >INFO since INFO is used to + * for management greeting and we don't want to confuse the client + */ + struct buffer out = alloc_buf_gc(256, &gc); + buf_printf(&out, ">%s:%s", "INFOMSG", m); + management_notify_generic(management, BSTR(&out)); + + gc_free(&gc); + } + #endif + msg(D_PUSH, "Info command was pushed by server ('%s')", m); +} + +void +receive_cr_response(struct context *c, const struct buffer *buffer) +{ + struct buffer buf = *buffer; + const char *m = ""; + + if (buf_advance(&buf, 11) && buf_read_u8(&buf) == ',' && BLEN(&buf)) + { + m = BSTR(&buf); + } +#ifdef MANAGEMENT_DEF_AUTH + struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; + struct man_def_auth_context *mda = session->opt->mda_context; + struct env_set *es = session->opt->es; + int key_id = session->key[KS_PRIMARY].key_id; + + + management_notify_client_cr_response(key_id, mda, es, m); +#endif + msg(D_PUSH, "CR response was sent by client ('%s')", m); +} + /** * Add an option to the given push list by providing a format string. * @@ -233,6 +287,30 @@ send_auth_failed(struct context *c, const char *client_reason) gc_free(&gc); } +bool +send_auth_pending_messages(struct context *c, const char *extra) +{ + send_control_channel_string(c, "AUTH_PENDING", D_PUSH); + + static const char info_pre[] = "INFO_PRE,"; + + + size_t len = strlen(extra)+1 + sizeof(info_pre); + if (len > PUSH_BUNDLE_SIZE) + { + return false; + } + struct gc_arena gc = gc_new(); + + struct buffer buf = alloc_buf_gc(len, &gc); + buf_printf(&buf, info_pre); + buf_printf(&buf, "%s", extra); + send_control_channel_string(c, BSTR(&buf), D_PUSH); + + gc_free(&gc); + return true; +} + /* * Send restart message from server to client. */ @@ -243,8 +321,6 @@ send_restart(struct context *c, const char *kill_msg) send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH); } -#endif /* if P2MP_SERVER */ - /* * Push/Pull */ @@ -254,15 +330,12 @@ 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); + int status = process_incoming_push_msg(c, buffer, c->options.pull, + pull_permission_mask(c), + &option_types_found); if (status == PUSH_MSG_ERROR) { @@ -282,29 +355,11 @@ incoming_push_message(struct context *c, const struct buffer *buffer) } } event_timeout_clear(&c->c2.push_request_interval); - } - else if (status == PUSH_MSG_REQUEST) - { - if (c->options.mode == MODE_SERVER) - { - struct frame *frame_fragment = NULL; -#ifdef ENABLE_FRAGMENT - if (c->options.ce.fragment) - { - frame_fragment = &c->c2.frame_fragment; - } -#endif - struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; - if (!tls_session_update_crypto_params(session, &c->options, - &c->c2.frame, frame_fragment)) - { - msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); - goto error; - } - } + event_timeout_clear(&c->c2.wait_for_connect); } goto cleanup; + error: register_signal(c, SIGUSR1, "process-push-msg-failed"); cleanup: @@ -328,10 +383,40 @@ send_push_request(struct context *c) } } -#if P2MP_SERVER +/** + * Prepare push option for auth-token + * @param tls_multi tls multi context of 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. + */ +void +prepare_auth_token_push_reply(struct tls_multi *tls_multi, struct gc_arena *gc, + struct push_list *push_list) +{ + /* + * If server uses --auth-gen-token and we have an auth token + * to send to the client + */ + if (tls_multi->auth_token) + { + push_option_fmt(gc, push_list, M_USAGE, + "auth-token %s", + tls_multi->auth_token); + if (!tls_multi->auth_token_initial) + { + /* + * Save the initial auth token for clients that ignore + * the updates to the token + */ + tls_multi->auth_token_initial = strdup(tls_multi->auth_token); + } + } +} /** - * Prepare push options, based on local options and available peer info. + * Prepare push options, based on local options * * @param context context structure storing data for VPN tunnel * @param gc gc arena for allocating push options @@ -339,13 +424,11 @@ send_push_request(struct context *c) * * @return true on success, false on failure. */ -static bool +bool 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 */ @@ -360,7 +443,8 @@ prepare_push_reply(struct context *c, struct gc_arena *gc, /* ipv4 */ if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local - && c->c2.push_ifconfig_remote_netmask) + && c->c2.push_ifconfig_remote_netmask + && !o->push_ifconfig_ipv4_blocked) { in_addr_t ifconfig_local = c->c2.push_ifconfig_local; if (c->c2.push_ifconfig_local_alias) @@ -373,58 +457,29 @@ prepare_push_reply(struct context *c, struct gc_arena *gc, 0, gc)); } - /* Send peer-id if client supports it */ - optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; - if (optstr) + if (tls_multi->use_peer_id) { - 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); - tls_multi->use_peer_id = true; - } + 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 log the fact and push the "what we have now" cipher - * (so the client is always told what we expect it to use) - */ - 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, " - "re-sending previously negotiated cipher '%s'", - o->ciphername ); - } - 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 + /* + * 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) + prepare_auth_token_push_reply(tls_multi, gc, push_list); + + /* + * Push the selected cipher, at this point the cipher has been + * already negotiated and been fixed. + * + * We avoid pushing the cipher to clients not supporting NCP + * to avoid error messages in their logs + */ + if (tls_peer_supports_ncp(c->c2.tls_multi->peer_info)) { - push_option_fmt(gc, push_list, M_USAGE, - "auth-token %s", tls_multi->auth_token); - tls_multi->auth_token_sent = true; + push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername); } + return true; } @@ -435,6 +490,7 @@ send_push_options(struct context *c, struct buffer *buf, { struct push_entry *e = push_list->head; + e = push_list->head; while (e) { if (e->enable) @@ -467,7 +523,26 @@ send_push_options(struct context *c, struct buffer *buf, return true; } -static bool +void +send_push_reply_auth_token(struct tls_multi *multi) +{ + struct gc_arena gc = gc_new(); + struct push_list push_list = { 0 }; + + prepare_auth_token_push_reply(multi, &gc, &push_list); + + /* prepare auth token should always add the auth-token option */ + struct push_entry *e = push_list.head; + ASSERT(e && e->enable); + + /* Construct a mimimal control channel push reply message */ + struct buffer buf = alloc_buf_gc(PUSH_BUNDLE_SIZE, &gc); + buf_printf(&buf, "%s, %s", push_reply_cmd, e->option); + send_control_channel_string_dowork(multi, BSTR(&buf), D_PUSH); + gc_free(&gc); +} + +bool send_push_reply(struct context *c, struct push_list *per_client_push_list) { struct gc_arena gc = gc_new(); @@ -586,7 +661,7 @@ clone_push_list(struct options *o) void push_options(struct options *o, char **p, int msglevel, struct gc_arena *gc) { - const char **argv = make_extended_arg_array(p, gc); + const char **argv = make_extended_arg_array(p, false, gc); char *opt = print_argv(argv, gc, 0); push_option(o, opt, msglevel); } @@ -620,6 +695,13 @@ push_remove_option(struct options *o, const char *p) { msg(D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p); + /* ifconfig is special, as not part of the push list */ + if (streq(p, "ifconfig")) + { + o->push_ifconfig_ipv4_blocked = true; + return; + } + /* ifconfig-ipv6 is special, as not part of the push list */ if (streq( p, "ifconfig-ipv6" )) { @@ -645,24 +727,19 @@ push_remove_option(struct options *o, const char *p) } } } -#endif /* if P2MP_SERVER */ -#if P2MP_SERVER int process_incoming_push_request(struct context *c) { int ret = PUSH_MSG_ERROR; -#ifdef ENABLE_ASYNC_PUSH - 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) { 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.context_auth == CAS_SUCCEEDED) { time_t now; @@ -674,10 +751,9 @@ process_incoming_push_request(struct context *c) else { /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */ - struct push_list push_list; + struct push_list push_list = { 0 }; struct gc_arena gc = gc_new(); - CLEAR(push_list); if (prepare_push_reply(c, &gc, &push_list) && send_push_reply(c, &push_list)) { @@ -694,7 +770,6 @@ process_incoming_push_request(struct context *c) return ret; } -#endif /* if P2MP_SERVER */ static void push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt) @@ -716,6 +791,63 @@ push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt) } } +static int +process_incoming_push_reply(struct context *c, + unsigned int permission_mask, + unsigned int *option_types_found, + struct buffer *buf) +{ + int ret = PUSH_MSG_ERROR; + const uint8_t ch = buf_read_u8(buf); + if (ch == ',') + { + struct buffer buf_orig = (*buf); + if (!c->c2.pulled_options_digest_init_done) + { + c->c2.pulled_options_state = md_ctx_new(); + md_ctx_init(c->c2.pulled_options_state, md_kt_get("SHA256")); + c->c2.pulled_options_digest_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); + md_ctx_free(c->c2.pulled_options_state); + c->c2.pulled_options_state = NULL; + c->c2.pulled_options_digest_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; +} + int process_incoming_push_msg(struct context *c, const struct buffer *buffer, @@ -723,70 +855,25 @@ process_incoming_push_msg(struct context *c, unsigned int permission_mask, unsigned int *option_types_found) { - int ret = PUSH_MSG_ERROR; struct buffer buf = *buffer; -#if P2MP_SERVER if (buf_string_compare_advance(&buf, "PUSH_REQUEST")) { - ret = process_incoming_push_request(c); + c->c2.push_request_received = true; + return process_incoming_push_request(c); + } + else if (honor_received_options + && buf_string_compare_advance(&buf, push_reply_cmd)) + { + return process_incoming_push_reply(c, permission_mask, + option_types_found, &buf); } 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_digest_init_done) - { - c->c2.pulled_options_state = md_ctx_new(); - md_ctx_init(c->c2.pulled_options_state, md_kt_get("SHA256")); - c->c2.pulled_options_digest_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); - md_ctx_free(c->c2.pulled_options_state); - c->c2.pulled_options_state = NULL; - c->c2.pulled_options_digest_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 PUSH_MSG_ERROR; } - return ret; } -#if P2MP_SERVER /* * Remove iroutes from the push_list. @@ -850,6 +937,4 @@ remove_iroutes_from_push_route_list(struct options *o) } } -#endif /* if P2MP_SERVER */ - #endif /* if P2MP */ |