From acd7a309360503fab9726f6546f88f80fdf7a529 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Thu, 4 Jun 2020 13:23:18 +0000 Subject: [PATCH] Import dhcpcd-9.1.1 with the following changes: * Restore dumping leases from stdin * auth: Only accept RECONFIGURE messages from LL addresses * auth: Access the RDM monotonic counter file via privsep * ARP: call arp_announced() when cancelling it * BSD: fwip(4) interfaces are now ignored by default * privsep: Ensure IPC buffers are large enough to carry messages * privsep: Only open RAW sockets for the needed protocols * privsep: Fix indirect ioctls returning data * privsep: wait for processes on SIGCHLD rather than when sent a STOP cmd * eloop: just use ppoll/pollts(2), falling back to pselect(2) --- contrib/dhcpcd/src/arp.c | 4 +- contrib/dhcpcd/src/auth.c | 74 ++++-- contrib/dhcpcd/src/auth.h | 6 +- contrib/dhcpcd/src/defs.h | 2 +- contrib/dhcpcd/src/dhcp.c | 16 +- contrib/dhcpcd/src/dhcp6.c | 37 ++- contrib/dhcpcd/src/dhcpcd.c | 24 +- contrib/dhcpcd/src/eloop.c | 547 ++++++-------------------------------- contrib/dhcpcd/src/eloop.h | 4 +- contrib/dhcpcd/src/if-bsd.c | 4 +- contrib/dhcpcd/src/privsep-bpf.c | 2 +- contrib/dhcpcd/src/privsep-bsd.c | 54 ++-- contrib/dhcpcd/src/privsep-inet.c | 4 +- contrib/dhcpcd/src/privsep-root.c | 110 ++++++-- contrib/dhcpcd/src/privsep-root.h | 3 +- contrib/dhcpcd/src/privsep.c | 123 +++++---- contrib/dhcpcd/src/privsep.h | 1 + 17 files changed, 389 insertions(+), 626 deletions(-) diff --git a/contrib/dhcpcd/src/arp.c b/contrib/dhcpcd/src/arp.c index db1e188c3c..fef12425ea 100644 --- a/contrib/dhcpcd/src/arp.c +++ b/contrib/dhcpcd/src/arp.c @@ -466,11 +466,13 @@ arp_announce(struct arp_state *astate) a2); if (r == -1) logerr(__func__); - else if (r != 0) + else if (r != 0) { logdebugx("%s: ARP announcement " "of %s cancelled", a2->iface->name, inet_ntoa(a2->addr)); + arp_announced(a2); + } } } diff --git a/contrib/dhcpcd/src/auth.c b/contrib/dhcpcd/src/auth.c index 29c00a9c47..b7891b6877 100644 --- a/contrib/dhcpcd/src/auth.c +++ b/contrib/dhcpcd/src/auth.c @@ -27,6 +27,8 @@ */ #include +#include + #include #include #include @@ -42,6 +44,7 @@ #include "dhcp.h" #include "dhcp6.h" #include "dhcpcd.h" +#include "privsep-root.h" #ifdef HAVE_HMAC_H #include @@ -408,11 +411,11 @@ finish: return t; } -static uint64_t -get_next_rdm_monotonic_counter(struct auth *auth) +int +auth_get_rdm_monotonic(uint64_t *rdm) { FILE *fp; - uint64_t rdm; + int err; #ifdef LOCK_EX int flocked; #endif @@ -420,41 +423,43 @@ get_next_rdm_monotonic_counter(struct auth *auth) fp = fopen(RDM_MONOFILE, "r+"); if (fp == NULL) { if (errno != ENOENT) - return ++auth->last_replay; /* report error? */ + return -1; fp = fopen(RDM_MONOFILE, "w"); if (fp == NULL) - return ++auth->last_replay; /* report error? */ + return -1; + if (chmod(RDM_MONOFILE, 0400) == -1) { + fclose(fp); + unlink(RDM_MONOFILE); + return -1; + } #ifdef LOCK_EX flocked = flock(fileno(fp), LOCK_EX); #endif - rdm = 0; + *rdm = 0; } else { #ifdef LOCK_EX flocked = flock(fileno(fp), LOCK_EX); #endif - if (fscanf(fp, "0x%016" PRIu64, &rdm) != 1) - rdm = 0; /* truncated? report error? */ + if (fscanf(fp, "0x%016" PRIu64, rdm) != 1) { + fclose(fp); + return -1; + } } - rdm++; + (*rdm)++; if (fseek(fp, 0, SEEK_SET) == -1 || ftruncate(fileno(fp), 0) == -1 || - fprintf(fp, "0x%016" PRIu64 "\n", rdm) != 19 || + fprintf(fp, "0x%016" PRIu64 "\n", *rdm) != 19 || fflush(fp) == EOF) - { - if (!auth->last_replay_set) { - auth->last_replay = rdm; - auth->last_replay_set = 1; - } else - rdm = ++auth->last_replay; - /* report error? */ - } + err = -1; + else + err = 0; #ifdef LOCK_EX if (flocked == 0) flock(fileno(fp), LOCK_UN); #endif fclose(fp); - return rdm; + return err; } #define NTP_EPOCH 2208988800U /* 1970 - 1900 in seconds */ @@ -476,11 +481,29 @@ get_next_rdm_monotonic_clock(struct auth *auth) } static uint64_t -get_next_rdm_monotonic(struct auth *auth) +get_next_rdm_monotonic(struct dhcpcd_ctx *ctx, struct auth *auth) { +#ifndef PRIVSEP + UNUSED(ctx); +#endif + + if (auth->options & DHCPCD_AUTH_RDM_COUNTER) { + uint64_t rdm; + int err; - if (auth->options & DHCPCD_AUTH_RDM_COUNTER) - return get_next_rdm_monotonic_counter(auth); +#ifdef PRIVSEP + if (IN_PRIVSEP(ctx)) { + + err = ps_root_getauthrdm(ctx, &rdm); + } else +#endif + err = auth_get_rdm_monotonic(&rdm); + if (err == -1) + return ++auth->last_replay; + + auth->last_replay = rdm; + return rdm; + } return get_next_rdm_monotonic_clock(auth); } @@ -495,7 +518,8 @@ get_next_rdm_monotonic(struct auth *auth) * data and dlen refer to the authentication option within the message. */ ssize_t -dhcp_auth_encode(struct auth *auth, const struct token *t, +dhcp_auth_encode(struct dhcpcd_ctx *ctx, struct auth *auth, + const struct token *t, void *vm, size_t mlen, int mp, int mt, void *vdata, size_t dlen) { @@ -611,11 +635,11 @@ dhcp_auth_encode(struct auth *auth, const struct token *t, *data++ = auth->rdm; switch (auth->rdm) { case AUTH_RDM_MONOTONIC: - rdm = get_next_rdm_monotonic(auth); + rdm = get_next_rdm_monotonic(ctx, auth); break; default: /* This block appeases gcc, clang doesn't need it */ - rdm = get_next_rdm_monotonic(auth); + rdm = get_next_rdm_monotonic(ctx, auth); break; } rdm = htonll(rdm); diff --git a/contrib/dhcpcd/src/auth.h b/contrib/dhcpcd/src/auth.h index aa668c2628..30cc38b33a 100644 --- a/contrib/dhcpcd/src/auth.h +++ b/contrib/dhcpcd/src/auth.h @@ -90,7 +90,11 @@ const struct token * dhcp_auth_validate(struct authstate *, const void *, size_t, int, int, const void *, size_t); -ssize_t dhcp_auth_encode(struct auth *, const struct token *, +struct dhcpcd_ctx; +ssize_t dhcp_auth_encode(struct dhcpcd_ctx *, struct auth *, + const struct token *, void *, size_t, int, int, void *, size_t); + +int auth_get_rdm_monotonic(uint64_t *rdm); #endif diff --git a/contrib/dhcpcd/src/defs.h b/contrib/dhcpcd/src/defs.h index 389defb7b1..286e5a23a9 100644 --- a/contrib/dhcpcd/src/defs.h +++ b/contrib/dhcpcd/src/defs.h @@ -29,7 +29,7 @@ #define CONFIG_H #define PACKAGE "dhcpcd" -#define VERSION "9.1.0" +#define VERSION "9.1.1" #ifndef PRIVSEP_USER # define PRIVSEP_USER "_" PACKAGE diff --git a/contrib/dhcpcd/src/dhcp.c b/contrib/dhcpcd/src/dhcp.c index 4970cc7ada..b309b01c4c 100644 --- a/contrib/dhcpcd/src/dhcp.c +++ b/contrib/dhcpcd/src/dhcp.c @@ -1034,7 +1034,7 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) auth = NULL; /* appease GCC */ auth_len = 0; if (ifo->auth.options & DHCPCD_AUTH_SEND) { - ssize_t alen = dhcp_auth_encode(&ifo->auth, + ssize_t alen = dhcp_auth_encode(ifp->ctx, &ifo->auth, state->auth.token, NULL, 0, 4, type, NULL, 0); if (alen != -1 && alen > UINT8_MAX) { @@ -1129,7 +1129,7 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type) #ifdef AUTH if (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len != 0) - dhcp_auth_encode(&ifo->auth, state->auth.token, + dhcp_auth_encode(ifp->ctx, &ifo->auth, state->auth.token, (uint8_t *)bootp, len, 4, type, auth, auth_len); #endif @@ -2747,6 +2747,18 @@ dhcp_drop(struct interface *ifp, const char *reason) #endif } } +#ifdef AUTH + else if (state->auth.reconf != NULL) { + /* + * Drop the lease as the token may only be present + * in the initial reply message and not subsequent + * renewals. + * If dhcpcd is restarted, the token is lost. + * XXX persist this in another file? + */ + dhcp_unlink(ifp->ctx, state->leasefile); + } +#endif eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); #ifdef AUTH diff --git a/contrib/dhcpcd/src/dhcp6.c b/contrib/dhcpcd/src/dhcp6.c index e6e12e301a..0e7743e366 100644 --- a/contrib/dhcpcd/src/dhcp6.c +++ b/contrib/dhcpcd/src/dhcp6.c @@ -881,7 +881,7 @@ dhcp6_makemessage(struct interface *ifp) #ifdef AUTH auth_len = 0; if (ifo->auth.options & DHCPCD_AUTH_SEND) { - ssize_t alen = dhcp_auth_encode(&ifo->auth, + ssize_t alen = dhcp_auth_encode(ifp->ctx, &ifo->auth, state->auth.token, NULL, 0, 6, type, NULL, 0); if (alen != -1 && alen > UINT16_MAX) { errno = ERANGE; @@ -1196,9 +1196,9 @@ dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len) return -1; state = D6_STATE(ifp); - return dhcp_auth_encode(&ifp->options->auth, state->auth.token, - (uint8_t *)state->send, state->send_len, - 6, state->send->type, opt, opt_len); + return dhcp_auth_encode(ifp->ctx, &ifp->options->auth, + state->auth.token, (uint8_t *)state->send, state->send_len, 6, + state->send->type, opt, opt_len); } #endif @@ -3319,7 +3319,7 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom, loginfox("%s: accepted reconfigure key", ifp->name); } else if (ifo->auth.options & DHCPCD_AUTH_SEND) { if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) { - logerr("%s: no authentication from %s", + logerrx("%s: no authentication from %s", ifp->name, sfrom); return; } @@ -3595,15 +3595,12 @@ dhcp6_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, struct ipv6_addr *ia) } if (r->type == DHCP6_RECONFIGURE) { - logdebugx("%s: RECONFIGURE6 recv from %s," - " sending to all interfaces", - ifp->name, sfrom); - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - state = D6_CSTATE(ifp); - if (state != NULL && state->send != NULL) - dhcp6_recvif(ifp, sfrom, r, len); + if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) { + logerrx("%s: RECONFIGURE6 recv from %s, not LL", + ifp->name, sfrom); + return; } - return; + goto recvif; } state = D6_CSTATE(ifp); @@ -3679,6 +3676,7 @@ dhcp6_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, struct ipv6_addr *ia) len = (size_t)tlen; #endif +recvif: dhcp6_recvif(ifp, sfrom, r, len); } @@ -4041,6 +4039,19 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason) } dhcp_unlink(ifp->ctx, state->leasefile); } +#ifdef AUTH + else if (state->auth.reconf != NULL) { + /* + * Drop the lease as the token may only be present + * in the initial reply message and not subsequent + * renewals. + * If dhcpcd is restarted, the token is lost. + * XXX persist this in another file? + */ + dhcp_unlink(ifp->ctx, state->leasefile); + } +#endif + dhcp6_freedrop_addrs(ifp, drop, NULL); free(state->old); state->old = state->new; diff --git a/contrib/dhcpcd/src/dhcpcd.c b/contrib/dhcpcd/src/dhcpcd.c index a5fd741231..1363b3c7f3 100644 --- a/contrib/dhcpcd/src/dhcpcd.c +++ b/contrib/dhcpcd/src/dhcpcd.c @@ -87,6 +87,7 @@ const int dhcpcd_signals[] = { SIGHUP, SIGUSR1, SIGUSR2, + SIGCHLD, }; const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals); @@ -1341,6 +1342,9 @@ stop_all_interfaces(struct dhcpcd_ctx *ctx, unsigned long long opts) struct interface *ifp; ctx->options |= DHCPCD_EXITING; + if (ctx->ifaces == NULL) + return; + /* Drop the last interface first */ TAILQ_FOREACH_REVERSE(ifp, ctx->ifaces, if_head, next) { if (!ifp->active) @@ -1396,7 +1400,7 @@ dhcpcd_signal_cb(int sig, void *arg) unsigned long long opts; int exit_code; - if (ctx->options & DHCPCD_FORKED) { + if (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) { pid_t pid = pidfile_read(ctx->pidfile); if (pid == -1) { if (errno != ENOENT) @@ -1442,6 +1446,10 @@ dhcpcd_signal_cb(int sig, void *arg) if (logopen(ctx->logfile) == -1) logerr(__func__); return; + case SIGCHLD: + while (waitpid(-1, NULL, WNOHANG) > 0) + ; + return; default: logerrx("received signal %d but don't know what to do with it", sig); @@ -2036,13 +2044,9 @@ printpidfile: signal(dhcpcd_signals_ignore[si], SIG_IGN); /* Save signal mask, block and redirect signals to our handler */ - if (eloop_signal_set_cb(ctx.eloop, + eloop_signal_set_cb(ctx.eloop, dhcpcd_signals, dhcpcd_signals_len, - dhcpcd_signal_cb, &ctx) == -1) - { - logerr("%s: eloop_signal_set_cb", __func__); - goto exit_failure; - } + dhcpcd_signal_cb, &ctx); if (eloop_signal_mask(ctx.eloop, &ctx.sigset) == -1) { logerr("%s: eloop_signal_mask", __func__); goto exit_failure; @@ -2102,7 +2106,7 @@ printpidfile: break; #else logerrx("No DHCP support"); - goto exit_failure + goto exit_failure; #endif case AF_INET6: #ifdef DHCP6 @@ -2111,7 +2115,7 @@ printpidfile: break; #else logerrx("No DHCP6 support"); - goto exit_failure + goto exit_failure; #endif default: logerrx("Family not specified. Please use -4 or -6."); @@ -2208,7 +2212,6 @@ printpidfile: logerr("fork"); goto exit_failure; case 0: - eloop_requeue(ctx.eloop); break; default: ctx.options |= DHCPCD_FORKED; /* A lie */ @@ -2217,7 +2220,6 @@ printpidfile: } break; default: - waitpid(pid, &i, 0); ctx.options |= DHCPCD_FORKED; /* A lie */ ctx.fork_fd = sigpipe[0]; close(sigpipe[1]); diff --git a/contrib/dhcpcd/src/eloop.c b/contrib/dhcpcd/src/eloop.c index dfa794966e..39c43d9d8b 100644 --- a/contrib/dhcpcd/src/eloop.c +++ b/contrib/dhcpcd/src/eloop.c @@ -26,60 +26,30 @@ * SUCH DAMAGE. */ -#if (defined(__unix__) || defined(unix)) && !defined(USG) -#include -#endif #include #include #include #include +#include #include #include #include #include #include -/* config.h should define HAVE_KQUEUE, HAVE_EPOLL, etc. */ +/* config.h should define HAVE_PPOLL, etc. */ #if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_H) #include "config.h" #endif -/* Attempt to autodetect kqueue or epoll. - * Failing that, fall back to pselect. */ -#if !defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL) && !defined(HAVE_PSELECT) && \ - !defined(HAVE_POLLTS) && !defined(HAVE_PPOLL) -#if defined(BSD) -/* Assume BSD has a working sys/queue.h and kqueue(2) interface. */ -#define HAVE_SYS_QUEUE_H -#define HAVE_KQUEUE -#define WARN_SELECT -#elif defined(__linux__) || defined(__sun) -/* Assume Linux and Solaris have a working epoll(3) interface. */ -#define HAVE_EPOLL -#define WARN_SELECT -#else -/* pselect(2) is a POSIX standard. */ +#if defined(HAVE_PPOLL) +#elif defined(HAVE_POLLTS) +#define ppoll pollts +#elif !defined(HAVE_PSELECT) +#pragma message("Compiling eloop with pselect(2) support.") #define HAVE_PSELECT -#define WARN_SELECT -#endif -#endif - -/* pollts and ppoll require poll. - * pselect is wrapped in a pollts/ppoll style interface - * and as such require poll as well. */ -#if defined(HAVE_PSELECT) || defined(HAVE_POLLTS) || defined(HAVE_PPOLL) -#ifndef HAVE_POLL -#define HAVE_POLL -#endif -#if defined(HAVE_POLLTS) -#define POLLTS pollts -#elif defined(HAVE_PPOLL) -#define POLLTS ppoll -#else -#define POLLTS eloop_pollts -#define ELOOP_NEED_POLLTS -#endif +#define ppoll eloop_ppoll #endif #include "eloop.h" @@ -95,42 +65,9 @@ #endif #endif -#if defined(HAVE_KQUEUE) -#include -#include -#ifdef __NetBSD__ -/* udata is void * except on NetBSD. - * lengths are int except on NetBSD. */ -#define UPTR(x) ((intptr_t)(x)) -#define LENC(x) (x) -#else -#define UPTR(x) (x) -#define LENC(x) ((int)(x)) -#endif -#elif defined(HAVE_EPOLL) -#include -#elif defined(HAVE_POLL) -#if defined(HAVE_PSELECT) +#ifdef HAVE_PSELECT #include #endif -#include -#endif - -#ifdef WARN_SELECT -#if defined(HAVE_KQUEUE) -#pragma message("Compiling eloop with kqueue(2) support.") -#elif defined(HAVE_EPOLL) -#pragma message("Compiling eloop with epoll(7) support.") -#elif defined(HAVE_PSELECT) -#pragma message("Compiling eloop with pselect(2) support.") -#elif defined(HAVE_PPOLL) -#pragma message("Compiling eloop with ppoll(2) support.") -#elif defined(HAVE_POLLTS) -#pragma message("Compiling eloop with pollts(2) support.") -#else -#error Unknown select mechanism for eloop -#endif -#endif /* Our structures require TAILQ macros, which really every libc should * ship as they are useful beyond belief. @@ -171,6 +108,7 @@ struct eloop_event { void *read_cb_arg; void (*write_cb)(void *); void *write_cb_arg; + struct pollfd *pollfd; }; struct eloop_timeout { @@ -183,11 +121,9 @@ struct eloop_timeout { }; struct eloop { - size_t events_len; TAILQ_HEAD (event_head, eloop_event) events; + size_t nevents; struct event_head free_events; - int events_maxfd; - struct eloop_event **event_fds; struct timespec now; TAILQ_HEAD (timeout_head, eloop_timeout) timeouts; @@ -200,12 +136,8 @@ struct eloop { void (*signal_cb)(int, void *); void *signal_cb_ctx; -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) - int poll_fd; -#elif defined(HAVE_POLL) struct pollfd *fds; - size_t fds_len; -#endif + size_t nfds; int exitnow; int exitcode; @@ -229,30 +161,10 @@ eloop_realloca(void *ptr, size_t n, size_t size) } #endif -#ifdef HAVE_POLL -static void -eloop_event_setup_fds(struct eloop *eloop) -{ - struct eloop_event *e; - size_t i; - - i = 0; - TAILQ_FOREACH(e, &eloop->events, next) { - eloop->fds[i].fd = e->fd; - eloop->fds[i].events = 0; - if (e->read_cb) - eloop->fds[i].events |= POLLIN; - if (e->write_cb) - eloop->fds[i].events |= POLLOUT; - eloop->fds[i].revents = 0; - i++; - } -} - -#ifdef ELOOP_NEED_POLLTS -/* Wrapper around pselect, to imitate the NetBSD pollts call. */ +#ifdef HAVE_PSELECT +/* Wrapper around pselect, to imitate the ppoll call. */ static int -eloop_pollts(struct pollfd * fds, nfds_t nfds, +eloop_ppoll(struct pollfd * fds, nfds_t nfds, const struct timespec *ts, const sigset_t *sigmask) { fd_set read_fds, write_fds; @@ -287,10 +199,7 @@ eloop_pollts(struct pollfd * fds, nfds_t nfds, return r; } -#endif /* pollts */ -#else /* !HAVE_POLL */ -#define eloop_event_setup_fds(a) {} -#endif /* HAVE_POLL */ +#endif unsigned long long eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp, @@ -360,19 +269,33 @@ eloop_reduce_timers(struct eloop *eloop) eloop->now = now; } +static void +eloop_event_setup_fds(struct eloop *eloop) +{ + struct eloop_event *e; + struct pollfd *pfd; + + pfd = eloop->fds; + TAILQ_FOREACH(e, &eloop->events, next) { + e->pollfd = pfd; + pfd->fd = e->fd; + pfd->events = 0; + if (e->read_cb != NULL) + pfd->events |= POLLIN; + if (e->write_cb != NULL) + pfd->events |= POLLOUT; + pfd->revents = 0; + pfd++; + } +} + int eloop_event_add_rw(struct eloop *eloop, int fd, void (*read_cb)(void *), void *read_cb_arg, void (*write_cb)(void *), void *write_cb_arg) { struct eloop_event *e; -#if defined(HAVE_KQUEUE) - struct kevent ke[2]; -#elif defined(HAVE_EPOLL) - struct epoll_event epe; -#elif defined(HAVE_POLL) - struct pollfd *nfds; -#endif + struct pollfd *pfd; assert(eloop != NULL); assert(read_cb != NULL || write_cb != NULL); @@ -381,122 +304,41 @@ eloop_event_add_rw(struct eloop *eloop, int fd, return -1; } -#ifdef HAVE_EPOLL - memset(&epe, 0, sizeof(epe)); - epe.data.fd = fd; - epe.events = EPOLLIN; - if (write_cb) - epe.events |= EPOLLOUT; -#endif - - /* We should only have one callback monitoring the fd. */ - if (fd <= eloop->events_maxfd) { - if ((e = eloop->event_fds[fd]) != NULL) { - int error; - -#if defined(HAVE_KQUEUE) - EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ, EV_ADD, - 0, 0, UPTR(e)); - if (write_cb) - EV_SET(&ke[1], (uintptr_t)fd, EVFILT_WRITE, - EV_ADD, 0, 0, UPTR(e)); - else if (e->write_cb) - EV_SET(&ke[1], (uintptr_t)fd, EVFILT_WRITE, - EV_DELETE, 0, 0, UPTR(e)); - error = kevent(eloop->poll_fd, ke, - e->write_cb || write_cb ? 2 : 1, NULL, 0, NULL); -#elif defined(HAVE_EPOLL) - epe.data.ptr = e; - error = epoll_ctl(eloop->poll_fd, EPOLL_CTL_MOD, - fd, &epe); -#else - error = 0; -#endif - if (read_cb) { - e->read_cb = read_cb; - e->read_cb_arg = read_cb_arg; - } - if (write_cb) { - e->write_cb = write_cb; - e->write_cb_arg = write_cb_arg; - } - eloop_event_setup_fds(eloop); - return error; - } - } else { - struct eloop_event **new_fds; - int maxfd, i; - - /* Reserve ourself and 4 more. */ - maxfd = fd + 4; - new_fds = eloop_realloca(eloop->event_fds, - ((size_t)maxfd + 1), sizeof(*eloop->event_fds)); - if (new_fds == NULL) - return -1; - - /* set new entries NULL as the fd's may not be contiguous. */ - for (i = maxfd; i > eloop->events_maxfd; i--) - new_fds[i] = NULL; - - eloop->event_fds = new_fds; - eloop->events_maxfd = maxfd; + TAILQ_FOREACH(e, &eloop->events, next) { + if (e->fd == fd) + break; } - /* Allocate a new event if no free ones already allocated. */ - if ((e = TAILQ_FIRST(&eloop->free_events))) { - TAILQ_REMOVE(&eloop->free_events, e, next); - } else { - e = malloc(sizeof(*e)); - if (e == NULL) - goto err; - } + if (e == NULL) { + if (eloop->nevents + 1 > eloop->nfds) { + pfd = eloop_realloca(eloop->fds, eloop->nevents + 1, + sizeof(*pfd)); + if (pfd == NULL) + return -1; + eloop->fds = pfd; + eloop->nfds++; + } - /* Ensure we can actually listen to it. */ - eloop->events_len++; -#ifdef HAVE_POLL - if (eloop->events_len > eloop->fds_len) { - nfds = eloop_realloca(eloop->fds, - (eloop->fds_len + 5), sizeof(*eloop->fds)); - if (nfds == NULL) - goto err; - eloop->fds_len += 5; - eloop->fds = nfds; + e = TAILQ_FIRST(&eloop->free_events); + if (e != NULL) + TAILQ_REMOVE(&eloop->free_events, e, next); + else { + e = malloc(sizeof(*e)); + if (e == NULL) + return -1; + TAILQ_INSERT_HEAD(&eloop->events, e, next); + } + e->fd = fd; + eloop->nevents++; } -#endif - /* Now populate the structure and add it to the list. */ - e->fd = fd; e->read_cb = read_cb; e->read_cb_arg = read_cb_arg; e->write_cb = write_cb; e->write_cb_arg = write_cb_arg; -#if defined(HAVE_KQUEUE) - if (read_cb != NULL) - EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ, - EV_ADD, 0, 0, UPTR(e)); - if (write_cb != NULL) - EV_SET(&ke[1], (uintptr_t)fd, EVFILT_WRITE, - EV_ADD, 0, 0, UPTR(e)); - if (kevent(eloop->poll_fd, ke, write_cb ? 2 : 1, NULL, 0, NULL) == -1) - goto err; -#elif defined(HAVE_EPOLL) - epe.data.ptr = e; - if (epoll_ctl(eloop->poll_fd, EPOLL_CTL_ADD, fd, &epe) == -1) - goto err; -#endif - - TAILQ_INSERT_HEAD(&eloop->events, e, next); - eloop->event_fds[e->fd] = e; eloop_event_setup_fds(eloop); return 0; - -err: - if (e) { - eloop->events_len--; - TAILQ_INSERT_TAIL(&eloop->free_events, e, next); - } - return -1; } int @@ -519,17 +361,14 @@ int eloop_event_delete_write(struct eloop *eloop, int fd, int write_only) { struct eloop_event *e; -#if defined(HAVE_KQUEUE) - struct kevent ke[2]; -#elif defined(HAVE_EPOLL) - struct epoll_event epe; -#endif assert(eloop != NULL); - if (fd > eloop->events_maxfd || - (e = eloop->event_fds[fd]) == NULL) - { + TAILQ_FOREACH(e, &eloop->events, next) { + if (e->fd == fd) + break; + } + if (e == NULL) { errno = ENOENT; return -1; } @@ -541,41 +380,15 @@ eloop_event_delete_write(struct eloop *eloop, int fd, int write_only) goto remove; e->write_cb = NULL; e->write_cb_arg = NULL; -#if defined(HAVE_KQUEUE) - EV_SET(&ke[0], (uintptr_t)e->fd, - EVFILT_WRITE, EV_DELETE, 0, 0, UPTR(NULL)); - kevent(eloop->poll_fd, ke, 1, NULL, 0, NULL); -#elif defined(HAVE_EPOLL) - memset(&epe, 0, sizeof(epe)); - epe.data.fd = e->fd; - epe.data.ptr = e; - epe.events = EPOLLIN; - epoll_ctl(eloop->poll_fd, EPOLL_CTL_MOD, fd, &epe); -#endif - eloop_event_setup_fds(eloop); - return 1; + goto done; } remove: TAILQ_REMOVE(&eloop->events, e, next); - eloop->event_fds[e->fd] = NULL; TAILQ_INSERT_TAIL(&eloop->free_events, e, next); - eloop->events_len--; - -#if defined(HAVE_KQUEUE) - EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ, - EV_DELETE, 0, 0, UPTR(NULL)); - if (e->write_cb) - EV_SET(&ke[1], (uintptr_t)fd, - EVFILT_WRITE, EV_DELETE, 0, 0, UPTR(NULL)); - kevent(eloop->poll_fd, ke, e->write_cb ? 2 : 1, NULL, 0, NULL); -#elif defined(HAVE_EPOLL) - /* NULL event is safe because we - * rely on epoll_pwait which as added - * after the delete without event was fixed. */ - epoll_ctl(eloop->poll_fd, EPOLL_CTL_DEL, fd, NULL); -#endif + eloop->nevents--; +done: eloop_event_setup_fds(eloop); return 1; } @@ -681,7 +494,6 @@ eloop_q_timeout_add_msec(struct eloop *eloop, int queue, unsigned long when, (unsigned int)seconds, (unsigned int)nseconds, callback, arg); } -#if !defined(HAVE_KQUEUE) static int eloop_timeout_add_now(struct eloop *eloop, void (*callback)(void *), void *arg) @@ -692,7 +504,6 @@ eloop_timeout_add_now(struct eloop *eloop, eloop->timeout0_arg = arg; return 0; } -#endif int eloop_q_timeout_delete(struct eloop *eloop, int queue, @@ -727,106 +538,14 @@ eloop_exit(struct eloop *eloop, int code) eloop->exitnow = 1; } -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) -static int -eloop_open(struct eloop *eloop) -{ - -#if defined(HAVE_KQUEUE1) - return (eloop->poll_fd = kqueue1(O_CLOEXEC)); -#elif defined(HAVE_KQUEUE) - int i; - - if ((eloop->poll_fd = kqueue()) == -1) - return -1; - if ((i = fcntl(eloop->poll_fd, F_GETFD, 0)) == -1 || - fcntl(eloop->poll_fd, F_SETFD, i | FD_CLOEXEC) == -1) - { - close(eloop->poll_fd); - eloop->poll_fd = -1; - } - - return eloop->poll_fd; -#elif defined (HAVE_EPOLL) - return (eloop->poll_fd = epoll_create1(EPOLL_CLOEXEC)); -#else - return (eloop->poll_fd = -1); -#endif -} -#endif - -int -eloop_requeue(struct eloop *eloop) +void +eloop_enter(struct eloop *eloop) { -#if defined(HAVE_POLL) - - UNUSED(eloop); - return 0; -#else /* !HAVE_POLL */ - struct eloop_event *e; - int error; -#if defined(HAVE_KQUEUE) - size_t i; - struct kevent *ke; -#elif defined(HAVE_EPOLL) - struct epoll_event epe; -#endif - - assert(eloop != NULL); - - if (eloop->poll_fd != -1) - close(eloop->poll_fd); - if (eloop_open(eloop) == -1) - return -1; -#if defined (HAVE_KQUEUE) - i = eloop->signals_len; - TAILQ_FOREACH(e, &eloop->events, next) { - i++; - if (e->write_cb) - i++; - } - - if ((ke = malloc(sizeof(*ke) * i)) == NULL) - return -1; - - for (i = 0; i < eloop->signals_len; i++) - EV_SET(&ke[i], (uintptr_t)eloop->signals[i], - EVFILT_SIGNAL, EV_ADD, 0, 0, UPTR(NULL)); - - TAILQ_FOREACH(e, &eloop->events, next) { - EV_SET(&ke[i], (uintptr_t)e->fd, EVFILT_READ, - EV_ADD, 0, 0, UPTR(e)); - i++; - if (e->write_cb) { - EV_SET(&ke[i], (uintptr_t)e->fd, EVFILT_WRITE, - EV_ADD, 0, 0, UPTR(e)); - i++; - } - } - - error = kevent(eloop->poll_fd, ke, LENC(i), NULL, 0, NULL); - free(ke); - -#elif defined(HAVE_EPOLL) - error = 0; - TAILQ_FOREACH(e, &eloop->events, next) { - memset(&epe, 0, sizeof(epe)); - epe.data.fd = e->fd; - epe.events = EPOLLIN; - if (e->write_cb) - epe.events |= EPOLLOUT; - epe.data.ptr = e; - if (epoll_ctl(eloop->poll_fd, EPOLL_CTL_ADD, e->fd, &epe) == -1) - error = -1; - } -#endif - - return error; -#endif /* HAVE_POLL */ + eloop->exitnow = 0; } -int +void eloop_signal_set_cb(struct eloop *eloop, const int *signals, size_t signals_len, void (*signal_cb)(int, void *), void *signal_cb_ctx) @@ -838,10 +557,8 @@ eloop_signal_set_cb(struct eloop *eloop, eloop->signals_len = signals_len; eloop->signal_cb = signal_cb; eloop->signal_cb_ctx = signal_cb_ctx; - return eloop_requeue(eloop); } -#ifndef HAVE_KQUEUE struct eloop_siginfo { int sig; struct eloop *eloop; @@ -869,16 +586,16 @@ eloop_signal3(int sig, __unused siginfo_t *siginfo, __unused void *arg) eloop_timeout_add_now(_eloop_siginfo.eloop, eloop_signal1, &_eloop_siginfo); } -#endif int eloop_signal_mask(struct eloop *eloop, sigset_t *oldset) { sigset_t newset; size_t i; -#ifndef HAVE_KQUEUE - struct sigaction sa; -#endif + struct sigaction sa = { + .sa_sigaction = eloop_signal3, + .sa_flags = SA_SIGINFO, + }; assert(eloop != NULL); @@ -888,19 +605,13 @@ eloop_signal_mask(struct eloop *eloop, sigset_t *oldset) if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1) return -1; -#ifndef HAVE_KQUEUE _eloop = eloop; - - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = eloop_signal3; - sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); for (i = 0; i < eloop->signals_len; i++) { if (sigaction(eloop->signals[i], &sa, NULL) == -1) return -1; } -#endif return 0; } @@ -920,19 +631,11 @@ eloop_new(void) } TAILQ_INIT(&eloop->events); - eloop->events_maxfd = -1; TAILQ_INIT(&eloop->free_events); TAILQ_INIT(&eloop->timeouts); TAILQ_INIT(&eloop->free_timeouts); eloop->exitcode = EXIT_FAILURE; -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) - if (eloop_open(eloop) == -1) { - eloop_free(eloop); - return NULL; - } -#endif - return eloop; } @@ -945,10 +648,7 @@ eloop_clear(struct eloop *eloop) if (eloop == NULL) return; - free(eloop->event_fds); - eloop->event_fds = NULL; - eloop->events_len = 0; - eloop->events_maxfd = -1; + eloop->nevents = 0; eloop->signals = NULL; eloop->signals_len = 0; @@ -969,21 +669,15 @@ eloop_clear(struct eloop *eloop) free(t); } -#if defined(HAVE_POLL) free(eloop->fds); eloop->fds = NULL; - eloop->fds_len = 0; -#endif + eloop->nfds = 0; } void eloop_free(struct eloop *eloop) { -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) - if (eloop != NULL) - close(eloop->poll_fd); -#endif eloop_clear(eloop); free(eloop); } @@ -995,22 +689,10 @@ eloop_start(struct eloop *eloop, sigset_t *signals) struct eloop_event *e; struct eloop_timeout *t; void (*t0)(void *); -#if defined(HAVE_KQUEUE) - struct kevent ke; - UNUSED(signals); -#elif defined(HAVE_EPOLL) - struct epoll_event epe; -#endif -#if defined(HAVE_KQUEUE) || defined(HAVE_POLL) struct timespec ts, *tsp; -#endif -#ifndef HAVE_KQUEUE - int timeout; -#endif assert(eloop != NULL); - eloop->exitnow = 0; for (;;) { if (eloop->exitnow) break; @@ -1024,7 +706,7 @@ eloop_start(struct eloop *eloop, sigset_t *signals) } t = TAILQ_FIRST(&eloop->timeouts); - if (t == NULL && eloop->events_len == 0) + if (t == NULL && eloop->nevents == 0) break; if (t != NULL) @@ -1038,7 +720,6 @@ eloop_start(struct eloop *eloop, sigset_t *signals) } if (t != NULL) { -#if defined(HAVE_KQUEUE) || defined(HAVE_POLL) if (t->seconds > INT_MAX) { ts.tv_sec = (time_t)INT_MAX; ts.tv_nsec = 0; @@ -1047,43 +728,10 @@ eloop_start(struct eloop *eloop, sigset_t *signals) ts.tv_nsec = (long)t->nseconds; } tsp = &ts; -#endif - -#ifndef HAVE_KQUEUE - if (t->seconds > INT_MAX / 1000 || - (t->seconds == INT_MAX / 1000 && - ((t->nseconds + 999999) / 1000000 - > INT_MAX % 1000000))) - timeout = INT_MAX; - else - timeout = (int)(t->seconds * 1000 + - (t->nseconds + 999999) / 1000000); -#endif - } else { -#if defined(HAVE_KQUEUE) || defined(HAVE_POLL) + } else tsp = NULL; -#endif -#ifndef HAVE_KQUEUE - timeout = -1; -#endif - } -#if defined(HAVE_KQUEUE) - n = kevent(eloop->poll_fd, NULL, 0, &ke, 1, tsp); -#elif defined(HAVE_EPOLL) - if (signals) - n = epoll_pwait(eloop->poll_fd, &epe, 1, - timeout, signals); - else - n = epoll_wait(eloop->poll_fd, &epe, 1, timeout); -#elif defined(HAVE_POLL) - if (signals) - n = POLLTS(eloop->fds, (nfds_t)eloop->events_len, - tsp, signals); - else - n = poll(eloop->fds, (nfds_t)eloop->events_len, - timeout); -#endif + n = ppoll(eloop->fds, (nfds_t)eloop->nevents, tsp, signals); if (n == -1) { if (errno == EINTR) continue; @@ -1092,47 +740,20 @@ eloop_start(struct eloop *eloop, sigset_t *signals) if (n == 0) continue; - /* Process any triggered events. - * We go back to the start after calling each callback incase - * the current event or next event is removed. */ -#if defined(HAVE_KQUEUE) - if (ke.filter == EVFILT_SIGNAL) { - eloop->signal_cb((int)ke.ident, - eloop->signal_cb_ctx); - } else { - e = (struct eloop_event *)ke.udata; - if (ke.filter == EVFILT_WRITE && e->write_cb != NULL) - e->write_cb(e->write_cb_arg); - else if (ke.filter == EVFILT_READ && e->read_cb != NULL) - e->read_cb(e->read_cb_arg); - } -#elif defined(HAVE_EPOLL) - e = (struct eloop_event *)epe.data.ptr; - if (epe.events & EPOLLOUT && e->write_cb != NULL) - e->write_cb(e->write_cb_arg); - else if (epe.events & (EPOLLIN | EPOLLERR | EPOLLHUP) && - e->read_cb != NULL) - e->read_cb(e->read_cb_arg); -#elif defined(HAVE_POLL) - size_t i; - - for (i = 0; i < eloop->events_len; i++) { - if (eloop->fds[i].revents & POLLOUT) { - e = eloop->event_fds[eloop->fds[i].fd]; + TAILQ_FOREACH(e, &eloop->events, next) { + if (e->pollfd->revents & POLLOUT) { if (e->write_cb != NULL) { e->write_cb(e->write_cb_arg); break; } } - if (eloop->fds[i].revents) { - e = eloop->event_fds[eloop->fds[i].fd]; + if (e->pollfd->revents) { if (e->read_cb != NULL) { e->read_cb(e->read_cb_arg); break; } } } -#endif } return eloop->exitcode; diff --git a/contrib/dhcpcd/src/eloop.h b/contrib/dhcpcd/src/eloop.h index e5d881a167..9001c1b784 100644 --- a/contrib/dhcpcd/src/eloop.h +++ b/contrib/dhcpcd/src/eloop.h @@ -83,15 +83,15 @@ int eloop_q_timeout_add_msec(struct eloop *, int, unsigned long, void (*)(void *), void *); int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *); -int eloop_signal_set_cb(struct eloop *, const int *, size_t, +void eloop_signal_set_cb(struct eloop *, const int *, size_t, void (*)(int, void *), void *); int eloop_signal_mask(struct eloop *, sigset_t *oldset); struct eloop * eloop_new(void); -int eloop_requeue(struct eloop *); void eloop_clear(struct eloop *); void eloop_free(struct eloop *); void eloop_exit(struct eloop *, int); +void eloop_enter(struct eloop *); int eloop_start(struct eloop *, sigset_t *); #endif diff --git a/contrib/dhcpcd/src/if-bsd.c b/contrib/dhcpcd/src/if-bsd.c index 7bc1d7321c..78735c7b9f 100644 --- a/contrib/dhcpcd/src/if-bsd.c +++ b/contrib/dhcpcd/src/if-bsd.c @@ -100,10 +100,12 @@ #define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len)) #endif -/* Ignore these interface names which look like ethernet but are virtual. */ +/* Ignore these interface names which look like ethernet but are virtual or + * just won't work without explicit configuration. */ static const char * const ifnames_ignore[] = { "bridge", "fwe", /* Firewire */ + "fwip", /* Firewire */ "tap", "xvif", /* XEN DOM0 -> guest interface */ NULL diff --git a/contrib/dhcpcd/src/privsep-bpf.c b/contrib/dhcpcd/src/privsep-bpf.c index e1fd8bc72e..6189349db0 100644 --- a/contrib/dhcpcd/src/privsep-bpf.c +++ b/contrib/dhcpcd/src/privsep-bpf.c @@ -82,7 +82,7 @@ ps_bpf_recvbpf(void *arg) psm.ps_flags = bpf->bpf_flags; len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd, &psm, buf, (size_t)len); - if (len == -1 && errno != ECONNRESET) + if (len == -1) logerr(__func__); if (len == -1 || len == 0) break; diff --git a/contrib/dhcpcd/src/privsep-bsd.c b/contrib/dhcpcd/src/privsep-bsd.c index aa63bcd453..28b7435409 100644 --- a/contrib/dhcpcd/src/privsep-bsd.c +++ b/contrib/dhcpcd/src/privsep-bsd.c @@ -55,6 +55,12 @@ ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len) /* Only allow these ioctls */ switch(req) { +#ifdef SIOCG80211NWID + case SIOCG80211NWID: /* FALLTHROUGH */ +#endif +#ifdef SIOCGETVLAN + case SIOCGETVLAN: /* FALLTHROUGH */ +#endif #ifdef SIOCIFAFATTACH case SIOCIFAFATTACH: /* FALLTHROUGH */ #endif @@ -78,7 +84,7 @@ ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len) case SIOCSIFINFO_IN6: /* FALLTHROUGH */ #endif case SIOCAIFADDR_IN6: /* FALLTHROUGH */ - case SIOCDIFADDR_IN6: /* FALLTHROUGH */ + case SIOCDIFADDR_IN6: break; default: errno = EPERM; @@ -115,53 +121,56 @@ ps_root_doindirectioctl(unsigned long req, void *data, size_t len) { char *p = data; struct ifreq ifr = { .ifr_flags = 0 }; - ssize_t err; - switch(req) { - case SIOCG80211NWID: /* FALLTHROUGH */ - case SIOCGETVLAN: - break; - default: - errno = EPERM; - return -1; - } + /* ioctl filtering is done in ps_root_doioctldom */ - if (len < IFNAMSIZ) { + if (len < IFNAMSIZ + 1) { errno = EINVAL; return -1; } strlcpy(ifr.ifr_name, p, IFNAMSIZ); - ifr.ifr_data = p + IFNAMSIZ; - err = ps_root_doioctldom(PF_INET, req, &ifr, sizeof(ifr)); - if (err != -1) - memmove(data, ifr.ifr_data, len - IFNAMSIZ); - return err; + len -= IFNAMSIZ; + memmove(data, p + IFNAMSIZ, len); + ifr.ifr_data = data; + + return ps_root_doioctldom(PF_INET, req, &ifr, sizeof(ifr)); } #endif ssize_t -ps_root_os(struct ps_msghdr *psm, struct msghdr *msg) +ps_root_os(struct ps_msghdr *psm, struct msghdr *msg, + void **rdata, size_t *rlen) { struct iovec *iov = msg->msg_iov; void *data = iov->iov_base; size_t len = iov->iov_len; + ssize_t err; switch (psm->ps_cmd) { case PS_IOCTLLINK: - return ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len); + err = ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len); + break; case PS_IOCTL6: - return ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len); + err = ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len); + break; case PS_ROUTE: return ps_root_doroute(data, len); #ifdef HAVE_PLEDGE case PS_IOCTLINDIRECT: - return ps_root_doindirectioctl(psm->ps_flags, data, len); + err = ps_root_doindirectioctl(psm->ps_flags, data, len); + break; #endif default: errno = ENOTSUP; return -1; } + + if (err != -1) { + *rdata = data; + *rlen = len; + } + return err; } static ssize_t @@ -207,6 +216,11 @@ ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request, { char buf[PS_BUFLEN]; + if (IFNAMSIZ + len > sizeof(buf)) { + errno = ENOBUFS; + return -1; + } + strlcpy(buf, ifname, IFNAMSIZ); memcpy(buf + IFNAMSIZ, data, len); if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTLINDIRECT, diff --git a/contrib/dhcpcd/src/privsep-inet.c b/contrib/dhcpcd/src/privsep-inet.c index 87ba4eb591..48fb19a20e 100644 --- a/contrib/dhcpcd/src/privsep-inet.c +++ b/contrib/dhcpcd/src/privsep-inet.c @@ -170,8 +170,8 @@ ps_inet_startcb(void *arg) } #endif #ifdef DHCP6 - if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_DHCP6 | DHCPCD_MASTER)) == - (DHCPCD_IPV6 | DHCPCD_DHCP6 | DHCPCD_MASTER)) + if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MASTER)) == + (DHCPCD_IPV6 | DHCPCD_MASTER)) { ctx->dhcp6_rfd = dhcp6_openudp(0, NULL); if (ctx->dhcp6_rfd == -1) diff --git a/contrib/dhcpcd/src/privsep-root.c b/contrib/dhcpcd/src/privsep-root.c index affcf3c6f6..fdf4385681 100644 --- a/contrib/dhcpcd/src/privsep-root.c +++ b/contrib/dhcpcd/src/privsep-root.c @@ -43,6 +43,7 @@ #include #include +#include "auth.h" #include "common.h" #include "dev.h" #include "dhcpcd.h" @@ -124,6 +125,7 @@ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len) ps_root_readerrorcb, &psr_ctx) == -1) return -1; + eloop_enter(ctx->ps_eloop); eloop_start(ctx->ps_eloop, &ctx->sigset); errno = psr_ctx.psr_error.psr_errno; @@ -181,6 +183,7 @@ ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len) ps_root_mreaderrorcb, &psr_ctx) == -1) return -1; + eloop_enter(ctx->ps_eloop); eloop_start(ctx->ps_eloop, &ctx->sigset); errno = psr_ctx.psr_error.psr_errno; @@ -331,16 +334,29 @@ ps_root_dowritefile(const struct dhcpcd_ctx *ctx, return writefile(file, mode, nc, len - (size_t)(nc - file)); } +#ifdef AUTH +static ssize_t +ps_root_monordm(uint64_t *rdm, size_t len) +{ + + if (len != sizeof(*rdm)) { + errno = EINVAL; + return -1; + } + return auth_get_rdm_monotonic(rdm); +} +#endif + #ifdef HAVE_CAPSICUM #define IFA_NADDRS 3 static ssize_t ps_root_dogetifaddrs(void **rdata, size_t *rlen) { - struct ifaddrs *ifaddrs, *ifa; + struct ifaddrs *ifaddrs, *ifa, *ifa_next; size_t len; uint8_t *buf, *sap; socklen_t salen; - void *ifdata; + void *ifa_data; if (getifaddrs(&ifaddrs) == -1) return -1; @@ -380,12 +396,15 @@ ps_root_dogetifaddrs(void **rdata, size_t *rlen) *rlen = len; for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { - /* Don't carry ifa_data. */ - ifdata = ifa->ifa_data; + /* Don't carry ifa_data or ifa_next. */ + ifa_data = ifa->ifa_data; + ifa_next = ifa->ifa_next; ifa->ifa_data = NULL; + ifa->ifa_next = NULL; memcpy(buf, ifa, sizeof(*ifa)); buf += ALIGN(sizeof(*ifa)); - ifa->ifa_data = ifdata; + ifa->ifa_data = ifa_data; + ifa->ifa_next = ifa_next; strlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ); buf += ALIGN(IFNAMSIZ); @@ -443,9 +462,21 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) ps_freeprocess(psp); return ret; - } else if (!(psm->ps_cmd & PS_START)) - return ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg); - /* Process has already started .... */ + } else if (psm->ps_cmd & PS_START) { + /* Process has already started .... */ + return 0; + } + + err = ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg); + if (err == -1) { + logerr("%s: failed to send message to pid %d", + __func__, psp->psp_pid); + shutdown(psp->psp_fd, SHUT_RDWR); + close(psp->psp_fd); + psp->psp_fd = -1; + ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd); + ps_freeprocess(psp); + } return 0; } @@ -520,6 +551,15 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) rlen = sizeof(mtime); } break; +#ifdef AUTH + case PS_AUTH_MONORDM: + err = ps_root_monordm(data, len); + if (err != -1) { + rdata = data; + rlen = len; + } + break; +#endif #ifdef HAVE_CAPSICUM case PS_GETIFADDRS: err = ps_root_dogetifaddrs(&rdata, &rlen); @@ -540,7 +580,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) break; #endif default: - err = ps_root_os(psm, msg); + err = ps_root_os(psm, msg, &rdata, &rlen); break; } @@ -556,8 +596,7 @@ ps_root_recvmsg(void *arg) { struct dhcpcd_ctx *ctx = arg; - if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1 && - errno != ECONNRESET) + if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1) logerr(__func__); } @@ -604,19 +643,26 @@ ps_root_startcb(void *arg) * but makes life very easy for unicasting DHCPv6 in non master * mode as we no longer care about address selection. */ #ifdef INET - ctx->udp_wfd = xsocket(PF_INET, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP); - if (ctx->udp_wfd == -1) - return -1; + if (ctx->options & DHCPCD_IPV4) { + ctx->udp_wfd = xsocket(PF_INET, + SOCK_RAW | SOCK_CXNB, IPPROTO_UDP); + if (ctx->udp_wfd == -1) + logerr("%s: dhcp_openraw", __func__); + } #endif #ifdef INET6 - ctx->nd_fd = ipv6nd_open(false); - if (ctx->nd_fd == -1) - return -1; + if (ctx->options & DHCPCD_IPV6) { + ctx->nd_fd = ipv6nd_open(false); + if (ctx->udp_wfd == -1) + logerr("%s: ipv6nd_open", __func__); + } #endif #ifdef DHCP6 - ctx->dhcp6_wfd = dhcp6_openraw(); - if (ctx->dhcp6_wfd == -1) - return -1; + if (ctx->options & DHCPCD_IPV6) { + ctx->dhcp6_wfd = dhcp6_openraw(); + if (ctx->udp_wfd == -1) + logerr("%s: dhcp6_openraw", __func__); + } #endif #ifdef PLUGIN_DEV @@ -639,6 +685,13 @@ ps_root_signalcb(int sig, void *arg) if (sig == SIGINT) return; + /* Reap children */ + if (sig == SIGCHLD) { + while (waitpid(-1, NULL, WNOHANG) > 0) + ; + return; + } + logerrx("process %d unexpectedly terminating on signal %d", getpid(), sig); if (ctx->ps_root_pid == getpid()) { @@ -741,10 +794,9 @@ ps_root_start(struct dhcpcd_ctx *ctx) if ((ctx->ps_eloop = eloop_new()) == NULL) return -1; - if (eloop_signal_set_cb(ctx->ps_eloop, + eloop_signal_set_cb(ctx->ps_eloop, dhcpcd_signals, dhcpcd_signals_len, - ps_root_readerrorsig, ctx) == -1) - return -1; + ps_root_readerrorsig, ctx); return pid; } @@ -913,6 +965,18 @@ ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname) } #endif +#ifdef AUTH +int +ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm) +{ + + if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_AUTH_MONORDM, 0, + rdm, sizeof(rdm))== -1) + return -1; + return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm)); +} +#endif + #ifdef PLUGIN_DEV int ps_root_dev_initialized(struct dhcpcd_ctx *ctx, const char *ifname) diff --git a/contrib/dhcpcd/src/privsep-root.h b/contrib/dhcpcd/src/privsep-root.h index a56e30af03..a8e34227a2 100644 --- a/contrib/dhcpcd/src/privsep-root.h +++ b/contrib/dhcpcd/src/privsep-root.h @@ -44,9 +44,10 @@ 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_script(struct dhcpcd_ctx *, const void *, size_t); +int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *); int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **); -ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *); +ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *, void **, size_t *); #if defined(BSD) || defined(__sun) ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t); ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t); diff --git a/contrib/dhcpcd/src/privsep.c b/contrib/dhcpcd/src/privsep.c index ce4f056510..fbfb99d37f 100644 --- a/contrib/dhcpcd/src/privsep.c +++ b/contrib/dhcpcd/src/privsep.c @@ -131,6 +131,41 @@ 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; +} + pid_t ps_dostart(struct dhcpcd_ctx *ctx, pid_t *priv_pid, int *priv_fd, @@ -160,11 +195,13 @@ ps_dostart(struct dhcpcd_ctx *ctx, case 0: *priv_fd = fd[1]; close(fd[0]); + ps_setbuf(*priv_fd); break; default: *priv_pid = pid; *priv_fd = fd[0]; close(fd[1]); + ps_setbuf(*priv_fd); if (recv_unpriv_msg == NULL) ; #ifdef HAVE_CAPSICUM @@ -206,12 +243,8 @@ ps_dostart(struct dhcpcd_ctx *ctx, ctx->ps_inet_fd = -1; } - if (eloop_signal_set_cb(ctx->eloop, - dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx) == -1) - { - logerr("%s: eloop_signal_set_cb", __func__); - goto errexit; - } + eloop_signal_set_cb(ctx->eloop, + dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx); /* ctx->sigset aready has the initial sigmask set in main() */ if (eloop_signal_mask(ctx->eloop, NULL) == -1) { @@ -251,67 +284,35 @@ errexit: (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0); shutdown(*priv_fd, SHUT_RDWR); *priv_fd = -1; + eloop_exit(ctx->eloop, EXIT_FAILURE); return -1; } int ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd) { - int status; + int err = 0; #ifdef PRIVSEP_DEBUG logdebugx("%s: pid %d fd %d", __func__, *pid, *fd); #endif - if (*pid == 0) - return 0; - eloop_event_delete(ctx->eloop, *fd); - if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1 && - errno != ECONNRESET) - logerr(__func__); - if (shutdown(*fd, SHUT_RDWR) == -1 && errno != ENOTCONN) - logerr(__func__); - close(*fd); - *fd = -1; - /* We won't have permission for all processes .... */ -#if 0 - if (kill(*pid, SIGTERM) == -1) - logerr(__func__); -#endif - status = 0; -#ifdef HAVE_CAPSICUM - unsigned int cap_mode = 0; - int cap_err = cap_getmode(&cap_mode); - - if (cap_err == -1) { - if (errno != ENOSYS) - logerr("%s: cap_getmode", __func__); - } else if (cap_mode != 0) - goto nowait; -#endif - - /* Wait for the process to finish */ - while (waitpid(*pid, &status, 0) == -1) { - if (errno != EINTR) { - logerr("%s: waitpid", __func__); - status = 0; - break; + if (*fd != -1) { + eloop_event_delete(ctx->eloop, *fd); + if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1 || + shutdown(*fd, SHUT_RDWR) == -1) + { + logerr(__func__); + err = -1; } -#ifdef PRIVSEP_DEBUG - else - logerr("%s: waitpid ", __func__); -#endif + close(*fd); + *fd = -1; } -#ifdef HAVE_CAPSICUM -nowait: -#endif - *pid = 0; - -#ifdef PRIVSEP_DEBUG - logdebugx("%s: status %d", __func__, status); -#endif - return status; + /* Don't wait for the process as it may not respond to the shutdown + * request. We'll reap the process on receipt of SIGCHLD. */ + *pid = 0; + return err; } int @@ -507,7 +508,8 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, #ifdef PRIVSEP_DEBUG logdebugx("%s: %zd", __func__, len); #endif - if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED) + if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED && + !(ctx->options & DHCPCD_PRIVSEPROOT)) eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); return len; } @@ -650,8 +652,12 @@ ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd) #ifdef PRIVSEP_DEBUG logdebugx("%s: recv fd %d, %zd bytes", __func__, rfd, len); #endif - if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED) { - eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + + if (len == -1 || len == 0) { + if (ctx->options & DHCPCD_FORKED && + !(ctx->options & DHCPCD_PRIVSEPROOT)) + eloop_exit(ctx->eloop, + len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); return len; } @@ -660,7 +666,8 @@ ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd) #ifdef PRIVSEP_DEBUG logdebugx("%s: send fd %d, %zu bytes", __func__, wfd, len); #endif - if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED) + if ((len == -1 || len == 0) && ctx->options & DHCPCD_FORKED && + !(ctx->options & DHCPCD_PRIVSEPROOT)) eloop_exit(ctx->eloop, len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); return len; } @@ -682,8 +689,6 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, logdebugx("%s: %zd", __func__, len); #endif - if (len == -1 && (errno == ECONNRESET || errno == EBADF)) - len = 0; if (len == -1 || len == 0) stop = true; else { diff --git a/contrib/dhcpcd/src/privsep.h b/contrib/dhcpcd/src/privsep.h index 9305822575..f1fee41e9e 100644 --- a/contrib/dhcpcd/src/privsep.h +++ b/contrib/dhcpcd/src/privsep.h @@ -49,6 +49,7 @@ #define PS_READFILE 0x0014 #define PS_WRITEFILE 0x0015 #define PS_FILEMTIME 0x0016 +#define PS_AUTH_MONORDM 0x0017 /* BSD Commands */ #define PS_IOCTLLINK 0x0101 -- 2.11.4.GIT