changeset 5207:84b63f09c8a4 draft

privsep: Handle all file IO in the Priviledged Actioneer This allows us to move the database directory back into the root of the filesystem. While here, harden the files by denying any user read access to them. As part of this change, init the DUID from any machine data and cache the default DHCP vendor field before dropping priviledges as we may lose access to this later.
author Roy Marples <roy@marples.name>
date Tue, 12 May 2020 10:26:35 +0100
parents ec0df63fd3fa
children 6e53055c9989
files src/common.c src/common.h src/dhcp-common.c src/dhcp-common.h src/dhcp.c src/dhcp6.c src/dhcpcd-embedded.c.in src/dhcpcd-embedded.h.in src/dhcpcd.c src/dhcpcd.h src/duid.c src/duid.h src/genembedc src/if-bsd.c src/if-linux.c src/if-options.c src/if-options.h src/if.h src/ipv6.c src/logerr.c src/privsep-bpf.c src/privsep-bsd.c src/privsep-inet.c src/privsep-root.c src/privsep-root.h src/privsep.c src/privsep.h
diffstat 27 files changed, 513 insertions(+), 565 deletions(-) [+]
line wrap: on
line diff
--- a/src/common.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/common.c	Tue May 12 10:26:35 2020 +0100
@@ -26,17 +26,18 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/stat.h>
 #include <sys/statvfs.h>
 
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "common.h"
 #include "dhcpcd.h"
 #include "if-options.h"
-#include "logerr.h"
 
 const char *
 hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
@@ -103,36 +104,58 @@
 	return len;
 }
 
-size_t
-read_hwaddr_aton(uint8_t **data, const char *path)
+ssize_t
+readfile(const char *file, void *data, size_t len)
 {
-	FILE *fp;
-	char *buf;
-	size_t buf_len, len;
+	int fd;
+	struct stat st;
+	ssize_t bytes = -1;
+
+	fd = open(file, O_RDONLY);
+	if (fd == -1)
+		return -1;
 
-	if ((fp = fopen(path, "r")) == NULL)
-		return 0;
+	if (fstat(fd, &st) != 0)
+		goto out;
+	if (!S_ISREG(st.st_mode)) {
+		errno = EINVAL;
+		goto out;
+	}
+	if ((size_t)st.st_size > len) {
+		errno = E2BIG;
+		goto out;
+	}
+	bytes = read(fd, data, len);
 
-	buf = NULL;
-	buf_len = len = 0;
-	*data = NULL;
-	while (getline(&buf, &buf_len, fp) != -1) {
-		if ((len = hwaddr_aton(NULL, buf)) != 0) {
-			if (buf_len >= len)
-				*data = (uint8_t *)buf;
-			else {
-				if ((*data = malloc(len)) == NULL)
-					len = 0;
-			}
-			if (len != 0)
-				(void)hwaddr_aton(*data, buf);
-			if (buf_len < len)
-				free(buf);
-			break;
-		}
-	}
-	fclose(fp);
-	return len;
+out:
+	if (fd != -1)
+		close(fd);
+	return bytes;
+}
+
+ssize_t
+writefile(const char *file, mode_t mode, const void *data, size_t len)
+{
+	int fd;
+	ssize_t bytes;
+
+	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
+	if (fd == -1)
+		return -1;
+	bytes = write(fd, data, len);
+	close(fd);
+	return bytes;
+}
+
+int
+filemtime(const char *file, time_t *time)
+{
+	struct stat st;
+
+	if (stat(file, &st) == -1)
+		return -1;
+	*time = st.st_mtime;
+	return 0;
 }
 
 int
--- a/src/common.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/common.h	Tue May 12 10:26:35 2020 +0100
@@ -147,6 +147,8 @@
 
 const char *hwaddr_ntoa(const void *, size_t, char *, size_t);
 size_t hwaddr_aton(uint8_t *, const char *);
-size_t read_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);
+int filemtime(const char *, time_t *);
 int is_root_local(void);
 #endif
--- a/src/dhcp-common.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcp-common.c	Tue May 12 10:26:35 2020 +0100
@@ -26,7 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/stat.h>
 #include <sys/utsname.h>
 
 #include <ctype.h>
@@ -176,9 +175,10 @@
 		return -1;
 	p += l;
 	len -= (size_t)l;
-	l = if_machinearch(p, len);
+	l = if_machinearch(p + 1, len - 1);
 	if (l == -1 || (size_t)(l + 1) > len)
 		return -1;
+	*p = ':';
 	p += l;
 	return p - str;
 }
@@ -945,38 +945,85 @@
 		dhcp_zero_index(o);
 }
 
-size_t
-dhcp_read_lease_fd(int fd, void **lease)
+ssize_t
+dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len)
 {
-	struct stat st;
-	size_t sz;
-	void *buf;
-	ssize_t len;
+
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP &&
+	    !(ctx->options & DHCPCD_PRIVSEPROOT))
+		return ps_root_readfile(ctx, file, data, len);
+#else
+	UNUSED(ctx);
+#endif
+
+	return readfile(file, data, len);
+}
+
+ssize_t
+dhcp_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
+    const void *data, size_t len)
+{
+
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP &&
+	    !(ctx->options & DHCPCD_PRIVSEPROOT))
+		return ps_root_writefile(ctx, file, mode, data, len);
+#else
+	UNUSED(ctx);
+#endif
+
+	return writefile(file, mode, data, len);
+}
+
+int
+dhcp_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
+{
 
-	if (fstat(fd, &st) != 0)
-		goto out;
-	if (!S_ISREG(st.st_mode)) {
-		errno = EINVAL;
-		goto out;
-	}
-	if (st.st_size > UINT32_MAX) {
-		errno = E2BIG;
-		goto out;
-	}
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP &&
+	    !(ctx->options & DHCPCD_PRIVSEPROOT))
+		return ps_root_filemtime(ctx, file, time);
+#else
+	UNUSED(ctx);
+#endif
+
+	return filemtime(file, time);
+}
+
+int
+dhcp_unlink(struct dhcpcd_ctx *ctx, const char *file)
+{
+
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP &&
+	    !(ctx->options & DHCPCD_PRIVSEPROOT))
+		return ps_root_unlink(ctx, file);
+#else
+	UNUSED(ctx);
+#endif
 
-	sz = (size_t)st.st_size;
-	if (sz == 0)
-		goto out;
-	if ((buf = malloc(sz)) == NULL)
-		goto out;
-	if ((len = read(fd, buf, sz)) == -1) {
-		free(buf);
-		goto out;
-	}
-	*lease = buf;
-	return (size_t)len;
+	return unlink(file);
+}
+
+size_t
+dhcp_read_hwaddr_aton(struct dhcpcd_ctx *ctx, uint8_t **data, const char *file)
+{
+	char buf[BUFSIZ];
+	ssize_t bytes;
+	size_t len;
 
-out:
-	*lease = NULL;
-	return 0;
+	bytes = dhcp_readfile(ctx, file, buf, sizeof(buf));
+	if (bytes == -1 || bytes == sizeof(buf))
+		return 0;
+
+	bytes[buf] = '\0';
+	len = hwaddr_aton(NULL, buf);
+	if (len == 0)
+		return 0;
+	*data = malloc(len);
+	if (*data == NULL)
+		return 0;
+	hwaddr_aton(*data, buf);
+	return len;
 }
--- a/src/dhcp-common.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcp-common.h	Tue May 12 10:26:35 2020 +0100
@@ -136,6 +136,11 @@
     const uint8_t *, size_t, struct dhcp_opt **),
     const uint8_t *od, size_t ol);
 void dhcp_zero_index(struct dhcp_opt *);
-size_t dhcp_read_lease_fd(int, void **);
 
+ssize_t dhcp_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
+ssize_t dhcp_writefile(struct dhcpcd_ctx *, const char *, mode_t,
+    const void *, size_t);
+int dhcp_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
+int dhcp_unlink(struct dhcpcd_ctx *, const char *);
+size_t dhcp_read_hwaddr_aton(struct dhcpcd_ctx *, uint8_t **, const char *);
 #endif
--- a/src/dhcp.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcp.c	Tue May 12 10:26:35 2020 +0100
@@ -28,7 +28,6 @@
 
 #include <sys/param.h>
 #include <sys/socket.h>
-#include <sys/stat.h>
 
 #include <arpa/inet.h>
 #include <net/if.h>
@@ -1142,31 +1141,16 @@
 	return -1;
 }
 
