Skip to contentSkip to conversation

Use separate list for per-client push options

PATCH Superseded Lev Stipakov 4 participants 7 messages Oct 7, 2016 – Oct 11, 2016

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Use separate list for per-client push options

Move client-specific push options (currently peer-id and cipher) to separate list, which is deallocated after push_reply
has been send. This makes sure that options are fit into buf, not duplicated nor leak memory on renegotiation.

---
 src/openvpn/push.c | 142 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 83 insertions(+), 59 deletions(-)
src/openvpn/push.c +83 -59
index a1b999e..a968c29 100644
@@ -40,26 +40,29 @@
 
 #if P2MP
 
+static char push_reply_cmd[] = "PUSH_REPLY";
+
 /**
- * Add an option to the push list by providing a format string.
+ * Add an option to the given push list by providing a format string.
  *
  * The string added to the push options is allocated in o->gc, so the caller
  * does not have to preserve anything.
  *
- * @param o		The current connection's options
- * @param msglevel	The message level to use when printing errors
+ * @param gc		GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel  The message level to use when printing errors
  * @param fmt		Format string for the option
  * @param ...		Format string arguments
  *
  * @return true on success, false on failure.
  */
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, int msglevel,
     const char *fmt, ...)
 #ifdef __GNUC__
 #if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format (gnu_printf, 3, 4)))
+    __attribute__ ((format (gnu_printf, 4, 5)))
 #else
-    __attribute__ ((format (__printf__, 3, 4)))
+    __attribute__ ((format (__printf__, 4, 5)))
 #endif
 #endif
     ;
@@ -296,12 +299,14 @@ send_push_request (struct context *c)
  * Prepare push options, based on local options and available peer info.
  *
  * @param options	Connection options
+ * @param gc     	gc arena for allocating push options
+ * @param push_list     push list to where options are added
  * @param tls_multi	TLS state structure for the current tunnel
  *
  * @return true on success, false on failure.
  */
 static bool
-prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+prepare_push_reply (struct options *o, struct gc_arena *gc, struct push_list *push_list, struct tls_multi *tls_multi)
 {
   const char *optstr = NULL;
   const char * const peer_info = tls_multi->peer_info;
@@ -314,7 +319,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       int r = sscanf(optstr, "IV_PROTO=%d", &proto);
       if ((r == 1) && (proto >= 2))
 	{
-	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	  push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->peer_id);
 	}
     }
 
@@ -335,29 +340,63 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
 	{
 	  /* 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);
+	  char *push_cipher = string_alloc(o->ncp_ciphers, gc);
 	  o->ciphername = strtok (push_cipher, ":");
-	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+	  push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
 	}
     }
   return true;
 }
 
 static bool
-send_push_reply (struct context *c)
+send_push_options (struct context *c, struct buffer *buf, struct push_list *push_list,
+    int safe_cap, bool *push_sent, bool *multi_push)
+{
+  struct push_entry *e = push_list->head;
+
+  while (e)
+    {
+      if (e->enable)
+	{
+	  const int l = strlen (e->option);
+	  if (BLEN (buf) + l >= safe_cap)
+	    {
+	      buf_printf (buf, ",push-continuation 2");
+		{
+		  const bool status = send_control_channel_string (c, BSTR (buf), D_PUSH);
+		  if (!status)
+		    return false;
+		  *push_sent = true;
+		  *multi_push = true;
+		  buf_reset_len (buf);
+		  buf_printf (buf, "%s", push_reply_cmd);
+		}
+	    }
+	  if (BLEN (buf) + l >= safe_cap)
+	    {
+	      msg (M_WARN, "--push option is too long");
+	      return false;
+	    }
+	  buf_printf (buf, ",%s", e->option);
+	}
+      e = e->next;
+    }
+  return true;
+}
+
+static bool
+send_push_reply (struct context *c, struct push_list *per_client_push_list)
 {
   struct gc_arena gc = gc_new ();
   struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
-  struct push_entry *e = c->options.push_list.head;
   bool multi_push = false;
-  static char cmd[] = "PUSH_REPLY";
   const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
   const int safe_cap = BCAP (&buf) - extra;
   bool push_sent = false;
 
   msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
 
-  buf_printf (&buf, "%s", cmd);
+  buf_printf (&buf, "%s", push_reply_cmd);
 
   if ( c->c2.push_ifconfig_ipv6_defined &&
           !c->options.push_ifconfig_ipv6_blocked )
@@ -374,33 +413,13 @@ send_push_reply (struct context *c)
 	}
     }
 
-  while (e)
-    {
-      if (e->enable)
-	{
-	  const int l = strlen (e->option);
-	  if (BLEN (&buf) + l >= safe_cap)
-	    {
-	      buf_printf (&buf, ",push-continuation 2");
-	      {
-		const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
-		if (!status)
-		  goto fail;
-		push_sent = true;
-		multi_push = true;
-		buf_reset_len (&buf);
-		buf_printf (&buf, "%s", cmd);
-	      }
-	    }
-	  if (BLEN (&buf) + l >= safe_cap)
-	    {
-	      msg (M_WARN, "--push option is too long");
-	      goto fail;
-	    }
-	  buf_printf (&buf, ",%s", e->option);
-	}
-      e = e->next;
-    }
+  /* send options which are common tp all clients */
+  if (!send_push_options (c, &buf, &c->options.push_list, safe_cap, &push_sent, &multi_push))
+    goto fail;
+
+  /* send client-specific options */ 
+  if (!send_push_options (c, &buf, per_client_push_list, safe_cap, &push_sent, &multi_push))
+    goto fail;
 
   if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
     {
@@ -414,7 +433,7 @@ send_push_reply (struct context *c)
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  if (BLEN (&buf) > sizeof(cmd)-1)
+  if (BLEN (&buf) > sizeof(push_reply_cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
       if (!status)
@@ -430,7 +449,7 @@ send_push_reply (struct context *c)
       bool status = false;
 
       buf_reset_len (&buf);
-      buf_printf (&buf, "%s", cmd);
+      buf_printf (&buf, "%s", push_reply_cmd);
       status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
       if (!status)
 	goto fail;
@@ -445,7 +464,7 @@ send_push_reply (struct context *c)
 }
 
 static void
-push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
+push_option_ex (struct gc_arena *gc, struct push_list *push_list, const char *opt, bool enable, int msglevel)
 {
   if (!string_class (opt, CC_ANY, CC_COMMA))
     {
@@ -454,20 +473,20 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
   else
     {
       struct push_entry *e;
-      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
+      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc);
       e->enable = true;
       e->option = opt;
-      if (o->push_list.head)
+      if (push_list->head)
 	{
-	  ASSERT(o->push_list.tail);
-	  o->push_list.tail->next = e;
-	  o->push_list.tail = e;
+	  ASSERT(push_list->tail);
+	  push_list->tail->next = e;
+	  push_list->tail = e;
 	}
       else
 	{
-	  ASSERT(!o->push_list.tail);
-	  o->push_list.head = e;
-	  o->push_list.tail = e;
+	  ASSERT(!push_list->tail);
+	  push_list->head = e;
+	  push_list->tail = e;
 	}
     }
 }
@@ -475,7 +494,7 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
 void
 push_option (struct options *o, const char *opt, int msglevel)
 {
-  push_option_ex (o, opt, true, msglevel);
+  push_option_ex (&o->gc, &o->push_list, opt, true, msglevel);
 }
 
 void
@@ -487,7 +506,7 @@ clone_push_list (struct options *o)
       push_reset (o);
       while (e)
 	{
-	  push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
+	  push_option_ex (&o->gc, &o->push_list, string_alloc (e->option, &o->gc), true, M_FATAL);
 	  e = e->next;
 	}
     }
@@ -501,18 +520,18 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, int msglevel,
     const char *format, ...)
 {
   va_list arglist;
   char tmp[256] = {0};
-  int len = -1;
+  int len;
   va_start (arglist, format);
   len = vsnprintf (tmp, sizeof(tmp), format, arglist);
   va_end (arglist);
   if (len > sizeof(tmp)-1)
     return false;
-  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel);
   return true;
 }
 
