summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2008-09-06 09:42:17 +0000
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2008-09-06 09:42:17 +0000
commitb8fb090c167ff500a8d702f612a42914d4f0bb03 (patch)
tree982fe657f91c834bc17d1e81f04672323a2dda1a
parent0a838de8adf3b06590e73cff6415275d9b1cd4fe (diff)
downloadopenvpn-b8fb090c167ff500a8d702f612a42914d4f0bb03.zip
openvpn-b8fb090c167ff500a8d702f612a42914d4f0bb03.tar.gz
openvpn-b8fb090c167ff500a8d702f612a42914d4f0bb03.tar.xz
2.1_rc8 and earlier did implicit shell expansion on script
arguments since all scripts were called by system(). The security hardening changes made to 2.1_rc9 no longer use system(), but rather use the safer execve or CreateProcess system calls. The security hardening also introduced a backward incompatibility with 2.1_rc8 and earlier in that script parameters were no longer shell-expanded, so for example: client-connect "docc CLIENT-CONNECT" would fail to work because execve would try to execute a script called "docc CLIENT-CONNECT" instead of "docc" with "CLIENT-CONNECT" as the first argument. This patch fixes the issue, bringing the script argument semantics back to pre 2.1_rc9 behavior in order to preserve backward compatibility while still using execve or CreateProcess to execute the script/executable. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@3311 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r--buffer.c252
-rw-r--r--buffer.h32
-rw-r--r--errlevel.h1
-rw-r--r--init.c2
-rw-r--r--misc.c378
-rw-r--r--misc.h36
-rw-r--r--multi.c6
-rw-r--r--socket.c2
-rw-r--r--ssl.c4
9 files changed, 350 insertions, 363 deletions
diff --git a/buffer.c b/buffer.c
index dfaaaa1..c1ffb83 100644
--- a/buffer.c
+++ b/buffer.c
@@ -235,258 +235,6 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
}
/*
- * 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;
-}
-
-struct argv
-argv_new (void)
-{
- struct argv ret;
- argv_init (&ret);
- return ret;
-}
-
-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;
-}
-
-struct argv
-argv_insert_head (const struct argv *a, const char *head)
-{
- struct argv r;
- size_t i;
-
- r.argc = (a ? a->argc : 0) + 1;
- ALLOC_ARRAY_CLEAR (r.argv, char *, r.argc + 1);
- r.argv[0] = string_alloc (head, NULL);
- if (a)
- {
- for (i = 0; i < a->argc; ++i)
- r.argv[i+1] = string_alloc (a->argv[i], NULL);
- }
- return r;
-}
-
-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_msg (const int msglev, const struct argv *a)
-{
- struct gc_arena gc = gc_new ();
- msg (msglev, "%s", argv_str (a, &gc, 0));
- gc_free (&gc);
-}
-
-void
-argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
-{
- struct gc_arena gc = gc_new ();
- msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
- gc_free (&gc);
-}
-
-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"))
- {
- char *s = va_arg (arglist, char *);
- if (!s)
- s = "";
- a->argv[argc++] = string_alloc (s, 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 if (!strcmp (term, "%s/%d"))
- {
- char numstr[64];
- char *s = va_arg (arglist, char *);
-
- if (!s)
- s = "";
-
- openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
-
- {
- const size_t len = strlen(s) + strlen(numstr) + 2;
- char *combined = (char *) malloc (len);
- check_malloc_return (combined);
-
- strcpy (combined, s);
- strcat (combined, "/");
- strcat (combined, numstr);
- a->argv[argc++] = combined;
- }
- }
- else if (!strcmp (term, "%s%s"))
- {
- char *s1 = va_arg (arglist, char *);
- char *s2 = va_arg (arglist, char *);
- char *combined;
-
- if (!s1) s1 = "";
- if (!s2) s2 = "";
- combined = (char *) malloc (strlen(s1) + strlen(s2) + 1);
- check_malloc_return (combined);
- strcpy (combined, s1);
- strcat (combined, s2);
- a->argv[argc++] = combined;
- }
- else
- ASSERT (0);
- free (term);
- }
- else
- {
- a->argv[argc++] = term;
- }
- }
- ASSERT (argc == a->argc);
-}
-
-/*
* write a string to the end of a buffer that was
* truncated by buf_printf
*/
diff --git a/buffer.h b/buffer.h
index 2320e4c..e16b670 100644
--- a/buffer.h
+++ b/buffer.h
@@ -60,6 +60,7 @@ struct buffer
/* used by argv_x functions */
struct argv {
+ size_t capacity;
size_t argc;
char **argv;
};
@@ -293,37 +294,6 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
;
/*
- * 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);
-struct argv argv_new (void);
-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);
-struct argv argv_insert_head (const struct argv *a, const char *head);
-void argv_msg (const int msglev, const struct argv *a);
-void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
-
-#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/errlevel.h b/errlevel.h
index 38480ef..95c8a30 100644
--- a/errlevel.h
+++ b/errlevel.h
@@ -75,6 +75,7 @@
#define D_CLOSE LOGLEV(2, 22, 0) /* show socket and TUN/TAP close */
#define D_SHOW_OCC_HASH LOGLEV(2, 23, 0) /* show MD5 hash of option compatibility string */
#define D_PROXY LOGLEV(2, 24, 0) /* show http proxy control packets */
+#define D_ARGV LOGLEV(2, 25, 0) /* show struct argv errors */
#define D_TLS_DEBUG_LOW LOGLEV(3, 20, 0) /* low frequency info from tls_session routines */
#define D_GREMLIN LOGLEV(3, 30, 0) /* show simulated outage info from gremlin module */
diff --git a/init.c b/init.c
index 08918d6..2aa1bd0 100644
--- a/init.c
+++ b/init.c
@@ -923,7 +923,7 @@ do_route (const struct options *options,
{
struct argv argv = argv_new ();
setenv_str (es, "script_type", "route-up");
- argv_printf (&argv, "%s", options->route_script);
+ argv_printf (&argv, "%sc", options->route_script);
openvpn_execve_check (&argv, es, S_SCRIPT, "Route script failed");
argv_reset (&argv);
}
diff --git a/misc.c b/misc.c
index 4cdc3de..45356a6 100644
--- a/misc.c
+++ b/misc.c
@@ -220,7 +220,7 @@ run_up_down (const char *command,
ASSERT (arg);
setenv_str (es, "script_type", script_type);
argv_printf (&argv,
- "%s %s %d %d %s %s %s",
+ "%sc %s %d %d %s %s %s",
command,
arg,
tun_mtu, link_mtu,
@@ -1190,24 +1190,6 @@ absolute_pathname (const char *pathname)
return false;
}
-/*
- * Return the next largest power of 2
- * or u if u is a power of 2.
- */
-unsigned int
-adjust_power_of_2 (unsigned int u)
-{
- unsigned int ret = 1;
-
- while (ret < u)
- {
- ret <<= 1;
- ASSERT (ret > 0);
- }
-
- return ret;
-}
-
#ifdef HAVE_GETPASS
static FILE *
@@ -1666,56 +1648,309 @@ openvpn_sleep (const int n)
sleep (n);
}
-#if 0
/*
- * Configure PATH. On Windows, sometimes PATH is not set correctly
- * by default.
+ * Return the next largest power of 2
+ * or u if u is a power of 2.
*/
+size_t
+adjust_power_of_2 (size_t u)
+{
+ size_t ret = 1;
+
+ while (ret < u)
+ {
+ ret <<= 1;
+ ASSERT (ret > 0);
+ }
+
+ 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
-configure_path (void)
+argv_init (struct argv *a)
{
-#ifdef WIN32
- FILE *fp;
- fp = fopen ("c:\\windows\\system32\\route.exe", "rb");
- if (fp)
+ a->capacity = 0;
+ a->argc = 0;
+ a->argv = NULL;
+}
+
+struct argv
+argv_new (void)
+{
+ struct argv ret;
+ argv_init (&ret);
+ return ret;
+}
+
+void
+argv_reset (struct argv *a)
+{
+ size_t i;
+ for (i = 0; i < a->argc; ++i)
+ free (a->argv[i]);
+ free (a->argv);
+ argv_init (a);
+}
+
+static void
+argv_extend (struct argv *a, const size_t newcap)
+{
+ if (newcap > a->capacity)
{
- const int bufsiz = 4096;
- struct gc_arena gc = gc_new ();
- struct buffer oldpath = alloc_buf_gc (bufsiz, &gc);
- struct buffer newpath = alloc_buf_gc (bufsiz, &gc);
- const char* delim = ";";
- DWORD status;
- fclose (fp);
- status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath));
-#if 0
- status = 0;
-#endif
- if (!status)
+ char **newargv;
+ size_t i;
+ ALLOC_ARRAY_CLEAR (newargv, char *, newcap);
+ for (i = 0; i < a->argc; ++i)
+ newargv[i] = a->argv[i];
+ free (a->argv);
+ a->argv = newargv;
+ a->capacity = newcap;
+ }
+}
+
+static void
+argv_grow (struct argv *a, const size_t add)
+{
+ const size_t newargc = a->argc + add + 1;
+ ASSERT (newargc > a->argc);
+ argv_extend (a, adjust_power_of_2 (newargc));
+}
+
+static void
+argv_append (struct argv *a, char *str) /* str must have been malloced or be NULL */
+{
+ argv_grow (a, 1);
+ a->argv[a->argc++] = str;
+}
+
+struct argv
+argv_clone (const struct argv *a, const size_t headroom)
+{
+ struct argv r;
+ size_t i;
+
+ argv_init (&r);
+ for (i = 0; i < headroom; ++i)
+ argv_append (&r, NULL);
+ if (a)
+ {
+ for (i = 0; i < a->argc; ++i)
+ argv_append (&r, string_alloc (a->argv[i], NULL));
+ }
+ return r;
+}
+
+struct argv
+argv_insert_head (const struct argv *a, const char *head)
+{
+ struct argv r;
+
+ r = argv_clone (a, 1);
+ r.argv[0] = string_alloc (head, NULL);
+
+ return r;
+}
+
+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)
{
- *BPTR(&oldpath) = '\0';
- delim = "";
+ if (!isspace (c))
+ ++termlen;
+ else
+ break;
}
- buf_printf (&newpath, "C:\\WINDOWS\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem%s%s",
- delim,
- BSTR(&oldpath));
- SetEnvironmentVariable ("PATH", BSTR(&newpath));
-#if 0
- status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath));
- if (status > 0)
- printf ("PATH: %s\n", BSTR(&oldpath));
-#endif
- gc_free (&gc);
+ else
+ {
+ if (!isspace (c))
+ {
+ term = p;
+ termlen = 1;
+ }
+ }
+ ++p;
}
-#endif
+ *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_msg (const int msglev, const struct argv *a)
+{
+ struct gc_arena gc = gc_new ();
+ msg (msglev, "%s", argv_str (a, &gc, 0));
+ gc_free (&gc);
+}
+
+void
+argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+ msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
+ gc_free (&gc);
+}
+
+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)
+{
+ struct gc_arena gc = gc_new ();
+ char *term;
+ const char *f = format;
+
+ if (!(flags & APA_CAT))
+ argv_reset (a);
+ argv_extend (a, 1); /* ensure trailing NULL */
+
+ while ((term = argv_term (&f)) != NULL)
+ {
+ if (term[0] == '%')
+ {
+ if (!strcmp (term, "%s"))
+ {
+ char *s = va_arg (arglist, char *);
+ if (!s)
+ s = "";
+ argv_append (a, string_alloc (s, NULL));
+ }
+ else if (!strcmp (term, "%sc"))
+ {
+ char *s = va_arg (arglist, char *);
+ if (s)
+ {
+ int nparms;
+ char *parms[MAX_PARMS+1];
+ int i;
+
+ nparms = parse_line (s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, M_FATAL, &gc);
+ for (i = 0; i < nparms; ++i)
+ argv_append (a, string_alloc (parms[i], NULL));
+ }
+ else
+ argv_append (a, string_alloc ("", NULL));
+ }
+ else if (!strcmp (term, "%d"))
+ {
+ char numstr[64];
+ openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
+ argv_append (a, string_alloc (numstr, NULL));
+ }
+ else if (!strcmp (term, "%u"))
+ {
+ char numstr[64];
+ openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
+ argv_append (a, string_alloc (numstr, NULL));
+ }
+ else if (!strcmp (term, "%s/%d"))
+ {
+ char numstr[64];
+ char *s = va_arg (arglist, char *);
+
+ if (!s)
+ s = "";
+
+ openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
+
+ {
+ const size_t len = strlen(s) + strlen(numstr) + 2;
+ char *combined = (char *) malloc (len);
+ check_malloc_return (combined);
+
+ strcpy (combined, s);
+ strcat (combined, "/");
+ strcat (combined, numstr);
+ argv_append (a, combined);
+ }
+ }
+ else if (!strcmp (term, "%s%s"))
+ {
+ char *s1 = va_arg (arglist, char *);
+ char *s2 = va_arg (arglist, char *);
+ char *combined;
+
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ combined = (char *) malloc (strlen(s1) + strlen(s2) + 1);
+ check_malloc_return (combined);
+ strcpy (combined, s1);
+ strcat (combined, s2);
+ argv_append (a, combined);
+ }
+ else
+ ASSERT (0);
+ free (term);
+ }
+ else
+ {
+ argv_append (a, term);
+ }
+ }
+ gc_free (&gc);
}
-#endif
#ifdef ARGV_TEST
void
argv_test (void)
{
struct gc_arena gc = gc_new ();
- char line[512];
const char *s;
struct argv a;
@@ -1729,7 +1964,7 @@ argv_test (void)
#endif
argv_msg_prefix (M_INFO, &a, "ARGV");
- openvpn_execve_check (&a, NULL, 0, "command failed");
+ //openvpn_execve_check (&a, NULL, 0, "command failed");
argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
s = argv_str (&a, &gc, PA_BRACKET);
@@ -1742,7 +1977,7 @@ argv_test (void)
printf ("%s\n", s);
}
- argv_printf (&a, "foo bar %d", 99);
+ argv_printf (&a, "%sc foo bar %d", "\"multi term\" command following \\\"spaces", 99);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
@@ -1752,25 +1987,28 @@ argv_test (void)
printf ("%s\n", s);
argv_printf (&a, "foo bar %d", 99);
- argv_printf_cat (&a, "bar %d foo", 42);
+ argv_printf_cat (&a, "bar %d foo %sc", 42, "nonesuch");
argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("%s\n", s);
#if 0
- 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);
- }
- }
+ {
+ char line[512];
+ 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);
+ }
+ }
+ }
#endif
argv_reset (&a);
diff --git a/misc.h b/misc.h
index 87cdb31..dd81d82 100644
--- a/misc.h
+++ b/misc.h
@@ -221,9 +221,6 @@ bool delete_file (const char *filename);
/* return true if pathname is absolute */
bool absolute_pathname (const char *pathname);
-/* return the next largest power of 2 */
-unsigned int adjust_power_of_2 (unsigned int u);
-
/*
* Get and store a username/password
*/
@@ -300,4 +297,37 @@ extern const char *iproute_path;
#define SSEC_PW_ENV 3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
extern int script_security; /* GLOBAL */
+/* return the next largest power of 2 */
+size_t adjust_power_of_2 (size_t u);
+
+/*
+ * 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);
+struct argv argv_new (void);
+void argv_reset (struct argv *a);
+char *argv_term (const char **f);
+const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
+struct argv argv_insert_head (const struct argv *a, const char *head);
+void argv_msg (const int msglev, const struct argv *a);
+void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
+
+#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
+ ;
+
#endif
diff --git a/multi.c b/multi.c
index 5f70b6f..78cb5f3 100644
--- a/multi.c
+++ b/multi.c
@@ -103,7 +103,7 @@ learn_address_script (const struct multi_context *m,
{
struct argv argv = argv_new ();
setenv_str (es, "script_type", "learn-address");
- argv_printf (&argv, "%s %s %s",
+ argv_printf (&argv, "%sc %s %s",
m->top.options.learn_address_script,
op,
mroute_addr_print (addr, &gc));
@@ -473,7 +473,7 @@ multi_client_disconnect_script (struct multi_context *m,
{
struct argv argv = argv_new ();
setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
- argv_printf (&argv, "%s", mi->context.options.client_disconnect_script);
+ argv_printf (&argv, "%sc", mi->context.options.client_disconnect_script);
openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
argv_reset (&argv);
}
@@ -1568,7 +1568,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
delete_file (dc_file);
- argv_printf (&argv, "%s %s",
+ argv_printf (&argv, "%sc %s",
mi->context.options.client_connect_script,
dc_file);
diff --git a/socket.c b/socket.c
index 9622637..4d7c180 100644
--- a/socket.c
+++ b/socket.c
@@ -1539,7 +1539,7 @@ ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socke
const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc);
const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc);
if (include_cmd)
- argv_printf (argv, "%s %s %s",
+ argv_printf (argv, "%sc %s %s",
info->ipchange_command,
ip,
port);
diff --git a/ssl.c b/ssl.c
index cd1692b..f627999 100644
--- a/ssl.c
+++ b/ssl.c
@@ -718,7 +718,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
setenv_str (opt->es, "script_type", "tls-verify");
- argv_printf (&argv, "%s %d %s",
+ argv_printf (&argv, "%sc %d %s",
opt->verify_command,
ctx->error_depth,
subject);
@@ -2937,7 +2937,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
setenv_untrusted (session);
/* format command line */
- argv_printf (&argv, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
+ argv_printf (&argv, "%sc %s", session->opt->auth_user_pass_verify_script, tmp_file);
/* call command */
retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT);