Skip to content

Commit

Permalink
Peer-id patch v7
Browse files Browse the repository at this point in the history
Added new packet format P_DATA_V2, which includes peer-id. If server
supports, client sends all data packets in the new format. When data
packet arrives, server identifies peer by peer-id. If peer's ip/port has
changed, server assumes that client has floated, verifies HMAC and
updates ip/port in internal structs.

Changes in v7:
A few nitpicks.

Changes in v6:
Fixed: Make sure float won't happen if hmac check failed (regression).
Fixed: Access outside of bounds of array, which has caused memory
corruption and crash.
Various review fixes.

Changes in v5:
Protection agains replay attack by commiting float changes only after
existing packet processing flow has completed.

If peer floats to an address which is already taken by another active
session, drop float packet, otherwise disconnect existing session.

Changes in v4:
Handles correctly float to an address which is used by another peer.
This also has fixed crash on assert in multi_client_disconnect.

Changes in v3:
Bugfix: If float happens after TLS renegotiation and there are no
data packets between reneg and float, server will not recognize floated
client.
Acked-by: Steffan Karger <steffan.karger@fox-it.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1416755831-21250-1-git-send-email-lstipakov@gmail.com>
URL: http://article.gmane.org/gmane.network.openvpn.devel/9270

Signed-off-by: Gert Doering <gert@greenie.muc.de>
  • Loading branch information
lstipakov authored and cron2 committed Nov 27, 2014
1 parent 3341a98 commit 65eedc3
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 50 deletions.
50 changes: 33 additions & 17 deletions src/openvpn/forward.c
Expand Up @@ -722,20 +722,11 @@ read_incoming_link (struct context *c)
perf_pop ();
}

/*
* Input: c->c2.buf
* Output: c->c2.to_tun
*/

void
process_incoming_link (struct context *c)
bool
process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated)
{
struct gc_arena gc = gc_new ();
bool decrypt_status;
struct link_socket_info *lsi = get_link_socket_info (c);
const uint8_t *orig_buf = c->c2.buf.data;

perf_push (PERF_PROC_IN_LINK);
bool decrypt_status = false;

if (c->c2.buf.len > 0)
{
Expand Down Expand Up @@ -805,7 +796,7 @@ process_incoming_link (struct context *c)
* will load crypto_options with the correct encryption key
* and return false.
*/
if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options))
if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options, floated))
{
interval_action (&c->c2.tmp_int);

Expand All @@ -832,11 +823,25 @@ process_incoming_link (struct context *c)
/* decryption errors are fatal in TCP mode */
register_signal (c, SIGUSR1, "decryption-error"); /* SOFT-SIGUSR1 -- decryption error in TCP mode */
msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting");
goto done;
}

#else /* ENABLE_CRYPTO */
decrypt_status = true;
#endif /* ENABLE_CRYPTO */
}
else
{
buf_reset (&c->c2.to_tun);
}
gc_free (&gc);

return decrypt_status;
}

void
process_incoming_link_part2 (struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf)
{
if (c->c2.buf.len > 0)
{
#ifdef ENABLE_FRAGMENT
if (c->c2.fragment)
fragment_incoming (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment);
Expand Down Expand Up @@ -903,9 +908,20 @@ process_incoming_link (struct context *c)
{
buf_reset (&c->c2.to_tun);
}
done:
}

void
process_incoming_link (struct context *c)
{
perf_push (PERF_PROC_IN_LINK);

struct link_socket_info *lsi = get_link_socket_info (c);
const uint8_t *orig_buf = c->c2.buf.data;

process_incoming_link_part1(c, lsi, false);
process_incoming_link_part2(c, lsi, orig_buf);

perf_pop ();
gc_free (&gc);
}

/*
Expand Down
30 changes: 25 additions & 5 deletions src/openvpn/forward.h
Expand Up @@ -127,12 +127,11 @@ void encrypt_sign (struct context *c, bool comp_frag);
*/
void read_incoming_link (struct context *c);


/**
* Process a packet read from the external network interface.
* Starts processing a packet read from the external network interface.
* @ingroup external_multiplexer
*
* This function controls the processing of a data channel packet which
* This function starts the processing of a data channel packet which
* has come out of a VPN tunnel. It's high-level structure is as follows:
* - Verify that a nonzero length packet has been received from a valid
* source address for the given context \a c.
Expand All @@ -146,6 +145,25 @@ void read_incoming_link (struct context *c);
* - Call \c openvpn_decrypt() of the \link data_crypto Data Channel
* Crypto module\endlink to authenticate and decrypt the packet using
* the security parameters loaded by \c tls_pre_decrypt() above.
*
* @param c - The context structure of the VPN tunnel associated with the
* packet.
* @param lsi - link_socket_info obtained from context before processing.
* @param floated - Flag indicates that peer has floated.
*
* @return true if packet is authenticated, false otherwise.
*/
bool process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated);