@@ -580,12 +599,17 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
-	      send_push_reply (c))
+	  struct push_list push_list; /* per-client push options */
+	  struct gc_arena gc = gc_new ();
+
+	  CLEAR (push_list);
+	  if (prepare_push_reply (&c->options, &gc, &push_list, c->c2.tls_multi) &&
+	      send_push_reply (c, &push_list))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
 	    }
+	  gc_free(&gc);
 	}
     }
   else
Signature · 1 line
1.9.1
#1 lore raw

PATCH v2 1/1 Use separate list for per-client push options

v2:
- Also move ifconfig and ipv6-ifconfig to separate options list

Move client-specific push options (currently peer-id and cipher) to separate list, which is deallocated after push_reply
has been send. This makes sure that options fit into buf, not duplicated nor leak memory on renegotiation.

---
 src/openvpn/push.c | 186 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 104 insertions(+), 82 deletions(-)
src/openvpn/push.c +104 -82
index a1b999e..f7bcad1 100644
@@ -40,26 +40,29 @@
 
 #if P2MP
 
+static char push_reply_cmd[] = "PUSH_REPLY";
+
 /**
- * Add an option to the push list by providing a format string.
+ * Add an option to the given push list by providing a format string.
  *
  * The string added to the push options is allocated in o->gc, so the caller
  * does not have to preserve anything.
  *
- * @param o		The current connection's options
- * @param msglevel	The message level to use when printing errors
+ * @param gc		GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel  The message level to use when printing errors
  * @param fmt		Format string for the option
  * @param ...		Format string arguments
  *
  * @return true on success, false on failure.
  */
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, int msglevel,
     const char *fmt, ...)
 #ifdef __GNUC__
 #if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format (gnu_printf, 3, 4)))
+    __attribute__ ((format (gnu_printf, 4, 5)))
 #else
-    __attribute__ ((format (__printf__, 3, 4)))
+    __attribute__ ((format (__printf__, 4, 5)))
 #endif
 #endif
     ;
@@ -295,16 +298,39 @@ send_push_request (struct context *c)
 /**
  * Prepare push options, based on local options and available peer info.
  *
- * @param options	Connection options
- * @param tls_multi	TLS state structure for the current tunnel
+ * @param context	context structure storing data for 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.
  */
 static bool
-prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+prepare_push_reply (struct context *c, struct gc_arena *gc, struct push_list *push_list)
 {
   const char *optstr = NULL;
+  const struct tls_multi *tls_multi = c->c2.tls_multi;
   const char * const peer_info = tls_multi->peer_info;
+  struct options *o = &c->options;
+
+  /* ipv6 */
+  if (c->c2.push_ifconfig_ipv6_defined && !o->push_ifconfig_ipv6_blocked)
+    {
+      push_option_fmt (gc, push_list, M_USAGE, "ifconfig-ipv6 %s/%d %s",
+		       print_in6_addr (c->c2.push_ifconfig_ipv6_local, 0, gc),
+		       c->c2.push_ifconfig_ipv6_netbits,
+		       print_in6_addr (c->c2.push_ifconfig_ipv6_remote, 0, gc));
+    }
+
+  /* ipv4 */
+  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
+    {
+      in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
+      if (c->c2.push_ifconfig_local_alias)
+	ifconfig_local = c->c2.push_ifconfig_local_alias;
+      push_option_fmt (gc, push_list, M_USAGE, "ifconfig %s %s",
+		       print_in_addr_t (ifconfig_local, 0, gc),
+		       print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, gc));
+    }
 
   /* Send peer-id if client supports it */
   optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
