]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Added argv_x functions to buffer.[ch] to be used to safely build
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Wed, 23 Jul 2008 19:51:27 +0000 (19:51 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Wed, 23 Jul 2008 19:51:27 +0000 (19:51 +0000)
up argv strings for execve without the possibility of truncation
or misinterpretation of mid-argument spacing.

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@3107 e7ae566f-a301-0410-adde-c780ea21d3b5

buffer.c
buffer.h
init.c

index 498861ec96f1a17f448cf1d761c9cc65a0301862..c7eab0796fe36d4e10cbd860f5627bd9030e0950 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -226,6 +226,230 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
   return ret;
 }
 
+/*
+ * A printf-like function (that only recognizes a subset of standard printf
+ * format operators) that prints arguments to an argv list instead
+ * of a standard string.  This is used to build up argv arrays for passing
+ * to execve.
+ */
+
+void
+argv_init (struct argv *a)
+{
+  a->argc = 0;
+  a->argv = NULL;
+}
+
+void
+argv_reset (struct argv *a)
+{
+  size_t i;
+  for (i = 0; i < a->argc; ++i)
+    free (a->argv[i]);
+  free (a->argv);
+  a->argc = 0;
+  a->argv = NULL;
+}
+
+size_t
+argv_argc (const char *format)
+{
+  char *term;
+  const char *f = format;
+  size_t argc = 0;
+
+  while ((term = argv_term (&f)) != NULL) 
+    {
+      ++argc;
+      free (term);
+    }
+  return argc;
+}
+
+char *
+argv_term (const char **f)
+{
+  const char *p = *f;
+  const char *term = NULL;
+  size_t termlen = 0;
+
+  if (*p == '\0')
+    return NULL;
+
+  while (true)
+    {
+      const int c = *p;
+      if (c == '\0')
+       break;
+      if (term)
+       {
+         if (!isspace (c))
+           ++termlen;
+         else
+           break;
+       }
+      else
+       {
+         if (!isspace (c))
+           {
+             term = p;
+             termlen = 1;
+           }
+       }
+      ++p;
+    }
+  *f = p;
+
+  if (term)
+    {
+      char *ret;
+      ASSERT (termlen > 0);
+      ret = malloc (termlen + 1);
+      check_malloc_return (ret);
+      memcpy (ret, term, termlen);
+      ret[termlen] = '\0';
+      return ret;
+    }
+  else
+    return NULL;
+}
+
+const char *
+argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
+{
+  if (a->argv)
+    return print_argv ((const char **)a->argv, gc, flags);
+  else
+    return "";
+}
+
+void
+argv_printf (struct argv *a, const char *format, ...)
+{
+  va_list arglist;
+  va_start (arglist, format);
+  argv_printf_arglist (a, format, 0, arglist);
+  va_end (arglist);
+ }
+
+void
+argv_printf_cat (struct argv *a, const char *format, ...)
+{
+  va_list arglist;
+  va_start (arglist, format);
+  argv_printf_arglist (a, format, APA_CAT, arglist);
+  va_end (arglist);
+}
+
+void
+argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist)
+{
+  char *term;
+  const char *f = format;
+  size_t argc = 0;
+
+  if (flags & APA_CAT)
+    {
+      char **old_argv = a->argv;
+      size_t i;
+      argc = a->argc;
+      a->argc += argv_argc (format);
+      ALLOC_ARRAY_CLEAR (a->argv, char *, a->argc + 1);
+      for (i = 0; i < argc; ++i)
+       a->argv[i] = old_argv[i];
+      free (old_argv);
+    }
+  else
+    {
+      argv_reset (a);
+      a->argc = argv_argc (format);
+      ALLOC_ARRAY_CLEAR (a->argv, char *, a->argc + 1);
+    }
+
+  while ((term = argv_term (&f)) != NULL) 
+    {
+      ASSERT (argc < a->argc);
+      if (term[0] == '%')
+       {
+         if (!strcmp (term, "%s"))
+           {
+             a->argv[argc++] = string_alloc (va_arg (arglist, char *), NULL);
+           }
+         else if (!strcmp (term, "%d"))
+           {
+             char numstr[64];
+             openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
+             a->argv[argc++] = string_alloc (numstr, NULL);
+           }
+         else if (!strcmp (term, "%u"))
+           {
+             char numstr[64];
+             openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
+             a->argv[argc++] = string_alloc (numstr, NULL);
+           }
+         else
+           ASSERT (0);
+         free (term);
+       }
+      else
+       {
+         a->argv[argc++] = term;
+       }
+    }
+  ASSERT (argc == a->argc);
+}
+
+#ifdef ARGV_TEST
+void
+argv_test (void)
+{
+  struct gc_arena gc = gc_new ();
+  char line[512];
+  const char *s;
+
+  struct argv a;
+  argv_init (&a);
+  argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  argv_reset (&a);
+  printf ("%s\n", s);
+
+  argv_init (&a);
+  argv_printf (&a, "foo bar %d", 99);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  argv_reset (&a);
+  printf ("%s\n", s);
+
+  argv_init (&a);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  argv_reset (&a);
+  printf ("%s\n", s);
+
+  argv_init (&a);
+  argv_printf (&a, "foo bar %d", 99);
+  argv_printf_cat (&a, "bar %d foo", 42);
+  argv_printf_cat (&a, "cool %s %d u", "frood", 4);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  argv_reset (&a);
+  printf ("%s\n", s);
+
+  while (fgets (line, sizeof(line), stdin) != NULL)
+    {
+      char *term;
+      const char *f = line;
+      int i = 0;
+
+      while ((term = argv_term (&f)) != NULL) 
+       {
+         printf ("[%d] '%s'\n", i, term);
+         ++i;
+         free (term);
+       }
+    }
+  gc_free (&gc);
+}
+#endif
+
 /*
  * write a string to the end of a buffer that was
  * truncated by buf_printf
index 7de3363dc472d5bf7e3036f30680a6c950c4dad3..31902292b36144f19aef1f869199c681d1822a05 100644 (file)
--- a/buffer.h
+++ b/buffer.h
@@ -56,6 +56,12 @@ struct buffer
 #endif
 };
 
+/* used by argv_x functions */
+struct argv {
+  size_t argc;
+  char **argv;
+};
+
 /* for garbage collection */
 
 struct gc_entry
