summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Carl <danielcarl@gmx.de>2014-03-11 01:11:18 +0100
committerDaniel Carl <danielcarl@gmx.de>2014-03-11 01:11:18 +0100
commit8df725d51392131fd12d833200f03af677442641 (patch)
tree36b71532f3558ffabc0929ba44b10a57f6d793f6
parentdf87866aa27b14b6fcf2b347a22f9d98527208ba (diff)
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.
-rw-r--r--src/ascii.h60
-rw-r--r--src/setting.c2
-rw-r--r--src/util.c98
-rw-r--r--src/util.h2
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 <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;
}
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 */