@@ -314,7 +340,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       int r = sscanf(optstr, "IV_PROTO=%d", &proto);
       if ((r == 1) && (proto >= 2))
 	{
-	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	  push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->peer_id);
 	}
     }
 
@@ -324,7 +350,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       /* if we have already created our key, we cannot change our own
        * cipher, so disable NCP and warn = explain why
        */
-      struct tls_session *session = &tls_multi->session[TM_ACTIVE];
+      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 "
@@ -335,86 +361,76 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
 	{
 	  /* 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);
+	  char *push_cipher = string_alloc(o->ncp_ciphers, gc);
 	  o->ciphername = strtok (push_cipher, ":");
-	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+	  push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
 	}
     }
   return true;
 }
 
 static bool
-send_push_reply (struct context *c)
+send_push_options (struct context *c, struct buffer *buf, struct push_list *push_list,
+    int safe_cap, bool *push_sent, bool *multi_push)
 {
-  struct gc_arena gc = gc_new ();
-  struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
-  struct push_entry *e = c->options.push_list.head;
-  bool multi_push = false;
-  static char cmd[] = "PUSH_REPLY";
-  const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
-  const int safe_cap = BCAP (&buf) - extra;
-  bool push_sent = false;
-
-  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
-
-  buf_printf (&buf, "%s", cmd);
-
-  if ( c->c2.push_ifconfig_ipv6_defined &&
-          !c->options.push_ifconfig_ipv6_blocked )
-    {
-      /* IPv6 is put into buffer first, could be lengthy */
-      buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s",
-		    print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
-		    c->c2.push_ifconfig_ipv6_netbits,
-		    print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
-      if (BLEN (&buf) >= safe_cap)
-	{
-	  msg (M_WARN, "--push ifconfig-ipv6 option is too long");
-	  goto fail;
-	}
-    }
+  struct push_entry *e = push_list->head;
 
   while (e)
     {
       if (e->enable)
 	{
 	  const int l = strlen (e->option);
-	  if (BLEN (&buf) + l >= safe_cap)
+	  if (BLEN (buf) + l >= safe_cap)
 	    {
-	      buf_printf (&buf, ",push-continuation 2");
-	      {
-		const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
-		if (!status)
-		  goto fail;
-		push_sent = true;
-		multi_push = true;
-		buf_reset_len (&buf);
-		buf_printf (&buf, "%s", cmd);
-	      }
+	      buf_printf (buf, ",push-continuation 2");
+		{
+		  const bool status = send_control_channel_string (c, BSTR (buf), D_PUSH);
+		  if (!status)
+		    return false;
+		  *push_sent = true;
+		  *multi_push = true;
+		  buf_reset_len (buf);
+		  buf_printf (buf, "%s", push_reply_cmd);
+		}
 	    }
-	  if (BLEN (&buf) + l >= safe_cap)
+	  if (BLEN (buf) + l >= safe_cap)
 	    {
 	      msg (M_WARN, "--push option is too long");
-	      goto fail;
+	      return false;
 	    }
-	  buf_printf (&buf, ",%s", e->option);
+	  buf_printf (buf, ",%s", e->option);
 	}
       e = e->next;
     }
+  return true;
+}
+
+static bool
+send_push_reply (struct context *c, struct push_list *per_client_push_list)
+{
+  struct gc_arena gc = gc_new ();
+  struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
+  bool multi_push = false;
+  const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
+  const int safe_cap = BCAP (&buf) - extra;
+  bool push_sent = false;
+
+  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
+
+  buf_printf (&buf, "%s", push_reply_cmd);
+
+  /* send options which are common to all clients */
+  if (!send_push_options (c, &buf, &c->options.push_list, safe_cap, &push_sent, &multi_push))
+    goto fail;
+
+  /* send client-specific options */ 
+  if (!send_push_options (c, &buf, per_client_push_list, safe_cap, &push_sent, &multi_push))
+    goto fail;
 
-  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
-    {
-      in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
-      if (c->c2.push_ifconfig_local_alias)
-	ifconfig_local = c->c2.push_ifconfig_local_alias;
-      buf_printf (&buf, ",ifconfig %s %s",
-		  print_in_addr_t (ifconfig_local, 0, &gc),
-		  print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
-    }
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  if (BLEN (&buf) > sizeof(cmd)-1)
+  if (BLEN (&buf) > sizeof(push_reply_cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
       if (!status)
@@ -430,7 +446,7 @@ send_push_reply (struct context *c)
       bool status = false;
 
       buf_reset_len (&buf);
-      buf_printf (&buf, "%s", cmd);
+      buf_printf (&buf, "%s", push_reply_cmd);
       status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
       if (!status)
 	goto fail;
@@ -445,7 +461,7 @@ send_push_reply (struct context *c)
 }
 
 static void
-push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
+push_option_ex (struct gc_arena *gc, struct push_list *push_list, const char *opt, bool enable, int msglevel)
 {
   if (!string_class (opt, CC_ANY, CC_COMMA))
     {
@@ -454,20 +470,20 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
   else
     {
       struct push_entry *e;
-      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
+      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc);
       e->enable = true;
       e->option = opt;
-      if (o->push_list.head)
+      if (push_list->head)
 	{
-	  ASSERT(o->push_list.tail);
-	  o->push_list.tail->next = e;
-	  o->push_list.tail = e;
+	  ASSERT(push_list->tail);
+	  push_list->tail->next = e;
+	  push_list->tail = e;
 	}
       else
 	{
-	  ASSERT(!o->push_list.tail);
-	  o->push_list.head = e;
-	  o->push_list.tail = e;
+	  ASSERT(!push_list->tail);
+	  push_list->head = e;
+	  push_list->tail = e;
 	}
     }
 }
@@ -475,7 +491,7 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
 void
 push_option (struct options *o, const char *opt, int msglevel)
 {
-  push_option_ex (o, opt, true, msglevel);
+  push_option_ex (&o->gc, &o->push_list, opt, true, msglevel);
 }
 
 void
@@ -487,7 +503,7 @@ clone_push_list (struct options *o)
       push_reset (o);
       while (e)
 	{
-	  push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
+	  push_option_ex (&o->gc, &o->push_list, string_alloc (e->option, &o->gc), true, M_FATAL);
 	  e = e->next;
 	}
     }
@@ -501,18 +517,18 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, int msglevel,
     const char *format, ...)
 {
   va_list arglist;
   char tmp[256] = {0};
-  int len = -1;
+  int len;
   va_start (arglist, format);
   len = vsnprintf (tmp, sizeof(tmp), format, arglist);
   va_end (arglist);
   if (len > sizeof(tmp)-1)
     return false;
-  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel);
   return true;
 }
 
