From b6672224834c6f7eb283818325232a1feec5e44b Mon Sep 17 00:00:00 2001 From: Meno Abels Date: Sun, 30 Aug 2026 23:11:19 +0200 Subject: [PATCH] Make the state, runtime and dev paths configurable at runtime Add --dbdir, --rundir, --devdir and --instance to override the compiled in state, runtime, device and instance paths at runtime. The DUID, secret, lease, RDM, PID and control socket file paths are derived from these base directories and rebuilt whenever a base directory or the instance directory changes. When --instance is set, every file dhcpcd writes is placed under an instance directory and named after the state or runtime directory, e.g. /var/db/dhcpcd.duid. --instance must be a single path component and cannot contain '/'. The parent directory of every file dhcpcd writes is created on demand via new mkdirs() and ensure_dir() (mkdir -p semantics). The lease, lease6, PID and control socket paths are used as printf-style format strings, so '%' in the configured directories is escaped via escape_percent() to prevent them from being interpreted as format directives. The dbdir and rundir privsep path checks now require a '/' or end-of-string boundary so sibling paths do not match, and the path setters are ignored on SIGHUP reload so the PID file and control socket locations stay those established at initialization. CONFIG and SCRIPT were already configurable via -f/--config and -c/--script and are unchanged. --- src/auth.c | 10 ++- src/common.c | 84 +++++++++++++++++++ src/common.h | 3 + src/control.c | 4 +- src/defs.h | 26 ++++++ src/dev.c | 6 +- src/dhcp-common.c | 3 +- src/dhcpcd.8.in | 73 ++++++++++++++-- src/dhcpcd.c | 202 +++++++++++++++++++++++++++++++++++++++++++-- src/duid.c | 8 +- src/if-options.c | 24 ++++++ src/if-options.h | 4 + src/ipv6.c | 5 +- src/privsep-root.c | 11 ++- 14 files changed, 432 insertions(+), 31 deletions(-) diff --git a/src/auth.c b/src/auth.c index 856630720..d56326b36 100644 --- a/src/auth.c +++ b/src/auth.c @@ -41,6 +41,7 @@ #include "config.h" #include "auth.h" +#include "common.h" #include "dhcp.h" #include "dhcp6.h" #include "dhcpcd.h" @@ -437,16 +438,17 @@ auth_get_rdm_monotonic(uint64_t *rdm) int flocked; #endif - fp = fopen(RDM_MONOFILE, "r+"); + ensure_dir(dhcpcd_rdm_monofile, 0750); + fp = fopen(dhcpcd_rdm_monofile, "r+"); if (fp == NULL) { if (errno != ENOENT) return -1; - fp = fopen(RDM_MONOFILE, "w"); + fp = fopen(dhcpcd_rdm_monofile, "w"); if (fp == NULL) return -1; - if (chmod(RDM_MONOFILE, 0400) == -1) { + if (chmod(dhcpcd_rdm_monofile, 0400) == -1) { (void)fclose(fp); - unlink(RDM_MONOFILE); + unlink(dhcpcd_rdm_monofile); return -1; } #ifdef LOCK_EX diff --git a/src/common.c b/src/common.c index d938ef004..0c363f287 100644 --- a/src/common.c +++ b/src/common.c @@ -146,12 +146,96 @@ readfile(const char *file, void **data, size_t *len) return bytes; } +/* Return a copy of s with every '%' doubled so it is safe to use as a + * literal within a printf-style format string. Returns NULL on failure. */ +char * +escape_percent(const char *s) +{ + const char *p; + char *r, *d; + size_t extra = 0; + + if (s == NULL) + s = ""; + for (p = s; *p != '\0'; p++) + if (*p == '%') + extra++; + r = malloc(strlen(s) + extra + 1); + if (r == NULL) + return NULL; + for (p = s, d = r; *p != '\0'; p++) { + if (*p == '%') + *d++ = '%'; + *d++ = *p; + } + *d = '\0'; + return r; +} + +/* Create a directory and any missing parents, like mkdir -p. */ +int +mkdirs(const char *dir, mode_t mode) +{ + char *d, *p; + int r = 0; + + if (dir == NULL || dir[0] == '\0') { + errno = EINVAL; + return -1; + } + d = strdup(dir); + if (d == NULL) + return -1; + for (p = d + 1; *p != '\0'; p++) { + if (*p != '/') + continue; + *p = '\0'; + if (mkdir(d, mode) == -1 && errno != EEXIST) + r = -1; + *p = '/'; + } + if (mkdir(d, mode) == -1 && errno != EEXIST) + r = -1; + free(d); + return r; +} + +/* Ensure the directory part of a file path exists. */ +int +ensure_dir(const char *file, mode_t mode) +{ + char *d, *p; + int r; + + if (file == NULL) { + errno = EINVAL; + return -1; + } + d = strdup(file); + if (d == NULL) + return -1; + p = strrchr(d, '/'); + if (p == NULL) { + free(d); + return 0; + } + if (p == d) + *(p + 1) = '\0'; + else + *p = '\0'; + r = mkdirs(d, mode); + free(d); + return r; +} + ssize_t writefile(const char *file, mode_t mode, const void *data, size_t len) { int fd; ssize_t bytes; + if (ensure_dir(file, 0750) == -1) + return -1; fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); if (fd == -1) return -1; diff --git a/src/common.h b/src/common.h index 577afc4da..29df45e22 100644 --- a/src/common.h +++ b/src/common.h @@ -169,6 +169,9 @@ const char *hwaddr_ntoa(const void *, size_t, char *, size_t); size_t hwaddr_aton(uint8_t *, const char *); ssize_t readfile(const char *, void **, size_t *); ssize_t writefile(const char *, mode_t, const void *, size_t); +char *escape_percent(const char *); +int mkdirs(const char *, mode_t); +int ensure_dir(const char *, mode_t); int filemtime(const char *, time_t *); char *get_line(char **__restrict, size_t *__restrict); int is_root_local(void); diff --git a/src/control.c b/src/control.c index 9164b7ab1..6f8835385 100644 --- a/src/control.c +++ b/src/control.c @@ -491,7 +491,7 @@ make_path(char *path, size_t len, const char *ifname, sa_family_t family) per = ""; break; } - return snprintf(path, len, CONTROLSOCKET, ifname ? ifname : "", + return snprintf(path, len, dhcpcd_controlsock, ifname ? ifname : "", ifname ? per : "", "", ifname ? "." : ""); } @@ -521,6 +521,8 @@ control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) if (fd == -1) return -1; + ensure_dir(sa.sun_path, 0755); + len = (socklen_t)SUN_LEN(&sa); if (GID_SET(ctx->control_group) && GID_SET(ctx->read_group)) { if (ctx->control_group == ctx->read_group) { diff --git a/src/defs.h b/src/defs.h index fe8c51c48..4c4a7ad82 100644 --- a/src/defs.h +++ b/src/defs.h @@ -66,6 +66,32 @@ #define RDM_MONOFILE DBDIR "/rdm_monotonic" #endif +/* + * Runtime configurable paths. + * These default to the compiled in values but can be overridden at runtime + * with the corresponding command line options (--instance, --dbdir, --rundir, + * --devdir). The individual file paths are derived from these and rebuilt + * whenever a base directory is changed (see dhcpcd_paths_update()). + */ +extern const char *dhcpcd_dbdir; +extern const char *dhcpcd_rundir; +extern const char *dhcpcd_devdir; +extern const char *dhcpcd_instance; +extern const char *dhcpcd_duidfile; +extern const char *dhcpcd_secretfile; +extern const char *dhcpcd_rdm_monofile; +extern const char *dhcpcd_leasefile; +extern const char *dhcpcd_leasefile6; +extern const char *dhcpcd_pidfile; +extern const char *dhcpcd_controlsock; + +void dhcpcd_paths_init(void); +void dhcpcd_paths_update(void); +void dhcpcd_paths_set_dbdir(const char *); +void dhcpcd_paths_set_rundir(const char *); +void dhcpcd_paths_set_devdir(const char *); +void dhcpcd_paths_set_instance(const char *); + #ifndef NO_SIGNALS #define USE_SIGNALS #endif diff --git a/src/dev.c b/src/dev.c index bada148c8..45d9ce166 100644 --- a/src/dev.c +++ b/src/dev.c @@ -99,7 +99,7 @@ dev_start2(struct dhcpcd_ctx *ctx, const struct dev_dhcpcd *dev_dhcpcd, void (*fptr)(struct dev *, const struct dev_dhcpcd *); int r; - snprintf(file, sizeof(file), DEVDIR "/%s", name); + snprintf(file, sizeof(file), "%s/%s", dhcpcd_devdir, name); h = dlopen(file, RTLD_LAZY); if (h == NULL) { logerrx("dlopen: %s", dlerror()); @@ -143,9 +143,9 @@ dev_start1(struct dhcpcd_ctx *ctx, const struct dev_dhcpcd *dev_dhcpcd) if (ctx->dev_load) return dev_start2(ctx, dev_dhcpcd, ctx->dev_load); - dp = opendir(DEVDIR); + dp = opendir(dhcpcd_devdir); if (dp == NULL) { - logdebug("dev: %s", DEVDIR); + logdebug("dev: %s", dhcpcd_devdir); return -1; } diff --git a/src/dhcp-common.c b/src/dhcp-common.c index e87094de3..5b10a67c1 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -1041,7 +1041,8 @@ dhcp_set_leasefile(char *leasefile, size_t len, int family, } else ssid[0] = '\0'; return snprintf(leasefile, len, - family == AF_INET ? LEASEFILE : LEASEFILE6, ifname, ssid); + family == AF_INET ? dhcpcd_leasefile : dhcpcd_leasefile6, ifname, + ssid); } void diff --git a/src/dhcpcd.8.in b/src/dhcpcd.8.in index 26d37772d..9707ef284 100644 --- a/src/dhcpcd.8.in +++ b/src/dhcpcd.8.in @@ -61,6 +61,10 @@ .Op Fl Z , Fl Fl denyinterfaces Ar pattern .Op Fl z , Fl Fl allowinterfaces Ar pattern .Op Fl Fl inactive +.Op Fl Fl instance Ar instance +.Op Fl Fl dbdir Ar dir +.Op Fl Fl rundir Ar dir +.Op Fl Fl devdir Ar dir .Op Fl Fl configure .Op Fl Fl noconfigure .Op interface @@ -271,9 +275,9 @@ Use a DHCP Unique Identifier. If persistent storage is available then a DUID-LLT (link local address + time) is generated, otherwise DUID-LL is generated (link local address). -The DUID type can be hinted as an optional parameter if the file -.Pa @DBDIR@/duid -does not exist. + The DUID type can be hinted as an optional parameter if the default DUID file + .Pa @DBDIR@/duid + does not exist. If not .Va ll , .Va lt @@ -284,9 +288,9 @@ then will be converted from 00:11:22:33 format. This, plus the IAID will be used as the .Fl I , Fl Fl clientid . -The DUID generated will be held in -.Pa @DBDIR@/duid -and should not be copied to other hosts. + The DUID generated will be held in the default DUID file + .Pa @DBDIR@/duid + and should not be copied to other hosts. This file also takes precedence over the above rules except for setting a value. .It Fl d , Fl Fl debug Echo debug messages to the stderr and syslog. @@ -778,6 +782,51 @@ addition of Don't load any .Pa /dev management modules. +.It Fl Fl instance Ar instance +Prefix the state and runtime files that +.Nm +writes, namely the DUID, secret, lease, RDM, PID and control socket files, +with the +.Ar instance +name inside the state and runtime directories. +.Ar instance +must be a single path component and must not contain a +.Sq / . +For example, +.Fl Fl instance=foo +will cause the DUID file to be written to +.Pa @DBDIR@/ Ns Pa foo Ns .duid +and the PID file to +.Pa @RUNDIR@/ Ns Pa foo Ns .pid . +The base directories remain unchanged. +.It Fl Fl dbdir Ar dir +Specify the base directory where +.Nm +stores its lease and state database. +This overrides the compiled in default +.Pa @DBDIR@ . +Without +.Fl Fl instance +the DUID, secret, lease and RDM files are created directly here, otherwise +they are created here under the file name prefixed with the instance name. +.It Fl Fl rundir Ar dir +Specify the base directory where +.Nm +stores its runtime files, such as the PID file and control socket. +This overrides the compiled in default +.Pa @RUNDIR@ . +Without +.Fl Fl instance +the PID file and control socket are created directly here, otherwise they +are created here under the file name prefixed with the instance name. +.It Fl Fl devdir Ar dir +Specify the directory from which +.Nm +loads +.Pa /dev +management modules. +This overrides the compiled in default +.Pa @LIBDIR@/dhcpcd/dev . .El .Sh 3RDPARTY LINK MANAGEMENT Some interfaces require configuration by 3rd parties, such as PPP or VPN. @@ -818,6 +867,18 @@ sends to match. If using a DUID in place of the ClientID, edit .Pa @DBDIR@/duid accordingly. +.Pp +The file paths shown in this manual, such as +.Pa @DBDIR@/duid , +are the compiled in defaults. +The effective path used by +.Nm +can be changed with the +.Fl Fl dbdir , +.Fl Fl rundir +and +.Fl Fl instance +options described above. .Sh FILES .Bl -ohang .It Pa @SYSCONFDIR@/dhcpcd.conf diff --git a/src/dhcpcd.c b/src/dhcpcd.c index bf277bbca..500df63bc 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -101,6 +101,179 @@ const size_t dhcpcd_signals_ignore_len = __arraycount(dhcpcd_signals_ignore); const char *dhcpcd_default_script = SCRIPT; +/* + * Runtime configurable paths. + * The base directories (dbdir, rundir, devdir) default to the compiled in + * values but may be overridden on the command line. When dhcpcd_instance is + * set, all files dhcpcd writes (lease, DUID, secret, RDM, PID and control + * socket files) are placed under the instance directory and named after the + * state or runtime directory. The file paths are derived from the base + * directories by dhcpcd_paths_update(). + */ +const char *dhcpcd_dbdir; +const char *dhcpcd_rundir; +const char *dhcpcd_devdir; +const char *dhcpcd_instance; +const char *dhcpcd_duidfile; +const char *dhcpcd_secretfile; +const char *dhcpcd_rdm_monofile; +const char *dhcpcd_leasefile; +const char *dhcpcd_leasefile6; +const char *dhcpcd_pidfile; +const char *dhcpcd_controlsock; + +/* Initialise the runtime paths to the compiled in defaults and derive the + * file paths. Must be called before any other dhcpcd_paths_* function. */ +void +dhcpcd_paths_init(void) +{ + dhcpcd_dbdir = strdup(DBDIR); + dhcpcd_rundir = strdup(RUNDIR); + dhcpcd_devdir = strdup(DEVDIR); + if (dhcpcd_dbdir == NULL || dhcpcd_rundir == NULL || + dhcpcd_devdir == NULL) { + logerrx("out of memory"); + exit(EXIT_FAILURE); + } + dhcpcd_paths_update(); +} + +/* Replace *var with a copy of value, freeing the previous string. */ +static void +path_set_str(const char **var, const char *value) +{ + free(UNCONST(*var)); + *var = strdup(value); + if (*var == NULL) { + logerrx("out of memory"); + exit(EXIT_FAILURE); + } +} + +/* Set the state/lease database directory and rebuild the derived paths. */ +void +dhcpcd_paths_set_dbdir(const char *dir) +{ + path_set_str(&dhcpcd_dbdir, dir); + dhcpcd_paths_update(); +} + +/* Set the runtime directory and rebuild the derived paths. */ +void +dhcpcd_paths_set_rundir(const char *dir) +{ + path_set_str(&dhcpcd_rundir, dir); + dhcpcd_paths_update(); +} + +/* Set the directory from which /dev management modules are loaded. */ +void +dhcpcd_paths_set_devdir(const char *dir) +{ + path_set_str(&dhcpcd_devdir, dir); +} + +/* Set the instance directory and rebuild the derived paths. The instance + * must be a single path component without '/'. */ +void +dhcpcd_paths_set_instance(const char *instance) +{ + if (strchr(instance, '/') != NULL) { + logerrx("--instance must not contain a '/': %s", instance); + exit(EXIT_FAILURE); + } + path_set_str(&dhcpcd_instance, instance); + dhcpcd_paths_update(); +} + +/* Rebuild all derived file paths from the current base directories and + * instance. The lease, lease6, PID and control socket paths are returned + * as printf-style format strings. */ +void +dhcpcd_paths_update(void) +{ + char *duidfile = NULL, *secretfile = NULL, *rdm_monofile = NULL; + char *leasefile = NULL, *leasefile6 = NULL, *pidfile = NULL; + char *controlsock = NULL; + char *einstance, *edbdir, *erundir; + + free(UNCONST(dhcpcd_duidfile)); + free(UNCONST(dhcpcd_secretfile)); + free(UNCONST(dhcpcd_rdm_monofile)); + free(UNCONST(dhcpcd_leasefile)); + free(UNCONST(dhcpcd_leasefile6)); + free(UNCONST(dhcpcd_pidfile)); + free(UNCONST(dhcpcd_controlsock)); + + /* lease, lease6, pid and controlsock are later used as printf-style + * format strings, so escape '%' in the configured directories. */ + einstance = escape_percent(dhcpcd_instance); + edbdir = escape_percent(dhcpcd_dbdir); + erundir = escape_percent(dhcpcd_rundir); + if (einstance == NULL || edbdir == NULL || erundir == NULL) + goto oom; + + if (dhcpcd_instance != NULL && dhcpcd_instance[0] != '\0') { + /* + * The instance name is used as a file name base inside the + * state and runtime directories, e.g. /.duid. + */ + if (asprintf(&duidfile, "%s/%s.duid", dhcpcd_dbdir, + dhcpcd_instance) == -1 || + asprintf(&secretfile, "%s/%s.secret", dhcpcd_dbdir, + dhcpcd_instance) == -1 || + asprintf(&rdm_monofile, "%s/%s.rdm_monotonic", dhcpcd_dbdir, + dhcpcd_instance) == -1 || + asprintf(&leasefile, "%s/%s.%%s%%s.lease", edbdir, + einstance) == -1 || + asprintf(&leasefile6, "%s/%s.%%s%%s.lease6", edbdir, + einstance) == -1 || + asprintf(&pidfile, "%s/%s.%%s%%s%%spid", erundir, + einstance) == -1 || + asprintf(&controlsock, "%s/%s.%%s%%s%%s%%ssock", erundir, + einstance) == -1) + goto oom; + } else { + if (asprintf(&duidfile, "%s/duid", dhcpcd_dbdir) == -1 || + asprintf(&secretfile, "%s/secret", dhcpcd_dbdir) == -1 || + asprintf(&rdm_monofile, "%s/rdm_monotonic", dhcpcd_dbdir) == + -1 || + asprintf(&leasefile, "%s/%%s%%s.lease", edbdir) == -1 || + asprintf(&leasefile6, "%s/%%s%%s.lease6", edbdir) == -1 || + asprintf(&pidfile, "%s/%%s%%s%%spid", erundir) == -1 || + asprintf(&controlsock, "%s/%%s%%s%%s%%ssock", erundir) == + -1) + goto oom; + } + + free(einstance); + free(edbdir); + free(erundir); + + dhcpcd_duidfile = duidfile; + dhcpcd_secretfile = secretfile; + dhcpcd_rdm_monofile = rdm_monofile; + dhcpcd_leasefile = leasefile; + dhcpcd_leasefile6 = leasefile6; + dhcpcd_pidfile = pidfile; + dhcpcd_controlsock = controlsock; + return; + +oom: + free(duidfile); + free(secretfile); + free(rdm_monofile); + free(leasefile); + free(leasefile6); + free(pidfile); + free(controlsock); + free(einstance); + free(edbdir); + free(erundir); + logerrx("out of memory"); + exit(EXIT_FAILURE); +} + static void usage(void) { @@ -119,7 +292,8 @@ usage(void) "\t\t[-v, --vendor code, value] [-W, --whitelist address[/cidr]] [-w]\n" "\t\t[--waitip [4 | 6]] [-y, --reboot seconds]\n" "\t\t[-X, --blacklist address[/cidr]] [-Z, --denyinterfaces pattern]\n" - "\t\t[-z, --allowinterfaces pattern] [--inactive] [interface] [...]\n" + "\t\t[-z, --allowinterfaces pattern] [--inactive] [--instance instance]\n" + "\t\t[--dbdir dir] [--rundir dir] [--devdir dir] [interface] [...]\n" " " PACKAGE "\t-n, --rebind [interface]\n" " " PACKAGE "\t-k, --release [interface]\n" " " PACKAGE "\t-U, --dumplease interface\n" @@ -2138,6 +2312,7 @@ main(int argc, char **argv, char **envp) memset(&ctx, 0, sizeof(ctx)); closefrom(STDERR_FILENO + 1); + dhcpcd_paths_init(); ifo = NULL; ctx.cffile = CONFIG; @@ -2247,6 +2422,18 @@ main(int argc, char **argv, char **envp) case 'V': i = 2; break; + case O_DBDIR: + dhcpcd_paths_set_dbdir(optarg); + break; + case O_RUNDIR: + dhcpcd_paths_set_rundir(optarg); + break; + case O_DEVDIR: + dhcpcd_paths_set_devdir(optarg); + break; + case O_INSTANCE: + dhcpcd_paths_set_instance(optarg); + break; case '?': if (ctx.options & DHCPCD_PRINT_PIDFILE) continue; @@ -2354,13 +2541,14 @@ main(int argc, char **argv, char **envp) default: per = ""; } - if (asprintf(&ctx.pidfile, PIDFILE, ifname, per, ".") == - -1) { + if (asprintf(&ctx.pidfile, dhcpcd_pidfile, ifname, per, + ".") == -1) { logerr("%s: asprintf", __func__); goto exit_failure; } } else { - if (asprintf(&ctx.pidfile, PIDFILE, "", "", "") == -1) { + if (asprintf(&ctx.pidfile, dhcpcd_pidfile, "", "", + "") == -1) { logerr("%s: asprintf", __func__); goto exit_failure; } @@ -2539,10 +2727,7 @@ main(int argc, char **argv, char **envp) if (!(ctx.options & DHCPCD_TEST)) { /* Ensure we have the needed directories */ - if (mkdir(DBDIR, 0750) == -1 && errno != EEXIST) - logerr("%s: mkdir: %s", __func__, DBDIR); - if (mkdir(RUNDIR, 0755) == -1 && errno != EEXIST) - logerr("%s: mkdir: %s", __func__, RUNDIR); + ensure_dir(ctx.pidfile, 0755); if ((pid = pidfile_lock(ctx.pidfile)) != 0) { if (pid == -1) logerr("%s: pidfile_lock: %s", __func__, @@ -2648,6 +2833,7 @@ main(int argc, char **argv, char **envp) start_manager: ctx.options |= DHCPCD_STARTED; + ensure_dir(ctx.pidfile, 0755); if ((pid = pidfile_lock(ctx.pidfile)) != 0) { logerr("%s: pidfile_lock %d", __func__, (int)pid); #ifdef PRIVSEP diff --git a/src/duid.c b/src/duid.c index d3cba7e6d..95b437d57 100644 --- a/src/duid.c +++ b/src/duid.c @@ -163,16 +163,16 @@ duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp) /* If we already have a DUID then use it as it's never supposed * to change once we have one even if the interfaces do */ - if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) { + if ((len = dhcp_read_hwaddr_aton(ctx, &data, dhcpcd_duidfile)) != 0) { if (len <= DUID_LEN) { ctx->duid = data; return len; } - logerrx("DUID too big (max %u): %s", DUID_LEN, DUID); + logerrx("DUID too big (max %u): %s", DUID_LEN, dhcpcd_duidfile); /* Keep the buffer, will assign below. */ } else { if (errno != ENOENT) - logerr("%s", DUID); + logerr("%s", dhcpcd_duidfile); if ((data = malloc(DUID_LEN)) == NULL) { logerr(__func__); return 0; @@ -228,7 +228,7 @@ duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp) line[slen++] = '\n'; line[slen] = '\0'; } - if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) { + if (dhcp_writefile(ctx, dhcpcd_duidfile, 0640, line, slen) == -1) { logerr("%s: cannot write duid", __func__); if (ctx->duid_type != DUID_LL) return duid_make(data, ifp, DUID_LL); diff --git a/src/if-options.c b/src/if-options.c index 7b445f459..f50a323f1 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -180,6 +180,10 @@ const struct option cf_options[] = { { "background", no_argument, NULL, 'b' }, { "backoff_cutoff", required_argument, NULL, O_BACKOFF_CUTOFF }, { "backoff_jitter", required_argument, NULL, O_BACKOFF_JITTER }, { "allow", required_argument, NULL, O_ALLOW }, + { "dbdir", required_argument, NULL, O_DBDIR }, + { "rundir", required_argument, NULL, O_RUNDIR }, + { "devdir", required_argument, NULL, O_DEVDIR }, + { "instance", required_argument, NULL, O_INSTANCE }, { NULL, 0, NULL, '\0' } }; static char * @@ -2647,6 +2651,26 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, return -1; } break; + case O_DBDIR: + ARG_REQUIRED; + if (!(ctx->options & DHCPCD_STARTED)) + dhcpcd_paths_set_dbdir(arg); + break; + case O_RUNDIR: + ARG_REQUIRED; + if (!(ctx->options & DHCPCD_STARTED)) + dhcpcd_paths_set_rundir(arg); + break; + case O_DEVDIR: + ARG_REQUIRED; + if (!(ctx->options & DHCPCD_STARTED)) + dhcpcd_paths_set_devdir(arg); + break; + case O_INSTANCE: + ARG_REQUIRED; + if (!(ctx->options & DHCPCD_STARTED)) + dhcpcd_paths_set_instance(arg); + break; default: return 0; } diff --git a/src/if-options.h b/src/if-options.h index 0158b9ae9..6823941b8 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -205,6 +205,10 @@ #define O_BACKOFF_JITTER O_BASE + 62 #define O_ALLOW O_BASE + 63 #define O_READGRP O_BASE + 64 +#define O_DBDIR O_BASE + 65 +#define O_RUNDIR O_BASE + 66 +#define O_DEVDIR O_BASE + 67 +#define O_INSTANCE O_BASE + 68 extern const struct option cf_options[]; diff --git a/src/ipv6.c b/src/ipv6.c index 80ee4ad3a..d1dd0f5fb 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -154,7 +154,8 @@ ipv6_readsecret(struct dhcpcd_ctx *ctx) size_t len; uint32_t r; - ctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, SECRET); + ctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, + dhcpcd_secretfile); if (ctx->secret_len != 0) return (ssize_t)ctx->secret_len; @@ -185,7 +186,7 @@ ipv6_readsecret(struct dhcpcd_ctx *ctx) line[len++] = '\n'; line[len] = '\0'; } - if (dhcp_writefile(ctx, SECRET, S_IRUSR, line, len) == -1) { + if (dhcp_writefile(ctx, dhcpcd_secretfile, S_IRUSR, line, len) == -1) { logerr("%s: cannot write secret", __func__); ctx->secret_len = 0; return -1; diff --git a/src/privsep-root.c b/src/privsep-root.c index 47170c8e5..c4c59d956 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -264,12 +264,17 @@ static bool ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path, size_t len) { + size_t dbdirlen, rundirlen; + /* path must be a valid string */ if (memchr(path, '\0', len) == NULL) { errno = EINVAL; return false; } + dbdirlen = strlen(dhcpcd_dbdir); + rundirlen = strlen(dhcpcd_rundir); + /* Avoid a previous directory attack to avoid /proc/../ * dhcpcd should never use a path with double dots. */ if (strstr(path, "..") != NULL) @@ -283,9 +288,11 @@ ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path, if (strcmp(ctx->cffile, path) == 0) return true; } - if (strncmp(DBDIR, path, strlen(DBDIR)) == 0) + if (strncmp(dhcpcd_dbdir, path, dbdirlen) == 0 && + (path[dbdirlen] == '\0' || path[dbdirlen] == '/')) return true; - if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0) + if (strncmp(dhcpcd_rundir, path, rundirlen) == 0 && + (path[rundirlen] == '\0' || path[rundirlen] == '/')) return true; #ifdef __linux__