From 1099d663ab1a8c9313a16a4a0b472140618cee5a Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 1 Aug 2026 11:14:55 +0200 Subject: [PATCH] conf: escape runtime strings in generated .conf files The runparts directory and the dbus pidfile and daemon paths are embedded in double-quoted values of the generated block files. A literal quote in either ends the value early and libconfuse rejects the whole file, and a backslash is read as an escape sequence, silently mangling the path. The legacy one-liners had no quoting, so neither failure existed before the block conversion. conf_escape() doubles backslashes and escapes quotes; verified by round-tripping hostile paths through cfg_parse_buf(). Signed-off-by: Joachim Wiberg --- plugins/dbus.c | 8 ++++++-- src/conf.c | 23 ++++++++++++++++++++++- src/conf.h | 1 + 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/plugins/dbus.c b/plugins/dbus.c index 961f840c..80c653e9 100644 --- a/plugins/dbus.c +++ b/plugins/dbus.c @@ -100,6 +100,7 @@ static void setup(void *arg) { char *group = DBUS_DAEMONGROUP; char *user = DBUS_DAEMONUSER; + char esccmd[256]; char pid[300]; char *pidfn; mode_t prev; @@ -153,7 +154,10 @@ static void setup(void *arg) */ pid[0] = 0; if (pidfn) { - snprintf(pid, sizeof(pid), "\tpidfile = \"%s\"\n", pidfn); + char esc[280]; + + snprintf(pid, sizeof(pid), "\tpidfile = \"%s\"\n", + conf_escape(pidfn, esc, sizeof(esc))); free(pidfn); } conf_save_service(SVC_TYPE_SERVICE, "dbus", "dbus.conf", @@ -163,7 +167,7 @@ static void setup(void *arg) "\tcgroup system {}\n" "%s" "\tcommand = \"%s " DBUS_ARGS "\"\n", - pid, cmd); + pid, conf_escape(cmd, esccmd, sizeof(esccmd))); free(cmd); } diff --git a/src/conf.c b/src/conf.c index 61536f7c..002e3861 100644 --- a/src/conf.c +++ b/src/conf.c @@ -664,6 +664,25 @@ done: fclose(fp); } +/* + * Escape backslash and double quote so a runtime string can sit in a + * double-quoted .conf value. Truncates to fit @buf, always returns a + * terminated string. + */ +char *conf_escape(const char *str, char *buf, size_t len) +{ + size_t i = 0; + + while (*str && i + 2 < len) { + if (*str == '"' || *str == '\\') + buf[i++] = '\\'; + buf[i++] = *str++; + } + buf[i] = 0; + + return buf; +} + /* * Called by plugins and similar that dynamically generate system * services. @body holds the block contents, one indented line per @@ -2565,6 +2584,7 @@ int conf_init(uev_ctx_t *ctx) */ if (runparts && fisdir(runparts) && !rescue) { char args[10] = { 0 }; + char dir[256]; if (debug) strlcat(args, "-d ", sizeof(args)); @@ -2573,6 +2593,7 @@ int conf_init(uev_ctx_t *ctx) if (runparts_sysv) strlcat(args, "-s ", sizeof(args)); + conf_escape(runparts, dir, sizeof(dir)); conf_save_service(SVC_TYPE_TASK, "runparts", "runparts.conf", "\tdescription = \"Calling runparts %s in the background\"\n" "\trunlevel = \"S\"\n" @@ -2580,7 +2601,7 @@ int conf_init(uev_ctx_t *ctx) "\tnotify = \"none\"\n" "\tlog { file = \"/dev/console\" }\n" "\tcommand = \"%s %s%s\"\n", - runparts, _PATH_RUNPARTS, args, runparts); + dir, _PATH_RUNPARTS, args, dir); } return 0; diff --git a/src/conf.h b/src/conf.h index 01479df5..3d191f98 100644 --- a/src/conf.h +++ b/src/conf.h @@ -71,6 +71,7 @@ int conf_monitor (void); void conf_saverc (void); void conf_save_exec_order (svc_t *svc, char *cmdline, int result); +char *conf_escape (const char *str, char *buf, size_t len); void conf_save_service (int type, const char *name, char *file, const char *fmt, ...) __attribute__ ((format (printf, 4, 5))); void conf_parse_cmdline (int argc, char *argv[]);