Browse code

Added argv_x functions to buffer.[ch] to be used to safely build 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

james authored on 2008/07/24 04:51:27
Showing 3 changed files
... ...
@@ -227,6 +227,230 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
227 227
 }
228 228
 
229 229
 /*
230
+ * A printf-like function (that only recognizes a subset of standard printf
231
+ * format operators) that prints arguments to an argv list instead
232
+ * of a standard string.  This is used to build up argv arrays for passing
233
+ * to execve.
234
+ */
235
+
236
+void
237
+argv_init (struct argv *a)
238
+{
239
+  a->argc = 0;
240
+  a->argv = NULL;
241
+}
242
+
243
+void
244
+argv_reset (struct argv *a)
245
+{
246
+  size_t i;
247
+  for (i = 0; i < a->argc; ++i)
248
+    free (a->argv[i]);
249
+  free (a->argv);
250
+  a->argc = 0;
251
+  a->argv = NULL;
252
+}
253
+
254
+size_t
255
+argv_argc (const char *format)
256
+{
257
+  char *term;
258
+  const char *f = format;
259
+  size_t argc = 0;
260
+
261
+  while ((term = argv_term (&f)) != NULL) 
262
+    {
263
+      ++argc;
264
+      free (term);
265
+    }
266
+  return argc;
267
+}
268
+
269
+char *
270
+argv_term (const char **f)
271
+{
272
+  const char *p = *f;
273
+  const char *term = NULL;
274
+  size_t termlen = 0;
275
+
276
+  if (*p == '\0')
277
+    return NULL;
278
+
279
+  while (true)
280
+    {
281
+      const int c = *p;
282
+      if (c == '\0')
283
+	break;
284
+      if (term)
285
+	{
286
+	  if (!isspace (c))
287
+	    ++termlen;
288
+	  else
289
+	    break;
290
+	}
291
+      else
292
+	{
293
+	  if (!isspace (c))
294
+	    {
295
+	      term = p;
296
+	      termlen = 1;
297
+	    }
298
+	}
299
+      ++p;
300
+    }
301
+  *f = p;
302
+
303
+  if (term)
304
+    {
305
+      char *ret;
306
+      ASSERT (termlen > 0);
307
+      ret = malloc (termlen + 1);
308
+      check_malloc_return (ret);
309
+      memcpy (ret, term, termlen);
310
+      ret[termlen] = '\0';
311
+      return ret;
312
+    }
313
+  else
314
+    return NULL;
315
+}
316
+
317
+const char *
318
+argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
319
+{
320
+  if (a->argv)
321
+    return print_argv ((const char **)a->argv, gc, flags);
322
+  else
323
+    return "";
324
+}
325
+
326
+void
327
+argv_printf (struct argv *a, const char *format, ...)
328
+{
329
+  va_list arglist;
330
+  va_start (arglist, format);
331
+  argv_printf_arglist (a, format, 0, arglist);
332
+  va_end (arglist);
333
+ }
334
+
335
+void
336
+argv_printf_cat (struct argv *a, const char *format, ...)
337
+{
338
+  va_list arglist;
339
+  va_start (arglist, format);
340
+  argv_printf_arglist (a, format, APA_CAT, arglist);
341
+  va_end (arglist);
342
+}
343
+
344
+void
345
+argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist)
346
+{
347
+  char *term;
348
+  const char *f = format;
349
+  size_t argc = 0;
350
+
351
+  if (flags & APA_CAT)
352
+    {
353
+      char **old_argv = a->argv;
354
+      size_t i;
355
+      argc = a->argc;
356
+      a->argc += argv_argc (format);
357
+      ALLOC_ARRAY_CLEAR (a->argv, char *, a->argc + 1);
358
+      for (i = 0; i < argc; ++i)
359
+	a->argv[i] = old_argv[i];
360
+      free (old_argv);
361
+    }
362
+  else
363
+    {
364
+      argv_reset (a);
365
+      a->argc = argv_argc (format);
366
+      ALLOC_ARRAY_CLEAR (a->argv, char *, a->argc + 1);
367
+    }
368
+
369
+  while ((term = argv_term (&f)) != NULL) 
370
+    {
371
+      ASSERT (argc < a->argc);
372
+      if (term[0] == '%')
373
+	{
374
+	  if (!strcmp (term, "%s"))
375
+	    {
376
+	      a->argv[argc++] = string_alloc (va_arg (arglist, char *), NULL);
377
+	    }
378
+	  else if (!strcmp (term, "%d"))
379
+	    {
380
+	      char numstr[64];
381
+	      openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
382
+	      a->argv[argc++] = string_alloc (numstr, NULL);
383
+	    }
384
+	  else if (!strcmp (term, "%u"))
385
+	    {
386
+	      char numstr[64];
387
+	      openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
388
+	      a->argv[argc++] = string_alloc (numstr, NULL);
389
+	    }
390
+	  else
391
+	    ASSERT (0);
392
+	  free (term);
393
+	}
394
+      else
395
+	{
396
+	  a->argv[argc++] = term;
397
+	}
398
+    }
399
+  ASSERT (argc == a->argc);
400
+}
401
+
402
+#ifdef ARGV_TEST
403
+void
404
+argv_test (void)
405
+{
406
+  struct gc_arena gc = gc_new ();
407
+  char line[512];
408
+  const char *s;
409
+
410
+  struct argv a;
411
+  argv_init (&a);
412
+  argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
413
+  s = argv_str (&a, &gc, PA_BRACKET);
414
+  argv_reset (&a);
415
+  printf ("%s\n", s);
416
+
417
+  argv_init (&a);
418
+  argv_printf (&a, "foo bar %d", 99);
419
+  s = argv_str (&a, &gc, PA_BRACKET);
420
+  argv_reset (&a);
421
+  printf ("%s\n", s);
422
+
423
+  argv_init (&a);
424
+  s = argv_str (&a, &gc, PA_BRACKET);
425
+  argv_reset (&a);
426
+  printf ("%s\n", s);
427
+
428
+  argv_init (&a);
429
+  argv_printf (&a, "foo bar %d", 99);
430
+  argv_printf_cat (&a, "bar %d foo", 42);
431
+  argv_printf_cat (&a, "cool %s %d u", "frood", 4);
432
+  s = argv_str (&a, &gc, PA_BRACKET);
433
+  argv_reset (&a);
434
+  printf ("%s\n", s);
435
+
436
+  while (fgets (line, sizeof(line), stdin) != NULL)
437
+    {
438
+      char *term;
439
+      const char *f = line;
440
+      int i = 0;
441
+
442
+      while ((term = argv_term (&f)) != NULL) 
443
+	{
444
+	  printf ("[%d] '%s'\n", i, term);
445
+	  ++i;
446
+	  free (term);
447
+	}
448
+    }
449
+  gc_free (&gc);
450
+}
451
+#endif
452
+
453
+/*
230 454
  * write a string to the end of a buffer that was
231 455
  * truncated by buf_printf
232 456
  */