-static ssize_t
-write_lease(const struct interface *ifp, const struct bootp *bootp, size_t len)
-{
-	int fd;
-	ssize_t bytes;
-	const struct dhcp_state *state = D_CSTATE(ifp);
-
-	logdebugx("%s: writing lease `%s'", ifp->name, state->leasefile);
-
-	fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-	if (fd == -1)
-		return -1;
-	bytes = write(fd, bootp, len);
-	close(fd);
-	return bytes;
-}
-
 static size_t
 read_lease(struct interface *ifp, struct bootp **bootp)
 {
-	int fd;
-	bool fd_opened;
+	union {
+		struct bootp bootp;
+		uint8_t buf[FRAMELEN_MAX];
+	} buf;
 	struct dhcp_state *state = D_STATE(ifp);
 	struct bootp *lease;
-	size_t bytes;
+	ssize_t bytes;
 	uint8_t type;
 #ifdef AUTH
 	const uint8_t *auth;
@@ -1177,37 +1161,26 @@
 	*bootp = NULL;
 
 	if (state->leasefile[0] == '\0') {
-		fd = fileno(stdin);
-		fd_opened = false;
+		logdebugx("reading standard input");
+		bytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));
 	} else {
-		fd = open(state->leasefile, O_RDONLY);
-		fd_opened = true;
+		logdebugx("%s: reading lease `%s'",
+		    ifp->name, state->leasefile);
+		bytes = dhcp_readfile(ifp->ctx, state->leasefile,
+		    buf.buf, sizeof(buf.buf));
 	}
-	if (fd == -1) {
+	if (bytes == -1) {
 		if (errno != ENOENT)
-			logerr("%s: open `%s'",
-			    ifp->name, state->leasefile);
+			logerr("%s: %s", ifp->name, state->leasefile);
 		return 0;
 	}
-	if (state->leasefile[0] == '\0')
-		logdebugx("reading standard input");
-	else
-		logdebugx("%s: reading lease `%s'",
-		    ifp->name, state->leasefile);
-
-	bytes = dhcp_read_lease_fd(fd, (void **)&lease);
-	if (fd_opened)
-		close(fd);
-	if (bytes == 0)
-		return 0;
 
 	/* Ensure the packet is at lease BOOTP sized
 	 * with a vendor area of 4 octets
 	 * (it should be more, and our read packet enforces this so this
 	 * code should not be needed, but of course people could
 	 * scribble whatever in the stored lease file. */
-	if (bytes < DHCP_MIN_LEN) {
-		free(lease);
+	if ((size_t)bytes < DHCP_MIN_LEN) {
 		logerrx("%s: %s: truncated lease", ifp->name, __func__);
 		return 0;
 	}
@@ -1216,20 +1189,19 @@
 		goto out;
 
 	/* We may have found a BOOTP server */
-	if (get_option_uint8(ifp->ctx, &type, (struct bootp *)lease, bytes,
+	if (get_option_uint8(ifp->ctx, &type, &buf.bootp, bytes,
 	    DHO_MESSAGETYPE) == -1)
 		type = 0;
 
 #ifdef AUTH
 	/* Authenticate the message */
-	auth = get_option(ifp->ctx, (struct bootp *)lease, bytes,
+	auth = get_option(ifp->ctx, &buf.bootp, bytes,
 	    DHO_AUTHENTICATION, &auth_len);
 	if (auth) {
 		if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
 		    lease, bytes, 4, type, auth, auth_len) == NULL)
 		{
 			logerr("%s: authentication failed", ifp->name);
-			free(lease);
 			return 0;
 		}
 		if (state->auth.token)
@@ -1241,14 +1213,18 @@
 	    DHCPCD_AUTH_SENDREQUIRE)
 	{
 		logerrx("%s: authentication now required", ifp->name);
-		free(lease);
 		return 0;
 	}
 #endif
 
 out:
-	*bootp = (struct bootp *)lease;
-	return bytes;
+	*bootp = malloc(bytes);
+	if (*bootp == NULL) {
+		logerr(__func__);
+		return 0;
+	}
+	memcpy(*bootp, buf.buf, bytes);
+	return (size_t)bytes;
 }
 
 static const struct dhcp_opt *
@@ -1929,7 +1905,7 @@
 
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 	dhcp_drop(ifp, "EXPIRE");
-	unlink(state->leasefile);
+	dhcp_unlink(ifp->ctx, state->leasefile);
 	state->interval = 0;
 	if (!(ifp->options->options & DHCPCD_LINK) || ifp->carrier > LINK_DOWN)
 		dhcp_discover(ifp);
@@ -2072,7 +2048,7 @@
 
 	/* RFC 2131 3.1.5, Client-server interaction */
 	logerrx("%s: DAD detected %s", ifp->name, inet_ntoa(*ia));
-	unlink(state->leasefile);
+	dhcp_unlink(ifp->ctx, state->leasefile);
 	if (!(opts & DHCPCD_STATIC) && !state->lease.frominfo)
 		dhcp_decline(ifp);
 #ifdef IN_IFF_DUPLICATED
@@ -2299,9 +2275,13 @@
 	}
 	state->state = DHS_BOUND;
 	if (!state->lease.frominfo &&
-	    !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
-		if (write_lease(ifp, state->new, state->new_len) == -1)
-			logerr("write_lease: %s", state->leasefile);
+	    !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) {
+		logdebugx("%s: writing lease `%s'",
+		    ifp->name, state->leasefile);
+		if (dhcp_writefile(ifp->ctx, state->leasefile, 0640,
+		    state->new, state->new_len) == -1)
+			logerr("dhcp_writefile: %s", state->leasefile);
+	}
 
 	/* Close the BPF filter as we can now receive DHCP messages
 	 * on a UDP socket. */
@@ -2749,7 +2729,7 @@
 			return;
 		state->state = DHS_RELEASE;
 
-		unlink(state->leasefile);
+		dhcp_unlink(ifp->ctx, state->leasefile);
 		if (ifp->carrier > LINK_DOWN &&
 		    state->new != NULL &&
 		    state->lease.server.s_addr != INADDR_ANY)
@@ -3114,7 +3094,7 @@
 			return;
 		if (!(ifp->ctx->options & DHCPCD_TEST)) {
 			dhcp_drop(ifp, "NAK");
-			unlink(state->leasefile);
+			dhcp_unlink(ifp->ctx, state->leasefile);
 		}
 
 		/* If we constantly get NAKS then we should slowly back off */
@@ -3788,7 +3768,7 @@
 	/* We need to drop the leasefile so that dhcp_start
 	 * doesn't load it. */
 	if (ifo->options & DHCPCD_REQUEST)
-		unlink(state->leasefile);
+		dhcp_unlink(ifp->ctx, state->leasefile);
 
 	free(state->clientid);
 	state->clientid = NULL;
@@ -3862,7 +3842,6 @@
 	struct dhcpcd_ctx *ctx = ifp->ctx;
 	struct if_options *ifo = ifp->options;
 	struct dhcp_state *state;
-	struct stat st;
 	uint32_t l;
 	int nolease;
 
@@ -3947,6 +3926,7 @@
 	}
 	if (state->offer) {
 		struct ipv4_addr *ia;
+		time_t mtime;
 
 		get_lease(ifp, &state->lease, state->offer, state->offer_len);
 		state->lease.frominfo = 1;
@@ -3974,14 +3954,14 @@
 			state->offer_len = 0;
 		} else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) &&
 		    state->lease.leasetime != DHCP_INFINITE_LIFETIME &&