@@ -580,12 +596,18 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
-	      send_push_reply (c))
+	  /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */
+	  struct push_list push_list;
+	  struct gc_arena gc = gc_new ();
+
+	  CLEAR (push_list);
+	  if (prepare_push_reply (c, &gc, &push_list) &&
+	      send_push_reply (c, &push_list))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
 	    }
+	  gc_free(&gc);
 	}
     }
   else
Signature · 1 line
1.9.1
#2 lore raw

PATCH v3 1/1 Use separate list for per-client push options

v3:
- rebase on master

v2:
- Also move ifconfig and ipv6-ifconfig to separate options list

Move client-specific push options (currently peer-id and cipher) to separate list, which is deallocated after push_reply
has been send. This makes sure that options fit into buf, not duplicated nor leak memory on renegotiation.

---
 src/openvpn/push.c | 188 +++++++++++++++++++++++++++++------------------------
 1 file changed, 104 insertions(+), 84 deletions(-)
src/openvpn/push.c +104 -84
index df4f596..1f28826 100644
@@ -40,26 +40,29 @@
 
 #if P2MP
 
+static char push_reply_cmd[] = "PUSH_REPLY";
+
 /**
- * Add an option to the push list by providing a format string.
+ * Add an option to the given push list by providing a format string.
  *
  * The string added to the push options is allocated in o->gc, so the caller
  * does not have to preserve anything.
  *
- * @param o		The current connection's options
- * @param msglevel	The message level to use when printing errors
+ * @param gc		GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel  The message level to use when printing errors
  * @param fmt		Format string for the option
  * @param ...		Format string arguments
  *
  * @return true on success, false on failure.
  */
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, int msglevel,
     const char *fmt, ...)
 #ifdef __GNUC__
 #if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format (gnu_printf, 3, 4)))
+    __attribute__ ((format (gnu_printf, 4, 5)))
 #else
-    __attribute__ ((format (__printf__, 3, 4)))
+    __attribute__ ((format (__printf__, 4, 5)))
 #endif
 #endif
     ;
@@ -295,16 +298,39 @@ send_push_request (struct context *c)
 /**
  * Prepare push options, based on local options and available peer info.
  *
- * @param options	Connection options
- * @param tls_multi	TLS state structure for the current tunnel
+ * @param context	context structure storing data for 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.
  */
 static bool
-prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+prepare_push_reply (struct context *c, struct gc_arena *gc, struct push_list *push_list)
 {
   const char *optstr = NULL;
+  const struct tls_multi *tls_multi = c->c2.tls_multi;
   const char * const peer_info = tls_multi->peer_info;
+  struct options *o = &c->options;
+
+  /* ipv6 */
+  if (c->c2.push_ifconfig_ipv6_defined && !o->push_ifconfig_ipv6_blocked)
+    {
+      push_option_fmt (gc, push_list, M_USAGE, "ifconfig-ipv6 %s/%d %s",
+		       print_in6_addr (c->c2.push_ifconfig_ipv6_local, 0, gc),
+		       c->c2.push_ifconfig_ipv6_netbits,
+		       print_in6_addr (c->c2.push_ifconfig_ipv6_remote, 0, gc));
+    }
+
+  /* ipv4 */
+  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
+    {
+      in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
+      if (c->c2.push_ifconfig_local_alias)
+	ifconfig_local = c->c2.push_ifconfig_local_alias;
+      push_option_fmt (gc, push_list, M_USAGE, "ifconfig %s %s",
+		       print_in_addr_t (ifconfig_local, 0, gc),
+		       print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, gc));
+    }
 
   /* Send peer-id if client supports it */
   optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
@@ -314,8 +340,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       int r = sscanf(optstr, "IV_PROTO=%d", &proto);
       if ((r == 1) && (proto >= 2))
 	{
-	  push_remove_option(o, "peer-id");
-	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	  push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->peer_id);
 	}
     }
 
@@ -325,7 +350,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       /* if we have already created our key, we cannot change our own
        * cipher, so disable NCP and warn = explain why
        */
