diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-04-30 20:21:00 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-05-05 15:06:42 -0700 |
commit | 7410616cd9dbbec97cf98d75324da5cda2b2f7a2 (patch) | |
tree | 6d968995b3bdf961603ab4853bf078c0dbdce27c /src/shared/unit-name.c | |
parent | 6442185ab674cc202d63c18605057b9a51ca2722 (diff) |
core: rework unit name validation and manipulation logic
A variety of changes:
- Make sure all our calls distuingish OOM from other errors if OOM is
not the only error possible.
- Be much stricter when parsing escaped paths, do not accept trailing or
leading escaped slashes.
- Change unit validation to take a bit mask for allowing plain names,
instance names or template names or an combination thereof.
- Refuse manipulating invalid unit name
Diffstat (limited to 'src/shared/unit-name.c')
-rw-r--r-- | src/shared/unit-name.c | 600 |
1 files changed, 377 insertions, 223 deletions
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c index 86d6ce3df1..c41d7d86a7 100644 --- a/src/shared/unit-name.c +++ b/src/shared/unit-name.c @@ -33,45 +33,13 @@ DIGITS LETTERS \ ":-_.\\" -static const char* const unit_type_table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "service", - [UNIT_SOCKET] = "socket", - [UNIT_BUSNAME] = "busname", - [UNIT_TARGET] = "target", - [UNIT_SNAPSHOT] = "snapshot", - [UNIT_DEVICE] = "device", - [UNIT_MOUNT] = "mount", - [UNIT_AUTOMOUNT] = "automount", - [UNIT_SWAP] = "swap", - [UNIT_TIMER] = "timer", - [UNIT_PATH] = "path", - [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); - -static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { - [UNIT_STUB] = "stub", - [UNIT_LOADED] = "loaded", - [UNIT_NOT_FOUND] = "not-found", - [UNIT_ERROR] = "error", - [UNIT_MERGED] = "merged", - [UNIT_MASKED] = "masked" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); - -bool unit_name_is_valid(const char *n, enum template_valid template_ok) { +bool unit_name_is_valid(const char *n, UnitNameFlags flags) { const char *e, *i, *at; - /* Valid formats: - * - * string@instance.suffix - * string.suffix - */ + assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0); - assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID)); + if (_unlikely_(flags == 0)) + return false; if (isempty(n)) return false; @@ -95,15 +63,32 @@ bool unit_name_is_valid(const char *n, enum template_valid template_ok) { return false; } - if (at) { - if (at == n) - return false; + if (at == n) + return false; - if (template_ok != TEMPLATE_VALID && at+1 == e) - return false; - } + if (flags & UNIT_NAME_PLAIN) + if (!at) + return true; - return true; + if (flags & UNIT_NAME_INSTANCE) + if (at && e > at + 1) + return true; + + if (flags & UNIT_NAME_TEMPLATE) + if (at && e == at + 1) + return true; + + return false; +} + +bool unit_prefix_is_valid(const char *p) { + + /* We don't allow additional @ in the prefix string */ + + if (isempty(p)) + return false; + + return in_charset(p, VALID_CHARS); } bool unit_instance_is_valid(const char *i) { @@ -120,14 +105,41 @@ bool unit_instance_is_valid(const char *i) { return in_charset(i, "@" VALID_CHARS); } -bool unit_prefix_is_valid(const char *p) { +bool unit_suffix_is_valid(const char *s) { + if (isempty(s)) + return false; - /* We don't allow additional @ in the instance string */ + if (s[0] != '.') + return false; - if (isempty(p)) + if (unit_type_from_string(s + 1) < 0) return false; - return in_charset(p, VALID_CHARS); + return true; +} + +int unit_name_to_prefix(const char *n, char **ret) { + const char *p; + char *s; + + assert(n); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + p = strchr(n, '@'); + if (!p) + p = strrchr(n, '.'); + + assert_se(p); + + s = strndup(n, p - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } int unit_name_to_instance(const char *n, char **instance) { @@ -137,6 +149,9 @@ int unit_name_to_instance(const char *n, char **instance) { assert(n); assert(instance); + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + /* Everything past the first @ and before the last . is the instance */ p = strchr(n, '@'); if (!p) { @@ -144,13 +159,13 @@ int unit_name_to_instance(const char *n, char **instance) { return 0; } - d = strrchr(n, '.'); + p++; + + d = strrchr(p, '.'); if (!d) return -EINVAL; - if (d < p) - return -EINVAL; - i = strndup(p+1, d-p-1); + i = strndup(p, d-p); if (!i) return -ENOMEM; @@ -158,55 +173,95 @@ int unit_name_to_instance(const char *n, char **instance) { return 1; } -char *unit_name_to_prefix_and_instance(const char *n) { +int unit_name_to_prefix_and_instance(const char *n, char **ret) { const char *d; + char *s; assert(n); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; - assert_se(d = strrchr(n, '.')); - return strndup(n, d - n); + d = strrchr(n, '.'); + if (!d) + return -EINVAL; + + s = strndup(n, d - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } -char *unit_name_to_prefix(const char *n) { - const char *p; +UnitType unit_name_to_type(const char *n) { + const char *e; assert(n); - p = strchr(n, '@'); - if (p) - return strndup(n, p - n); + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return _UNIT_TYPE_INVALID; - return unit_name_to_prefix_and_instance(n); + assert_se(e = strrchr(n, '.')); + + return unit_type_from_string(e + 1); } -char *unit_name_change_suffix(const char *n, const char *suffix) { - char *e, *r; +int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { + char *e, *s; size_t a, b; assert(n); assert(suffix); - assert(suffix[0] == '.'); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; assert_se(e = strrchr(n, '.')); + a = e - n; b = strlen(suffix); - r = new(char, a + b + 1); - if (!r) - return NULL; + s = new(char, a + b + 1); + if (!s) + return -ENOMEM; - strcpy(mempcpy(r, n, a), suffix); - return r; + strcpy(mempcpy(s, n, a), suffix); + *ret = s; + + return 0; } -char *unit_name_build(const char *prefix, const char *instance, const char *suffix) { +int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) { + char *s; + assert(prefix); assert(suffix); + assert(ret); + + if (!unit_prefix_is_valid(prefix)) + return -EINVAL; + + if (instance && !unit_instance_is_valid(instance)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; if (!instance) - return strappend(prefix, suffix); + s = strappend(prefix, suffix); + else + s = strjoin(prefix, "@", instance, suffix, NULL); + if (!s) + return -ENOMEM; - return strjoin(prefix, "@", instance, suffix, NULL); + *ret = s; + return 0; } static char *do_escape_char(char c, char *t) { @@ -242,30 +297,6 @@ static char *do_escape(const char *f, char *t) { return t; } -static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) { - const char *valid_chars; - - assert(f); - assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB)); - assert(t); - - /* We'll only escape the obvious characters here, to play - * safe. */ - - valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(valid_chars, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - return t; -} - char *unit_name_escape(const char *f) { char *r, *t; @@ -281,14 +312,15 @@ char *unit_name_escape(const char *f) { return r; } -char *unit_name_unescape(const char *f) { - char *r, *t; +int unit_name_unescape(const char *f, char **ret) { + _cleanup_free_ char *r = NULL; + char *t; assert(f); r = strdup(f); if (!r) - return NULL; + return -ENOMEM; for (t = r; *f; f++) { if (*f == '-') @@ -296,180 +328,228 @@ char *unit_name_unescape(const char *f) { else if (*f == '\\') { int a, b; - if (f[1] != 'x' || - (a = unhexchar(f[2])) < 0 || - (b = unhexchar(f[3])) < 0) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - } else { - *(t++) = (char) ((a << 4) | b); - f += 3; - } + if (f[1] != 'x') + return -EINVAL; + + a = unhexchar(f[2]); + if (a < 0) + return -EINVAL; + + b = unhexchar(f[3]); + if (b < 0) + return -EINVAL; + + *(t++) = (char) ((a << 4) | b); + f += 3; } else *(t++) = *f; } *t = 0; - return r; + *ret = r; + r = NULL; + + return 0; } -char *unit_name_path_escape(const char *f) { - _cleanup_free_ char *p = NULL; +int unit_name_path_escape(const char *f, char **ret) { + char *p, *s; assert(f); + assert(ret); - p = strdup(f); + p = strdupa(f); if (!p) - return NULL; + return -ENOMEM; path_kill_slashes(p); if (STR_IN_SET(p, "/", "")) - return strdup("-"); - - return unit_name_escape(p[0] == '/' ? p + 1 : p); -} + s = strdup("-"); + else { + char *e; -char *unit_name_path_unescape(const char *f) { - char *e, *w; + if (!path_is_safe(p)) + return -EINVAL; - assert(f); + /* Truncate trailing slashes */ + e = endswith(p, "/"); + if (e) + *e = 0; - e = unit_name_unescape(f); - if (!e) - return NULL; + /* Truncate leading slashes */ + if (p[0] == '/') + p++; - if (e[0] != '/') { - w = strappend("/", e); - free(e); - return w; + s = unit_name_escape(p); } + if (!s) + return -ENOMEM; - return e; + *ret = s; + return 0; } -bool unit_name_is_template(const char *n) { - const char *p, *e; - - assert(n); +int unit_name_path_unescape(const char *f, char **ret) { + char *s, *w; + int r; - p = strchr(n, '@'); - if (!p) - return false; + assert(f); - e = strrchr(p+1, '.'); - if (!e) - return false; + if (streq(f, "-")) { + s = strdup("/"); + if (!s) + return -ENOMEM; - return e == p + 1; -} + *ret = s; + return 0; + } -bool unit_name_is_instance(const char *n) { - const char *p, *e; + r = unit_name_unescape(f, &s); + if (r < 0) + return r; - assert(n); + /* Don't accept trailing or leading slashes */ + if (startswith(s, "/") || endswith(s, "/")) { + free(s); + return -EINVAL; + } - p = strchr(n, '@'); - if (!p) - return false; + /* Prefix a slash again */ + w = strappend("/", s); + free(s); + if (!w) + return -ENOMEM; - e = strrchr(p+1, '.'); - if (!e) - return false; + if (!path_is_safe(w)) { + free(w); + return -EINVAL; + } - return e > p + 1; + *ret = w; + return 0; } -char *unit_name_replace_instance(const char *f, const char *i) { +int unit_name_replace_instance(const char *f, const char *i, char **ret) { const char *p, *e; - char *r; + char *s; size_t a, b; assert(f); assert(i); + assert(ret); - p = strchr(f, '@'); - if (!p) - return strdup(f); + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + if (!unit_instance_is_valid(i)) + return -EINVAL; - e = strrchr(f, '.'); - if (!e) - e = strchr(f, 0); + assert_se(p = strchr(f, '@')); + assert_se(e = strrchr(f, '.')); a = p - f; b = strlen(i); - r = new(char, a + 1 + b + strlen(e) + 1); - if (!r) - return NULL; + s = new(char, a + 1 + b + strlen(e) + 1); + if (!s) + return -ENOMEM; - strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e); - return r; + strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); + + *ret = s; + return 0; } -char *unit_name_template(const char *f) { +int unit_name_template(const char *f, char **ret) { const char *p, *e; - char *r; + char *s; size_t a; assert(f); + assert(ret); - p = strchr(f, '@'); - if (!p) - return strdup(f); + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; - e = strrchr(f, '.'); - if (!e) - e = strchr(f, 0); + assert_se(p = strchr(f, '@')); + assert_se(e = strrchr(f, '.')); a = p - f; - r = new(char, a + 1 + strlen(e) + 1); - if (!r) - return NULL; + s = new(char, a + 1 + strlen(e) + 1); + if (!s) + return -ENOMEM; - strcpy(mempcpy(r, f, a + 1), e); - return r; + strcpy(mempcpy(s, f, a + 1), e); + + *ret = s; + return 0; } -char *unit_name_from_path(const char *path, const char *suffix) { +int unit_name_from_path(const char *path, const char *suffix, char **ret) { _cleanup_free_ char *p = NULL; + char *s = NULL; + int r; assert(path); assert(suffix); + assert(ret); - p = unit_name_path_escape(path); - if (!p) - return NULL; + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; - return strappend(p, suffix); + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; + + s = strappend(p, suffix); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; } -char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) { +int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) { _cleanup_free_ char *p = NULL; + char *s; + int r; assert(prefix); assert(path); assert(suffix); + assert(ret); - p = unit_name_path_escape(path); - if (!p) - return NULL; + if (!unit_prefix_is_valid(prefix)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; + + s = strjoin(prefix, "@", p, suffix, NULL); + if (!s) + return -ENOMEM; - return strjoin(prefix, "@", p, suffix, NULL); + *ret = s; + return 0; } -char *unit_name_to_path(const char *name) { - _cleanup_free_ char *w = NULL; +int unit_name_to_path(const char *name, char **ret) { + _cleanup_free_ char *prefix = NULL; + int r; assert(name); - w = unit_name_to_prefix(name); - if (!w) - return NULL; + r = unit_name_to_prefix(name, &prefix); + if (r < 0) + return r; - return unit_name_path_unescape(w); + return unit_name_path_unescape(prefix, ret); } char *unit_dbus_path_from_name(const char *name) { @@ -500,6 +580,30 @@ int unit_name_from_dbus_path(const char *path, char **name) { return 0; } +static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { + const char *valid_chars; + + assert(f); + assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB)); + assert(t); + + /* We'll only escape the obvious characters here, to play + * safe. */ + + valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; + + for (; *f; f++) { + if (*f == '/') + *(t++) = '-'; + else if (!strchr(valid_chars, *f)) + t = do_escape_char(*f, t); + else + *(t++) = *f; + } + + return t; +} + /** * Convert a string to a unit name. /dev/blah is converted to dev-blah.device, * /blah/blah is converted to blah-blah.mount, anything else is left alone, @@ -507,54 +611,75 @@ int unit_name_from_dbus_path(const char *path, char **name) { * * If @allow_globs, globs characters are preserved. Otherwise they are escaped. */ -char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) { - char *r, *t; +int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) { + char *s, *t; + int r; assert(name); assert(suffix); - assert(suffix[0] == '.'); + assert(ret); - if (is_device_path(name)) - return unit_name_from_path(name, ".device"); + if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */ + return -EINVAL; - if (path_is_absolute(name)) - return unit_name_from_path(name, ".mount"); + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; - r = new(char, strlen(name) * 4 + strlen(suffix) + 1); - if (!r) - return NULL; + if (unit_name_is_valid(name, UNIT_NAME_ANY)) { + /* No mangling necessary... */ + s = strdup(name); + if (!s) + return -ENOMEM; - t = do_escape_mangle(name, allow_globs, r); + *ret = s; + return 0; + } - if (unit_name_to_type(name) < 0) - strcpy(t, suffix); - else - *t = 0; + if (is_device_path(name)) { + r = unit_name_from_path(name, ".device", ret); + if (r >= 0) + return 1; + if (r != -EINVAL) + return r; + } - return r; -} + if (path_is_absolute(name)) { + r = unit_name_from_path(name, ".mount", ret); + if (r >= 0) + return 1; + if (r != -EINVAL) + return r; + } -UnitType unit_name_to_type(const char *n) { - const char *e; + s = new(char, strlen(name) * 4 + strlen(suffix) + 1); + if (!s) + return -ENOMEM; - assert(n); + t = do_escape_mangle(name, allow_globs, s); + *t = 0; - e = strrchr(n, '.'); - if (!e) - return _UNIT_TYPE_INVALID; + if (unit_name_to_type(s) < 0) + strcpy(t, suffix); - return unit_type_from_string(e + 1); + *ret = s; + return 1; } -int build_subslice(const char *slice, const char*name, char **subslice) { - char *ret; +int slice_build_subslice(const char *slice, const char*name, char **ret) { + char *subslice; assert(slice); assert(name); - assert(subslice); + assert(ret); + + if (!unit_name_is_valid(slice, UNIT_NAME_PLAIN)) + return -EINVAL; + + if (!unit_prefix_is_valid(name)) + return -EINVAL; if (streq(slice, "-.slice")) - ret = strappend(name, ".slice"); + subslice = strappend(name, ".slice"); else { char *e; @@ -562,17 +687,46 @@ int build_subslice(const char *slice, const char*name, char **subslice) { if (!e) return -EINVAL; - ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); - if (!ret) + subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); + if (!subslice) return -ENOMEM; - stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice"); + stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice"); } - *subslice = ret; + *ret = subslice; return 0; } +static const char* const unit_type_table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "service", + [UNIT_SOCKET] = "socket", + [UNIT_BUSNAME] = "busname", + [UNIT_TARGET] = "target", + [UNIT_SNAPSHOT] = "snapshot", + [UNIT_DEVICE] = "device", + [UNIT_MOUNT] = "mount", + [UNIT_AUTOMOUNT] = "automount", + [UNIT_SWAP] = "swap", + [UNIT_TIMER] = "timer", + [UNIT_PATH] = "path", + [UNIT_SLICE] = "slice", + [UNIT_SCOPE] = "scope" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); + +static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { + [UNIT_STUB] = "stub", + [UNIT_LOADED] = "loaded", + [UNIT_NOT_FOUND] = "not-found", + [UNIT_ERROR] = "error", + [UNIT_MERGED] = "merged", + [UNIT_MASKED] = "masked" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); + static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = "Requires", [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable", |