From c8445cc2255c40615179df7cf4ce1565a01b1a11 Mon Sep 17 00:00:00 2001 From: Sebastien Helleu Date: Sun, 18 Nov 2012 10:45:26 +0100 Subject: [PATCH] core: do not call shell to execute command in hook_process (fix security problem when a plugin/script gives untrusted command) (bug #37764) --- src/core/wee-hook.c | 22 ++++- src/core/wee-string.c | 190 ++++++++++++++++++++++++++++++++++++++++++ src/core/wee-string.h | 1 + 3 files changed, 209 insertions(+), 4 deletions(-) diff --git a/src/core/wee-hook.c b/src/core/wee-hook.c index 5080e7f53..0b7b601cb 100644 --- a/src/core/wee-hook.c +++ b/src/core/wee-hook.c @@ -1387,9 +1387,9 @@ hook_process (struct t_weechat_plugin *plugin, void hook_process_child (struct t_hook *hook_process) { - char *exec_args[4] = { "sh", "-c", NULL, NULL }; + char **exec_args; const char *ptr_url; - int rc; + int rc, i; /* * close stdin, so that process will fail to read stdin (process reading @@ -1428,10 +1428,24 @@ hook_process_child (struct t_hook *hook_process) else { /* launch command */ - exec_args[2] = HOOK_PROCESS(hook_process, command); - execvp (exec_args[0], exec_args); + exec_args = string_split_shell (HOOK_PROCESS(hook_process, command)); + if (exec_args) + { + if (weechat_debug_core >= 1) + { + log_printf ("hook_process, command='%s'", + HOOK_PROCESS(hook_process, command)); + for (i = 0; exec_args[i]; i++) + { + log_printf (" args[%02d] == '%s'", i, exec_args[i]); + } + } + execvp (exec_args[0], exec_args); + } /* should not be executed if execvp was ok */ + if (exec_args) + string_free_split (exec_args); fprintf (stderr, "Error with command '%s'\n", HOOK_PROCESS(hook_process, command)); rc = EXIT_FAILURE; diff --git a/src/core/wee-string.c b/src/core/wee-string.c index 1d16d6d0f..dfc3315ec 100644 --- a/src/core/wee-string.c +++ b/src/core/wee-string.c @@ -1138,6 +1138,196 @@ string_split (const char *string, const char *separators, int keep_eol, return array; } +/* + * string_split_shell: split a string like the shell does for a command with + * arguments. + * Note: result must be freed with string_free_split. + * This function is a C conversion of python class "shlex" + * (file: Lib/shlex.py in python repository) + * Doc: http://docs.python.org/3/library/shlex.html + * Copyrights in shlex.py: + * Module and documentation by Eric S. Raymond, 21 Dec 1998 + * Input stacking and error message cleanup added by ESR, March 2000 + * push_source() and pop_source() made explicit by ESR, January 2001. + * Posix compliance, split(), string arguments, and + * iterator interface by Gustavo Niemeyer, April 2003. + */ + +char ** +string_split_shell (const char *string) +{ + int temp_len, num_args, add_char_to_temp, add_temp_to_args, quoted; + char *string2, *temp, **args, **args2, state, escapedstate; + char *ptr_string, *ptr_next, saved_char; + + if (!string) + return NULL; + + string2 = strdup (string); + if (!string2) + return NULL; + + /* + * prepare "args" with one pointer to NULL, the "args" will be reallocated + * later, each time a new argument is added + */ + num_args = 0; + args = malloc ((num_args + 1) * sizeof (args[0])); + if (!args) + { + free (string2); + return NULL; + } + args[0] = NULL; + + /* prepare a temp string for working (adding chars one by one) */ + temp = malloc ((2 * strlen (string)) + 1); + if (!temp) + { + free (string2); + free (args); + return NULL; + } + temp[0] = '\0'; + temp_len = 0; + + state = ' '; + escapedstate = ' '; + quoted = 0; + ptr_string = string2; + while (ptr_string[0]) + { + add_char_to_temp = 0; + add_temp_to_args = 0; + ptr_next = utf8_next_char (ptr_string); + saved_char = ptr_next[0]; + ptr_next[0] = '\0'; + if (state == ' ') + { + if ((ptr_string[0] == ' ') || (ptr_string[0] == '\t') + || (ptr_string[0] == '\r') || (ptr_string[0] == '\n')) + { + if (temp[0] || quoted) + add_temp_to_args = 1; + } + else if (ptr_string[0] == '\\') + { + escapedstate = 'a'; + state = ptr_string[0]; + } + else if ((ptr_string[0] == '\'') || (ptr_string[0] == '"')) + { + state = ptr_string[0]; + } + else + { + add_char_to_temp = 1; + state = 'a'; + } + } + else if ((state == '\'') || (state == '"')) + { + quoted = 1; + if (ptr_string[0] == state) + { + state = 'a'; + } + else if ((state == '"') && (ptr_string[0] == '\\')) + { + escapedstate = state; + state = ptr_string[0]; + } + else + { + add_char_to_temp = 1; + } + } + else if (state == '\\') + { + if (((escapedstate == '\'') || (escapedstate == '"')) + && (ptr_string[0] != state) && (ptr_string[0] != escapedstate)) + { + temp[temp_len] = state; + temp_len++; + temp[temp_len] = '\0'; + } + add_char_to_temp = 1; + state = escapedstate; + } + else if (state == 'a') + { + if ((ptr_string[0] == ' ') || (ptr_string[0] == '\t') + || (ptr_string[0] == '\r') || (ptr_string[0] == '\n')) + { + state = ' '; + if (temp[0] || quoted) + add_temp_to_args = 1; + } + else if (ptr_string[0] == '\\') + { + escapedstate = 'a'; + state = ptr_string[0]; + } + else if ((ptr_string[0] == '\'') || (ptr_string[0] == '"')) + { + state = ptr_string[0]; + } + else + { + add_char_to_temp = 1; + } + } + if (add_char_to_temp) + { + memcpy (temp + temp_len, ptr_string, ptr_next - ptr_string); + temp_len += (ptr_next - ptr_string); + temp[temp_len] = '\0'; + } + if (add_temp_to_args) + { + num_args++; + args2 = realloc (args, (num_args + 1) * sizeof (args[0])); + if (!args2) + { + free (string2); + free (temp); + return args; + } + args = args2; + args[num_args - 1] = strdup (temp); + args[num_args] = NULL; + temp[0] = '\0'; + temp_len = 0; + escapedstate = ' '; + quoted = 0; + } + ptr_next[0] = saved_char; + ptr_string = ptr_next; + } + + if (temp[0] || (state != ' ')) + { + num_args++; + args2 = realloc (args, (num_args + 1) * sizeof (args[0])); + if (!args2) + { + free (string2); + free (temp); + return args; + } + args = args2; + args[num_args - 1] = strdup (temp); + args[num_args] = NULL; + temp[0] = '\0'; + temp_len = 0; + } + + free (string2); + free (temp); + + return args; +} + /* * string_free_split: free a split string */ diff --git a/src/core/wee-string.h b/src/core/wee-string.h index a000e65f3..2d7fa6b7a 100644 --- a/src/core/wee-string.h +++ b/src/core/wee-string.h @@ -59,6 +59,7 @@ extern int string_has_highlight_regex_compiled (const char *string, extern int string_has_highlight_regex (const char *string, const char *regex); extern char **string_split (const char *string, const char *separators, int keep_eol, int num_items_max, int *num_items); +extern char **string_split_shell (const char *string); extern void string_free_split (char **split_string); extern char *string_build_with_split_string (const char **split_string, const char *separator);