Add support for floating in tls mode using the HMAC of a packet. It costs
a roundtrip through the clients. Because it is based on the HMAC, it is
secure in ways of cryptography. Before the iteration starts, some sanity
checks are done.
It would be nice if you could take a look into the 
multi_find_instance_udp function. I'm unsure if there is a gc missing.

In the last days I had no failures in several installations.

Signed-off-by: André Valentin <avalen...@marcant.net>
---
 src/openvpn/crypto.c  |   55 ++++++++++++++++++++++++
 src/openvpn/crypto.h  |    4 ++
 src/openvpn/mudp.c    |  112 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/mudp.h    |   18 ++++++++
 src/openvpn/options.c |    3 ++
 src/openvpn/perf.h    |    2 +
 src/openvpn/ssl.c     |   28 +++++++++++++
 src/openvpn/ssl.h     |    6 +++
 8 files changed, 228 insertions(+)

diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c
index c4c356d..6446eee 100644
--- a/src/openvpn/crypto.c
+++ b/src/openvpn/crypto.c
@@ -389,6 +389,61 @@ openvpn_decrypt (struct buffer *buf, struct buffer work,
 }
 
 /*
+ * This verifies if a packet and its HMAC fit to a crypto context.
+ *
+ * On success true is returned.
+ */
+bool
+crypto_test_hmac (struct buffer *buf,
+          const struct crypto_options *opt)
+{
+  struct gc_arena gc;
+  gc_init (&gc);
+  int offset = 1;
+
+  if (buf->len > 0 && opt->key_ctx_bi)
+    {
+      struct key_ctx *ctx = &opt->key_ctx_bi->decrypt;
+
+      /* Verify the HMAC */
+      if (ctx->hmac)
+        {
+          int hmac_len;
+          uint8_t local_hmac[MAX_HMAC_KEY_LENGTH]; /* HMAC of ciphertext 
computed locally */
+
+          hmac_ctx_reset(ctx->hmac);
+
+          /* Assume the length of the input HMAC */
+          hmac_len = hmac_ctx_size (ctx->hmac);
+
+          /* Authentication fails if insufficient data in packet for HMAC */
+          if ((buf->len - offset) < hmac_len) 
+            {
+              gc_free (&gc);
+              return false;
+            }
+
+          hmac_ctx_update (ctx->hmac, BPTR (buf) + offset + hmac_len, BLEN 
(buf) - offset - hmac_len);
+          hmac_ctx_final (ctx->hmac, local_hmac);
+
+          /* Compare locally computed HMAC with packet HMAC */
+          if (memcmp (local_hmac, BPTR (buf) + offset, hmac_len))
+            {
+              gc_free (&gc);
+              return false;
+            }
+            
+          gc_free (&gc);
+          return true;
+        }
+    }
+
+  gc_free (&gc);
+  return false;
+}
+
+
+/*
  * How many bytes will we add to frame buffer for a given
  * set of crypto options?
  */
diff --git a/src/openvpn/crypto.h b/src/openvpn/crypto.h
index 3b4b88e..296519a 100644
--- a/src/openvpn/crypto.h
+++ b/src/openvpn/crypto.h
@@ -279,6 +279,10 @@ bool openvpn_decrypt (struct buffer *buf, struct buffer 
work,
                      const struct crypto_options *opt,
                      const struct frame* frame);
 