-      struct tls_session *session = &tls_multi->session[TM_ACTIVE];
+      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 "
@@ -336,87 +361,76 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
 	{
 	  /* 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);
+	  char *push_cipher = string_alloc(o->ncp_ciphers, gc);
 	  o->ciphername = strtok (push_cipher, ":");
-	  push_remove_option(o, "cipher");
-	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+	  push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
 	}
     }
   return true;
 }
 
 static bool
-send_push_reply (struct context *c)
+send_push_options (struct context *c, struct buffer *buf, struct push_list *push_list,
+    int safe_cap, bool *push_sent, bool *multi_push)
 {
-  struct gc_arena gc = gc_new ();
-  struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
-  struct push_entry *e = c->options.push_list.head;
-  bool multi_push = false;
-  static char cmd[] = "PUSH_REPLY";
-  const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
-  const int safe_cap = BCAP (&buf) - extra;
-  bool push_sent = false;
-
-  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
-
-  buf_printf (&buf, "%s", cmd);
-
-  if ( c->c2.push_ifconfig_ipv6_defined &&
-          !c->options.push_ifconfig_ipv6_blocked )
-    {
-      /* IPv6 is put into buffer first, could be lengthy */
-      buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s",
-		    print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
-		    c->c2.push_ifconfig_ipv6_netbits,
-		    print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
-      if (BLEN (&buf) >= safe_cap)
-	{
-	  msg (M_WARN, "--push ifconfig-ipv6 option is too long");
-	  goto fail;
-	}
-    }
+  struct push_entry *e = push_list->head;
 
   while (e)
     {
       if (e->enable)
 	{
 	  const int l = strlen (e->option);
-	  if (BLEN (&buf) + l >= safe_cap)
+	  if (BLEN (buf) + l >= safe_cap)
 	    {
-	      buf_printf (&buf, ",push-continuation 2");
-	      {
-		const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
-		if (!status)
-		  goto fail;
-		push_sent = true;
-		multi_push = true;
-		buf_reset_len (&buf);
-		buf_printf (&buf, "%s", cmd);
-	      }
+	      buf_printf (buf, ",push-continuation 2");
+		{
+		  const bool status = send_control_channel_string (c, BSTR (buf), D_PUSH);
+		  if (!status)
+		    return false;
+		  *push_sent = true;
+		  *multi_push = true;
+		  buf_reset_len (buf);
+		  buf_printf (buf, "%s", push_reply_cmd);
+		}
 	    }
-	  if (BLEN (&buf) + l >= safe_cap)
+	  if (BLEN (buf) + l >= safe_cap)
 	    {
 	      msg (M_WARN, "--push option is too long");
-	      goto fail;
+	      return false;
 	    }
-	  buf_printf (&buf, ",%s", e->option);
+	  buf_printf (buf, ",%s", e->option);
 	}
       e = e->next;
     }
+  return true;
+}
+
+static bool
+send_push_reply (struct context *c, struct push_list *per_client_push_list)
+{
+  struct gc_arena gc = gc_new ();
+  struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
+  bool multi_push = false;
+  const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
+  const int safe_cap = BCAP (&buf) - extra;
+  bool push_sent = false;
+
+  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
+
+  buf_printf (&buf, "%s", push_reply_cmd);
+
+  /* send options which are common to all clients */
+  if (!send_push_options (c, &buf, &c->options.push_list, safe_cap, &push_sent, &multi_push))
+    goto fail;
+
+  /* send client-specific options */ 
+  if (!send_push_options (c, &buf, per_client_push_list, safe_cap, &push_sent, &multi_push))
+    goto fail;
 
-  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
-    {
-      in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
-      if (c->c2.push_ifconfig_local_alias)
-	ifconfig_local = c->c2.push_ifconfig_local_alias;
-      buf_printf (&buf, ",ifconfig %s %s",
-		  print_in_addr_t (ifconfig_local, 0, &gc),
-		  print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
-    }
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  if (BLEN (&buf) > sizeof(cmd)-1)
+  if (BLEN (&buf) > sizeof(push_reply_cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
       if (!status)
@@ -432,7 +446,7 @@ send_push_reply (struct context *c)
       bool status = false;
 
       buf_reset_len (&buf);
-      buf_printf (&buf, "%s", cmd);
+      buf_printf (&buf, "%s", push_reply_cmd);
       status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
       if (!status)
 	goto fail;
@@ -447,7 +461,7 @@ send_push_reply (struct context *c)
 }
 
 static void
-push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
+push_option_ex (struct gc_arena *gc, struct push_list *push_list, const char *opt, bool enable, int msglevel)
 {
   if (!string_class (opt, CC_ANY, CC_COMMA))
     {
@@ -456,20 +470,20 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
   else
     {
       struct push_entry *e;
-      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
+      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc);
       e->enable = true;
       e->option = opt;
-      if (o->push_list.head)
+      if (push_list->head)
 	{
-	  ASSERT(o->push_list.tail);
-	  o->push_list.tail->next = e;
-	  o->push_list.tail = e;
+	  ASSERT(push_list->tail);
+	  push_list->tail->next = e;
+	  push_list->tail = e;
 	}
       else
 	{
-	  ASSERT(!o->push_list.tail);
-	  o->push_list.head = e;
-	  o->push_list.tail = e;
+	  ASSERT(!push_list->tail);
+	  push_list->head = e;
+	  push_list->tail = e;
 	}
     }
 }
@@ -477,7 +491,7 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
 void
 push_option (struct options *o, const char *opt, int msglevel)
 {
-  push_option_ex (o, opt, true, msglevel);
+  push_option_ex (&o->gc, &o->push_list, opt, true, msglevel);
 }
 
 void
@@ -489,7 +503,7 @@ clone_push_list (struct options *o)
       push_reset (o);
       while (e)
 	{
-	  push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
+	  push_option_ex (&o->gc, &o->push_list, string_alloc (e->option, &o->gc), true, M_FATAL);
 	  e = e->next;
 	}
     }
@@ -503,18 +517,18 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
-static bool push_option_fmt(struct options *o, int msglevel,
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, int msglevel,
     const char *format, ...)
 {
   va_list arglist;
   char tmp[256] = {0};
-  int len = -1;
+  int len;
   va_start (arglist, format);
   len = vsnprintf (tmp, sizeof(tmp), format, arglist);
   va_end (arglist);
   if (len > sizeof(tmp)-1)
     return false;
-  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel);
   return true;
 }
 
@@ -582,12 +596,18 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
-	      send_push_reply (c))
+	  /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */
+	  struct push_list push_list;
+	  struct gc_arena gc = gc_new ();
+
+	  CLEAR (push_list);
+	  if (prepare_push_reply (c, &gc, &push_list) &&
+	      send_push_reply (c, &push_list))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
 	    }