/**
* Continues processing a packet read from the external network interface.
* @ingroup external_multiplexer
*
* This function continues the processing of a data channel packet which
* has come out of a VPN tunnel. It must be called after
* \c process_incoming_link_part1() function.
*
* It's high-level structure is as follows:
* - Call \c fragment_incoming() of the \link fragmentation Data Channel
* Fragmentation module\endlink to reassemble the packet if it's
* fragmented.
Expand All @@ -158,9 +176,11 @@ void read_incoming_link (struct context *c);
*
* @param c - The context structure of the VPN tunnel associated with the
* packet.
* @param lsi - link_socket_info obtained from context before processing.
* @param orig_buf - Pointer to a buffer data.
*
*/
void process_incoming_link (struct context *c);

void process_incoming_link_part2 (struct context *c, struct link_socket_info *lsi, const uint8_t *orig_buf);

/**
* Write a packet to the external network interface.
Expand Down
12 changes: 11 additions & 1 deletion src/openvpn/init.c
Expand Up @@ -1718,7 +1718,8 @@ pull_permission_mask (const struct context *c)
| OPT_P_MESSAGES
| OPT_P_EXPLICIT_NOTIFY
| OPT_P_ECHO
| OPT_P_PULL_MODE;
| OPT_P_PULL_MODE
| OPT_P_PEER_ID;

if (!c->options.route_nopull)
flags |= (OPT_P_ROUTE | OPT_P_IPWIN32);
Expand Down Expand Up @@ -1795,6 +1796,15 @@ do_deferred_options (struct context *c, const unsigned int found)
msg (D_PUSH, "OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified");
if (found & OPT_P_SETENV)
msg (D_PUSH, "OPTIONS IMPORT: environment modified");

#ifdef ENABLE_SSL
if (found & OPT_P_PEER_ID)
{
msg (D_PUSH, "OPTIONS IMPORT: peer-id set");
c->c2.tls_multi->use_peer_id = true;
c->c2.tls_multi->peer_id = c->options.peer_id;
}
#endif
}

/*
Expand Down
57 changes: 43 additions & 14 deletions src/openvpn/mudp.c
Expand Up @@ -33,6 +33,7 @@
#if P2MP_SERVER

#include "multi.h"
#include <inttypes.h>
#include "forward-inline.h"

#include "memdbg.h"
Expand All @@ -44,7 +45,7 @@
*/

struct multi_instance *
multi_get_create_instance_udp (struct multi_context *m)
multi_get_create_instance_udp (struct multi_context *m, bool *floated)
{
struct gc_arena gc = gc_new ();
struct mroute_addr real;
Expand All @@ -56,14 +57,39 @@ multi_get_create_instance_udp (struct multi_context *m)
struct hash_element *he;
const uint32_t hv = hash_value (hash, &real);
struct hash_bucket *bucket = hash_bucket (hash, hv);

he = hash_lookup_fast (hash, bucket, &real, hv);
uint8_t* ptr = BPTR(&m->top.c2.buf);
uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
uint32_t peer_id;
int i;

if (he)
/* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */
if (op == P_DATA_V2 && m->top.c2.buf.len >= (1 + 3))
{
mi = (struct multi_instance *) he->value;
peer_id = ntohl(*(uint32_t*)ptr) & 0xFFFFFF;
if ((peer_id < m->max_clients) && (m->instances[peer_id]))
{
mi = m->instances[peer_id];

*floated = !link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from);

if (*floated)
{
/* reset prefix, since here we are not sure peer is the one it claims to be */
ungenerate_prefix(mi);
msg (D_MULTI_ERRORS, "Untrusted peer %" PRIu32 " wants to float to %s", peer_id,
mroute_addr_print (&real, &gc));
}
}
}
else
{
he = hash_lookup_fast (hash, bucket, &real, hv);
if (he)
{
mi = (struct multi_instance *) he->value;
}
}
if (!mi)
{
if (!m->top.c2.tls_auth_standalone
|| tls_pre_decrypt_lite (m->top.c2.tls_auth_standalone, &m->top.c2.from, &m->top.c2.buf))
Expand All @@ -75,6 +101,16 @@ multi_get_create_instance_udp (struct multi_context *m)
{
hash_add_fast (hash, bucket, &mi->real, hv, mi);
mi->did_real_hash = true;

for (i = 0; i < m->max_clients; ++i)
{
if (!m->instances[i])
{
mi->context.c2.tls_multi->peer_id = i;
m->instances[i] = mi;
break;
}
}
}
}
else
Expand All @@ -89,15 +125,8 @@ multi_get_create_instance_udp (struct multi_context *m)
#ifdef ENABLE_DEBUG
if (check_debug_level (D_MULTI_DEBUG))
{
const char *status;

if (he && mi)
status = "[succeeded]";
else if (!he && mi)
status = "[created]";
else
status = "[failed]";

const char *status = mi ? "[ok]" : "[failed]";

dmsg (D_MULTI_DEBUG, "GET INST BY REAL: %s %s",
mroute_addr_print (&real, &gc),
status);
Expand Down
2 changes: 1 addition & 1 deletion src/openvpn/mudp.h
Expand Up @@ -65,7 +65,7 @@ void tunnel_server_udp (struct context *top);
* packet's source address or if one was a newly created successfully.
* NULL if one did not yet exist and a new one was not created.
*/
struct multi_instance *multi_get_create_instance_udp (struct multi_context *m);
struct multi_instance *multi_get_create_instance_udp (struct multi_context *m, bool *floated);

#endif
#endif

0 comments on commit 65eedc3

Please sign in to comment.