diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-12-14 00:05:41 +0100 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-12-25 07:13:17 +0100 |
commit | f6caab8995a27244db185f075e751a119e4bdedc (patch) | |
tree | 0d17633120b8c8dab7209dc3f5e4532738231291 /src/udev/udev-rules.c | |
parent | test: add tests for strnpcpy_full() and friends (diff) | |
download | systemd-f6caab8995a27244db185f075e751a119e4bdedc.tar.xz systemd-f6caab8995a27244db185f075e751a119e4bdedc.zip |
udev: warn when result of string substitution is truncated
Diffstat (limited to 'src/udev/udev-rules.c')
-rw-r--r-- | src/udev/udev-rules.c | 232 |
1 files changed, 199 insertions, 33 deletions
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 1a384d6b38..b353b5b810 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1381,11 +1381,14 @@ static bool token_match_string(UdevRuleToken *token, const char *str) { return token->op == (match ? OP_MATCH : OP_NOMATCH); } -static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) { +static bool token_match_attr(UdevRules *rules, UdevRuleToken *token, sd_device *dev, UdevEvent *event) { char nbuf[UDEV_NAME_SIZE], vbuf[UDEV_NAME_SIZE]; const char *name, *value; + bool truncated; + assert(rules); assert(token); + assert(IN_SET(token->type, TK_M_ATTR, TK_M_PARENTS_ATTR)); assert(dev); assert(event); @@ -1393,7 +1396,15 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev switch (token->attr_subst_type) { case SUBST_TYPE_FORMAT: - (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false); + (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, + "The sysfs attribute name '%s' is truncated while substituting into '%s', " + "assuming the %s key does not match.", nbuf, name, + token->type == TK_M_ATTR ? "ATTR" : "ATTRS"); + return false; + } + name = nbuf; _fallthrough_; case SUBST_TYPE_PLAIN: @@ -1497,19 +1508,22 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) { char buf[UDEV_PATH_SIZE], *p; const char *tail; size_t len, size; + bool truncated; assert(attr); tail = strstr(attr, "/*/"); if (!tail) - return 0; + return 0; len = tail - attr + 1; /* include slash at the end */ tail += 2; /* include slash at the beginning */ p = buf; size = sizeof(buf); - size -= strnpcpy(&p, size, attr, len); + size -= strnpcpy_full(&p, size, attr, len, &truncated); + if (truncated) + return -ENOENT; dir = opendir(buf); if (!dir) @@ -1519,7 +1533,10 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) { if (de->d_name[0] == '.') continue; - strscpyl(p, size, de->d_name, tail, NULL); + strscpyl_full(p, size, &truncated, de->d_name, tail, NULL); + if (truncated) + continue; + if (faccessat(dirfd(dir), p, F_OK, 0) < 0) continue; @@ -1645,12 +1662,19 @@ static int udev_rule_apply_token_to_event( } case TK_M_ATTR: case TK_M_PARENTS_ATTR: - return token_match_attr(token, dev, event); + return token_match_attr(rules, token, dev, event); case TK_M_SYSCTL: { _cleanup_free_ char *value = NULL; char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', " + "assuming the SYSCTL key does not match.", buf, (const char*) token->data); + return false; + } - (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false); r = sysctl_read(sysctl_normalize(buf), &value); if (r < 0 && r != -ENOENT) return log_rule_error_errno(dev, rules, r, "Failed to read sysctl '%s': %m", buf); @@ -1661,9 +1685,15 @@ static int udev_rule_apply_token_to_event( mode_t mode = PTR_TO_MODE(token->data); char buf[UDEV_PATH_SIZE]; struct stat statbuf; - bool match; + bool match, truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The file name '%s' is truncated while substituting into '%s', " + "assuming the TEST key does not match", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); if (!path_is_absolute(buf) && udev_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) { char tmp[UDEV_PATH_SIZE]; @@ -1673,8 +1703,11 @@ static int udev_rule_apply_token_to_event( if (r < 0) return log_rule_error_errno(dev, rules, r, "Failed to get syspath: %m"); - strscpy(tmp, sizeof(tmp), buf); - strscpyl(buf, sizeof(buf), val, "/", tmp, NULL); + strscpy_full(tmp, sizeof(tmp), buf, &truncated); + assert(!truncated); + strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL); + if (truncated) + return false; } r = attr_subst_subdir(buf); @@ -1694,10 +1727,17 @@ static int udev_rule_apply_token_to_event( } case TK_M_PROGRAM: { char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE]; + bool truncated; size_t count; event->program_result = mfree(event->program_result); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The command '%s' is trucated while substituting into '%s', " + "assuming the PROGRAM key does not match.", buf, token->value); + return false; + } + log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf); r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result)); @@ -1721,8 +1761,15 @@ static int udev_rule_apply_token_to_event( case TK_M_IMPORT_FILE: { _cleanup_fclose_ FILE *f = NULL; char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The file name '%s' to be imported is truncated while substituting into '%s', " + "assuming the IMPORT key does not match.", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Importing properties from '%s'", buf); f = fopen(buf, "re"); @@ -1768,8 +1815,15 @@ static int udev_rule_apply_token_to_event( case TK_M_IMPORT_PROGRAM: { _cleanup_strv_free_ char **lines = NULL; char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE], **line; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The command '%s' is truncated while substituting into '%s', " + "assuming the IMPORT key does not match.", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf); r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result); @@ -1813,6 +1867,7 @@ static int udev_rule_apply_token_to_event( assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX); unsigned mask = 1U << (int) cmd; char buf[UDEV_PATH_SIZE]; + bool truncated; if (udev_builtin_run_once(cmd)) { /* check if we ran already */ @@ -1826,7 +1881,13 @@ static int udev_rule_apply_token_to_event( event->builtin_run |= mask; } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The builtin command '%s' is truncated while substituting into '%s', " + "assuming the IMPORT key does not match", buf, token->value); + return false; + } + log_rule_debug(dev, rules, "Importing properties from results of builtin command '%s'", buf); r = udev_builtin_run(dev, &event->rtnl, cmd, buf, false); @@ -1875,8 +1936,15 @@ static int udev_rule_apply_token_to_event( } case TK_M_IMPORT_PARENT: { char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The property name '%s' is truncated while substituting into '%s', " + "assuming the IMPORT key does not match.", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); r = import_parent_into_properties(dev, buf); if (r < 0) return log_rule_error_errno(dev, rules, r, @@ -1925,13 +1993,20 @@ static int udev_rule_apply_token_to_event( case TK_A_OWNER: { char owner[UDEV_NAME_SIZE]; const char *ow = owner; + bool truncated; if (event->owner_final) break; if (token->op == OP_ASSIGN_FINAL) event->owner_final = true; - (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false); + (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The user name '%s' is truncated while substituting into '%s', " + "refusing to apply the OWNER key.", owner, token->value); + break; + } + r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(dev, rules, r, "user", owner); @@ -1942,13 +2017,20 @@ static int udev_rule_apply_token_to_event( case TK_A_GROUP: { char group[UDEV_NAME_SIZE]; const char *gr = group; + bool truncated; if (event->group_final) break; if (token->op == OP_ASSIGN_FINAL) event->group_final = true; - (void) udev_event_apply_format(event, token->value, group, sizeof(group), false); + (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The group name '%s' is truncated while substituting into '%s', " + "refusing to apply the GROUP key.", group, token->value); + break; + } + r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(dev, rules, r, "group", group); @@ -1958,13 +2040,20 @@ static int udev_rule_apply_token_to_event( } case TK_A_MODE: { char mode_str[UDEV_NAME_SIZE]; + bool truncated; if (event->mode_final) break; if (token->op == OP_ASSIGN_FINAL) event->mode_final = true; - (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false); + (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The mode '%s' is truncated while substituting into %s, " + "refusing to apply the MODE key.", mode_str, token->value); + break; + } + r = parse_mode(mode_str, &event->mode); if (r < 0) log_rule_error_errno(dev, rules, r, "Failed to parse mode '%s', ignoring: %m", mode_str); @@ -2005,12 +2094,19 @@ static int udev_rule_apply_token_to_event( case TK_A_SECLABEL: { _cleanup_free_ char *name = NULL, *label = NULL; char label_str[UDEV_LINE_SIZE] = {}; + bool truncated; name = strdup(token->data); if (!name) return log_oom(); - (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false); + (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The security label '%s' is truncated while substituting into '%s', " + "refusing to apply the SECLABEL key.", label_str, token->value); + break; + } + if (!isempty(label_str)) label = strdup(label_str); else @@ -2037,6 +2133,7 @@ static int udev_rule_apply_token_to_event( const char *val, *name = token->data; char value_new[UDEV_NAME_SIZE], *p = value_new; size_t count, l = sizeof(value_new); + bool truncated; if (isempty(token->value)) { if (token->op == OP_ADD) @@ -2048,10 +2145,22 @@ static int udev_rule_apply_token_to_event( } if (token->op == OP_ADD && - sd_device_get_property_value(dev, name, &val) >= 0) - l = strpcpyl(&p, l, val, " ", NULL); + sd_device_get_property_value(dev, name, &val) >= 0) { + l = strpcpyl_full(&p, l, &truncated, val, " ", NULL); + if (truncated) { + log_rule_warning(dev, rules, "The buffer for the property '%s' is full, " + "refusing to append the new value '%s'.", name, token->value); + break; + } + } + + (void) udev_event_apply_format(event, token->value, p, l, false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The property value '%s' is truncated while substituting into '%s', " + "refusing to add property '%s'.", p, token->value, name); + break; + } - (void) udev_event_apply_format(event, token->value, p, l, false); if (event->esc == ESCAPE_REPLACE) { count = udev_replace_chars(p, NULL); if (count > 0) @@ -2066,8 +2175,16 @@ static int udev_rule_apply_token_to_event( } case TK_A_TAG: { char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The tag name '%s' is truncated while substituting into '%s'," + "refusing to %s the tag.", buf, token->value, + token->op == OP_REMOVE ? "remove" : "add"); + break; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); if (token->op == OP_ASSIGN) device_cleanup_tags(dev); @@ -2086,6 +2203,7 @@ static int udev_rule_apply_token_to_event( } case TK_A_NAME: { char buf[UDEV_PATH_SIZE]; + bool truncated; size_t count; if (event->name_final) @@ -2100,7 +2218,13 @@ static int udev_rule_apply_token_to_event( break; } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The network interface name '%s' is truncated while substituting into '%s', " + "refusing to set the name.", buf, token->value); + break; + } + if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) { if (naming_scheme_has(NAMING_REPLACE_STRICTLY)) count = udev_replace_ifname(buf); @@ -2119,6 +2243,7 @@ static int udev_rule_apply_token_to_event( } case TK_A_DEVLINK: { char buf[UDEV_PATH_SIZE], *p; + bool truncated; size_t count; if (event->devlink_final) @@ -2131,7 +2256,13 @@ static int udev_rule_apply_token_to_event( device_cleanup_devlinks(dev); /* allow multiple symlinks separated by spaces */ - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The symbolic link path '%s' is truncated while substituting into '%s', " + "refusing to add the device symbolic link.", buf, token->value); + break; + } + if (event->esc == ESCAPE_UNSET) count = udev_replace_chars(buf, "/ "); else if (event->esc == ESCAPE_REPLACE) @@ -2152,7 +2283,10 @@ static int udev_rule_apply_token_to_event( next = skip_leading_chars(next, NULL); } - strscpyl(filename, sizeof(filename), "/dev/", p, NULL); + strscpyl_full(filename, sizeof(filename), &truncated, "/dev/", p, NULL); + if (truncated) + continue; + r = device_add_devlink(dev, filename); if (r < 0) return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename); @@ -2165,17 +2299,30 @@ static int udev_rule_apply_token_to_event( case TK_A_ATTR: { char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE]; const char *val, *key_name = token->data; + bool truncated; if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 && - sd_device_get_syspath(dev, &val) >= 0) - strscpyl(buf, sizeof(buf), val, "/", key_name, NULL); + sd_device_get_syspath(dev, &val) >= 0) { + strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL); + if (truncated) { + log_rule_warning(dev, rules, + "The path to the attribute '%s/%s' is too long, refusing to set the attribute.", + val, key_name); + break; + } + } r = attr_subst_subdir(buf); if (r < 0) { log_rule_error_errno(dev, rules, r, "Could not find file matches '%s', ignoring: %m", buf); break; } - (void) udev_event_apply_format(event, token->value, value, sizeof(value), false); + (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The attribute value '%s' is truncated while substituting into '%s', " + "refusing to set the attribute '%s'", value, token->value, buf); + break; + } log_rule_debug(dev, rules, "ATTR '%s' writing '%s'", buf, value); r = write_string_file(buf, value, @@ -2189,9 +2336,22 @@ static int udev_rule_apply_token_to_event( } case TK_A_SYSCTL: { char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', " + "refusing to set the sysctl entry.", buf, (const char*) token->data); + break; + } + + (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The sysctl value '%s' is truncated while substituting into '%s', " + "refusing to set the sysctl entry '%s'", value, token->value, buf); + break; + } - (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false); - (void) udev_event_apply_format(event, token->value, value, sizeof(value), false); sysctl_normalize(buf); log_rule_debug(dev, rules, "SYSCTL '%s' writing '%s'", buf, value); r = sysctl_write(buf, value); @@ -2203,6 +2363,7 @@ static int udev_rule_apply_token_to_event( case TK_A_RUN_PROGRAM: { _cleanup_free_ char *cmd = NULL; char buf[UDEV_PATH_SIZE]; + bool truncated; if (event->run_final) break; @@ -2212,7 +2373,12 @@ static int udev_rule_apply_token_to_event( if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL)) ordered_hashmap_clear_free_key(event->run_list); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The command '%s' is truncated while substituting into '%s', " + "refusing to invoke the command.", buf, token->value); + break; + } cmd = strdup(buf); if (!cmd) |