-		    stat(state->leasefile, &st) == 0)
+		    dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == 0)
 		{
 			time_t now;
 
 			/* Offset lease times and check expiry */
 			now = time(NULL);
 			if (now == -1 ||
-			    (time_t)state->lease.leasetime < now - st.st_mtime)
+			    (time_t)state->lease.leasetime < now - mtime)
 			{
 				logdebugx("%s: discarding expired lease",
 				    ifp->name);
@@ -4006,7 +3986,7 @@
 					dhcp_drop(ifp, "EXPIRE");
 #endif
 			} else {
-				l = (uint32_t)(now - st.st_mtime);
+				l = (uint32_t)(now - mtime);
 				state->lease.leasetime -= l;
 				state->lease.renewaltime -= l;
 				state->lease.rebindtime -= l;
--- a/src/dhcp6.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcp6.c	Tue May 12 10:26:35 2020 +0100
@@ -26,7 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/stat.h>
 #include <sys/utsname.h>
 
 #include <netinet/in.h>
@@ -258,9 +257,8 @@
 dhcp6_makevendor(void *data, const struct interface *ifp)
 {
 	const struct if_options *ifo;
-	size_t len, i;
+	size_t len, vlen, i;
 	uint8_t *p;
-	ssize_t vlen;
 	const struct vivco *vivco;
 	char vendor[VENDORCLASSID_MAX_LEN];
 	struct dhcp6_option o;
@@ -274,11 +272,8 @@
 			len += sizeof(uint16_t) + vivco->len;
 		vlen = 0; /* silence bogus gcc warning */
 	} else {
-		vlen = dhcp_vendor(vendor, sizeof(vendor));
-		if (vlen == -1)
-			vlen = 0;
-		else
-			len += sizeof(uint16_t) + (size_t)vlen;
+		vlen = strlcpy(vendor, ifp->ctx->vendor, sizeof(vendor));
+		len += sizeof(uint16_t) + vlen;
 	}
 
 	if (len > UINT16_MAX) {
@@ -1748,7 +1743,7 @@
 		state->new_len = 0;
 		if (state->old != NULL)
 			script_runreason(ifp, "EXPIRE6");
-		unlink(state->leasefile);
+		dhcp_unlink(ifp->ctx, state->leasefile);
 	}
 
 	if (!dhcp6_startdiscoinform(ifp)) {
@@ -2590,106 +2585,77 @@
 }
 
 static ssize_t
-dhcp6_writelease(const struct interface *ifp)
-{
-	const struct dhcp6_state *state;
-	int fd;
-	ssize_t bytes;
-
-	state = D6_CSTATE(ifp);
-	logdebugx("%s: writing lease `%s'", ifp->name, state->leasefile);
-
-	fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-	if (fd == -1)
-		return -1;
-	bytes = write(fd, state->new, state->new_len);
-	close(fd);
-	return bytes;
-}
-
-static int
 dhcp6_readlease(struct interface *ifp, int validate)
 {
+	union {
+		struct dhcp6_message dhcp6;
+		uint8_t buf[UDPLEN_MAX];
+	} buf;
 	struct dhcp6_state *state;
-	struct stat st;
+	ssize_t bytes;
 	int fd;
-	time_t now;
-	int retval;
-	bool read_stdin, fd_opened;
+	time_t mtime, now;
 #ifdef AUTH
 	uint8_t *o;
 	uint16_t ol;
 #endif
 
 	state = D6_STATE(ifp);
-	read_stdin = state->leasefile[0] == '\0';
-	if (read_stdin) {
+	if (state->leasefile[0] == '\0') {
 		logdebugx("reading standard input");
-		fd = fileno(stdin);
-		fd_opened = false;
+		bytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));
 	} else {
-		logdebugx("%s: reading lease `%s'", ifp->name,state->leasefile);
-		fd = open(state->leasefile, O_RDONLY);
-		if (fd != -1 && fstat(fd, &st) == -1) {
-			close(fd);
-			fd = -1;
-		}
-		fd_opened = true;
+		logdebugx("%s: reading lease `%s'",
+		    ifp->name, state->leasefile);
+		bytes = dhcp_readfile(ifp->ctx, state->leasefile,
+		    buf.buf, sizeof(buf.buf));
 	}
-	if (fd == -1)
-		return -1;
-	retval = -1;
-	free(state->new);
-	state->new_len = dhcp_read_lease_fd(fd, (void **)&state->new);
-	if (fd_opened)
-		close(fd);
-
-	if (ifp->ctx->options & DHCPCD_DUMPLEASE || read_stdin)
-		return 0;
-
-	if (state->new_len == 0) {
-		retval = 0;
+	if (bytes == -1)
 		goto ex;
-	}
+
+	if (ifp->ctx->options & DHCPCD_DUMPLEASE || state->leasefile[0] == '\0')
+		goto out;
+
+	if (bytes == 0)
+		goto ex;
 
 	/* If not validating IA's and if they have expired,
 	 * skip to the auth check. */
-	if (!validate) {
-		fd = 0;
+	if (!validate)
 		goto auth;
-	}
-
+
+	if (dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == -1)
+		goto ex;
 	clock_gettime(CLOCK_MONOTONIC, &state->acquired);
 	if ((now = time(NULL)) == -1)
 		goto ex;
-	state->acquired.tv_sec -= now - st.st_mtime;
+	state->acquired.tv_sec -= now - mtime;
 
 	/* Check to see if the lease is still valid */
-	fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL,
+	fd = dhcp6_validatelease(ifp, &buf.dhcp6, bytes, NULL,
 	    &state->acquired);
 	if (fd == -1)
 		goto ex;
 
 	if (state->expire != ND6_INFINITE_LIFETIME &&
-	    (time_t)state->expire < now - st.st_mtime &&
+	    (time_t)state->expire < now - mtime &&
 	    !(ifp->options->options & DHCPCD_LASTLEASE_EXTEND))
 	{
 		logdebugx("%s: discarding expired lease", ifp->name);
-		retval = 0;
+		bytes = 0;
 		goto ex;
 	}
 
 auth:
-	retval = 0;
 #ifdef AUTH
 	/* Authenticate the message */
-	o = dhcp6_findmoption(state->new, state->new_len, D6_OPTION_AUTH, &ol);
+	o = dhcp6_findmoption(&buf.dhcp6, bytes, D6_OPTION_AUTH, &ol);
 	if (o) {
 		if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
-		    (uint8_t *)state->new, state->new_len, 6, state->new->type,
-		    o, ol) == NULL)
+		    buf.buf, bytes, 6, buf.dhcp6.type, o, ol) == NULL)
 		{
 			logerr("%s: authentication failed", ifp->name);
+			bytes = 0;
 			goto ex;
 		}
 		if (state->auth.token)
@@ -2705,22 +2671,32 @@
 	}
 #endif
 
-	return fd;
+out:
+	free(state->new);
+	state->new = malloc(bytes);
+	if (state->new == NULL) {
+		logerr(__func__);
+		goto ex;
+	}
+
+	memcpy(state->new, buf.buf, bytes);
+	state->new_len = bytes;
+	return bytes;
 
 ex:
 	dhcp6_freedrop_addrs(ifp, 0, NULL);
-	unlink(state->leasefile);
+	dhcp_unlink(ifp->ctx, state->leasefile);
 	free(state->new);
 	state->new = NULL;
 	state->new_len = 0;
-	return retval;
+	return bytes == 0 ? 0 : -1;
 }
 
 static void
 dhcp6_startinit(struct interface *ifp)
 {
 	struct dhcp6_state *state;
-	int r;
+	ssize_t r;
 	uint8_t has_ta, has_non_ta;
 	size_t i;
 
@@ -3259,9 +3235,13 @@
 			logmessage(loglevel, "%s: expire in %"PRIu32" seconds",
 			    ifp->name, state->expire);
 		rt_build(ifp->ctx, AF_INET6);
-		if (!confirmed && !timedout)
-			if (dhcp6_writelease(ifp) == -1)
-				logerr("dhcp6_writelease: %s",state->leasefile);
+		if (!confirmed && !timedout) {
+			logdebugx("%s: writing lease `%s'",
+			    ifp->name, state->leasefile);
+			if (dhcp_writefile(ifp->ctx, state->leasefile, 0640,
+			    state->new, state->new_len) == -1)
+				logerr("dhcp_writefile: %s",state->leasefile);
+		}
 #ifndef SMALL
 		dhcp6_delegate_prefix(ifp);
 #endif
@@ -3688,7 +3668,7 @@
 	 * replay it in my code.
 	 */
 	static int replyn = 0;
-	char fname[PATH_MAX], tbuf[64 * 1024];
+	char fname[PATH_MAX], tbuf[UDPLEN_MAX];
 	int fd;
 	ssize_t tlen;
 	uint8_t *si1, *si2;
@@ -3725,7 +3705,7 @@
 	struct sockaddr_in6 from;
 	union {
 		struct dhcp6_message dhcp6;
-		uint8_t buf[64 * 1024]; /* Maximum UDP message size */
+		uint8_t buf[UDPLEN_MAX]; /* Maximum UDP message size */
 	} iovbuf;
 	struct iovec iov = {
 		.iov_base = iovbuf.buf, .iov_len = sizeof(iovbuf.buf),
@@ -4044,7 +4024,7 @@
 				dhcp6_startrelease(ifp);
 				return;
 			}
-			unlink(state->leasefile);
+			dhcp_unlink(ifp->ctx, state->leasefile);
 		}
 		dhcp6_freedrop_addrs(ifp, drop, NULL);
 		free(state->old);