+	  gc_free(&gc);
 	}
     }
   else
Signature · 1 line
1.9.1
#3 lore raw

Hi,

Quoted 4 lines · Lev Stipakov

Move client-specific push options (currently peer-id and cipher) to separate list, which is deallocated after push_reply
has been send. This makes sure that options fit into buf, not duplicated nor leak memory on renegotiation.

Feature-ACK. Very needed refactoring.

But some very minor comments:

Quoted 6 lines

- * @param o The current connection's options
- * @param msglevel The message level to use when printing errors
+ * @param gc GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel The message level to use when printing errors
* @param fmt Format string for the option

Some whitespace inconsistencies here (looks funny with ts=8).

src/openvpn/push.c send_push_request (struct context *c)

Quoted 5 lines
src/openvpn/push.c +3 -0
+prepare_push_reply (struct context *c, struct gc_arena *gc, struct push_list *push_list)
 
+  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
 
+	  push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", tls_multi->peer_id);

These lines exceed 80 chars (and won't get much harder to read from wrapping), so please wrap them.

I ran some smoke tests, but my jenkins broke down do didn't test very thoroughly. Still, the code looks very reasonable and smoke tests succeed, so ACK if the above nitpicks are taken care of.

-Steffan

#4 lore raw

PATCH v4 1/1 Use separate list for per-client push options

v4:
- fix whitespaces, wrap long lines

v3:
- rebase on master

v2:
- Also move ifconfig and ipv6-ifconfig to separate options list

Move client-specific push options (currently peer-id and cipher) to separate list, which is deallocated after push_reply
has been send. This makes sure that options fit into buf, not duplicated nor leak memory on renegotiation.

Fix whitespaces and wrap long lines

---
 src/openvpn/push.c | 206 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 118 insertions(+), 88 deletions(-)
src/openvpn/push.c +118 -88
index df4f596..145fb7d 100644
@@ -40,26 +40,29 @@
 
 #if P2MP
 
+static char push_reply_cmd[] = "PUSH_REPLY";
+
 /**
- * Add an option to the push list by providing a format string.
+ * Add an option to the given push list by providing a format string.
  *
  * The string added to the push options is allocated in o->gc, so the caller
  * does not have to preserve anything.
  *
- * @param o		The current connection's options
- * @param msglevel	The message level to use when printing errors
- * @param fmt		Format string for the option
- * @param ...		Format string arguments
+ * @param gc        GC arena where options are allocated
+ * @param push_list Push list containing options
+ * @param msglevel  The message level to use when printing errors
+ * @param fmt       Format string for the option
+ * @param ...       Format string arguments
  *
  * @return true on success, false on failure.
  */
-static bool push_option_fmt(struct options *o, int msglevel,
-    const char *fmt, ...)
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
+			    int msglevel, const char *fmt, ...)
 #ifdef __GNUC__
 #if __USE_MINGW_ANSI_STDIO
-    __attribute__ ((format (gnu_printf, 3, 4)))
+    __attribute__ ((format (gnu_printf, 4, 5)))
 #else
-    __attribute__ ((format (__printf__, 3, 4)))
+    __attribute__ ((format (__printf__, 4, 5)))
 #endif
 #endif
     ;
@@ -295,16 +298,43 @@ send_push_request (struct context *c)
 /**
  * Prepare push options, based on local options and available peer info.
  *
- * @param options	Connection options
- * @param tls_multi	TLS state structure for the current tunnel
+ * @param context	context structure storing data for 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.
  */
 static bool
-prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
+prepare_push_reply (struct context *c, struct gc_arena *gc,
+		    struct push_list *push_list)
 {
   const char *optstr = NULL;
+  const struct tls_multi *tls_multi = c->c2.tls_multi;
   const char * const peer_info = tls_multi->peer_info;
+  struct options *o = &c->options;
+
+  /* ipv6 */
+  if (c->c2.push_ifconfig_ipv6_defined && !o->push_ifconfig_ipv6_blocked)
+    {
+      push_option_fmt (gc, push_list, M_USAGE, "ifconfig-ipv6 %s/%d %s",
+		       print_in6_addr (c->c2.push_ifconfig_ipv6_local, 0, gc),
+		       c->c2.push_ifconfig_ipv6_netbits,
+		       print_in6_addr (c->c2.push_ifconfig_ipv6_remote,
+				       0, gc));
+    }
+
+  /* ipv4 */
+  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local &&
+      c->c2.push_ifconfig_remote_netmask)
+    {
+      in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
+      if (c->c2.push_ifconfig_local_alias)
+	ifconfig_local = c->c2.push_ifconfig_local_alias;
+      push_option_fmt (gc, push_list, M_USAGE, "ifconfig %s %s",
+		       print_in_addr_t (ifconfig_local, 0, gc),
+		       print_in_addr_t (c->c2.push_ifconfig_remote_netmask,
+					0, gc));
+    }
 
   /* Send peer-id if client supports it */
   optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL;
@@ -314,8 +344,8 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       int r = sscanf(optstr, "IV_PROTO=%d", &proto);
       if ((r == 1) && (proto >= 2))
 	{
-	  push_remove_option(o, "peer-id");
-	  push_option_fmt(o, M_USAGE, "peer-id %d", tls_multi->peer_id);
+	  push_option_fmt(gc, push_list, M_USAGE, "peer-id %d",
+			  tls_multi->peer_id);
 	}
     }
 
