Mercurial > hg > dhcpcd
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 *);
