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.

Signed-off-by: Lev Stipakov <lstipa...@gmail.com>
---
 src/openvpn/push.c | 142 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 83 insertions(+), 59 deletions(-)

diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index a1b999e..a968c29 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -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
-- 
1.9.1


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to