... ...
@@ -56,6 +56,12 @@ struct buffer
56 56
 #endif
57 57
 };
58 58
 
59
+/* used by argv_x functions */
60
+struct argv {
61
+  size_t argc;
62
+  char **argv;
63
+};
64
+
59 65
 /* for garbage collection */
60 66
 
61 67
 struct gc_entry
... ...
@@ -222,6 +228,33 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
222 222
     ;
223 223
 
224 224
 /*
225
+ * A printf-like function (that only recognizes a subset of standard printf
226
+ * format operators) that prints arguments to an argv list instead
227
+ * of a standard string.  This is used to build up argv arrays for passing
228
+ * to execve.
229
+ */
230
+void argv_init (struct argv *a);
231
+void argv_reset (struct argv *a);
232
+size_t argv_argc (const char *format);
233
+char *argv_term (const char **f);
234
+const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
235
+
236
+#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
237
+void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
238
+
239
+void argv_printf (struct argv *a, const char *format, ...)
240
+#ifdef __GNUC__
241
+  __attribute__ ((format (printf, 2, 3)))
242
+#endif
243
+  ;
244
+
245
+void argv_printf_cat (struct argv *a, const char *format, ...)
246
+#ifdef __GNUC__
247
+  __attribute__ ((format (printf, 2, 3)))
248
+#endif
249
+  ;
250
+
251
+/*
225 252
  * remove/add trailing characters
226 253
  */
227 254
 
... ...
@@ -465,6 +465,14 @@ init_static (void)
465 465
   return false;
466 466
 #endif
467 467
 
468
+#ifdef ARGV_TEST
469
+  {
470
+    void argv_test (void);
471
+    argv_test ();
472
+    return false;
473
+  }
474
+#endif
475
+
468 476
   return true;
469 477
 }
470 478