diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2014-07-29 22:01:36 -0400 |
---|---|---|
committer | Anthony G. Basile <blueness@gentoo.org> | 2014-08-04 11:52:34 -0400 |
commit | 5f63fcb01095830daa7d42156c880c6fcf554e56 (patch) | |
tree | 4414341e4b559d8673601c8b092a9fb00fed281b | |
parent | 837a0df0287638354fe71665a76f0f41bd5a417e (diff) |
Reject invalid quoted strings
String which ended in an unfinished quote were accepted, potentially
with bad memory accesses.
Reject anything which ends in a unfished quote, or contains
non-whitespace characters right after the closing quote.
_FOREACH_WORD now returns the invalid character in *state. But this return
value is not checked anywhere yet.
Also, make 'word' and 'state' variables const pointers, and rename 'w'
to 'word' in various places. Things are easier to read if the same name
is used consistently.
mbiebl_> am I correct that something like this doesn't work
mbiebl_> ExecStart=/usr/bin/encfs --extpass='/bin/systemd-ask-passwd "Unlock EncFS"'
mbiebl_> systemd seems to strip of the quotes
mbiebl_> systemctl status shows
mbiebl_> ExecStart=/usr/bin/encfs --extpass='/bin/systemd-ask-password Unlock EncFS $RootDir $MountPoint
mbiebl_> which is pretty weird
Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
-rw-r--r-- | src/libudev/util.c | 100 | ||||
-rw-r--r-- | src/libudev/util.h | 8 | ||||
-rw-r--r-- | src/udev/udevd.c | 6 |
3 files changed, 50 insertions, 64 deletions
diff --git a/src/libudev/util.c b/src/libudev/util.c index 116d723a0f..5ff767b6a7 100644 --- a/src/libudev/util.c +++ b/src/libudev/util.c @@ -263,79 +263,63 @@ int safe_atolli(const char *s, long long int *ret_lli) { return 0; } +static size_t strcspn_escaped(const char *s, const char *reject) { + bool escaped = false; + size_t n; + + for (n=0; s[n]; n++) { + if (escaped) + escaped = false; + else if (s[n] == '\\') + escaped = true; + else if (strchr(reject, s[n])) + break; + } + /* if s ends in \, return index of previous char */ + return n - escaped; +} + /* Split a string into words. */ -char *split(const char *c, size_t *l, const char *separator, char **state) { - char *current; +const char* split(const char **state, size_t *l, const char *separator, bool quoted) { + const char *current; - current = *state ? *state : (char*) c; + current = *state; - if (!*current || *c == 0) + if (!*current) { + assert(**state == '\0'); return NULL; + } current += strspn(current, separator); - *l = strcspn(current, separator); - *state = current+*l; - - return (char*) current; -} - -/* Split a string into words, but consider strings enclosed in '' and - * "" as words even if they include spaces. */ -char *split_quoted(const char *c, size_t *l, char **state) { - char *current, *e; - bool escaped = false; - - current = *state ? *state : (char*) c; - - if (!*current || *c == 0) + if (!*current) { + *state = current; return NULL; + } - current += strspn(current, WHITESPACE); - - if (*current == '\'') { - current ++; + if (quoted && strchr("\'\"", *current)) { + char quotechars[2] = {*current, '\0'}; - for (e = current; *e; e++) { - if (escaped) - escaped = false; - else if (*e == '\\') - escaped = true; - else if (*e == '\'') - break; + *l = strcspn_escaped(current + 1, quotechars); + if (current[*l + 1] == '\0' || + (current[*l + 2] && !strchr(separator, current[*l + 2]))) { + /* right quote missing or garbage at the end*/ + *state = current; + return NULL; } - - *l = e-current; - *state = *e == 0 ? e : e+1; - } else if (*current == '\"') { - current ++; - - for (e = current; *e; e++) { - if (escaped) - escaped = false; - else if (*e == '\\') - escaped = true; - else if (*e == '\"') - break; - } - - *l = e-current; - *state = *e == 0 ? e : e+1; + assert(current[*l + 1] == quotechars[0]); + *state = current++ + *l + 2; + } else if (quoted) { + *l = strcspn_escaped(current, separator); + *state = current + *l; } else { - for (e = current; *e; e++) { - if (escaped) - escaped = false; - else if (*e == '\\') - escaped = true; - else if (strchr(WHITESPACE, *e)) - break; - } - *l = e-current; - *state = e; + *l = strcspn(current, separator); + *state = current + *l; } - return (char*) current; + return current; } + char *truncate_nl(char *s) { assert(s); diff --git a/src/libudev/util.h b/src/libudev/util.h index a38e8e61c5..15a11cc537 100644 --- a/src/libudev/util.h +++ b/src/libudev/util.h @@ -134,11 +134,13 @@ int safe_atoi(const char *s, int *ret_i); int safe_atollu(const char *s, unsigned long long *ret_u); int safe_atolli(const char *s, long long int *ret_i); -char *split(const char *c, size_t *l, const char *separator, char **state); -char *split_quoted(const char *c, size_t *l, char **state); +const char* split(const char **state, size_t *l, const char *separator, bool quoted); #define FOREACH_WORD_QUOTED(word, length, s, state) \ - for ((state) = NULL, (word) = split_quoted((s), &(length), &(state)); (word); (word) = split_quoted((s), &(length), &(state))) + _FOREACH_WORD(word, length, s, WHITESPACE, true, state) + +#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ + for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); diff --git a/src/udev/udevd.c b/src/udev/udevd.c index f59de08a0e..385bb7edad 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -991,7 +991,7 @@ static void static_dev_create_from_modules(struct udev *udev) { */ static void kernel_cmdline_options(struct udev *udev) { _cleanup_free_ char *line = NULL; - char *w, *state; + const char *word, *state; size_t l; int r; @@ -1001,10 +1001,10 @@ static void kernel_cmdline_options(struct udev *udev) { if (r <= 0) return; - FOREACH_WORD_QUOTED(w, l, line, state) { + FOREACH_WORD_QUOTED(word, l, line, state) { char *s, *opt; - s = strndup(w, l); + s = strndup(word, l); if (!s) break; |