--- a/src/dhcpcd-embedded.c.in	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcpcd-embedded.c.in	Tue May 12 10:26:35 2020 +0100
@@ -33,4 +33,4 @@
 
 #include <unistd.h>
 
-const char * const dhcpcd_embedded_conf[] = {
+const char dhcpcd_embedded_conf[] =
--- a/src/dhcpcd-embedded.h.in	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcpcd-embedded.h.in	Tue May 12 10:26:35 2020 +0100
@@ -35,4 +35,4 @@
 #define INITDEFINE6S	@INITDEFINE6S@
 #endif
 
-extern const char * const dhcpcd_embedded_conf[];
+extern const char dhcpcd_embedded_conf[];
--- a/src/dhcpcd.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcpcd.c	Tue May 12 10:26:35 2020 +0100
@@ -357,8 +357,17 @@
 	eloop_event_delete(ctx->eloop, ctx->fork_fd);
 	close(ctx->fork_fd);
 	ctx->fork_fd = -1;
-	(void)freopen(_PATH_DEVNULL, "w", stdout);
-	(void)freopen(_PATH_DEVNULL, "w", stderr);
+#ifdef PRIVSEP
+	if (ctx->options & DHCPCD_PRIVSEP) {
+		/* Aside from Linux, we don't have access to /dev/null */
+		fclose(stdout);
+		fclose(stderr);
+	} else
+#endif
+	{
+		(void)freopen(_PATH_DEVNULL, "w", stdout);
+		(void)freopen(_PATH_DEVNULL, "w", stderr);
+	}
 #endif
 }
 
@@ -825,12 +834,27 @@
 		    ifp->name, ifn->name);
 }
 
+static void
+dhcpcd_initduid(struct dhcpcd_ctx *ctx, struct interface *ifp)
+{
+	char buf[DUID_LEN * 3];
+
+	if (ctx->duid != NULL)
+		return;
+
+	duid_init(ctx, ifp);
+	if (ctx->duid == NULL)
+		return;
+
+	loginfox("DUID %s",
+	    hwaddr_ntoa(ctx->duid, ctx->duid_len, buf, sizeof(buf)));
+}
+
 void
 dhcpcd_startinterface(void *arg)
 {
 	struct interface *ifp = arg;
 	struct if_options *ifo = ifp->options;
-	char buf[DUID_LEN * 3];
 	int carrier;
 
 	if (ifo->options & DHCPCD_LINK) {
@@ -863,26 +887,21 @@
 	if (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6) &&
 	    !(ifo->options & DHCPCD_ANONYMOUS))
 	{
+		char buf[sizeof(ifo->iaid) * 3];
 #ifdef INET6
 		size_t i;
 		struct if_ia *ia;
 #endif
 
-		/* Report client DUID */
-		if (ifp->ctx->duid == NULL) {
-			if (duid_init(ifp) == 0)
-				return;
-			loginfox("DUID %s",
-			    hwaddr_ntoa(ifp->ctx->duid,
-			    ifp->ctx->duid_len,
-			    buf, sizeof(buf)));
-		}
+		/* Try and init DUID from the interface hardware address */
+		dhcpcd_initduid(ifp->ctx, ifp);
 
 		/* Report IAIDs */
 		loginfox("%s: IAID %s", ifp->name,
 		    hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid),
 		    buf, sizeof(buf)));
 		warn_iaid_conflict(ifp, 0, ifo->iaid);
+
 #ifdef INET6
 		for (i = 0; i < ifo->ia_len; i++) {
 			ia = &ifo->ia[i];
@@ -2251,8 +2270,16 @@
 	dhcpcd_setlinkrcvbuf(&ctx);
 #endif
 
+	/* Try and create DUID from the machine UUID. */
+	dhcpcd_initduid(&ctx, NULL);
+
+	/* Cache the default vendor option. */
+	if (dhcp_vendor(ctx.vendor, sizeof(ctx.vendor)) == -1)
+		logerrx("dhcp_vendor");
+
 #ifdef PRIVSEP
 	if (ctx.options & DHCPCD_PRIVSEP) {
+
 		/*
 		 * PSF_CAP_ENTER is not set because the following functions
 		 * won't work in it:
--- a/src/dhcpcd.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/dhcpcd.h	Tue May 12 10:26:35 2020 +0100
@@ -125,6 +125,7 @@
 
 struct dhcpcd_ctx {
 	char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];
+	char vendor[256];
 	int fork_fd;	/* FD for the fork init signal pipe */
 	const char *cffile;
 	unsigned long long options;
--- a/src/duid.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/duid.c	Tue May 12 10:26:35 2020 +0100
@@ -149,20 +149,18 @@
 
 #define DUID_STRLEN DUID_LEN * 3
 static size_t
-duid_get(uint8_t **d, const struct interface *ifp)
+duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
 {
-	FILE *fp;
 	uint8_t *data;
-	size_t len;
-	int x = 0;
+	size_t len, slen;
 	char line[DUID_STRLEN];
 	const struct interface *ifp2;
 
 	/* 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 = read_hwaddr_aton(&data, DUID)) != 0) {
+	if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) {
 		if (len <= DUID_LEN) {
-			*d = data;
+			ctx->duid = data;
 			return len;
 		}
 		logerrx("DUID too big (max %u): %s", DUID_LEN, DUID);
@@ -176,13 +174,18 @@
 		}
 	}
 
-	/* Regardless of what happens we will create a DUID to use. */
-	*d = data;
+	/* No file? OK, lets make one based the machines UUID */
+	if (ifp == NULL) {
+		len = duid_make_uuid(data);
+		if (len == 0)
+			free(data);
+		else
+			ctx->duid = data;
+		return len;
+	}
 
-	/* No file? OK, lets make one based the machines UUID */
-	len = duid_make_uuid(data);
-	if (len > 0)
-		return len;
+	/* Regardless of what happens we will create a DUID to use. */
+	ctx->duid = data;
 
 	/* No UUID? OK, lets make one based on our interface */
 	if (ifp->hwlen == 0) {
@@ -202,27 +205,25 @@
 		}
 	}
 
-	if (!(fp = fopen(DUID, "w"))) {
-		logerr("%s", DUID);
-		return duid_make(data, ifp, DUID_LL);
+	len = duid_make(data, ifp, DUID_LLT);
+	hwaddr_ntoa(data, len, line, sizeof(line));
+	slen = strlen(line);
+	if (slen < sizeof(line) - 2) {
+		line[slen++] = '\n';
+		line[slen] = '\0';
 	}
-	len = duid_make(data, ifp, DUID_LLT);
-	x = fprintf(fp, "%s\n", hwaddr_ntoa(data, len, line, sizeof(line)));
-	if (fclose(fp) == EOF)
-		x = -1;
-	/* Failed to write the duid? scrub it, we cannot use it */
-	if (x < 1) {
-		logerr("%s", DUID);
-		unlink(DUID);
+	if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) {
+		logerr("%s: cannot write duid", __func__);
 		return duid_make(data, ifp, DUID_LL);
 	}
 	return len;
 }
 
-size_t duid_init(const struct interface *ifp)
+size_t
+duid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp)
 {
 
-	if (ifp->ctx->duid == NULL)
-		ifp->ctx->duid_len = duid_get(&ifp->ctx->duid, ifp);
-	return ifp->ctx->duid_len;
+	if (ctx->duid == NULL)
+		ctx->duid_len = duid_get(ctx, ifp);
+	return ctx->duid_len;
 }
--- a/src/duid.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/duid.h	Tue May 12 10:26:35 2020 +0100
@@ -35,6 +35,6 @@
 #define	DUID_UUID	4
 
 size_t duid_make(void *, const struct interface *, uint16_t);
-size_t duid_init(const struct interface *);
+size_t duid_init(struct dhcpcd_ctx *, const struct interface *);
 
 #endif
--- a/src/genembedc	Sun May 10 17:32:15 2020 +0100
+++ b/src/genembedc	Tue May 12 10:26:35 2020 +0100
@@ -13,7 +13,7 @@
 	-e 's/#.*$//' \
 	-e '/^$/d' \
 	-e 's/^/"/g' \
-	-e 's/$/\",/g' \
+	-e 's/$/\\n\"/g' \
 	-e 's/ [ ]*/ /g' \
 	-e 's/	[	]*/ /g' \
 	$CONF_SMALL
@@ -22,9 +22,9 @@
 	-e 's/#.*$//' \
 	-e '/^$/d' \
 	-e 's/^/"/g' \
-	-e 's/$/\",/g' \
+	-e 's/$/\\n"/g' \
 	-e 's/ [ ]*/ /g' \
 	-e 's/	[	]*/ /g' \
 	$CONF
 echo "#endif"
-printf "%s\n%s\n" "NULL" "};"
+printf "%s\n%s\n" '"\0";'
--- a/src/if-bsd.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/if-bsd.c	Tue May 12 10:26:35 2020 +0100
@@ -1555,7 +1555,7 @@
 }
 
 #ifndef SYS_NMLN	/* OSX */