+
+bool crypto_test_hmac (struct buffer *buf,
+           const struct crypto_options *opt);
+           
 /** @} name Functions for performing security operations on data channel 
packets */
 
 void crypto_adjust_frame_parameters(struct frame *frame,
diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c
index 3468dab..9fb4fb5 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -63,6 +63,12 @@ multi_get_create_instance_udp (struct multi_context *m)
        {
          mi = (struct multi_instance *) he->value;
        }
+      else if (multi_find_instance_udp (m,  mi, real))
+        {
+          /* found instance */
+          msg (D_MULTI_LOW, "MULTI: Floated with HMAC authentication to a new 
client address: %s", 
+               print_link_socket_actual (&m->top.c2.from, &gc));
+        }
       else
        {
          if (!m->top.c2.tls_auth_standalone
@@ -111,6 +117,112 @@ multi_get_create_instance_udp (struct multi_context *m)
 }
 
 /*
+ * Find a client instance based on the HMAC, if auth is used. The function 
+ * iterates over all peers to find a fitting instance. The found instance is
+ * updated with the current peer address.
+ * If the instance doesn't exist, return false.
+ */
+bool
+multi_find_instance_udp (struct multi_context *m,  struct multi_instance *mi, 
+                         struct mroute_addr real)
+{
+  struct gc_arena gc = gc_new ();
+  struct hash *hash = m->hash;
+  struct hash_element *he;
+  const uint32_t hv = hash_value (hash, &real);
+  struct hash_bucket *bucket = hash_bucket (hash, hv);
+  struct hash_iterator hi;
+  struct mroute_addr real_old;
+  int op;
+  uint8_t c;
+  
+  perf_push (PERF_MULTI_FIND_INSTANCE);
+  
+  /* try to detect client floating */
+  if (!m->top.options.ce.remote_float 
+      || !m->top.options.authname_defined)
+   goto err;
+
+  /* minimum size 1 byte */
+  if (m->top.c2.buf.len < 1)
+    goto err;
+
+  /* Only accept DATA_V1 opcode */
+  c = *BPTR (&m->top.c2.buf);
+  op = c >> P_OPCODE_SHIFT;
+  if (op != P_DATA_V1)
+    goto err;
+
+  hash_iterator_init (hash, &hi);
+  while ((he = hash_iterator_next (&hi)))
+    {
+      mi = (struct multi_instance *) he->value;
+    
+      /* verify if this instance allows hmac verification */
+      if (!crypto_test_hmac (&m->top.c2.buf, &mi->context.c2.crypto_options))
+        continue;
+
+      generate_prefix (mi);
+      msg (D_MULTI_MEDIUM, "MULTI: Detected floating by hmac test, new client 
address: %s", 
+           print_link_socket_actual (&m->top.c2.from, &gc));
+
+      /* update address */
+      real_old = mi->real;
+      mi->real = real;
+      mi->context.c1.link_socket_addr = m->top.c1.link_socket_addr;
+      mi->context.c2.from = m->top.c2.from;             
+      mi->context.c2.to_link_addr = &mi->context.c2.from;
+  
+      /* switch to new log prefix */
+      generate_prefix (mi);
+  
+      /* inherit buffers */
+      mi->context.c2.buffers = m->top.c2.buffers;
+
+      /* inherit parent link_socket and link_socket_info */
+      mi->context.c2.link_socket = m->top.c2.link_socket;
+      mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from;
+    
+      /* fix remote_addr in tls structure */
+      tls_update_remote_addr (mi->context.c2.tls_multi, &mi->context.c2.from);
+
+      mi->did_open_context = true;
+      if (IS_SIG (&mi->context))
+        goto errloop;
+
+      /* remove and readd this instance under the new address */
+      hash_iterator_delete_element (&hi);
+      hash_add_fast (hash, bucket, &mi->real, hv, mi);
+      hash_remove (m->iter, &real_old);
+      hash_add (m->iter, &mi->real, mi, false);
+#ifdef MANAGEMENT_DEF_AUTH
+      hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid);
+      hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, false);
+#endif
+
+      /* enforce update */
+      mi->did_real_hash = true;
+      mi->did_iter = true;
+#ifdef MANAGEMENT_DEF_AUTH
+      mi->did_cid_hash = true;
+#endif
+      
+      /* cleanup */
+      hash_iterator_free (&hi);
+      perf_pop ();
+      gc_free (&gc);
+      return true;
+   }
+
+  errloop:   
+    hash_iterator_free (&hi);
+  err:
+    perf_pop ();
+    gc_free (&gc);
+    return false;
+}
+
+/*
  * Send a packet to TCP/UDP socket.
  */
 static inline void