@@ -221,6 +227,33 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
 #endif
     ;
 
+/*
+ * A printf-like function (that only recognizes a subset of standard printf
+ * format operators) that prints arguments to an argv list instead
+ * of a standard string.  This is used to build up argv arrays for passing
+ * to execve.
+ */
+void argv_init (struct argv *a);
+void argv_reset (struct argv *a);
+size_t argv_argc (const char *format);
+char *argv_term (const char **f);
+const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
+
+#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
+void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
+
+void argv_printf (struct argv *a, const char *format, ...)
+#ifdef __GNUC__
+  __attribute__ ((format (printf, 2, 3)))
+#endif
+  ;
+
+void argv_printf_cat (struct argv *a, const char *format, ...)
+#ifdef __GNUC__
+  __attribute__ ((format (printf, 2, 3)))
+#endif
+  ;
+
 /*
  * remove/add trailing characters
  */
diff --git a/init.c b/init.c
index 61b23e3e45e9274eebff63384961c57600cb246b..ebdc9f38ba2d0c45989fb052221f62e024538a98 100644 (file)
--- a/init.c
+++ b/init.c
@@ -465,6 +465,14 @@ init_static (void)
   return false;
 #endif
 
+#ifdef ARGV_TEST
+  {
+    void argv_test (void);
+    argv_test ();
+    return false;
+  }
+#endif
+
   return true;
 }