-#  define SYS_NMLN 256
+#  define SYS_NMLN __SYS_NAMELEN
 #endif
 #ifndef HW_MACHINE_ARCH
 #  ifdef HW_MODEL	/* OpenBSD */
@@ -1566,14 +1566,8 @@
 if_machinearch(char *str, size_t len)
 {
 	int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
-	char march[SYS_NMLN];
-	size_t marchlen = sizeof(march);
 
-return -1;
-	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
-	    march, &marchlen, NULL, 0) != 0)
-		return -1;
-	return snprintf(str, len, ":%s", march);
+	return sysctl(mib, sizeof(mib) / sizeof(mib[0]), str, &len, NULL, 0);
 }
 
 #ifdef INET6
--- a/src/if-linux.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/if-linux.c	Tue May 12 10:26:35 2020 +0100
@@ -211,7 +211,7 @@
 		    fscanf(fp, "%255s", buf) == 1)
 		{
 		        fclose(fp);
-			return snprintf(str, len, ":%s", buf);
+			return snprintf(str, len, "%s", buf);
 		}
 	}
 	fclose(fp);
--- a/src/if-options.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/if-options.c	Tue May 12 10:26:35 2020 +0100
@@ -27,7 +27,6 @@
  */
 
 #include <sys/param.h>
-#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <arpa/inet.h>
@@ -2265,25 +2264,31 @@
  * We strip leading space and avoid comment lines, making the code that calls
  * us smaller. */
 static char *
-get_line(char ** __restrict buf, size_t * __restrict buflen,
-    FILE * __restrict fp)
+get_line(char ** __restrict buf, ssize_t * __restrict buflen)
 {
 	char *p, *c;
-	ssize_t bytes;
-	int quoted;
+	bool quoted;
 
 	do {
-		bytes = getline(buf, buflen, fp);
-		if (bytes == -1)
-			return NULL;
-		for (p = *buf; *p == ' ' || *p == '\t'; p++)
+		p = *buf;
+		c = memchr(*buf, '\n', *buflen);
+		if (c == NULL) {
+			c = memchr(*buf, '\0', *buflen);
+			if (c == NULL)
+				return NULL;
+			*buflen = c - *buf;
+			*buf = NULL;
+		} else {
+			*c++ = '\0';
+			*buflen -= c - *buf;
+			*buf = c;
+		}
+		for (; *p == ' ' || *p == '\t'; p++)
 			;
 	} while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';');
-	if ((*buf)[--bytes] == '\n')
-		(*buf)[bytes] = '\0';
 
 	/* Strip embedded comments unless in a quoted string or escaped */