@@ -325,7 +355,7 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
       /* if we have already created our key, we cannot change our own
        * cipher, so disable NCP and warn = explain why
        */
-      struct tls_session *session = &tls_multi->session[TM_ACTIVE];
+      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 "
@@ -336,87 +366,79 @@ prepare_push_reply (struct options *o, struct tls_multi *tls_multi)
 	{
 	  /* 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);
+	  char *push_cipher = string_alloc(o->ncp_ciphers, gc);
 	  o->ciphername = strtok (push_cipher, ":");
-	  push_remove_option(o, "cipher");
-	  push_option_fmt(o, M_USAGE, "cipher %s", o->ciphername);
+	  push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
 	}
     }
   return true;
 }
 
 static bool
-send_push_reply (struct context *c)
+send_push_options (struct context *c, struct buffer *buf,
+		   struct push_list *push_list, int safe_cap,
+		   bool *push_sent, bool *multi_push)
 {
-  struct gc_arena gc = gc_new ();
-  struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
-  struct push_entry *e = c->options.push_list.head;
-  bool multi_push = false;
-  static char cmd[] = "PUSH_REPLY";
-  const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
-  const int safe_cap = BCAP (&buf) - extra;
-  bool push_sent = false;
-
-  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
-
-  buf_printf (&buf, "%s", cmd);
-
-  if ( c->c2.push_ifconfig_ipv6_defined &&
-          !c->options.push_ifconfig_ipv6_blocked )
-    {
-      /* IPv6 is put into buffer first, could be lengthy */
-      buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s",
-		    print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc),
-		    c->c2.push_ifconfig_ipv6_netbits,
-		    print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) );
-      if (BLEN (&buf) >= safe_cap)
-	{
-	  msg (M_WARN, "--push ifconfig-ipv6 option is too long");
-	  goto fail;
-	}
-    }
+  struct push_entry *e = push_list->head;
 
   while (e)
     {
       if (e->enable)
 	{
 	  const int l = strlen (e->option);
-	  if (BLEN (&buf) + l >= safe_cap)
+	  if (BLEN (buf) + l >= safe_cap)
 	    {
-	      buf_printf (&buf, ",push-continuation 2");
-	      {
-		const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
-		if (!status)
-		  goto fail;
-		push_sent = true;
-		multi_push = true;
-		buf_reset_len (&buf);
-		buf_printf (&buf, "%s", cmd);
-	      }
+	      buf_printf (buf, ",push-continuation 2");
+		{
+		  const bool status = send_control_channel_string (c, BSTR (buf), D_PUSH);
+		  if (!status)
+		    return false;
+		  *push_sent = true;
+		  *multi_push = true;
+		  buf_reset_len (buf);
+		  buf_printf (buf, "%s", push_reply_cmd);
+		}
 	    }
-	  if (BLEN (&buf) + l >= safe_cap)
+	  if (BLEN (buf) + l >= safe_cap)
 	    {
 	      msg (M_WARN, "--push option is too long");
-	      goto fail;
+	      return false;
 	    }
-	  buf_printf (&buf, ",%s", e->option);
+	  buf_printf (buf, ",%s", e->option);
 	}
       e = e->next;
     }
+  return true;
+}
+
+static bool
+send_push_reply (struct context *c, struct push_list *per_client_push_list)
+{
+  struct gc_arena gc = gc_new ();
+  struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
+  bool multi_push = false;
+  const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
+  const int safe_cap = BCAP (&buf) - extra;
+  bool push_sent = false;
+
+  msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap );
+
+  buf_printf (&buf, "%s", push_reply_cmd);
+
+  /* send options which are common to all clients */
+  if (!send_push_options (c, &buf, &c->options.push_list, safe_cap,
+			  &push_sent, &multi_push))
+    goto fail;
+
+  /* send client-specific options */ 
+  if (!send_push_options (c, &buf, per_client_push_list, safe_cap,
+			  &push_sent, &multi_push))
+    goto fail;
 
-  if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
-    {
-      in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
-      if (c->c2.push_ifconfig_local_alias)
-	ifconfig_local = c->c2.push_ifconfig_local_alias;
-      buf_printf (&buf, ",ifconfig %s %s",
-		  print_in_addr_t (ifconfig_local, 0, &gc),
-		  print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
-    }
   if (multi_push)
     buf_printf (&buf, ",push-continuation 1");
 
-  if (BLEN (&buf) > sizeof(cmd)-1)
+  if (BLEN (&buf) > sizeof(push_reply_cmd)-1)
     {
       const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
       if (!status)
@@ -432,7 +454,7 @@ send_push_reply (struct context *c)
       bool status = false;
 
       buf_reset_len (&buf);
-      buf_printf (&buf, "%s", cmd);
+      buf_printf (&buf, "%s", push_reply_cmd);
       status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
       if (!status)
 	goto fail;
@@ -447,7 +469,8 @@ send_push_reply (struct context *c)
 }
 
 static void
-push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
+push_option_ex (struct gc_arena *gc, struct push_list *push_list,
+		const char *opt, bool enable, int msglevel)
 {
   if (!string_class (opt, CC_ANY, CC_COMMA))
     {
@@ -456,20 +479,20 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
   else
     {
       struct push_entry *e;
-      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
+      ALLOC_OBJ_CLEAR_GC (e, struct push_entry, gc);
       e->enable = true;
       e->option = opt;
-      if (o->push_list.head)
+      if (push_list->head)
 	{
-	  ASSERT(o->push_list.tail);
-	  o->push_list.tail->next = e;
-	  o->push_list.tail = e;
+	  ASSERT(push_list->tail);
+	  push_list->tail->next = e;
+	  push_list->tail = e;
 	}
       else
 	{
-	  ASSERT(!o->push_list.tail);
-	  o->push_list.head = e;
-	  o->push_list.tail = e;
+	  ASSERT(!push_list->tail);
+	  push_list->head = e;
+	  push_list->tail = e;
 	}
     }
 }
@@ -477,7 +500,7 @@ push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
 void
 push_option (struct options *o, const char *opt, int msglevel)
 {
-  push_option_ex (o, opt, true, msglevel);
+  push_option_ex (&o->gc, &o->push_list, opt, true, msglevel);
 }
 
 void
@@ -489,7 +512,8 @@ clone_push_list (struct options *o)
       push_reset (o);
       while (e)
 	{
-	  push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
+	  push_option_ex (&o->gc, &o->push_list,
+			  string_alloc (e->option, &o->gc), true, M_FATAL);
 	  e = e->next;
 	}
     }
@@ -503,18 +527,18 @@ push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
   push_option (o, opt, msglevel);
 }
 
