Don't expand string via shell.
authorDaniel Carl <danielcarl@gmx.de>
Tue, 11 Mar 2014 00:11:18 +0000 (01:11 +0100)
committerDaniel Carl <danielcarl@gmx.de>
Tue, 11 Mar 2014 00:11:18 +0000 (01:11 +0100)
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
src/setting.c
src/util.c
src/util.h

index 2f74365..9180a80 100644 (file)
 #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
index 0e727e6..c76325e 100644 (file)
@@ -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);
index 9456de7..f3fb470 100644 (file)
 
 #include "config.h"
 #include <stdio.h>
+#include <pwd.h>
 #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;
 }
index 657f741..a4b51fb 100644 (file)
@@ -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 */