diff --git a/src/openvpn/mudp.h b/src/openvpn/mudp.h
index 97f961b..759ea6d 100644
--- a/src/openvpn/mudp.h
+++ b/src/openvpn/mudp.h
@@ -67,5 +67,23 @@ void tunnel_server_udp (struct context *top);
  */
 struct multi_instance *multi_get_create_instance_udp (struct multi_context *m);
 
+
+/**************************************************************************/
+/**
+ * Find a client instance based on the HMAC, if auth is used.
+ * @ingroup external_multiplexer
+ *
+ * Find a client instance based on the HMAC, if auth is used. The function 
+ * iterates over all peers to find a fitting instance. The found instance is
+ * updated with the current peer address.
+ *  
+ * @param m            - The single multi_context structure.
+ * @param mi           - The multi_instance structure.
+ * @param real         - The mroute_addr structure.
+ *
+ * @return Boolen, true if peer found, false if not.
+ */
+bool multi_find_instance_udp (struct multi_context *m,  struct multi_instance 
*mi, struct mroute_addr real);
+
 #endif
 #endif
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 4d0271f..e14a1b7 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -166,6 +166,9 @@ static const char usage_message[] =
   "                  Set n=\"infinite\" to retry indefinitely.\n"
   "--float         : Allow remote to change its IP address/port, such as 
through\n"
   "                  DHCP (this is the default if --remote is not used).\n"
+#ifdef ENABLE_CRYPTO
+  "                  In server mode a valid/default auth algo is needed.\n"
+#endif
   "--ipchange cmd  : Run command cmd on remote ip address initial\n"
   "                  setting or change -- execute as: cmd ip-address port#\n"
   "--port port     : TCP/UDP port # for both local and remote.\n"
diff --git a/src/openvpn/perf.h b/src/openvpn/perf.h
index c531d9c..e1121d2 100644
--- a/src/openvpn/perf.h
+++ b/src/openvpn/perf.h
@@ -57,6 +57,8 @@
 #define PERF_PROC_OUT_TUN           18
 #define PERF_PROC_OUT_TUN_MTCP      19
 #define PERF_N                      20
+#define PERF_MULTI_FIND_INSTANCE    21
+
 
 #ifdef ENABLE_PERFORMANCE_METRICS
 
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 69f77f3..709edf7 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -3454,6 +3454,34 @@ tls_rec_payload (struct tls_multi *multi,
   return ret;
 }
 
+/* Update the remote_addr, needed if a client floats. */
+void
+tls_update_remote_addr (struct tls_multi *multi,
+               const struct link_socket_actual *from)
+{
+  struct gc_arena gc = gc_new ();
+  int i;
+  
+  for (i = 0; i < KEY_SCAN_SIZE; ++i)
+    {
+      struct key_state *ks = multi->key_scan[i];
+      if (DECRYPT_KEY_ENABLED (multi, ks)
+         && ks->authenticated
+         && link_socket_actual_defined(&ks->remote_addr)
+      )
+       {
+         if (link_socket_actual_match (from, &ks->remote_addr))
+           continue;
+          dmsg (D_TLS_KEYSELECT,
+              "TLS: tls_update_remote_addr from IP=%s to IP=%s",
+              print_link_socket_actual (&ks->remote_addr, &gc),
+              print_link_socket_actual (from, &gc));
+          ks->remote_addr = *from;
+       }
+    }
+  gc_free (&gc);
+}
+
 /*
  * Dump a human-readable rendition of an openvpn packet
  * into a garbage collectable string which is returned.
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index cd7cae2..c501b64 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -431,6 +431,12 @@ bool tls_send_payload (struct tls_multi *multi,
 bool tls_rec_payload (struct tls_multi *multi,
                      struct buffer *buf);
 
+/*
+ * Update remote address of a tls_multi structure
+ */
+void tls_update_remote_addr (struct tls_multi *multi,
+                 const struct link_socket_actual *from);
+                 
 #ifdef MANAGEMENT_DEF_AUTH
 static inline char *
 tls_get_peer_info(const struct tls_multi *multi)
-- 
1.7.10.4


Reply via email to