-static bool push_option_fmt(struct options *o, int msglevel,
-    const char *format, ...)
+static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
+			    int msglevel, const char *format, ...)
 {
   va_list arglist;
   char tmp[256] = {0};
-  int len = -1;
+  int len;
   va_start (arglist, format);
   len = vsnprintf (tmp, sizeof(tmp), format, arglist);
   va_end (arglist);
   if (len > sizeof(tmp)-1)
     return false;
-  push_option (o, string_alloc (tmp, &o->gc), msglevel);
+  push_option_ex (gc, push_list, string_alloc (tmp, gc), true, msglevel);
   return true;
 }
 
@@ -582,12 +606,18 @@ process_incoming_push_request (struct context *c)
 	}
       else
 	{
-	  if (prepare_push_reply(&c->options, c->c2.tls_multi) &&
-	      send_push_reply (c))
+	  /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */
+	  struct push_list push_list;
+	  struct gc_arena gc = gc_new ();
+
+	  CLEAR (push_list);
+	  if (prepare_push_reply (c, &gc, &push_list) &&
+	      send_push_reply (c, &push_list))
 	    {
 	      ret = PUSH_MSG_REQUEST;
 	      c->c2.sent_push_reply_expiry = now + 30;
 	    }
+	  gc_free(&gc);
 	}
     }
   else
Signature · 1 line
1.9.1
#5 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Your patch has been applied to the master branch.

commit 9bc893d0d365be0bd2911d91a004be8b3739307b

Author: Lev Stipakov
Date:   Tue Oct 11 11:03:50 2016 +0300
     Use separate list for per-client push options

     Signed-off-by: Lev Stipakov <[email protected]...>
     Acked-by: Steffan Karger <[email protected]...>
     Message-Id: <[email protected]...>
     URL: http://www.mail-archive.com/search?l=mid&[email protected]...
     Signed-off-by: David Sommerseth <[email protected]...>

- --
kind regards,

David Sommerseth

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJX/NczAAoJEIbPlEyWcf3y7toP/02oE4s8XexHWf5sEqGlqHXg GtbLPsOj9Yj7TgP7vF9i0/UOOIu/zeOf8/T+BUD8WSPCndNEITuIkI/BVHYi6vbG P0lR2geQJegZSzRgeMD8LDPSp2SQWmJ1Y44tOTj1fdlEFadLVhBoEE4yF0UcOmB1 iRdp8mRLzohkRdbqjj0T1tWIwK2UdFIdEkkcxqDDcb6Xaq+N93BAVegBm9qvEIov wNGLUy9cbsSs+GRQYBySVSgdXW3RmizyuCIPNC/Ssxxvi709omSZG5hQAE3clymk IqIEUqd4YmL2pG1dAPt8Rw0qC39L6O3t9Lp0ICfuy/373kX4F+iki5GRS/ck0BFy sYUrC8YOi2dbpw5JLPKzag5p+YvTqRms8r+J4N4ZLZFsbYfX65Sm1JRUZ4S1RrE4 Uk0JMxYlKt8Ur8Y7wiMWsEriqOSJpLLHo/m0r1vjOgui0+DYvQvI97B+BGS+HH+S 2qyAu7jhsug0pmLt0/DJDlMPHxeRZq3fKhGWHz6EpaNHUajiK/ePi1OJ1jdFDh0x AfHT1pg5OYIywUFb/N0tByxktqSlYoAF2BiJWXvL6LsVokGlEaDGqKaHZ07RbXrK k4eOJ5UgEJ34OxfqpQ0r49DbRMnW9Ydblequ+22pNOCpPv/oMQJCkrmEwYibQR0q EBzGqTNm5lKy1l0lybMy
=dkrZ
-----END PGP SIGNATURE-----

#6 lore raw

I sent this too quick. I just slightly modified the commit message (removing a duplicated sentence after the signed-off-by line). So the proper commitish is

commit 88c4b9d6ad1d346931d9090b247e9e3d2d86bce7

Quoted 6 lines · David Sommerseth

Your patch has been applied to the master branch.

commit 9bc893d0d365be0bd2911d91a004be8b3739307b Author: Lev
Stipakov Date: Tue Oct 11 11:03:50 2016 +0300

Quoted 8 lines

Use separate list for per-client push options

Karger <[email protected]...> Message-Id:
<[email protected]...> URL:
http://www.mail-archive.com/search?l=mid&[email protected]...

Quoted 10 lines

-- kind regards,

David Sommerseth

------------------------------------------------------------------------------

Check out the vibrant tech community on one of the world's most

Quoted 5 lines

engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________ Openvpn-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Signature · 3 lines
kind regards,

David Sommerseth