-	quoted = 0;
+	quoted = false;
 	for (c = p; *c != '\0'; c++) {
 		if (*c == '\\') {
 			c++; /* escaped */
@@ -2334,16 +2339,11 @@
     const char *ifname, const char *ssid, const char *profile)
 {
 	struct if_options *ifo;
-	FILE *fp;
-	struct stat sb;
-	char *line, *buf, *option, *p;
-	size_t buflen;
+	char buf[UDPLEN_MAX], *bp; /* 64k max config file size */
+	char *line, *option, *p;
+	ssize_t buflen;
 	ssize_t vlen;
 	int skip, have_profile, new_block, had_block;
-#ifndef EMBEDDED_CONFIG
-	const char * const *e;
-	size_t ol;
-#endif
 #if !defined(INET) || !defined(INET6)
 	size_t i;
 	struct dhcp_opt *opt;
@@ -2366,12 +2366,9 @@
 	ifo->options |= DHCPCD_DHCP6;
 #endif
 
-	vlen = dhcp_vendor((char *)ifo->vendorclassid + 1,
-	            sizeof(ifo->vendorclassid) - 1);
-	ifo->vendorclassid[0] = (uint8_t)(vlen == -1 ? 0 : vlen);
-
-	buf = NULL;
-	buflen = 0;
+	vlen = strlcpy((char *)ifo->vendorclassid + 1, ctx->vendor,
+	    sizeof(ifo->vendorclassid) - 1);
+	ifo->vendorclassid[0] = (uint8_t)(vlen > 255 ? 0 : vlen);
 
 	/* Reset route order */
 	ctx->rt_order = 0;
@@ -2407,38 +2404,26 @@
 
 		/* Now load our embedded config */
 #ifdef EMBEDDED_CONFIG
-		fp = fopen(EMBEDDED_CONFIG, "r");
-		if (fp == NULL)
-			logerr("%s: fopen `%s'", __func__, EMBEDDED_CONFIG);
-
-		while (fp && (line = get_line(&buf, &buflen, fp))) {
-#else
-		buflen = 80;
-		buf = malloc(buflen);
-		if (buf == NULL) {
-			logerr(__func__);
-			free_options(ctx, ifo);
-			return NULL;
+		buflen = dhcp_readfile(ctx, EMBEDDED_CONFIG, buf, sizeof(buf));
+		if (buflen == -1) {
+			logerr("%s: %s", __func__, EMBEDDED_CONFIG);
+			return ifo;
+		}
+		if (buf[buflen] != '\0') {
+			if (buflen < sizeof(buf) - 1)
+				bulen++;
+			buf[buflen] = '\0';
 		}
-		ldop = edop = NULL;
-		for (e = dhcpcd_embedded_conf; *e; e++) {
-			ol = strlen(*e) + 1;
-			if (ol > buflen) {
-				char *nbuf;
-
-				buflen = ol;
-				nbuf = realloc(buf, buflen);
-				if (nbuf == NULL) {
-					logerr(__func__);
-					free(buf);
-					free_options(ctx, ifo);
-					return NULL;
-				}
-				buf = nbuf;
-			}
-			memcpy(buf, *e, ol);
-			line = buf;
+#else
+		buflen = strlcpy(buf, dhcpcd_embedded_conf, sizeof(buf));
+		if ((size_t)buflen >= sizeof(buf)) {
+			logerrx("%s: embedded config too big", __func__);
+			return ifo;
+		}
+		/* Our embedded config is NULL terminated */
 #endif
+		bp = buf;
+		while ((line = get_line(&bp, &buflen)) != NULL) {
 			option = strsep(&line, " \t");
 			if (line)
 				line = strskipwhite(line);
@@ -2452,13 +2437,8 @@
 			}
 			parse_config_line(ctx, NULL, ifo, option, line,
 			    &ldop, &edop);
-
 		}
 
-#ifdef EMBEDDED_CONFIG
-		if (fp)
-			fclose(fp);
-#endif
 #ifdef INET
 		ctx->dhcp_opts = ifo->dhcp_override;
 		ctx->dhcp_opts_len = ifo->dhcp_override_len;
@@ -2503,26 +2483,25 @@
 	}
 
 	/* Parse our options file */
-#ifdef PRIVSEP
-	if (ctx->options & DHCPCD_PRIVSEP &&
-	    ps_root_copychroot(ctx, ctx->cffile) == -1)
-		logwarn("%s: ps_root_copychroot `%s'", __func__, ctx->cffile);
-#endif
-	fp = fopen(ctx->cffile, "r");
-	if (fp == NULL) {
+	buflen = dhcp_readfile(ctx, ctx->cffile, buf, sizeof(buf));
+	if (buflen == -1) {
 		/* dhcpcd can continue without it, but no DNS options
 		 * would be requested ... */
-		logwarn("%s: fopen `%s'", __func__, ctx->cffile);
-		free(buf);
+		logerr("%s: %s", __func__, ctx->cffile);
 		return ifo;
 	}
-	if (stat(ctx->cffile, &sb) == 0)
-		ifo->mtime = sb.st_mtime;
+	if (buf[buflen] != '\0') {
+		if ((size_t)buflen < sizeof(buf) - 1)
+			buflen++;
+		buf[buflen] = '\0';
+	}
+	dhcp_filemtime(ctx, ctx->cffile, &ifo->mtime);
 
 	ldop = edop = NULL;
 	skip = have_profile = new_block = 0;
 	had_block = ifname == NULL ? 1 : 0;
-	while ((line = get_line(&buf, &buflen, fp))) {
+	bp = buf;
+	while ((line = get_line(&bp, &buflen)) != NULL) {
 		option = strsep(&line, " \t");
 		if (line)
 			line = strskipwhite(line);
@@ -2596,10 +2575,9 @@
 			continue;
 		if (skip)
 			continue;
+
 		parse_config_line(ctx, ifname, ifo, option, line, &ldop, &edop);
 	}
-	fclose(fp);
-	free(buf);
 
 	if (profile && !have_profile) {
 		free_options(ctx, ifo);
--- a/src/if-options.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/if-options.h	Tue May 12 10:26:35 2020 +0100
@@ -120,6 +120,7 @@
 #define DHCPCD_ONESHOT			(1ULL << 60)
 #define DHCPCD_INACTIVE			(1ULL << 61)
 #define	DHCPCD_SLAACTEMP		(1ULL << 62)
+#define DHCPCD_PRIVSEPROOT		(1ULL << 63)
 
 #define DHCPCD_NODROP	(DHCPCD_EXITING | DHCPCD_PERSISTENT)
 
--- a/src/if.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/if.h	Tue May 12 10:26:35 2020 +0100
@@ -95,6 +95,8 @@
 #define	FRAMEHDRLEN_MAX			14	/* only ethernet support */
 #define	FRAMELEN_MAX			(FRAMEHDRLEN_MAX + 9216)
 
+#define UDPLEN_MAX			64 * 1024
+
 /* Work out if we have a private address or not
  * 10/8
  * 172.16/12
--- a/src/ipv6.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/ipv6.c	Tue May 12 10:26:35 2020 +0100
@@ -147,18 +147,17 @@
 static ssize_t
 ipv6_readsecret(struct dhcpcd_ctx *ctx)
 {
-	FILE *fp;
 	char line[1024];
 	unsigned char *p;
 	size_t len;
 	uint32_t r;
-	int x;
 
-	if ((ctx->secret_len = read_hwaddr_aton(&ctx->secret, SECRET)) != 0)
+	ctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, SECRET);
+	if (ctx->secret_len != 0)
 		return (ssize_t)ctx->secret_len;
 
 	if (errno != ENOENT)
-		logerr("%s: %s", __func__, SECRET);
+		logerr("%s: cannot read secret", __func__);
 
 	/* Chaining arc4random should be good enough.
 	 * RFC7217 section 5.1 states the key SHOULD be at least 128 bits.
@@ -178,27 +177,18 @@
 		p += sizeof(r);
 	}
 
-	/* Ensure that only the dhcpcd user can read the secret.
-	 * Write permission is also denied as changing it would remove
-	 * it's stability. */
-	if ((fp = fopen(SECRET, "w")) == NULL ||
-	    chmod(SECRET, S_IRUSR) == -1)
-		goto eexit;
-	x = fprintf(fp, "%s\n",
-	    hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line)));
-	if (fclose(fp) == EOF)
-		x = -1;
-	fp = NULL;
-	if (x > 0)
-		return (ssize_t)ctx->secret_len;
-
-eexit:
-	logerr("%s: %s", __func__, SECRET);
-	if (fp != NULL)
-		fclose(fp);
-	unlink(SECRET);
-	ctx->secret_len = 0;
-	return -1;
+	hwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line));
+	len = strlen(line);
+	if (len < sizeof(line) - 2) {
+		line[len++] = '\n';
+		line[len] = '\0';
+	}
+	if (dhcp_writefile(ctx, SECRET, S_IRUSR, line, len) == -1) {
+		logerr("%s: cannot write secret", __func__);
+		ctx->secret_len = 0;
+		return -1;
+	}
+	return (ssize_t)ctx->secret_len;
 }
 
 /* http://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml
--- a/src/logerr.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/logerr.c	Tue May 12 10:26:35 2020 +0100
@@ -104,7 +104,6 @@
 		return -1;
 
 	now = tv.tv_sec;
-	tzset();
 	if (localtime_r(&now, &tmnow) == NULL)
 		return -1;
 	if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
@@ -366,6 +365,9 @@
 {
 	struct logctx *ctx = &_logctx;
 
+	/* Cache timezone */
+	tzset();
+
 	if (path == NULL) {
 		int opts = 0;
 
--- a/src/privsep-bpf.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep-bpf.c	Tue May 12 10:26:35 2020 +0100
@@ -91,7 +91,7 @@
 
 #ifdef ARP
 static ssize_t
-ps_bpf_arp_addr(uint8_t cmd, struct ps_process *psp, struct msghdr *msg)
+ps_bpf_arp_addr(uint16_t cmd, struct ps_process *psp, struct msghdr *msg)
 {
 	struct interface *ifp = &psp->psp_ifp;
 	struct iovec *iov = msg->msg_iov;
@@ -197,7 +197,7 @@
 ssize_t
 ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
 {
-	uint8_t cmd;
+	uint16_t cmd;
 	struct ps_process *psp;
 	pid_t start;
 	struct iovec *iov = msg->msg_iov;
@@ -205,7 +205,7 @@
 	struct ipv4_state *istate;
 	unsigned int flags = PSF_DROPPRIVS | PSF_CAP_ENTER;
 
-	cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
+	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
 	psp = ps_findprocess(ctx, &psm->ps_id);
 
 #ifdef PRIVSEP_DEBUG
@@ -261,7 +261,7 @@
 		 * Pledge is currently useless for BPF ARP because we cannot
 		 * change the filter:
 		 * http://openbsd-archive.7691.n7.nabble.com/ \
-		 * 	  pledge-bpf-32bit-arch-unbreak-td299901.html
+		 *	  pledge-bpf-32bit-arch-unbreak-td299901.html
 		 */
 		break;
 #endif
@@ -321,7 +321,7 @@
 }
 
 static ssize_t
-ps_bpf_send(const struct interface *ifp, uint8_t cmd,
+ps_bpf_send(const struct interface *ifp, uint16_t cmd,
     const void *data, size_t len)
 {
 	struct dhcpcd_ctx *ctx = ifp->ctx;
--- a/src/privsep-bsd.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep-bsd.c	Tue May 12 10:26:35 2020 +0100
@@ -114,7 +114,7 @@
 }
 
 static ssize_t
-ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint8_t domain, unsigned long request,
+ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request,
     void *data, size_t len)
 {
 
@@ -126,7 +126,7 @@
 
 ssize_t
 ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request,
-    void *data,size_t len)
+    void *data, size_t len)
 {
 
 	return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len);
--- a/src/privsep-inet.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep-inet.c	Tue May 12 10:26:35 2020 +0100
@@ -498,12 +498,12 @@
 ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,
     __unused struct msghdr *msg)
 {
-	uint8_t cmd;
+	uint16_t cmd;
 	struct ps_process *psp;
 	int (*start_func)(void *);
 	pid_t start;
 
-	cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
+	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
 	psp = ps_findprocess(ctx, &psm->ps_id);
 
 #ifdef PRIVSEP_DEBUG
@@ -570,7 +570,7 @@
 
 #ifdef INET
 static ssize_t
-ps_inet_in_docmd(struct ipv4_addr *ia, uint8_t cmd, const struct msghdr *msg)
+ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg)
 {
 	assert(ia != NULL);
 	struct dhcpcd_ctx *ctx = ia->iface->ctx;
@@ -614,7 +614,7 @@
 #ifdef INET6
 #ifdef __sun
 static ssize_t
-ps_inet_ifp_docmd(struct interface *ifp, uint8_t cmd, const struct msghdr *msg)
+ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg)
 {
 	struct dhcpcd_ctx *ctx = ifp->ctx;
 	struct ps_msghdr psm = {
@@ -659,7 +659,7 @@
 
 #ifdef DHCP6
 static ssize_t
-ps_inet_in6_docmd(struct ipv6_addr *ia, uint8_t cmd, const struct msghdr *msg)
+ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg)
 {
 	struct dhcpcd_ctx *ctx = ia->iface->ctx;
 	struct ps_msghdr psm = {
--- a/src/privsep-root.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep-root.c	Tue May 12 10:26:35 2020 +0100
@@ -217,124 +217,35 @@
 	return status;
 }
 
-#if defined(__linux__) && !defined(st_mtimespec)
-#define	st_atimespec  st_atim
-#define	st_mtimespec  st_mtim
-#endif
-ssize_t
-ps_root_docopychroot(struct dhcpcd_ctx *ctx, const char *file)
+static ssize_t
+ps_root_dowritefile(mode_t mode, void *data, size_t len)
 {
-
-	char path[PATH_MAX], buf[BUFSIZ], *slash;
-	struct stat from_sb, to_sb;
-	int from_fd, to_fd;
-	ssize_t rcount, wcount, total;
-#if defined(BSD) || defined(__linux__)
-	struct timespec ts[2];
-#else
-	struct timeval tv[2];
-#endif
-
-	if (snprintf(path, sizeof(path), "%s/%s", ctx->ps_chroot, file) == -1)
-		return -1;
-	if (stat(file, &from_sb) == -1)
-		return -1;
-	if (stat(path, &to_sb) == 0) {
-#if defined(BSD) || defined(__linux__)
-		if (from_sb.st_mtimespec.tv_sec == to_sb.st_mtimespec.tv_sec &&
-		    from_sb.st_mtimespec.tv_nsec == to_sb.st_mtimespec.tv_nsec)
-			return 0;
-#else
-		if (from_sb.st_mtime == to_sb.st_mtime)
-			return 0;
-#endif
-	} else {
-		/* Ensure directory exists */
-		slash = strrchr(path, '/');
-		if (slash != NULL) {
-			*slash = '\0';
-			ps_mkdir(path);
-			*slash = '/';
-		}
-	}
+	char *file = data, *nc;
 
-	if (unlink(path) == -1 && errno != ENOENT)
-		return -1;
-	if ((from_fd = open(file, O_RDONLY, 0)) == -1)
-		return -1;
-	if ((to_fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0555)) == -1)
-		return -1;
-
-	total = 0;
-	while ((rcount = read(from_fd, buf, sizeof(buf))) > 0) {
-		wcount = write(to_fd, buf, (size_t)rcount);
-		if (wcount != rcount) {
-			total = -1;
-			break;
-		}
-		total += wcount;
-	}
-
-#if defined(BSD) || defined(__linux__)
-	ts[0] = from_sb.st_atimespec;
-	ts[1] = from_sb.st_mtimespec;
-	if (futimens(to_fd, ts) == -1)
-		total = -1;
-#else
-	tv[0].tv_sec = from_sb.st_atime;
-	tv[0].tv_usec = 0;
-	tv[1].tv_sec = from_sb.st_mtime;
-	tv[1].tv_usec = 0;
-	if (futimes(to_fd, tv) == -1)
-		total = -1;
-#endif
-	close(from_fd);
-	close(to_fd);
-
-	return total;
-}
-
-static ssize_t
-ps_root_dofileop(struct dhcpcd_ctx *ctx, void *data, size_t len, uint8_t op)
-{
-	char *path = data;
-	size_t plen;
-
-	if (len < sizeof(plen)) {
+	nc = memchr(file, '\0', len);
+	if (nc == NULL) {
 		errno = EINVAL;
 		return -1;
 	}
-
-	memcpy(&plen, path, sizeof(plen));
-	path += sizeof(plen);
-	if (sizeof(plen) + plen > len) {
-		errno = EINVAL;
-		return -1;
-	}
-
-	switch(op) {
-	case PS_COPY:
-		return ps_root_docopychroot(ctx, path);
-	case PS_UNLINK:
-		return (ssize_t)unlink(path);
-	default:
-		errno = ENOTSUP;
-		return -1;
-	}
+	nc++;
+	return writefile(file, mode, nc, len - (nc - file));
 }
 
 static ssize_t
 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
 {
 	struct dhcpcd_ctx *ctx = arg;
-	uint8_t cmd;
+	uint16_t cmd;
 	struct ps_process *psp;
 	struct iovec *iov = msg->msg_iov;
 	void *data = iov->iov_base;
 	size_t len = iov->iov_len;
+	uint8_t buf[PS_BUFLEN];
+	time_t mtime;
 	ssize_t err;
+	bool retdata = false;
 
-	cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
+	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
 	psp = ps_findprocess(ctx, &psm->ps_id);
 
 #ifdef PRIVSEP_DEBUG
@@ -388,20 +299,40 @@
 	switch (psm->ps_cmd) {
 	case PS_IOCTL:
 		err = ps_root_doioctl(psm->ps_flags, data, len);
+		retdata = true;
 		break;
 	case PS_SCRIPT:
 		err = ps_root_run_script(ctx, data, len);
 		break;
-	case PS_COPY:	/* FALLTHROUGH */
 	case PS_UNLINK:
-		err = ps_root_dofileop(ctx, data, len, psm->ps_cmd);
+		err = unlink(data);
+		break;
+	case PS_READFILE:
+		errno = 0;
+		err = readfile(data, buf, sizeof(buf));
+		if (err != -1) {
+			data = buf;
+			len = (size_t)err;
+			retdata = true;
+		}
+		break;
+	case PS_WRITEFILE:
+		err = ps_root_dowritefile(psm->ps_flags, data, len);
+		break;
+	case PS_FILEMTIME:
+		err = filemtime(data, &mtime);
+		if (err != -1) {
+			data = &mtime;
+			len = sizeof(mtime);
+			retdata = true;
+		}
 		break;
 	default:
 		err = ps_root_os(psm, msg);
 		break;
 	}
 
-	return ps_root_writeerror(ctx, err, data, len);
+	return ps_root_writeerror(ctx, err, data, retdata ? len : 0);
 }
 
 /* Receive from state engine, do an action. */
@@ -428,6 +359,7 @@
 		    ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
 		    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
 	ctx->ps_root_pid = getpid();
+	ctx->options |= DHCPCD_PRIVSEPROOT;
 	return 0;
 }
 
@@ -574,38 +506,53 @@
 	return ps_root_readerror(ctx, data, len);
 }
 
-static ssize_t
-ps_root_fileop(struct dhcpcd_ctx *ctx, const char *path, uint8_t op)
+ssize_t
+ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
 {
-	char buf[PATH_MAX], *p = buf;
-	size_t plen = strlen(path) + 1;
-	size_t len = sizeof(plen) + plen;
 
-	if (len > sizeof(buf)) {
-		errno = ENOBUFS;
-		return -1;
-	}
-
-	memcpy(p, &plen, sizeof(plen));
-	p += sizeof(plen);
-	memcpy(p, path, plen);
-
-	if (ps_sendcmd(ctx, ctx->ps_root_fd, op, 0, buf, len) == -1)
+	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0,
+	    file, strlen(file) + 1) == -1)
 		return -1;
 	return ps_root_readerror(ctx, NULL, 0);
 }
 
