diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 8de092ee4e..37d0afb0cb 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1591,7 +1591,7 @@ int bus_set_transient_exec_command( if (!exec_chars) return -ENOMEM; - a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS); + a = unit_concat_strv(c->argv, UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_EXEC_SYNTAX); if (!a) return -ENOMEM; @@ -1601,7 +1601,8 @@ int bus_set_transient_exec_command( _cleanup_free_ char *t = NULL; const char *p; - p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t); + p = unit_escape_setting(c->path, + UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_EXEC_SYNTAX, &t); if (!p) return -ENOMEM; diff --git a/src/core/unit.c b/src/core/unit.c index 5f1c6109b0..05cee225ed 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -4254,51 +4254,62 @@ static const char* unit_drop_in_dir(Unit *u, UnitWriteFlags flags) { } char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) { - char *ret = NULL; + assert(!FLAGS_SET(flags, UNIT_ESCAPE_EXEC_SYNTAX | UNIT_ESCAPE_C)); + + _cleanup_free_ char *t = NULL; if (!s) return NULL; - /* Escapes the input string as requested. Returns the escaped string. If 'buf' is specified then the allocated - * return buffer pointer is also written to *buf, except if no escaping was necessary, in which case *buf is - * set to NULL, and the input pointer is returned as-is. This means the return value always contains a properly - * escaped version, but *buf when passed only contains a pointer if an allocation was necessary. If *buf is - * not specified, then the return value always needs to be freed. Callers can use this to optimize memory - * allocations. */ + /* Escapes the input string as requested. Returns the escaped string. If 'buf' is specified then the + * allocated return buffer pointer is also written to *buf, except if no escaping was necessary, in + * which case *buf is set to NULL, and the input pointer is returned as-is. This means the return + * value always contains a properly escaped version, but *buf when passed only contains a pointer if + * an allocation was necessary. If *buf is not specified, then the return value always needs to be + * freed. Callers can use this to optimize memory allocations. */ if (flags & UNIT_ESCAPE_SPECIFIERS) { - ret = specifier_escape(s); - if (!ret) + t = specifier_escape(s); + if (!t) return NULL; - s = ret; + s = t; } - if (flags & UNIT_ESCAPE_C) { - char *a; + /* We either do c-escaping or shell-escaping, to additionally escape characters that we parse for + * ExecStart= and friend, i.e. '$' and ';' and quotes. */ - a = cescape(s); - free(ret); - if (!a) + if (flags & UNIT_ESCAPE_EXEC_SYNTAX) { + char *t2 = shell_escape(s, "$;'\""); + if (!t2) return NULL; + free_and_replace(t, t2); - ret = a; + s = t; + + } else if (flags & UNIT_ESCAPE_C) { + char *t2 = cescape(s); + if (!t2) + return NULL; + free_and_replace(t, t2); + + s = t; } if (buf) { - *buf = ret; - return ret ?: (char*) s; + *buf = TAKE_PTR(t); + return (char*) s; } - return ret ?: strdup(s); + return TAKE_PTR(t) ?: strdup(s); } char* unit_concat_strv(char **l, UnitWriteFlags flags) { _cleanup_free_ char *result = NULL; size_t n = 0; - /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command lines in a - * way suitable for ExecStart= stanzas */ + /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command + * lines in a way suitable for ExecStart= stanzas. */ STRV_FOREACH(i, l) { _cleanup_free_ char *buf = NULL; diff --git a/src/core/unit.h b/src/core/unit.h index fc8edaade5..01cef89525 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -531,19 +531,22 @@ typedef struct UnitStatusMessageFormats { /* Flags used when writing drop-in files or transient unit files */ typedef enum UnitWriteFlags { /* Write a runtime unit file or drop-in (i.e. one below /run) */ - UNIT_RUNTIME = 1 << 0, + UNIT_RUNTIME = 1 << 0, /* Write a persistent drop-in (i.e. one below /etc) */ - UNIT_PERSISTENT = 1 << 1, + UNIT_PERSISTENT = 1 << 1, /* Place this item in the per-unit-type private section, instead of [Unit] */ - UNIT_PRIVATE = 1 << 2, + UNIT_PRIVATE = 1 << 2, /* Apply specifier escaping before writing */ - UNIT_ESCAPE_SPECIFIERS = 1 << 3, + UNIT_ESCAPE_SPECIFIERS = 1 << 3, + + /* Escape elements of ExecStart= syntax before writing */ + UNIT_ESCAPE_EXEC_SYNTAX = 1 << 4, /* Apply C escaping before writing */ - UNIT_ESCAPE_C = 1 << 4, + UNIT_ESCAPE_C = 1 << 5, } UnitWriteFlags; /* Returns true if neither persistent, nor runtime storage is requested, i.e. this is a check invocation only */