string-util: beef up string_is_safe()
This tightens the checks of string_is_safe() and then adds flags to relax certain aspects of it. This does alter the rules on certain strings we pass a bit. We mostly tighten the rules (but I think it's find and good) but we relax them on others. I let claude review the changes in behaviour for the various call sites that I made. It summarized things in this table: ╭───────────────────────────────────────────────────┬──────────────────────────────────────────────╮ │ CALL SITE │ EFFECTIVE DELTA │ ├───────────────────────────────────────────────────┼──────────────────────────────────────────────┤ │ src/basic/syslog-util log_namespace_name_valid │ +UTF-8 required (globs already blocked) │ │ src/bootctl --efi-boot-option-description │ RELAXED: '\' and quotes now permitted │ │ src/core/dbus-manager pretimeout governor │ +UTF-8, +no-globs │ │ src/core/load-fragment ExecStart= path │ +UTF-8, +no-globs │ │ src/core/main pretimeout governor (kcmdline) │ +UTF-8, +no-globs │ │ src/core/service sd_notify STATUS= │ +no-globs (ASCII-only preserved) │ │ src/home/homectl --<identity field>= │ empty now REJECTED; +UTF-8 │ │ src/libsystemd-network dhcp_option_parse_string │ (equivalent, just explicit) │ │ src/libsystemd-network sd_dhcp_server boot_fname │ ""→NULL coerced; else equivalent │ │ src/libsystemd/journal SYSLOG_IDENTIFIER fb │ +UTF-8, +no-globs │ │ src/libsystemd/sd-json SD_JSON_STRICT strings │ +UTF-8 required │ │ src/login/logind session desktop= │ +UTF-8 required │ │ src/pcrlock EFI variable string │ +UTF-8 │ │ src/pcrlock EFI action string │ RELAXED: empty + '\' now ok; +UTF-8 │ │ src/resolve dns-delegate id (from filename) │ +UTF-8, +no-globs │ │ src/shared/boot-entry boot_entry_token_valid │ (equivalent) │ │ src/shared/conf-parser section header │ +UTF-8, +no-globs │ │ src/shared/conf-parser CONFIG_PARSE_STRING_SAFE │ +UTF-8 required │ │ src/shared/kbd-util keymap_is_valid │ (equivalent; folded into STRING_FILENAME) │ │ src/shared/tpm2 nvpcr name │ +UTF-8 required │ │ src/shared/vconsole x11 layout/model/variant/opt │ +UTF-8, +no-globs │ │ src/systemctl --kernel-cmdline= │ +0x7f DEL rejected; empty path split out │ │ src/veritysetup salt= │ RELAXED: safety check removed entirely │ │ src/vmspawn --ssh-key-type= │ +UTF-8 required │ ╰───────────────────────────────────────────────────┴──────────────────────────────────────────────╯
This commit is contained in:
committed by
Lennart Poettering
parent
a84bbd15d9
commit
812aa57d2c
+24
-9
@@ -1099,25 +1099,40 @@ int strdup_to_full(char **ret, const char *src) {
|
||||
}
|
||||
};
|
||||
|
||||
bool string_is_safe(const char *p) {
|
||||
if (!p)
|
||||
bool string_is_safe(const char *p, StringSafeFlags flags) {
|
||||
|
||||
/* Baseline checks are:
|
||||
* • No control characters (i.e. 0…31 + 127)
|
||||
* • UTF-8 valid (well, technically we skip this test if STRING_ASCII is set, since that is a tighter test)
|
||||
*/
|
||||
|
||||
if (FLAGS_SET(flags, STRING_ALLOW_EMPTY) ? !p : isempty(p))
|
||||
return false;
|
||||
|
||||
/* Checks if the specified string contains no quotes or control characters */
|
||||
if (!FLAGS_SET(flags, STRING_ASCII) && !utf8_is_valid(p))
|
||||
return false;
|
||||
|
||||
for (const char *t = p; *t; t++) {
|
||||
if (*t > 0 && *t < ' ') /* no control characters */
|
||||
if ((*t > 0 && *t < ' ') || *t == 0x7f) /* never allow control characters */
|
||||
return false;
|
||||
|
||||
if (strchr(QUOTES "\\\x7f", *t))
|
||||
if (!FLAGS_SET(flags, STRING_ALLOW_BACKSLASHES) && *t == '\\')
|
||||
return false;
|
||||
|
||||
if (!FLAGS_SET(flags, STRING_ALLOW_QUOTES) && strchr(QUOTES, *t))
|
||||
return false;
|
||||
|
||||
if (!FLAGS_SET(flags, STRING_ALLOW_GLOBS) && strchr(GLOB_CHARS, *t))
|
||||
return false;
|
||||
|
||||
if (FLAGS_SET(flags, STRING_ASCII) && (uint8_t) *t >= 0x80)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (FLAGS_SET(flags, STRING_FILENAME) && !filename_is_valid(p))
|
||||
return false;
|
||||
|
||||
bool string_is_safe_ascii(const char *p) {
|
||||
return ascii_is_valid(p) && string_is_safe(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
char* str_realloc(char *p) {
|
||||
|
||||
+10
-2
@@ -220,8 +220,16 @@ static inline int strdup_to(char **ret, const char *src) {
|
||||
return r < 0 ? r : 0; /* Suppress return value of 1. */
|
||||
}
|
||||
|
||||
bool string_is_safe(const char *p) _pure_;
|
||||
bool string_is_safe_ascii(const char *p) _pure_;
|
||||
typedef enum StringSafeFlags {
|
||||
STRING_ASCII = 1 << 0, /* Verify string is 7-Bit ASCII (rather than just UTF-8) */
|
||||
STRING_ALLOW_EMPTY = 1 << 1, /* Allow empty strings */
|
||||
STRING_ALLOW_BACKSLASHES = 1 << 2, /* Allow backslashes (\) */
|
||||
STRING_ALLOW_QUOTES = 1 << 3, /* Allow quotes (" or ') */
|
||||
STRING_ALLOW_GLOBS = 1 << 4, /* Allow globs (?, * or [) */
|
||||
STRING_FILENAME = 1 << 5, /* Verify the string is valid as regular filename */
|
||||
} StringSafeFlags;
|
||||
|
||||
bool string_is_safe(const char *p, StringSafeFlags flags) _pure_;
|
||||
|
||||
DISABLE_WARNING_STRINGOP_TRUNCATION;
|
||||
static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
|
||||
|
||||
+2
-10
@@ -4,9 +4,7 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "glob-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "path-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "syslog-util.h"
|
||||
@@ -111,7 +109,8 @@ bool log_namespace_name_valid(const char *s) {
|
||||
* (so that /var/log/journal/<machine-id>.<namespace> can be created based on it). Also make sure it
|
||||
* is suitable as unit instance name, and does not contain fishy characters. */
|
||||
|
||||
if (!filename_is_valid(s))
|
||||
/* Let's avoid globbing for now */
|
||||
if (!string_is_safe(s, STRING_FILENAME))
|
||||
return false;
|
||||
|
||||
if (strlen(s) > LOG_NAMESPACE_MAX)
|
||||
@@ -120,12 +119,5 @@ bool log_namespace_name_valid(const char *s) {
|
||||
if (!unit_instance_is_valid(s))
|
||||
return false;
|
||||
|
||||
if (!string_is_safe(s))
|
||||
return false;
|
||||
|
||||
/* Let's avoid globbing for now */
|
||||
if (string_is_glob(s))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user