-
 ssize_t
-ps_root_copychroot(struct dhcpcd_ctx *ctx, const char *path)
+ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
+    void *data, size_t len)
 {
-
-	return ps_root_fileop(ctx, path, PS_COPY);
+	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0,
+	    file, strlen(file) + 1) == -1)
+		return -1;
+	return ps_root_readerror(ctx, data, len);
 }
 
 ssize_t
-ps_root_unlink(struct dhcpcd_ctx *ctx, const char *path)
+ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
+    const void *data, size_t len)
+{
+	char buf[PS_BUFLEN];
+	size_t flen;
+
+	flen = strlcpy(buf, file, sizeof(buf));
+	flen += 1;
+	if (flen > sizeof(buf) || flen + len > sizeof(buf)) {
+		errno = ENOBUFS;
+		return -1;
+	}
+	memcpy(buf + flen, data, len);
+
+	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode,
+	    buf, flen + len) == -1)
+		return -1;
+	return ps_root_readerror(ctx, NULL, 0);
+}
+
+int
+ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
 {
 
-	return ps_root_fileop(ctx, path, PS_UNLINK);
+	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0,
+	    file, strlen(file) + 1) == -1)
+		return -1;
+	return ps_root_readerror(ctx, time, sizeof(*time));
 }
--- a/src/privsep-root.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep-root.h	Tue May 12 10:26:35 2020 +0100
@@ -35,10 +35,12 @@
 int ps_root_stop(struct dhcpcd_ctx *ctx);
 
 ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);
