From: Roy Marples Date: Thu, 18 Jun 2026 14:39:22 +0000 (+0100) Subject: privsep: Remove PS_BUFLEN X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cfac52dc7e9387bbf7eb9e560a28d5a4482ad22a;p=thirdparty%2Fdhcpcd.git privsep: Remove PS_BUFLEN We always send a header with the expected lengths. So use malloced buffers to send and receive using this knowledge. --- diff --git a/src/common.c b/src/common.c index 764209cb..953db6f6 100644 --- a/src/common.c +++ b/src/common.c @@ -111,21 +111,35 @@ hwaddr_aton(uint8_t *buffer, const char *addr) } ssize_t -readfile(const char *file, void *data, size_t len) +readfile(const char *file, void **data, size_t *len) { int fd; - ssize_t bytes; + ssize_t bytes = -1; + struct stat st; + size_t nlen; + char *buf; fd = open(file, O_RDONLY); if (fd == -1) return -1; - bytes = read(fd, data, len); + if (fstat(fd, &st) == -1) + goto out; + nlen = (size_t)st.st_size + 1; + if (nlen > *len) { + void *ndata = realloc(*data, nlen); + if (ndata == NULL) + goto out; + *data = ndata; + *len = nlen; + } + bytes = read(fd, *data, *len); +out: close(fd); - if ((size_t)bytes == len) { - errno = ENOBUFS; + if (bytes == -1) return -1; - } - return bytes; + buf = *data; + buf[bytes] = '\0'; + return bytes + 1; } ssize_t @@ -158,7 +172,7 @@ filemtime(const char *file, time_t *time) * We strip leading space and avoid comment lines, making the code that calls * us smaller. */ char * -get_line(char **__restrict buf, ssize_t *__restrict buflen) +get_line(char **__restrict buf, size_t *__restrict buflen) { char *p, *c; bool quoted; @@ -167,16 +181,16 @@ get_line(char **__restrict buf, ssize_t *__restrict buflen) p = *buf; if (*buf == NULL) return NULL; - c = memchr(*buf, '\n', (size_t)*buflen); + c = memchr(*buf, '\n', *buflen); if (c == NULL) { - c = memchr(*buf, '\0', (size_t)*buflen); + c = memchr(*buf, '\0', *buflen); if (c == NULL) return NULL; - *buflen = c - *buf; + *buflen = (size_t)(c - *buf); *buf = NULL; } else { *c++ = '\0'; - *buflen -= c - *buf; + *buflen -= (size_t)(c - *buf); *buf = c; } for (; *p == ' ' || *p == '\t'; p++) diff --git a/src/common.h b/src/common.h index 04276615..577afc4d 100644 --- a/src/common.h +++ b/src/common.h @@ -167,10 +167,10 @@ const char *hwaddr_ntoa(const void *, size_t, char *, size_t); size_t hwaddr_aton(uint8_t *, const char *); -ssize_t readfile(const char *, void *, size_t); +ssize_t readfile(const char *, void **, size_t *); ssize_t writefile(const char *, mode_t, const void *, size_t); int filemtime(const char *, time_t *); -char *get_line(char **__restrict, ssize_t *__restrict); +char *get_line(char **__restrict, size_t *__restrict); int is_root_local(void); uint32_t lifetime_left(uint32_t, const struct timespec *, struct timespec *); #endif diff --git a/src/dhcp-common.c b/src/dhcp-common.c index c428b7ce..04970b04 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -1009,10 +1009,44 @@ dhcp_zero_index(struct dhcp_opt *opt) } ssize_t -dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len) +dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void **data, + size_t *len) { + size_t rlen = 0; + + if (len == NULL) + len = &rlen; + + if (file == NULL || *file == '\0') { + uint8_t *p = *data; + size_t needed = BUFSIZ, bytes = 0, left; + ssize_t nread; + + for (;;) { + if (*len < needed) { + void *nbuf = realloc(*data, needed); + if (nbuf == NULL) + return -1; + p = *data = nbuf; + p += bytes; + *len = needed; + } + + left = *len - bytes; + nread = read(fileno(stdin), p, left); + if (nread == -1) + return -1; + bytes += (size_t)nread; + if ((size_t)nread < left || nread == 0) + return (ssize_t)bytes; + + /* We need a bigger buffer */ + needed += BUFSIZ; + } + } + #ifdef PRIVSEP - if (ctx->options & DHCPCD_PRIVSEP && + if (file != NULL && ctx->options & DHCPCD_PRIVSEP && !(ctx->options & DHCPCD_PRIVSEPROOT)) return ps_root_readfile(ctx, file, data, len); #else @@ -1068,21 +1102,18 @@ dhcp_unlink(struct dhcpcd_ctx *ctx, const char *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; - bytes = dhcp_readfile(ctx, file, buf, sizeof(buf)); - if (bytes == -1 || bytes == sizeof(buf)) + bytes = dhcp_readfile(ctx, file, &ctx->io_buf, &ctx->io_buflen); + if (bytes == -1) return 0; - - bytes[buf] = '\0'; - len = hwaddr_aton(NULL, buf); + len = hwaddr_aton(NULL, ctx->io_buf); if (len == 0) return 0; *data = malloc(len); if (*data == NULL) return 0; - hwaddr_aton(*data, buf); + hwaddr_aton(*data, ctx->io_buf); return len; } diff --git a/src/dhcp-common.h b/src/dhcp-common.h index 49c831f9..cdc5dded 100644 --- a/src/dhcp-common.h +++ b/src/dhcp-common.h @@ -132,7 +132,7 @@ void dhcp_envoption(struct dhcpcd_ctx *, FILE *, const char *, const char *, const uint8_t *od, size_t ol); void dhcp_zero_index(struct dhcp_opt *); -ssize_t dhcp_readfile(struct dhcpcd_ctx *, const char *, void *, size_t); +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 *); diff --git a/src/dhcp.c b/src/dhcp.c index d22a99b1..799a7466 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -1232,10 +1232,6 @@ toobig: static size_t read_lease(struct interface *ifp, struct bootp **bootp) { - union { - struct bootp bootp; - uint8_t buf[FRAMELEN_MAX]; - } buf; struct dhcp_state *state = D_STATE(ifp); ssize_t sbytes; size_t bytes; @@ -1248,14 +1244,13 @@ read_lease(struct interface *ifp, struct bootp **bootp) /* Safety */ *bootp = NULL; - if (state->leasefile[0] == '\0') { + if (state->leasefile[0] == '\0') logdebugx("reading standard input"); - sbytes = read(fileno(stdin), buf.buf, sizeof(buf.buf)); - } else { + else logdebugx("%s: reading lease: %s", ifp->name, state->leasefile); - sbytes = dhcp_readfile(ifp->ctx, state->leasefile, buf.buf, - sizeof(buf.buf)); - } + *bootp = NULL; + sbytes = dhcp_readfile(ifp->ctx, state->leasefile, (void **)bootp, + NULL); if (sbytes == -1) { if (errno != ENOENT) logerr("%s: %s", ifp->name, state->leasefile); @@ -1277,17 +1272,17 @@ read_lease(struct interface *ifp, struct bootp **bootp) goto out; /* We may have found a BOOTP server */ - if (get_option_uint8(ifp->ctx, &type, &buf.bootp, bytes, - DHO_MESSAGETYPE) == -1) + if (get_option_uint8(ifp->ctx, &type, *bootp, bytes, DHO_MESSAGETYPE) == + -1) type = 0; #ifdef AUTH /* Authenticate the message */ - auth = get_option(ifp->ctx, &buf.bootp, bytes, DHO_AUTHENTICATION, + auth = get_option(ifp->ctx, *bootp, bytes, DHO_AUTHENTICATION, &auth_len); if (auth) { if (dhcp_auth_validate(&state->auth, &ifp->options->auth, - &buf.bootp, bytes, 4, type, auth, auth_len) == NULL) { + *bootp, bytes, 4, type, auth, auth_len) == NULL) { logerr("%s: authentication failed", ifp->name); return 0; } @@ -1304,12 +1299,6 @@ read_lease(struct interface *ifp, struct bootp **bootp) #endif out: - *bootp = malloc(bytes); - if (*bootp == NULL) { - logerr(__func__); - return 0; - } - memcpy(*bootp, buf.buf, bytes); return bytes; } diff --git a/src/dhcp6.c b/src/dhcp6.c index 0193967a..97737675 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -2672,10 +2672,7 @@ dhcp6_validatelease(struct interface *ifp, struct dhcp6_message *m, size_t len, static ssize_t dhcp6_readlease(struct interface *ifp, int validate) { - union { - struct dhcp6_message dhcp6; - uint8_t buf[UDPLEN_MAX]; - } buf; + struct dhcp6_message *dhcp6; struct dhcp6_state *state; ssize_t bytes; int fd; @@ -2686,14 +2683,12 @@ dhcp6_readlease(struct interface *ifp, int validate) #endif state = D6_STATE(ifp); - if (state->leasefile[0] == '\0') { + if (state->leasefile[0] == '\0') logdebugx("reading standard input"); - bytes = read(fileno(stdin), buf.buf, sizeof(buf.buf)); - } else { + else logdebugx("%s: reading lease: %s", ifp->name, state->leasefile); - bytes = dhcp_readfile(ifp->ctx, state->leasefile, buf.buf, - sizeof(buf.buf)); - } + bytes = dhcp_readfile(ifp->ctx, state->leasefile, (void **)&dhcp6, + NULL); if (bytes == -1) goto ex; @@ -2716,7 +2711,7 @@ dhcp6_readlease(struct interface *ifp, int validate) state->acquired.tv_sec -= now - mtime; /* Check to see if the lease is still valid */ - fd = dhcp6_validatelease(ifp, &buf.dhcp6, (size_t)bytes, NULL, + fd = dhcp6_validatelease(ifp, dhcp6, (size_t)bytes, NULL, &state->acquired); if (fd == -1) { bytes = 0; /* We have already reported the error */ @@ -2733,11 +2728,10 @@ dhcp6_readlease(struct interface *ifp, int validate) auth: #ifdef AUTH /* Authenticate the message */ - o = dhcp6_findmoption(&buf.dhcp6, (size_t)bytes, D6_OPTION_AUTH, &ol); + o = dhcp6_findmoption(dhcp6, (size_t)bytes, D6_OPTION_AUTH, &ol); if (o) { - if (dhcp_auth_validate(&state->auth, &ifp->options->auth, - buf.buf, (size_t)bytes, 6, buf.dhcp6.type, o, - ol) == NULL) { + if (dhcp_auth_validate(&state->auth, &ifp->options->auth, dhcp6, + (size_t)bytes, 6, dhcp6->type, o, ol) == NULL) { logerr("%s: authentication failed", ifp->name); bytes = 0; goto ex; @@ -2756,13 +2750,7 @@ auth: out: free(state->new); - state->new = malloc((size_t)bytes); - if (state->new == NULL) { - logerr(__func__); - goto ex; - } - - memcpy(state->new, buf.buf, (size_t)bytes); + state->new = dhcp6; state->new_len = (size_t)bytes; return bytes; diff --git a/src/dhcpcd.c b/src/dhcpcd.c index cb7ebadb..36ee0e3b 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -2808,6 +2808,7 @@ exit1: #endif free(ctx.script_buf); free(ctx.script_env); + free(ctx.io_buf); rt_dispose(&ctx); free(ctx.duid); if_closesockets(&ctx); diff --git a/src/dhcpcd.h b/src/dhcpcd.h index d2fae30b..cb2a9303 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -170,6 +170,8 @@ struct dhcpcd_ctx { size_t script_buflen; char **script_env; size_t script_envlen; + void *io_buf; + size_t io_buflen; int control_fd; int control_unpriv_fd; @@ -202,6 +204,8 @@ struct dhcpcd_ctx { int ps_log_root_fd; /* outside chroot log reader */ struct fd_list *ps_control; /* Queue for the above */ struct fd_list *ps_control_client; /* Queue for the above */ + void *ps_buf; /* IPC buffer */ + size_t ps_buflen; /* IPC buffer length */ #endif #ifdef INET diff --git a/src/if-linux.c b/src/if-linux.c index c5278a52..0359d1b2 100644 --- a/src/if-linux.c +++ b/src/if-linux.c @@ -256,12 +256,11 @@ if_machinearch(char *str, size_t len) static int check_proc_int(struct dhcpcd_ctx *ctx, const char *path) { - char buf[64]; int error, i; - if (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1) + if (dhcp_readfile(ctx, path, &ctx->io_buf, &ctx->io_buflen) == -1) return -1; - i = (int)strtoi(buf, NULL, 0, INT_MIN, INT_MAX, &error); + i = (int)strtoi(ctx->io_buf, NULL, 0, INT_MIN, INT_MAX, &error); if (error != 0 && error != ENOTSUP) { errno = error; return -1; @@ -272,12 +271,11 @@ check_proc_int(struct dhcpcd_ctx *ctx, const char *path) static int check_proc_uint(struct dhcpcd_ctx *ctx, const char *path, unsigned int *u) { - char buf[64]; int error; - if (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1) + if (dhcp_readfile(ctx, path, &ctx->io_buf, &ctx->io_buflen) == -1) return -1; - *u = (unsigned int)strtou(buf, NULL, 0, 0, UINT_MAX, &error); + *u = (unsigned int)strtou(ctx->io_buf, NULL, 0, 0, UINT_MAX, &error); if (error != 0 && error != ENOTSUP) { errno = error; return error; @@ -365,10 +363,10 @@ if_conf(struct interface *ifp) static bool if_bridge(struct dhcpcd_ctx *ctx, const char *ifname) { - char path[sizeof(SYS_BRIDGE) + IF_NAMESIZE], buf[64]; + char path[sizeof(SYS_BRIDGE) + IF_NAMESIZE]; snprintf(path, sizeof(path), SYS_BRIDGE, ifname); - if (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1) + if (dhcp_readfile(ctx, path, &ctx->io_buf, &ctx->io_buflen) == -1) return false; return true; } diff --git a/src/if-options.c b/src/if-options.c index d7b9ffb1..03aae3d5 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -2661,10 +2661,9 @@ read_config(struct dhcpcd_ctx *ctx, const char *ifname, const char *ssid, const char *profile) { struct if_options *ifo; - char buf[UDPLEN_MAX], *bp; /* 64k max config file size */ - char *line, *option, *p; - ssize_t buflen; - size_t vlen; + char *bp, *line, *option, *p; + ssize_t nread; + size_t buflen, vlen; int skip, have_profile, new_block, had_block; #if !defined(INET) || !defined(INET6) size_t i; @@ -2741,26 +2740,28 @@ read_config(struct dhcpcd_ctx *ctx, const char *ifname, const char *ssid, /* Now load our embedded config */ #ifdef EMBEDDED_CONFIG - buflen = dhcp_readfile(ctx, EMBEDDED_CONFIG, buf, sizeof(buf)); - if (buflen == -1) { + nread = dhcp_readfile(ctx, EMBEDDED_CONFIG, &ctx->io_buf, + &ctx->io_buflen); + if (nread == -1) { logerr("%s: %s", __func__, EMBEDDED_CONFIG); return ifo; } - if (buf[buflen - 1] != '\0') { - if ((size_t)buflen < sizeof(buf) - 1) - buflen++; - buf[buflen - 1] = '\0'; - } + buflen = (size_t)nread; #else - buflen = (ssize_t)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 */ + buflen = strlen(dhcpcd_embedded_conf) + 1; + if (ctx->io_buflen < buflen) { + void *nbuf = realloc(ctx->io_buf, buflen); + if (nbuf == NULL) { + logerr("%s: realloc", __func__); + return ifo; + } + ctx->io_buf = nbuf; + ctx->io_buflen = buflen; + } + buflen = strlcpy(ctx->io_buf, dhcpcd_embedded_conf, + ctx->io_buflen); #endif - bp = buf; + bp = ctx->io_buf; while ((line = get_line(&bp, &buflen)) != NULL) { option = strsep(&line, " \t"); if (line) @@ -2817,24 +2818,20 @@ read_config(struct dhcpcd_ctx *ctx, const char *ifname, const char *ssid, } /* Parse our options file */ - buflen = dhcp_readfile(ctx, ctx->cffile, buf, sizeof(buf)); - if (buflen == -1) { + nread = dhcp_readfile(ctx, ctx->cffile, &ctx->io_buf, &ctx->io_buflen); + if (nread == -1) { /* dhcpcd can continue without it, but no DNS options * would be requested ... */ logerr("%s: %s", __func__, ctx->cffile); return ifo; } - if (buf[buflen - 1] != '\0') { - if ((size_t)buflen < sizeof(buf) - 1) - buflen++; - buf[buflen - 1] = '\0'; - } + buflen = (size_t)nread; dhcp_filemtime(ctx, ctx->cffile, &ifo->mtime); ldop = edop = NULL; skip = have_profile = new_block = 0; had_block = ifname == NULL ? 1 : 0; - bp = buf; + bp = ctx->io_buf; while ((line = get_line(&bp, &buflen)) != NULL) { option = strsep(&line, " \t"); if (line) diff --git a/src/privsep-bsd.c b/src/privsep-bsd.c index 42bc45eb..4cca33a4 100644 --- a/src/privsep-bsd.c +++ b/src/privsep-bsd.c @@ -389,21 +389,20 @@ ssize_t ps_root_sysctl(struct dhcpcd_ctx *ctx, const int *name, unsigned int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { - char buf[PS_BUFLEN], *p = buf; + char *p; unsigned long flags = 0; size_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen; + size_t buflen = sizeof(namelen) + (sizeof(*name) * namelen) + + sizeof(oldlenp) + sizeof(newlen) + newlen; - if (sizeof(namelen) + (sizeof(*name) * namelen) + sizeof(oldlenp) + - sizeof(newlen) + newlen > - sizeof(buf)) { - errno = ENOBUFS; + if (ps_bufalloc(ctx, buflen) == -1) return -1; - } if (oldlenp) flags |= PS_SYSCTL_OLEN; if (oldp) flags |= PS_SYSCTL_ODATA; + p = ctx->ps_buf; memcpy(p, &namelen, sizeof(namelen)); p += sizeof(namelen); memcpy(p, name, sizeof(*name) * namelen); @@ -417,14 +416,14 @@ ps_root_sysctl(struct dhcpcd_ctx *ctx, const int *name, unsigned int namelen, p += newlen; } - if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL, flags, buf, - (size_t)(p - buf)) == -1) + if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL, flags, ctx->ps_buf, + (size_t)(p - (char *)ctx->ps_buf)) == -1) return -1; - if (ps_root_readerror(ctx, buf, sizeof(buf)) == -1) + if (ps_root_readerror(ctx, ctx->ps_buf, ctx->ps_buflen) == -1) return -1; - p = buf; + p = ctx->ps_buf; memcpy(&nolen, p, sizeof(nolen)); p += sizeof(nolen); if (oldlenp) { diff --git a/src/privsep-root.c b/src/privsep-root.c index 558fad92..032c71d3 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -102,9 +102,14 @@ ps_root_readerrorcb(struct psr_ctx *pc) return len; if (pc->psr_mallocdata) { - pc->psr_data = malloc(psr_error->psr_datalen); - if (pc->psr_data == NULL) - PSR_ERROR(errno); + if (psr_error->psr_datalen > pc->psr_datalen) { + void *nbuf = realloc(pc->psr_data, + psr_error->psr_datalen); + if (nbuf == NULL) + PSR_ERROR(errno); + pc->psr_data = nbuf; + pc->psr_datalen = psr_error->psr_datalen; + } } else if (psr_error->psr_datalen > pc->psr_datalen) PSR_ERROR(EMSGSIZE); @@ -122,10 +127,6 @@ ps_root_readerrorcb(struct psr_ctx *pc) error: psr_error->psr_result = -1; - if (pc->psr_mallocdata && pc->psr_data != NULL) { - free(pc->psr_data); - pc->psr_data = NULL; - } return -1; } @@ -147,16 +148,18 @@ ssize_t ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len) { struct psr_ctx pc = { .psr_ctx = ctx, - .psr_data = NULL, - .psr_datalen = 0, + .psr_data = *data, + .psr_datalen = *len, .psr_mallocdata = true }; ps_root_readerrorcb(&pc); errno = pc.psr_error.psr_errno; *data = pc.psr_data; - *len = pc.psr_error.psr_datalen; - return pc.psr_error.psr_result; + *len = pc.psr_datalen; + if (pc.psr_error.psr_result == -1) + return -1; + return (ssize_t)pc.psr_error.psr_datalen; } static ssize_t @@ -438,7 +441,6 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) struct iovec *iov = msg->msg_iov; void *data = iov->iov_base, *rdata = NULL; size_t len = iov->iov_len, rlen = 0; - uint8_t buf[PS_BUFLEN]; time_t mtime; ssize_t err; bool free_rdata = false; @@ -534,9 +536,9 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) err = -1; break; } - err = readfile(data, buf, sizeof(buf)); + err = readfile(data, &ctx->ps_buf, &ctx->ps_buflen); if (err != -1) { - rdata = buf; + rdata = ctx->ps_buf; rlen = (size_t)err; } break; @@ -565,10 +567,13 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) #endif #ifdef PRIVSEP_GETHOSTNAME case PS_GETHOSTNAME: - err = gethostname((char *)buf, sizeof(buf)); + err = ps_bufalloc(ctx, _POSIX_HOST_NAME_MAX + 1); + if (err == -1) + break; + err = gethostname((char *)ctx->ps_buf, ctx->ps_buflen); if (err != -1) { - rdata = buf; - rlen = strlen((char *)buf) + 1; + rdata = ctx->ps_buf; + rlen = strlen((char *)ctx->ps_buf) + 1; } break; #endif @@ -858,8 +863,6 @@ ps_root_start(struct dhcpcd_ctx *ctx) if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, datafd) == -1) return -1; - if (ps_setbuf_fdpair(datafd) == -1) - return -1; #ifdef PRIVSEP_RIGHTS if (ps_rights_limit_fdpair(datafd) == -1) return -1; @@ -969,6 +972,8 @@ ps_root_stop(struct dhcpcd_ctx *ctx) ctx->ps_data_fd = -1; } + free(ctx->ps_buf); + /* Only the manager process gets past this point. */ if (ctx->options & DHCPCD_FORKED) { err = 0; @@ -1042,33 +1047,38 @@ ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file) } ssize_t -ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, - size_t len) +ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file, void **data, + size_t *len) { if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_READFILE, 0, file, strlen(file) + 1) == -1) return -1; - return ps_root_readerror(ctx, data, len); + + return ps_root_mreaderror(ctx, data, len); } ssize_t 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; + struct iovec iov[] = { + { + .iov_base = UNCONST(file), + .iov_len = strlen(file) + 1, + }, + { + .iov_base = UNCONST(data), + .iov_len = len, + }, + }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = __arraycount(iov), + }; - flen = strlcpy(buf, file, sizeof(buf)); - flen += 1; - if (flen > sizeof(buf) || flen + len > sizeof(buf)) { - errno = ENOBUFS; + if (ps_sendcmdmsg(ctx, PS_ROOT_FD(ctx), PS_WRITEFILE, mode, &msg) == -1) return -1; - } - memcpy(buf + flen, data, len); - if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_WRITEFILE, mode, buf, - flen + len) == -1) - return -1; return ps_root_readerror(ctx, NULL, 0); } @@ -1107,7 +1117,7 @@ ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead) void *buf = NULL; char *bp, *sap; socklen_t salen; - size_t len; + size_t len = 0; ssize_t err; if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_GETIFADDRS, 0, NULL, 0) == -1) diff --git a/src/privsep-root.h b/src/privsep-root.h index 3901ef4f..58fe8da8 100644 --- a/src/privsep-root.h +++ b/src/privsep-root.h @@ -46,7 +46,7 @@ ssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *); ssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t); ssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *); ssize_t 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_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_logreopen(struct dhcpcd_ctx *); diff --git a/src/privsep.c b/src/privsep.c index 4ecad2d9..a0806994 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -186,48 +186,6 @@ ps_dropprivs(struct dhcpcd_ctx *ctx) return 0; } -static int -ps_setbuf0(int fd, int ctl, int minlen) -{ - int len; - socklen_t slen; - - slen = sizeof(len); - if (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1) - return -1; - -#ifdef __linux__ - len /= 2; -#endif - if (len >= minlen) - return 0; - - return setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen)); -} - -static int -ps_setbuf(int fd) -{ - /* Ensure we can receive a fully sized privsep message. - * Double the send buffer. */ - int minlen = (int)sizeof(struct ps_msg); - - if (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 || - ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1) { - logerr(__func__); - return -1; - } - return 0; -} - -int -ps_setbuf_fdpair(int fd[]) -{ - if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1) - return -1; - return 0; -} - #ifdef PRIVSEP_RIGHTS int ps_rights_limit_ioctl(int fd) @@ -348,10 +306,7 @@ ps_startprocess(struct ps_process *psp, logerr("%s: socketpair", __func__); return -1; } - if (ps_setbuf_fdpair(fd) == -1) { - logerr("%s: ps_setbuf_fdpair", __func__); - return -1; - } + #ifdef PRIVSEP_RIGHTS if (ps_rights_limit_fdpair(fd) == -1) { logerr("%s: ps_rights_limit_fdpair", __func__); @@ -522,6 +477,23 @@ ps_stopprocess(struct ps_process *psp) return err; } +int +ps_bufalloc(struct dhcpcd_ctx *ctx, size_t len) +{ + void *nbuf; + + if (ctx->ps_buflen >= len) + return 0; + + nbuf = realloc(ctx->ps_buf, len); + if (nbuf == NULL) + return -1; + + ctx->ps_buf = nbuf; + ctx->ps_buflen = len; + return 0; +} + int ps_start(struct dhcpcd_ctx *ctx) { @@ -529,6 +501,11 @@ ps_start(struct dhcpcd_ctx *ctx) uint32_t rnd; TAILQ_INIT(&ctx->ps_processes); + /* Alloc a reasonable buffer up front */ + if (ps_bufalloc(ctx, BUFSIZ) == -1) { + logerr("%s: ps_bufalloc", __func__); + return -1; + } switch (pid = ps_root_start(ctx)) { case -1: @@ -1073,7 +1050,6 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, void *cbctx) { struct ps_msghdr psm; - uint8_t ps_data[PS_BUFLEN]; ssize_t len; size_t dlen; struct iovec iov[1]; @@ -1126,18 +1102,16 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, dlen = psm.ps_namelen + psm.ps_controllen + cmsg_padlen + psm.ps_datalen; if (dlen != 0) { - if (dlen > sizeof(ps_data)) { - errno = EMSGSIZE; + if (ps_bufalloc(ctx, dlen) == -1) goto stop; - } - len = recv(fd, ps_data, dlen, MSG_WAITALL); + len = recv(fd, ctx->ps_buf, dlen, MSG_WAITALL); if ((size_t)len != dlen) { errno = EINVAL; goto stop; } } - if (ps_unrollmsg(&msg, &psm, ps_data, dlen) == -1) + if (ps_unrollmsg(&msg, &psm, ctx->ps_buf, dlen) == -1) return -1; if (callback == NULL) diff --git a/src/privsep.h b/src/privsep.h index 2141acd0..8c6ec054 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -91,19 +91,8 @@ #define PS_SYSCTL_ODATA 0x0002 /* Process commands */ -#define PS_START 0x4000 -#define PS_STOP 0x8000 - -#ifdef INET6 -#define PS_BUFLEN6 CMSG_SPACE(sizeof(struct in6_pktinfo) + sizeof(int)) -#else -#define PS_BUFLEN6 0 -#endif - -/* Max INET message size + meta data for IPC */ -#define PS_BUFLEN \ - ((64 * 1024) + sizeof(struct ps_msghdr) + sizeof(struct msghdr) + \ - PS_BUFLEN6) +#define PS_START 0x4000 +#define PS_STOP 0x8000 #define PSP_NAMESIZE 16 + INET_MAX_ADDRSTRLEN @@ -169,11 +158,6 @@ struct ps_msghdr { size_t ps_datalen; }; -struct ps_msg { - struct ps_msghdr psm_hdr; - uint8_t psm_data[PS_BUFLEN]; -}; - struct bpf; struct ps_process { @@ -209,6 +193,7 @@ TAILQ_HEAD(ps_process_head, ps_process); #include "privsep-bpf.h" #endif +int ps_bufalloc(struct dhcpcd_ctx *, size_t); int ps_init(struct dhcpcd_ctx *); int ps_start(struct dhcpcd_ctx *); int ps_stop(struct dhcpcd_ctx *); @@ -232,9 +217,6 @@ ssize_t ps_recvmsg(int, unsigned short, uint16_t, int); ssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int, unsigned short, ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *); -/* Internal privsep functions. */ -int ps_setbuf_fdpair(int[]); - #ifdef PRIVSEP_RIGHTS int ps_rights_limit_ioctl(int); int ps_rights_limit_fd_fctnl(int);