From 8df725d51392131fd12d833200f03af677442641 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Mar 2014 01:11:18 +0100 Subject: [PATCH] Don't expand string via shell. The shell expansion was done via shell to keep complexity away from vimb, but this allowed to run other shell commands too, what's a big security issue. To avoid problems, the ~/, ~user, $ENV and ${ENV} expansions are done in the c code o vimb. --- src/ascii.h | 60 +++++++++++++++++++++++++++++++ src/setting.c | 2 +- src/util.c | 98 +++++++++++++++++++++++++++++++++++++++------------ src/util.h | 2 +- 4 files changed, 138 insertions(+), 24 deletions(-) diff --git a/src/ascii.h b/src/ascii.h index 2f74365..9180a80 100644 --- a/src/ascii.h +++ b/src/ascii.h @@ -20,6 +20,66 @@ #ifndef _ASCII_H #define _ASCII_H +#define VB_UPPER 0x01 +#define VB_LOWER 0x02 +#define VB_DIGIT 0x04 +#define VB_SPACE 0x08 +#define VB_PUNKT 0x10 +#define VB_CTRL 0x20 +#define VB_IDENT 0x40 +#define VB_ALPHA (VB_UPPER|VB_LOWER) +#define VB_ALNUM (VB_ALPHA|VB_DIGIT) + +#define U VB_UPPER +#define L VB_LOWER +#define D VB_DIGIT +#define P VB_PUNKT +#define S VB_SPACE +#define C VB_CTRL +#define I VB_IDENT +#define LI VB_LOWER|VB_IDENT +#define UI VB_UPPER|VB_IDENT +#define SC VB_SPACE|VB_CTRL +#define PI VB_PUNKT|VB_IDENT +static const unsigned char chartable[256] = { + C, C, C, C, C, C, C, C, C, SC, SC, C, SC, SC, C, C, + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, + S, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, P, + PI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, + UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, UI, P, P, P, P, PI, + P, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, + LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, LI, P, P, P, P, C, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, + P, P, P, P, P, P, P, P, P, P, P, P, P, P, P, P +}; +#undef U +#undef L +#undef D +#undef P +#undef S +#undef C +#undef I +#undef LI +#undef UI +#undef SC + +#define VB_IS_UPPER(c) ((chartable[(unsigned char)c] & VB_UPPER) != 0) +#define VB_IS_LOWER(c) ((chartable[(unsigned char)c] & VB_LOWER) != 0) +#define VB_IS_DIGIT(c) ((chartable[(unsigned char)c] & VB_DIGIT) != 0) +#define VB_IS_PUNKT(c) ((chartable[(unsigned char)c] & VB_PUNKT) != 0) +#define VB_IS_SPACE(c) ((chartable[(unsigned char)c] & VB_SPACE) != 0) +#define VB_IS_CTRL(c) ((chartable[(unsigned char)c] & VB_CTRL) != 0) +#define VB_IS_IDENT(c) ((chartable[(unsigned char)c] & VB_IDENT) != 0) +#define VB_IS_ALPHA(c) ((chartable[(unsigned char)c] & VB_ALPHA) != 0) +#define VB_IS_ALNUM(c) ((chartable[(unsigned char)c] & VB_ALNUM) != 0) + /* CSI (control sequence introducer) is the first byte of a control sequence * and is always followed by two bytes. */ #define CSI 0x80 diff --git a/src/setting.c b/src/setting.c index 0e727e6..c76325e 100644 --- a/src/setting.c +++ b/src/setting.c @@ -671,7 +671,7 @@ static SettingStatus download_path(const Setting *s, const SettingType type) g_free(vb.config.download_dir); vb.config.download_dir = NULL; } - path = util_shell_expand(s->arg.s); + path = util_expand(s->arg.s); /* if path is not absolute create it in the home directory */ if (*path != G_DIR_SEPARATOR) { vb.config.download_dir = g_build_filename(util_get_home_dir(), path, NULL); diff --git a/src/util.c b/src/util.c index 9456de7..f3fb470 100644 --- a/src/util.c +++ b/src/util.c @@ -19,8 +19,10 @@ #include "config.h" #include +#include #include "ctype.h" #include "util.h" +#include "ascii.h" char *util_get_config_dir(void) { @@ -300,15 +302,15 @@ char *util_build_path(const char *path, const char *dir) char *fullPath = NULL, *fexp, *dexp, *p; /* if the path could be expanded */ - if ((fexp = util_shell_expand(path))) { - if (*fexp == '/') { + if ((fexp = util_expand(path))) { + if (*fexp == G_DIR_SEPARATOR) { /* path is already absolute, no need to use given dir - there is * no need to free fexp, bacuse this should be done by the caller * on fullPath later */ fullPath = fexp; } else if (dir) { /* try to expand also the dir given - this may be ~/path */ - if ((dexp = util_shell_expand(dir))) { + if ((dexp = util_expand(dir))) { /* use expanded dir and append expanded path */ fullPath = g_build_filename(dexp, fexp, NULL); g_free(dexp); @@ -332,31 +334,83 @@ char *util_build_path(const char *path, const char *dir) } /** - * Run the shell to expand given string 'echo -n str'. + * Expand ~user, ~/, $ENV and ${ENV} for given string into new allocated + * string. * * Returned path must be g_freed. */ -char *util_shell_expand(const char *str) +char *util_expand(const char *src) { - GError *error = NULL; - char *shellcmd, *cmd, *out = NULL; - - /* first check if the string may contain expandable chars */ - if (*str == '~' || strchr(str, '$')) { - cmd = g_strconcat("echo -n ", str, NULL); - shellcmd = g_strdup_printf(SHELL_CMD, cmd); - if (!g_spawn_command_line_sync(shellcmd, &out, NULL, NULL, &error)) { - g_warning("Could not run shell expansion: %s", error->message); - g_clear_error(&error); + GString *dst = g_string_new(""); + GString *name; + gboolean start = true; /* is start of string of subpart */ + const char *env; + char *result; + struct passwd *pwd; + + while (*src) { + /* expand ~/path and ~user */ + if (*src == '~' && start) { + /* skip the ~ */ + src++; + if (*src == G_DIR_SEPARATOR) { + g_string_append(dst, util_get_home_dir()); + } else { + name = g_string_new(""); + /* look ahead to / space or end of string */ + while (*src && *src != G_DIR_SEPARATOR && !VB_IS_SPACE(*src)) { + g_string_append_c(name, *src); + src++; + } + /* append the name to the destination string */ + if ((pwd = getpwnam(name->str))) { + g_string_append(dst, pwd->pw_dir); + } + g_string_free(name, true); + } + } else if (*src == '$') { + name = g_string_new(""); + /* skip the $ */ + src++; + /* look for ${VAR}*/ + if (*src == '{') { + /* skip { */ + src++; + /* look ahead to } or end of string */ + while (*src && *src != '}') { + g_string_append_c(name, *src); + src++; + } + /* if the } was reached - skip this */ + if (*src == '}') { + src++; + } + } else { /* process $VAR */ + /* look ahead to /, space or end of string */ + while (*src && VB_IS_IDENT(*src)) { + g_string_append_c(name, *src); + src++; + } + } + /* append the variable to the destination string */ + if ((env = g_getenv(name->str))) { + g_string_append(dst, env); + } + g_string_free(name, true); + } else if (!VB_IS_ALNUM(*src)) { + /* if we match non alnum char - mark this as beginning of new part */ + start = true; + } else { + /* we left the start of phrase behind */ + start = false; } - g_free(shellcmd); - g_free(cmd); - } - /* if string needn't to be expanded or expansion fialed use it like it is */ - if (!out) { - out = g_strdup(str); + g_string_append_c(dst, *src); + src++; } - return out; + result = dst->str; + g_string_free(dst, false); + + return result; } diff --git a/src/util.h b/src/util.h index 657f741..a4b51fb 100644 --- a/src/util.h +++ b/src/util.h @@ -40,6 +40,6 @@ char* util_strcasestr(const char* haystack, const char* needle); char *util_str_replace(const char* search, const char* replace, const char* string); gboolean util_create_tmp_file(const char *content, char **file); char *util_build_path(const char *path, const char *dir); -char *util_shell_expand(const char *str); +char *util_expand(const char *src); #endif /* end of include guard: _UTIL_H */ -- 2.20.1