-ssize_t ps_root_docopychroot(struct dhcpcd_ctx *, const char *);
-ssize_t ps_root_copychroot(struct dhcpcd_ctx *, const char *);
 ssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
 ssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *);
+int ps_root_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
+ssize_t ps_root_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
+ssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_t,
+    const void *, size_t);
 ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *);
 #if defined(BSD) || defined(__sun)
 ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);
--- a/src/privsep.c	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep.c	Tue May 12 10:26:35 2020 +0100
@@ -78,30 +78,8 @@
 #endif
 
 int
-ps_mkdir(char *path)
-{
-	char *slash;
-	bool done;
-
-	slash = path;
-	for (;;) {
-		slash += strspn(slash, "/");
-		slash += strcspn(slash, "/");
-		done = (*slash == '\0');
-		*slash = '\0';
-		if (mkdir(path, 0755) == -1 && errno != EEXIST)
-			return -1;
-		if (done)
-			break;
-		*slash = '/';
-	}
-	return 0;
-}
-
-int
 ps_init(struct dhcpcd_ctx *ctx)
 {
-	char path[PATH_MAX];
 	struct passwd *pw;
 
 	errno = 0;
@@ -120,9 +98,7 @@
 		ctx->ps_chroot = pw->pw_dir;
 
 	/* If we pickup the _dhcp user refuse the default directory */
-	if (*ctx->ps_chroot != '/' ||
-	    strcmp(ctx->ps_chroot, "/var/empty") == 0)
-	{
+	if (*ctx->ps_chroot != '/') {
 		ctx->options &= ~DHCPCD_PRIVSEP;
 		logerrx("refusing chroot: %s: %s",
 		    PRIVSEP_USER, ctx->ps_chroot);
@@ -130,17 +106,6 @@
 		return -1;
 	}
 
-	/* Create the database directory. */
-	if (snprintf(path, sizeof(path), "%s%s", ctx->ps_chroot, DBDIR) == -1 ||
-	    ps_mkdir(path) == -1 ||
-	    chown(path, pw->pw_uid, pw->pw_gid) == -1 ||
-	    chmod(path, 0755) == -1)
-		logerr("%s: %s", __func__, path);
-
-	/* Ensure we have a localtime to correctly format dates. */
-	if (ps_root_docopychroot(ctx, "/etc/localtime") == -1 && errno!=ENOENT)
-		logerr("%s: %s", __func__, "/etc/localtime");
-
 	ctx->options |= DHCPCD_PRIVSEP;
 	return 0;
 }
@@ -152,7 +117,6 @@
 
 	if (!(ctx->options & DHCPCD_FORKED))
 		logdebugx("chrooting to `%s'", ctx->ps_chroot);
-
 	if (chroot(ctx->ps_chroot) == -1)
 		logerr("%s: chroot `%s'", __func__, ctx->ps_chroot);
 	if (chdir("/") == -1)
@@ -184,9 +148,8 @@
 		if (ctx->options & DHCPCD_UNPRIV)
 			promises = "stdio dns bpf";
 		else
-			/* SIOCGIFGROUP requries inet
-			 * lease files and foo require rpath, wpath and cpath */
-			promises = "stdio dns inet route rpath wpath cpath";
+			/* SIOCGIFGROUP requires inet */
+			promises = "stdio dns inet route";
                 if (pledge(promises, NULL) == -1) {
                         logerr("%s: pledge", __func__);
                         return -1;
@@ -568,7 +531,7 @@
 
 
 ssize_t
-ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
+ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
     const struct msghdr *msg)
 {
 	assert(msg->msg_iovlen == 1);
@@ -610,7 +573,7 @@
 }
 
 ssize_t
-ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint8_t cmd, unsigned long flags,
+ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
     const void *data, size_t len)
 {
 	struct ps_msghdr psm = {
@@ -628,7 +591,7 @@
 }
 
 static ssize_t
-ps_sendcmdmsg(int fd, uint8_t cmd, const struct msghdr *msg)
+ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
 {
 	struct ps_msghdr psm = { .ps_cmd = cmd };
 	uint8_t data[PS_BUFLEN], *p = data;
@@ -671,7 +634,7 @@
 }
 
 ssize_t
-ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint8_t cmd, int wfd)
+ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
 {
 	struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
 	uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
--- a/src/privsep.h	Sun May 10 17:32:15 2020 +0100
+++ b/src/privsep.h	Tue May 12 10:26:35 2020 +0100
@@ -36,32 +36,36 @@
 #define	PSF_CAP_ENTER		0x02
 #define	PSF_PLEDGE		0x04
 
-/* Commands */
-#define	PS_BOOTP		0x01
-#define	PS_ND			0x02
-#define	PS_DHCP6		0x03
-#define	PS_BPF_BOOTP		0x04
-#define	PS_BPF_ARP		0x05
-#define	PS_BPF_ARP_ADDR		0x06
+/* Protocols */
+#define	PS_BOOTP		0x0001
+#define	PS_ND			0x0002
+#define	PS_DHCP6		0x0003
+#define	PS_BPF_BOOTP		0x0004
+#define	PS_BPF_ARP		0x0005
+#define	PS_BPF_ARP_ADDR		0x0006
 
-#define	PS_IOCTL		0x10
-#define	PS_ROUTE		0x11	/* Also used for NETLINK */
-#define	PS_SCRIPT		0x12
-#define	PS_UNLINK		0x13
-#define	PS_COPY			0x14
+/* Generic commands */
+#define	PS_IOCTL		0x0010
+#define	PS_ROUTE		0x0011	/* Also used for NETLINK */
+#define	PS_SCRIPT		0x0012
+#define	PS_UNLINK		0x0013
+#define	PS_READFILE		0x0014
+#define	PS_WRITEFILE		0x0015
+#define	PS_FILEMTIME		0x0016
 
 /* BSD Commands */
-#define	PS_IOCTLLINK		0x15
-#define	PS_IOCTL6		0x16
-#define	PS_IOCTLINDIRECT	0x17
-#define	PS_IP6FORWARDING	0x18
+#define	PS_IOCTLLINK		0x0101
+#define	PS_IOCTL6		0x0102
+#define	PS_IOCTLINDIRECT	0x0103
+#define	PS_IP6FORWARDING	0x0104
 
 /* Linux commands */
-#define	PS_WRITEPATHUINT	0x19
+#define	PS_WRITEPATHUINT	0x0201
 
-#define	PS_DELETE		0x20
-#define	PS_START		0x40
-#define	PS_STOP			0x80
+/* Process commands */
+#define	PS_DELETE		0x2000
+#define	PS_START		0x4000
+#define	PS_STOP			0x8000
 
 /* Max INET message size + meta data for IPC */
 #define	PS_BUFLEN		((64 * 1024) +			\
@@ -96,13 +100,13 @@
 struct ps_id {
 	struct ps_addr psi_addr;
 	unsigned int psi_ifindex;
-	uint8_t psi_cmd;
-	uint8_t psi_pad[3];
+	uint16_t psi_cmd;
+	uint8_t psi_pad[2];
 };
 
 struct ps_msghdr {
-	uint8_t ps_cmd;
-	uint8_t ps_pad[sizeof(unsigned long) - 1];
+	uint16_t ps_cmd;
+	uint8_t ps_pad[sizeof(unsigned long) - sizeof(uint16_t)];
 	unsigned long ps_flags;
 	struct ps_id ps_id;
 	socklen_t ps_namelen;
@@ -141,7 +145,6 @@
 #include "privsep-bpf.h"
 #endif
 
-int ps_mkdir(char *);
 int ps_init(struct dhcpcd_ctx *);
 int ps_dropprivs(struct dhcpcd_ctx *, unsigned int);
 int ps_start(struct dhcpcd_ctx *);
@@ -152,11 +155,11 @@
     struct ps_msghdr *, const struct msghdr *);
 ssize_t ps_sendpsmdata(struct dhcpcd_ctx *, int,
     struct ps_msghdr *, const void *, size_t);
-ssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint8_t, unsigned long,
+ssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
     const struct msghdr *);
-ssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint8_t, unsigned long,
+ssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
     const void *data, size_t len);
-ssize_t ps_recvmsg(struct dhcpcd_ctx *, int, uint8_t, int);
+ssize_t ps_recvmsg(struct dhcpcd_ctx *, int, uint16_t, int);
 ssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int,
     ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *);