This commit is contained in:
roy 2020-06-04 13:08:13 +00:00
parent d37f9bca9d
commit b652e16f07
7 changed files with 260 additions and 142 deletions

View File

@ -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
@ -4176,3 +4188,24 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
return ia;
}
#ifndef SMALL
int
dhcp_dump(struct interface *ifp)
{
struct dhcp_state *state;
ifp->if_data[IF_DATA_DHCP] = state = calloc(1, sizeof(*state));
if (state == NULL) {
logerr(__func__);
return -1;
}
state->new_len = read_lease(ifp, &state->new);
if (state->new == NULL) {
logerr("read_lease");
return -1;
}
state->reason = "DUMP";
return script_runreason(ifp, state->reason);
}
#endif

View File

@ -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
@ -1483,7 +1483,7 @@ void dhcp6_renew(struct interface *ifp)
dhcp6_startrenew(ifp);
}
int
bool
dhcp6_dadcompleted(const struct interface *ifp)
{
const struct dhcp6_state *state;
@ -1493,9 +1493,9 @@ dhcp6_dadcompleted(const struct interface *ifp)
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->flags & IPV6_AF_ADDED &&
!(ap->flags & IPV6_AF_DADCOMPLETED))
return 0;
return false;
}
return 1;
return true;
}
static void
@ -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;
@ -4293,3 +4304,24 @@ delegated:
return 1;
}
#endif
#ifndef SMALL
int
dhcp6_dump(struct interface *ifp)
{
struct dhcp6_state *state;
ifp->if_data[IF_DATA_DHCP6] = state = calloc(1, sizeof(*state));
if (state == NULL) {
logerr(__func__);
return -1;
}
TAILQ_INIT(&state->addrs);
if (dhcp6_readlease(ifp, 0) == -1) {
logerr("dhcp6_readlease");
return -1;
}
state->reason = "DUMP6";
return script_runreason(ifp, state->reason);
}
#endif

View File

@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd May 21, 2020
.Dd May 31, 2020
.Dt DHCPCD 8
.Os
.Sh NAME
@ -72,7 +72,7 @@
.Op interface
.Nm
.Fl U , Fl Fl dumplease
.Ar interface
.Op Ar interface
.Nm
.Fl Fl version
.Nm
@ -685,15 +685,20 @@ option is not sent in TEST mode so that the server does not lease an address.
To test INFORM the interface needs to be configured with the desired address
before starting
.Nm .
.It Fl U , Fl Fl dumplease Ar interface
.It Fl U , Fl Fl dumplease Op Ar interface
Dumps the current lease for the
.Ar interface
to stdout.
If no
.Ar interface
is given then all interfaces are dumped.
Use the
.Fl 4
or
.Fl 6
flags to specify an address family.
If a lease is piped in via standard input then that is dumped.
In this case, specifying an address family is mandatory.
.It Fl V , Fl Fl variables
Display a list of option codes, the associated variable and encoding for use in
.Xr dhcpcd-run-hooks 8 .

View File

@ -29,6 +29,7 @@
const char dhcpcd_copyright[] = "Copyright (c) 2006-2020 Roy Marples";
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
@ -86,6 +87,7 @@ const int dhcpcd_signals[] = {
SIGHUP,
SIGUSR1,
SIGUSR2,
SIGCHLD,
};
const size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals);
@ -1340,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)
@ -1395,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)
@ -1441,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);
@ -1663,20 +1672,13 @@ dumperr:
return 0;
}
static const char *dumpskip[] = {
"PATH=",
"pid=",
"chroot=",
};
static int
dhcpcd_readdump(struct dhcpcd_ctx *ctx)
{
int error = 0;
size_t nifaces, buflen = 0, dlen, i;
size_t nifaces, buflen = 0, dlen;
ssize_t len;
char *buf = NULL, *dp, *de;
const char *skip;
char *buf = NULL;
again1:
len = read(ctx->control_fd, &nifaces, sizeof(nifaces));
@ -1723,26 +1725,7 @@ again3:
error = -1;
goto out;
}
dp = buf;
de = dp + dlen;
if (*(de - 1) != '\0') {
errno = EINVAL;
error = -1;
goto out;
}
while (dp < de) {
for (i = 0; i < __arraycount(dumpskip); i++) {
skip = dumpskip[i];
if (strncmp(dp, skip, strlen(skip)) == 0)
break;
}
if (i == __arraycount(dumpskip)) {
if (strncmp(dp, "new_", 4) == 0)
dp += 4;
printf("%s\n", dp);
}
dp += strlen(dp) + 1;
}
script_dump(buf, dlen);
fflush(stdout);
if (nifaces != 1)
putchar('\n');
@ -2061,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;
@ -2107,6 +2086,45 @@ printpidfile:
}
#endif
#ifndef SMALL
if (ctx.options & DHCPCD_DUMPLEASE &&
ioctl(fileno(stdin), FIONREAD, &i, sizeof(i)) == 0 &&
i > 0)
{
ifp = calloc(1, sizeof(*ifp));
if (ifp == NULL) {
logerr(__func__);
goto exit_failure;
}
ifp->ctx = &ctx;
ifp->options = ifo;
switch (family) {
case AF_INET:
#ifdef INET
if (dhcp_dump(ifp) == -1)
goto exit_failure;
break;
#else
logerrx("No DHCP support");
goto exit_failure;
#endif
case AF_INET6:
#ifdef DHCP6
if (dhcp6_dump(ifp) == -1)
goto exit_failure;
break;
#else
logerrx("No DHCP6 support");
goto exit_failure;
#endif
default:
logerrx("Family not specified. Please use -4 or -6.");
goto exit_failure;
}
goto exit_success;
}
#endif
/* Test against siga instead of sig to avoid gcc
* warning about a bogus potential signed overflow.
* The end result will be the same. */
@ -2194,7 +2212,6 @@ printpidfile:
logerr("fork");
goto exit_failure;
case 0:
eloop_requeue(ctx.eloop);
break;
default:
ctx.options |= DHCPCD_FORKED; /* A lie */
@ -2203,7 +2220,6 @@ printpidfile:
}
break;
default:
waitpid(pid, &i, 0);
ctx.options |= DHCPCD_FORKED; /* A lie */
ctx.fork_fd = sigpipe[0];
close(sigpipe[1]);

