From 1f9113336e8eb4bd89ca040e90c5fdc79b0c567f Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Tue, 11 Jul 2017 18:25:13 +0200 Subject: [PATCH] snd_user_file: avoid use wordexp As suggested in POSIX[1], wordexp might execute the shell. If the libc implementation does so, it will break the firefox sandbox which does not allow exec. This happened on Alpine Linux with musl libc[2]. Since we cannot guarantee that the system wordexp implementation does not execute shell, we cannot really use it, and need to implement the ~/ expansion ourselves. [1]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html#tag_16_684_08 [2]: http://bugs.alpinelinux.org/issues/7454#note-2 Signed-off-by: Natanael Copa --- src/userfile.c | 77 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/src/userfile.c b/src/userfile.c index 72779da4..0e3f5fae 100644 --- a/src/userfile.c +++ b/src/userfile.c @@ -21,6 +21,11 @@ #include #include #include +#include +#include +#include +#include +#include /** * \brief Get the full file name @@ -28,46 +33,58 @@ * \param result The pointer to store the resultant file name * \return 0 if successful, or a negative error code * - * Parses the given file name with POSIX-Shell-like expansion and - * stores the first matchine one. The returned string is strdup'ed. + * Parses the given file name with POSIX-Shell-like expansion for ~/. + * The returned string is strdup'ed. */ -#ifdef HAVE_WORDEXP_H -#include #include int snd_user_file(const char *file, char **result) { - wordexp_t we; int err; - + size_t len; + char *buf = NULL; + assert(file && result); - err = wordexp(file, &we, WRDE_NOCMD); - switch (err) { - case WRDE_NOSPACE: - wordfree(&we); - return -ENOMEM; - case 0: - if (we.we_wordc == 1) - break; - wordfree(&we); - /* fall thru */ - default: - return -EINVAL; + *result = NULL; + + /* expand ~/ if needed */ + if (file[0] == '~' && file[1] == '/') { + const char *home = getenv("HOME"); + if (home == NULL) { + struct passwd pwent, *p = NULL; + uid_t id = getuid(); + size_t bufsize = 1024; + + buf = malloc(bufsize); + if (buf == NULL) + goto out; + + while ((err = getpwuid_r(id, &pwent, buf, bufsize, &p)) == ERANGE) { + char *newbuf; + bufsize += 1024; + if (bufsize < 1024) + break; + newbuf = realloc(buf, bufsize); + if (newbuf == NULL) + goto out; + buf = newbuf; + } + home = err ? "" : pwent.pw_dir; + } + len = strlen(home) + strlen(&file[2]) + 2; + *result = malloc(len); + if (*result) + snprintf(*result, len, "%s/%s", home, &file[2]); + } else { + *result = strdup(file); } - *result = strdup(we.we_wordv[0]); - wordfree(&we); + +out: + if (buf) + free(buf); + if (*result == NULL) return -ENOMEM; return 0; } -#else /* !HAVE_WORDEXP_H */ -/* just copy the string - would be nicer to expand by ourselves, though... */ -int snd_user_file(const char *file, char **result) -{ - *result = strdup(file); - if (! *result) - return -ENOMEM; - return 0; -} -#endif /* HAVE_WORDEXP_H */ -- 2.13.2