summaryrefslogtreecommitdiff
path: root/src/openvpn/push.c
diff options
context:
space:
mode:
authorBernhard Schmidt <berni@debian.org>2020-09-01 16:52:17 +0200
committerBernhard Schmidt <berni@debian.org>2020-09-01 16:52:17 +0200
commit9fc3b98112217f2d92a67977dbde0987cc7a1803 (patch)
tree29fcc8654ee65d9dd89ade797bea2f3d9dfd9cfd /src/openvpn/push.c
parenta8758c0e03eed188dcb9da0e4fd781a67c25bf1e (diff)
parent69b02b1f7fd609d84ace13ab04697158de2418a9 (diff)
Merge branch 'debian/experimental-2.5'
Diffstat (limited to 'src/openvpn/push.c')
-rw-r--r--src/openvpn/push.c403
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 */