View File

@ -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

View File

@ -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
/* 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;
#ifdef PRIVSEP_DEBUG
logdebugx("%s: status %d", __func__, status);
#endif
return status;
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 {

View File

@ -1,4 +1,4 @@
/* stSPDX-License-Identifier: BSD-2-Clause */
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
* Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
@ -193,6 +193,8 @@ script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
}
}
assert(*(bufp - 1) == '\0');
if (nenv == 0)
return NULL;
if (ctx->script_envlen < nenv) {
env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
@ -235,6 +237,7 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
#ifdef DHCP6
const struct dhcp6_state *d6_state;
#endif
bool is_stdin = ifp->name[0] == '\0';
#ifdef HAVE_OPEN_MEMSTREAM
if (ctx->script_fp == NULL) {
@ -264,23 +267,19 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
}
#endif
/* Needed for scripts */
path = getenv("PATH");
if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1)
goto eexit;
if (efprintf(fp, "reason=%s", reason) == -1)
goto eexit;
if (efprintf(fp, "pid=%d", getpid()) == -1)
goto eexit;
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP && ctx->ps_user != NULL) {
if (efprintf(fp, "chroot=%s", ctx->ps_user->pw_dir) == -1)
if (!(ifp->ctx->options & DHCPCD_DUMPLEASE)) {
/* Needed for scripts */
path = getenv("PATH");
if (efprintf(fp, "PATH=%s",
path == NULL ? DEFAULT_PATH : path) == -1)
goto eexit;
if (efprintf(fp, "pid=%d", getpid()) == -1)
goto eexit;
}
if (!is_stdin) {
if (efprintf(fp, "reason=%s", reason) == -1)
goto eexit;
}
if (strcmp(reason, "CHROOT") == 0)
goto make;
#endif
ifo = ifp->options;
#ifdef INET
@ -340,9 +339,10 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
protocol = PROTO_DHCP;
#endif
if (efprintf(fp, "interface=%s", ifp->name) == -1)
goto eexit;
if (!is_stdin) {
if (efprintf(fp, "interface=%s", ifp->name) == -1)
goto eexit;
}
if (ifp->ctx->options & DHCPCD_DUMPLEASE)
goto dumplease;
if (efprintf(fp, "ifcarrier=%s",
@ -508,9 +508,6 @@ dumplease:
goto eexit;
}
#ifdef PRIVSEP
make:
#endif
/* Convert buffer to argv */
fflush(fp);
@ -536,6 +533,9 @@ make:
fp = NULL;
#endif
if (is_stdin)
return buf_pos;
if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
goto eexit;
@ -685,6 +685,27 @@ script_run(struct dhcpcd_ctx *ctx, char **argv)
return WEXITSTATUS(status);
}
int
script_dump(const char *env, size_t len)
{
const char *ep = env + len;
if (len == 0)
return 0;
if (*(ep - 1) != '\0') {
errno = EINVAL;
return -1;
}
for (; env < ep; env += strlen(env) + 1) {
if (strncmp(env, "new_", 4) == 0)
env += 4;
printf("%s\n", env);
}
return 0;
}
int
script_runreason(const struct interface *ifp, const char *reason)
{
@ -692,17 +713,21 @@ script_runreason(const struct interface *ifp, const char *reason)
char *argv[2];
int status = 0;
struct fd_list *fd;
long buflen;
if (ctx->script == NULL &&
TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
return 0;
/* Make our env */
if (make_env(ifp->ctx, ifp, reason) == -1) {
if ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) {
logerr(__func__);
return -1;
}
if (strncmp(reason, "DUMP", 4) == 0)
return script_dump(ctx->script_buf, (size_t)buflen);
if (ctx->script == NULL)
goto send_listeners;