diff options
author | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2008-09-06 09:42:17 +0000 |
---|---|---|
committer | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2008-09-06 09:42:17 +0000 |
commit | b8fb090c167ff500a8d702f612a42914d4f0bb03 (patch) | |
tree | 982fe657f91c834bc17d1e81f04672323a2dda1a | |
parent | Added --allow-pull-fqdn option which allows client to pull DNS names (diff) | |
download | 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.c | 252 | ||||
-rw-r--r-- | buffer.h | 32 | ||||
-rw-r--r-- | errlevel.h | 1 | ||||
-rw-r--r-- | init.c | 2 | ||||
-rw-r--r-- | misc.c | 378 | ||||
-rw-r--r-- | misc.h | 36 | ||||
-rw-r--r-- | multi.c | 6 | ||||
-rw-r--r-- | socket.c | 2 | ||||
-rw-r--r-- | ssl.c | 4 |
9 files changed, 350 insertions, 363 deletions
@@ -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 */ @@ -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 */ @@ -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 */ @@ -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); } @@ -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); @@ -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 @@ -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); @@ -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); @@ -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); |