From: Sebastien Helleu <flashcode@flashtux.org>
Description: do not call shell to execute command in hook_process (fix security
    issue when a plugin/script gives untrusted command)
Forwarded: not-needed
Last-Update: 2012-11-19
--- a/src/core/wee-hook.c
+++ b/src/core/wee-hook.c
@@ -1386,9 +1386,9 @@
 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
@@ -1427,10 +1427,24 @@
     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;
--- a/src/core/wee-string.c
+++ b/src/core/wee-string.c
@@ -1139,6 +1139,196 @@
 }
 
 /*
+ * 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
  */
 
--- a/src/core/wee-string.h
+++ b/src/core/wee-string.h
@@ -59,6 +59,7 @@
 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);
