@@ -528,14 +528,15 @@
c->c2.tls_multi->dco_peer_id = -1;
}
#endif
- int ret = dco_new_peer(&c->c1.tuntap->dco, multi->peer_id, sock->sd, NULL,
- proto_is_dgram(sock->info.proto) ? remoteaddr : NULL, NULL, NULL);
+ int ret = dco_new_peer(&c->c1.tuntap->dco, multi->rx_peer_id, sock->sd, NULL,
+ proto_is_dgram(sock->info.proto) ? remoteaddr : NULL,
+ NULL, NULL);
if (ret < 0)
{
return ret;
}
- c->c2.tls_multi->dco_peer_id = multi->peer_id;
+ c->c2.tls_multi->dco_peer_id = multi->rx_peer_id;
return 0;
}
@@ -610,7 +611,7 @@
{
struct context *c = &mi->context;
- int peer_id = c->c2.tls_multi->peer_id;
+ int peer_id = c->c2.tls_multi->rx_peer_id;
struct sockaddr *remoteaddr, *localaddr = NULL;
struct sockaddr_storage local = { 0 };
const socket_descriptor_t sd = c->c2.link_sockets[0]->sd;
@@ -689,8 +690,7 @@
if (addrtype == MR_ADDR_IPV6)
{
#if defined(_WIN32)
- dco_win_add_iroute_ipv6(&c->c1.tuntap->dco, addr->v6.addr, addr->netbits,
- c->c2.tls_multi->peer_id);
+ dco_win_add_iroute_ipv6(&c->c1.tuntap->dco, addr->v6.addr, addr->netbits, c->c2.tls_multi->rx_peer_id);
#else
const struct in6_addr *gateway = &mi->context.c2.push_ifconfig_ipv6_local;
if (addr->type & MR_ONLINK_DCO_ADDR)
@@ -706,8 +706,7 @@
else if (addrtype == MR_ADDR_IPV4)
{
#if defined(_WIN32)
- dco_win_add_iroute_ipv4(&c->c1.tuntap->dco, addr->v4.addr, addr->netbits,
- c->c2.tls_multi->peer_id);
+ dco_win_add_iroute_ipv4(&c->c1.tuntap->dco, addr->v4.addr, addr->netbits, c->c2.tls_multi->rx_peer_id);
#else
in_addr_t dest = htonl(addr->v4.addr);
const in_addr_t *gateway = &mi->context.c2.push_ifconfig_local;
@@ -2209,7 +2209,7 @@
if (o->use_peer_id)
{
- buf_printf(&out, ", peer-id: %d", o->peer_id);
+ buf_printf(&out, ", rx-peer-id: %u, tx-peer-id: %u", c->c2.tls_multi->rx_peer_id, c->c2.tls_multi->tx_peer_id);
}
#ifdef USE_COMP
@@ -2688,7 +2688,12 @@
{
msg(D_PUSH_DEBUG, "OPTIONS IMPORT: peer-id set");
c->c2.tls_multi->use_peer_id = true;
- c->c2.tls_multi->peer_id = c->options.peer_id;
+ c->c2.tls_multi->tx_peer_id = c->options.peer_id;
+ if (!c->c2.tls_multi->use_asymmetric_peer_id)
+ {
+ c->c2.tls_multi->rx_peer_id = c->options.peer_id;
+ c->c2.tls_multi->tx_peer_id = c->options.peer_id;
+ }
}
/* process (potentially) pushed options */
@@ -3485,6 +3490,10 @@
if (c->c2.tls_multi)
{
tls_multi_init_finalize(c->c2.tls_multi, c->options.ce.tls_mtu);
+ if (c->c2.tls_multi->rx_peer_id != MAX_PEER_ID)
+ {
+ c->options.use_peer_id = true;
+ }
ASSERT(c->c2.tls_multi->opt.frame.buf.payload_size <= c->c2.frame.buf.payload_size);
frame_print(&c->c2.tls_multi->opt.frame, D_MTU_INFO, "Control Channel MTU parms");
@@ -765,14 +765,15 @@
{
chomp(line);
if (validate_peer_info_line(line)
- && (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0))
+ && (strncmp(line, "IV_", 3) == 0 || strncmp(line, "UV_", 3) == 0
+ || strncmp(line, "ID", 2) == 0))
{
msg(M_INFO, "peer info: %s", line);
env_set_add(es, line);
}
else
{
- msg(M_WARN, "validation failed on peer_info line received from client");
+ msg(M_WARN, "validation failed on peer_info line received");
}
}
}
@@ -430,7 +430,7 @@
if (mi->context.c2.tls_multi && check_debug_level(D_DCO_DEBUG)
&& dco_enabled(&mi->context.options))
{
- buf_printf(&out, " peer-id=%d", mi->context.c2.tls_multi->peer_id);
+ buf_printf(&out, " rx-peer-id=%d", mi->context.c2.tls_multi->rx_peer_id);
}
return BSTR(&out);
}
@@ -598,9 +598,9 @@
}
#endif
- if (mi->context.c2.tls_multi->peer_id != MAX_PEER_ID)
+ if (mi->context.c2.tls_multi->rx_peer_id != MAX_PEER_ID)
{
- m->instances[mi->context.c2.tls_multi->peer_id] = NULL;
+ m->instances[mi->context.c2.tls_multi->rx_peer_id] = NULL;
/* Adjust the max_peerid as this might have been the highest
* peer id instance */
@@ -908,8 +908,7 @@
#else
sep,
#endif
- sep,
- mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX,
+ sep, mi->context.c2.tls_multi ? mi->context.c2.tls_multi->rx_peer_id : MAX_PEER_ID,
sep, translate_cipher_name_to_openvpn(mi->context.options.ciphername));
}
gc_free(&gc);
@@ -1727,6 +1726,7 @@
tls_multi->use_peer_id = true;
o->use_peer_id = true;
}
+
else if (dco_enabled(o))
{
msg(M_INFO, "Client does not support DATA_V2. Data channel offloading "
@@ -3121,12 +3121,12 @@
* has, so we disallow it. This can happen if a DCO netlink notification
* gets lost and we miss a floating step.
*/
- if (m1->peer_id == m2->peer_id)
+ if (m1->rx_peer_id == m2->rx_peer_id)
{
msg(M_WARN,
"disallowing peer %" PRIu32 " (%s) from floating to "
"its own address (%s)",
- m1->peer_id, tls_common_name(mi->context.c2.tls_multi, false),
+ m1->rx_peer_id, tls_common_name(mi->context.c2.tls_multi, false),
mroute_addr_print(&mi->real, &gc));
goto done;
}
@@ -3139,9 +3139,10 @@
}
msg(D_MULTI_MEDIUM, "peer %" PRIu32 " (%s) floated from %s to %s",
- mi->context.c2.tls_multi->peer_id, tls_common_name(mi->context.c2.tls_multi, false),
- mroute_addr_print_ex(&mi->real, MAPF_SHOW_FAMILY, &gc),
- mroute_addr_print_ex(&real, MAPF_SHOW_FAMILY, &gc));
+ mi->context.c2.tls_multi->rx_peer_id,
+ tls_common_name(mi->context.c2.tls_multi, false),
+ mroute_addr_print(&mi->real, &gc),
+ print_link_socket_actual(&m->top.c2.from, &gc));
/* remove old address from hash table before changing address */
ASSERT(hash_remove(m->hash, &mi->real));
@@ -4086,7 +4087,7 @@
{
if (!m->instances[i])
{
- mi->context.c2.tls_multi->peer_id = i;
+ mi->context.c2.tls_multi->rx_peer_id = i;
m->instances[i] = mi;
break;
}
@@ -4095,11 +4096,11 @@
/* should not really end up here, since multi_create_instance returns null
* if amount of clients exceeds max_clients and this method would then
* also not have been called */
- ASSERT(mi->context.c2.tls_multi->peer_id < m->max_clients);
+ ASSERT(mi->context.c2.tls_multi->rx_peer_id < m->max_clients);
- if (mi->context.c2.tls_multi->peer_id > m->max_peerid)
+ if (mi->context.c2.tls_multi->rx_peer_id > m->max_peerid)
{
- m->max_peerid = mi->context.c2.tls_multi->peer_id;
+ m->max_peerid = mi->context.c2.tls_multi->rx_peer_id;
}
}
@@ -3876,6 +3876,7 @@
"incompatible with each other.");
}
+
if (dco_enabled(o))
{
/* check if any option should force disabling DCO */
@@ -661,9 +661,10 @@
print_in_addr_t(c->c2.push_ifconfig_remote_netmask, 0, gc));
}
- if (tls_multi->use_peer_id)
+ if (!tls_multi->use_asymmetric_peer_id)
{
- push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->peer_id);
+ push_option_fmt(gc, push_list, M_USAGE, "peer-id %d",
+ tls_multi->rx_peer_id);
}
/*
* If server uses --auth-gen-token and we have an auth token
@@ -188,7 +188,7 @@
unsigned int permission_mask = pull_permission_mask(c);
if (process_push_update(c, &o, permission_mask, &option_types_found, &tmp_msg, true) == PUSH_MSG_ERROR)
{
- msg(M_WARN, "Failed to process push update message sent to client ID: %u", c->c2.tls_multi->peer_id);
+ msg(M_WARN, "Failed to process push update message sent to client ID: %u", c->c2.tls_multi->rx_peer_id);
}
e = e->next;
}
@@ -294,7 +294,7 @@
if (!support_push_update(mi))
{
- msg(M_CLIENT, "PUSH_UPDATE: not sending message to unsupported peer with ID: %u", mi->context.c2.tls_multi->peer_id);
+ msg(M_CLIENT, "PUSH_UPDATE: not sending message to unsupported peer with ID: %u", mi->context.c2.tls_multi->rx_peer_id);
buffer_list_free(msgs);
gc_free(&gc);
return 0;
@@ -329,7 +329,7 @@
/* Type is UPT_BROADCAST so we update every client */
if (!send_single_push_update(m, curr_mi, msgs))
{
- msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated", curr_mi->context.c2.tls_multi->peer_id);
+ msg(M_CLIENT, "ERROR: Peer ID: %u has not been updated", curr_mi->context.c2.tls_multi->rx_peer_id);
continue;
}
count++;
@@ -1168,7 +1168,10 @@
/* get command line derived options */
ret->opt = *tls_options;
ret->dco_peer_id = -1;
- ret->peer_id = MAX_PEER_ID;
+ ret->use_asymmetric_peer_id = false;
+ /* The rx_peer_id is also used to identify DCO clients */
+ ret->rx_peer_id = MAX_PEER_ID;
+ ret->tx_peer_id = MAX_PEER_ID;
return ret;
}
@@ -1181,6 +1184,24 @@
tls_session_init(multi, &multi->session[TM_ACTIVE]);
tls_session_init(multi, &multi->session[TM_INITIAL]);
+
+ if (!multi->opt.dco_enabled)
+ {
+ /* Calculate the asymmetric peer-id */
+ if (multi->rx_peer_id == MAX_PEER_ID && multi->session[TM_INITIAL].opt->mode != MODE_SERVER)
+ {
+ uint8_t peerid[3];
+
+ if (rand_bytes(peerid, sizeof(peerid)) != 1)
+ {
+ msg(M_FATAL, "rand_bytes() failed");
+ }
+
+ multi->rx_peer_id = ((uint32_t)peerid[0] << 16)
+ | ((uint32_t)peerid[1] << 8)
+ | (uint32_t)peerid[2];
+ }
+ }
}
/*
@@ -1858,6 +1879,25 @@
return str;
}
+static bool
+push_peer_info_peerid(struct buffer *out, struct tls_multi *multi, struct tls_session *session)
+{
+ if (multi->rx_peer_id == MAX_PEER_ID || session->opt->dco_enabled)
+ {
+ /* No valid peer id or DCO is enabled. Cannot use this feature */
+ return true;
+ }
+
+ /* In server mode we only add this when the client has announced its
+ * support for the feature */
+ if (session->opt->mode != MODE_SERVER || multi->use_asymmetric_peer_id)
+ {
+ return buf_printf(out, "ID=%x\n", multi->rx_peer_id);
+ }
+
+ return true;
+}
+
/**
* Prepares the IV_ and UV_ variables that are part of the
* exchange to signal the peer's capabilities. The amount
@@ -1871,15 +1911,22 @@
*
* @param buf the buffer to write these variables to
* @param session the TLS session object
+ * @param multi the TLS multi object
* @return true if no error was encountered
*/
static bool
-push_peer_info(struct buffer *buf, struct tls_session *session)
+push_peer_info(struct buffer *buf, struct tls_multi *multi, struct tls_session *session)
{
struct gc_arena gc = gc_new();
bool ret = false;
struct buffer out = alloc_buf_gc(512 * 3, &gc);
+ /* The asymmetric peer-id is always written when enabled */
+ if (!push_peer_info_peerid(&out, multi, session))
+ {
+ goto error;
+ }
+
if (session->opt->push_peer_info_detail > 1)
{
/* push version */
@@ -2027,7 +2074,9 @@
}
else
{
- if (!write_empty_string(buf)) /* no peer info */
+ if (!(session->opt->mode == MODE_SERVER && multi->use_asymmetric_peer_id
+ ? write_string(buf, BSTR(&out), -1)
+ : write_empty_string(buf)))
{
goto error;
}
@@ -2156,7 +2205,7 @@
}
}
- if (!push_peer_info(buf, session))
+ if (!push_peer_info(buf, multi, session))
{
goto error;
}
@@ -2269,6 +2318,44 @@
if (multi->peer_info)
{
output_peer_info_env(session->opt->es, multi->peer_info);
+ if (session->opt->mode == MODE_SERVER)
+ {
+ if (!session->opt->dco_enabled)
+ {
+ uint32_t peer_id = extract_asymmetric_peer_id(multi->peer_info);
+ if (peer_id != MAX_PEER_ID)
+ {
+ multi->tx_peer_id = peer_id;
+ multi->use_asymmetric_peer_id = true;
+ multi->use_peer_id = true;
+ }
+ else
+ {
+ /* Client has no asymmetric peer-id capability */
+ multi->tx_peer_id = multi->rx_peer_id;
+ }
+ }
+ else
+ {
+ /* With DCO we don't need the tx_peer_id atm */
+ multi->tx_peer_id = multi->rx_peer_id;
+ }
+ }
+ }
+ else
+ {
+ free(multi->peer_info);
+ multi->peer_info = read_string_alloc(buf);
+ }
+ if (session->opt->mode == MODE_POINT_TO_POINT && !session->opt->dco_enabled)
+ {
+ uint32_t peer_id = extract_asymmetric_peer_id(multi->peer_info);
+ if (peer_id != MAX_PEER_ID)
+ {
+ multi->tx_peer_id = peer_id;
+ multi->use_asymmetric_peer_id = true;
+ multi->use_peer_id = true;
+ }
}
free(multi->remote_ciphername);
@@ -4005,8 +4092,8 @@
msg(D_TLS_DEBUG, __func__);
ASSERT(ks);
-
- peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24 | (multi->peer_id & 0xFFFFFF));
+ peer = htonl(((P_DATA_V2 << P_OPCODE_SHIFT) | ks->key_id) << 24
+ | (multi->tx_peer_id & 0xFFFFFF));
ASSERT(buf_write_prepend(buf, &peer, 4));
}
@@ -697,8 +697,10 @@
#define AUTH_TOKEN_VALID_EMPTYUSER (1 << 2)
/* For P_DATA_V2 */
- uint32_t peer_id;
+ uint32_t rx_peer_id;
+ uint32_t tx_peer_id;
bool use_peer_id;
+ bool use_asymmetric_peer_id;
char *remote_ciphername; /**< cipher specified in peer's config file */
bool remote_usescomp; /**< remote announced comp-lzo in OCC string */
@@ -406,6 +406,7 @@
{
/* will return 0 if peer_info is null */
const unsigned int iv_proto_peer = extract_iv_proto(multi->peer_info);
+ const unsigned int tx_peer_id = extract_asymmetric_peer_id(multi->peer_info);
/* The other peer does not support P2P NCP */
if (!(iv_proto_peer & IV_PROTO_NCP_P2P))
@@ -416,7 +417,11 @@
if (iv_proto_peer & IV_PROTO_DATA_V2)
{
multi->use_peer_id = true;
- multi->peer_id = 0x76706e; /* 'v' 'p' 'n' */
+ if (tx_peer_id == MAX_PEER_ID)
+ {
+ multi->rx_peer_id = 0x76706e; /* 'v' 'p' 'n' */
+ multi->tx_peer_id = 0x76706e; /* 'v' 'p' 'n' */
+ }
}
if (iv_proto_peer & IV_PROTO_CC_EXIT_NOTIFY)
@@ -440,8 +445,16 @@
{
session->opt->crypto_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
- if (multi->use_peer_id)
+ /* The asymmetric peer-id trumps on the EKM generated ones */
+ if ((tx_peer_id != MAX_PEER_ID) && (!multi->opt.dco_enabled))
{
+ multi->tx_peer_id = tx_peer_id;
+ multi->use_asymmetric_peer_id = true;
+ }
+ else
+ {
+ multi->rx_peer_id = 0x76706e; /* 'v' 'p' 'n' */
+ multi->tx_peer_id = 0x76706e; /* 'v' 'p' 'n' */
/* Using a non hardcoded peer-id makes a tiny bit harder to
* fingerprint packets and also gives each connection a unique
* peer-id that can be useful for NAT tracking etc. */
@@ -458,7 +471,8 @@
}
else
{
- multi->peer_id = (peerid[0] << 16) + (peerid[1] << 8) + peerid[2];
+ multi->rx_peer_id = (peerid[0] << 16) + (peerid[1] << 8) + peerid[2];
+ multi->tx_peer_id = multi->rx_peer_id;
}
}
}
@@ -500,11 +514,13 @@
common_cipher = BSTR(&out);
}
- msg(D_TLS_DEBUG_LOW,
- "P2P mode NCP negotiation result: "
- "TLS_export=%d, DATA_v2=%d, peer-id %d, epoch=%d, cipher=%s",
- (bool)(session->opt->crypto_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT), multi->use_peer_id,
- multi->peer_id, (bool)(session->opt->crypto_flags & CO_EPOCH_DATA_KEY_FORMAT),
+ msg(D_TLS_DEBUG_LOW, "P2P mode NCP negotiation result: "
+ "TLS_export=%d, DATA_v2=%d, rx-peer-id %d, tx-peer-id %d, epoch=%d, cipher=%s",
+ (bool)(session->opt->crypto_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT),
+ multi->use_peer_id,
+ multi->rx_peer_id,
+ multi->tx_peer_id,
+ (bool)(session->opt->crypto_flags & CO_EPOCH_DATA_KEY_FORMAT),
common_cipher);
gc_free(&gc);
@@ -24,7 +24,7 @@
#endif
#include "syshead.h"
-
+#include "openvpn.h"
#include "ssl_util.h"
char *
@@ -72,6 +72,24 @@
return 0;
}
+uint32_t
+extract_asymmetric_peer_id(const char *peer_info)
+{
+ const char *optstr = peer_info ? strstr(peer_info, "ID=") : NULL;
+ if (optstr)
+ {
+ uint32_t peer_id = 0;
+ int r = sscanf(optstr, "ID=%x", &peer_id);
+ {
+ if (r == 1 && peer_id < MAX_PEER_ID)
+ {
+ return peer_id;
+ }
+ }
+ }
+ return MAX_PEER_ID;
+}
+
const char *
options_string_compat_lzo(const char *options, struct gc_arena *gc)
{
@@ -53,6 +53,15 @@
*/
unsigned int extract_iv_proto(const char *peer_info);
+
+/**
+ * Extracts the ID variable and returns its value or
+ * MAX_PEER_ID if it cannot be extracted.
+ *
+ * @param peer_info peer info string to search for ID
+ */
+uint32_t extract_asymmetric_peer_id(const char *peer_info);
+
/**
* Takes a locally produced OCC string for TLS server mode and modifies as
* if the option comp-lzo was enabled. This is to send a client in