core: do not call shell to execute command in hook_process (fix security problem when a plugin/script gives untrusted command) (bug #37764)
This commit is contained in:
parent
a198d22213
commit
c8445cc225
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user