echo "#ifndef PRIVSEP_USER" >>$CONFIG_H
echo "#define PRIVSEP_USER \"$PRIVSEP_USER\"" >>$CONFIG_H
echo "#endif" >>$CONFIG_H
- echo "DHCPCD_SRCS+= privsep.c privsep-root.c privsep-inet.c" \
+ echo "PRIVSEP_SRCS= privsep.c privsep-root.c privsep-inet.c" \
>>$CONFIG_MK
if [ -z "$INET" ] || [ "$INET" = yes ]; then
- echo "DHCPCD_SRCS+= privsep-bpf.c" >>$CONFIG_MK
+ echo "PRIVSEP_SRCS+= privsep-bpf.c" >>$CONFIG_MK
fi
case "$OS" in
- linux*) echo "DHCPCD_SRCS+= privsep-linux.c" >>$CONFIG_MK;;
- solaris*|sunos*) echo "DHCPCD_SRCS+= privsep-sun.c" >>$CONFIG_MK;;
- *) echo "DHCPCD_SRCS+= privsep-bsd.c" >>$CONFIG_MK;;
+ linux*) echo "PRIVSEP_SRCS+= privsep-linux.c" >>$CONFIG_MK;;
+ solaris*|sunos*) echo "PRIVSEP_SRCS+= privsep-sun.c" >>$CONFIG_MK;;
+ *) echo "PRIVSEP_SRCS+= privsep-bsd.c" >>$CONFIG_MK;;
esac
+else
+ echo "PRIVSEP_SRCS=" >>$CONFIG_MK
fi
echo "Using compiler .. $CC"
[ "$CC" != cc ] && echo "CC= $CC" >>$CONFIG_MK
$CC --version | $SED -e '1!d'
-if [ -z "$EMBEDDED" -o "$EMBEDDED" = yes ]; then
- echo "$DHCPCD_DEFS will be embedded in dhcpcd itself"
- echo "DHCPCD_SRCS+= dhcpcd-embedded.c" >>$CONFIG_MK
-else
- echo "$DHCPCD_DEFS will be installed to $LIBEXECDIR"
- echo "CPPFLAGS+= -DEMBEDDED_CONFIG=\\\"$LIBEXECDIR/dhcpcd-definitions.conf\\\"" >>$CONFIG_MK
- echo "EMBEDDEDINSTALL= _embeddedinstall" >>$CONFIG_MK
-fi
-
if [ "$PRIVSEP" = yes ]; then
+ PRIVSEP_CONTROLLER=true
printf "Testing for capsicum ... "
cat <<EOF >_capsicum.c
#include <sys/capsicum.h>
if $XCC _capsicum.c -o _capsicum 2>&3; then
echo "yes"
echo "#define HAVE_CAPSICUM" >>$CONFIG_H
+ PRIVSEP_CONTROLLER=false
else
echo "no"
fi
if $XCC _pledge.c -o _pledge 2>&3; then
echo "yes"
echo "#define HAVE_PLEDGE" >>$CONFIG_H
+ PRIVSEP_CONTROLLER=false
else
echo "no"
fi
rm -f _pledge.c _pledge
+
+ if $PRIVSEP_CONTROLLER; then
+ echo "#define PRIVSEP_CONTROLLER" >>$CONFIG_H
+ echo "PRIVSEP_SRCS+= privsep-control.c" >>$CONFIG_MK
+ fi
+fi
+
+if [ -z "$EMBEDDED" -o "$EMBEDDED" = yes ]; then
+ echo "$DHCPCD_DEFS will be embedded in dhcpcd itself"
+ echo "DHCPCD_SRCS+= dhcpcd-embedded.c" >>$CONFIG_MK
+else
+ echo "$DHCPCD_DEFS will be installed to $LIBEXECDIR"
+ echo "CPPFLAGS+= -DEMBEDDED_CONFIG=\\\"$LIBEXECDIR/dhcpcd-definitions.conf\\\"" >>$CONFIG_MK
+ echo "EMBEDDEDINSTALL= _embeddedinstall" >>$CONFIG_MK
fi
if [ "$OS" = linux ]; then
CFLAGS+= -std=${CSTD}
CPPFLAGS+= -I${TOP} -I${TOP}/src -I./crypt
-SRCS+= ${DHCPCD_SRCS}
+SRCS+= ${DHCPCD_SRCS} ${PRIVSEP_SRCS}
DHCPCD_DEF?= dhcpcd-definitions.conf
DHCPCD_DEFS= dhcpcd-definitions.conf dhcpcd-definitions-small.conf
free(fdp->data);
free(fdp);
}
- fd->queue_len = 0;
#ifdef CTL_FREE_LIST
while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
#endif
}
-static void
-control_delete(struct fd_list *fd)
+void
+control_free(struct fd_list *fd)
{
+#ifdef PRIVSEP_CONTROLLER
+ if (fd->ctx->ps_control_client == fd)
+ fd->ctx->ps_control_client = NULL;
+#endif
+
+ eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
- eloop_event_delete(fd->ctx->eloop, fd->fd);
- close(fd->fd);
control_queue_free(fd);
free(fd);
}
+void
+control_delete(struct fd_list *fd)
+{
+
+#ifdef PRIVSEP_CONTROLLER
+ if (IN_PRIVSEP_SE(fd->ctx))
+ return;
+#endif
+
+ eloop_event_delete(fd->ctx->eloop, fd->fd);
+ close(fd->fd);
+ control_free(fd);
+}
+
static void
control_handle_data(void *arg)
{
struct fd_list *fd = arg;
- char buffer[1024], *e, *p, *argvp[255], **ap, *a;
+ char buffer[1024];
ssize_t bytes;
- size_t len;
- int argc;
bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
+
if (bytes == -1 || bytes == 0) {
/* Control was closed or there was an error.
* Remove it from our list. */
control_delete(fd);
return;
}
- buffer[bytes] = '\0';
- p = buffer;
- e = buffer + bytes;
+
+#ifdef PRIVSEP_CONTROLLER
+ if (IN_PRIVSEP(fd->ctx)) {
+ ssize_t err;
+
+ fd->flags |= FD_SENDLEN;
+ err = ps_ctl_handleargs(fd, buffer, (size_t)bytes);
+ fd->flags &= ~FD_SENDLEN;
+ if (err == -1) {
+ logerr(__func__);
+ return;
+ }
+ if (err == 1 &&
+ ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
+ logerr(__func__);
+ control_delete(fd);
+ }
+ return;
+ }
+#endif
+
+ control_recvdata(fd, buffer, (size_t)bytes);
+}
+
+void
+control_recvdata(struct fd_list *fd, char *data, size_t len)
+{
+ char *p = data, *e;
+ char *argvp[255], **ap;
+ int argc;
/* Each command is \n terminated
* Each argument is NULL separated */
- while (p < e) {
+ while (len != 0) {
argc = 0;
ap = argvp;
- while (p < e) {
- argc++;
+ while (len != 0) {
+ if (*p == '\0') {
+ p++;
+ len--;
+ continue;
+ }
+ e = memchr(p, '\0', len);
+ if (e == NULL) {
+ errno = EINVAL;
+ logerrx("%s: no terminator", __func__);
+ return;
+ }
if ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {
errno = ENOBUFS;
+ logerrx("%s: no arg buffer", __func__);
return;
}
- a = *ap++ = p;
- len = strlen(p);
- p += len + 1;
- if (len && a[len - 1] == '\n') {
- a[len - 1] = '\0';
+ *ap++ = p;
+ argc++;
+ e++;
+ len -= (size_t)(e - p);
+ p = e;
+ e--;
+ if (*(--e) == '\n') {
+ *e = '\0';
break;
}
}
+ if (argc == 0) {
+ logerrx("%s: no args", __func__);
+ continue;
+ }
*ap = NULL;
if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
logerr(__func__);
}
}
+struct fd_list *
+control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
+{
+ struct fd_list *l;
+
+ l = malloc(sizeof(*l));
+ if (l == NULL)
+ return NULL;
+
+ l->ctx = ctx;
+ l->fd = fd;
+ l->flags = flags;
+ TAILQ_INIT(&l->queue);
+#ifdef CTL_FREE_LIST
+ TAILQ_INIT(&l->free_queue);
+#endif
+ TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
+ return l;
+}
+
static void
control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
{
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
goto error;
- l = malloc(sizeof(*l));
+#ifdef PRIVSEP_CONTROLLER
+ if (IN_PRIVSEP(ctx) && !IN_PRIVSEP_SE(ctx))
+ ;
+ else
+#endif
+ fd_flags |= FD_SENDLEN;
+
+ l = control_new(ctx, fd, fd_flags);
if (l == NULL)
goto error;
- l->ctx = ctx;
- l->fd = fd;
- l->flags = fd_flags;
- TAILQ_INIT(&l->queue);
- l->queue_len = 0;
-#ifdef CTL_FREE_LIST
- TAILQ_INIT(&l->free_queue);
-#endif
- TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
- eloop_event_add(ctx->eloop, l->fd, control_handle_data, l);
+ if (eloop_event_add(ctx->eloop, l->fd, control_handle_data, l) == -1)
+ logerr(__func__);
return;
error:
control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV);
}
+static int
+make_path(char *path, size_t len, const char *ifname, sa_family_t family)
+{
+ const char *per;
+
+ switch(family) {
+ case AF_INET:
+ per = "-4";
+ break;
+ case AF_INET6:
+ per = "-6";
+ break;
+ default:
+ per = "";
+ break;
+ }
+ return snprintf(path, len, CONTROLSOCKET,
+ ifname ? ifname : "", ifname ? per : "", ifname ? "." : "");
+}
+
static int
make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
bool unpriv)
sa->sun_family = AF_UNIX;
if (unpriv)
strlcpy(sa->sun_path, UNPRIVSOCKET, sizeof(sa->sun_path));
- else {
- const char *per;
-
- switch(family) {
- case AF_INET:
- per = "-4";
- break;
- case AF_INET6:
- per = "-6";
- break;
- default:
- per = "";
- break;
- }
- snprintf(sa->sun_path, sizeof(sa->sun_path), CONTROLSOCKET,
- ifname ? ifname : "", ifname ? per : "", ifname ? "." : "");
- }
+ else
+ make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family);
return fd;
}
{
int fd;
+#ifdef PRIVSEP_CONTROLLER
+ if (IN_PRIVSEP_SE(ctx)) {
+ make_path(ctx->control_sock, sizeof(ctx->control_sock),
+ ifname, family);
+ return 0;
+ }
+#endif
+
if ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1)
return -1;
int retval = 0;
struct fd_list *l;
- if (ctx->options & DHCPCD_FORKED)
- return 0;
+ while ((l = TAILQ_FIRST(&ctx->control_fds)) != NULL) {
+ control_free(l);
+ }
+
+#ifdef PRIVSEP_CONTROLLER
+ if (IN_PRIVSEP_SE(ctx)) {
+ if (ps_root_unlink(ctx, ctx->control_sock) == -1)
+ retval = -1;
+ if (ctx->options & DHCPCD_MASTER &&
+ control_unlink(ctx, UNPRIVSOCKET) == -1)
+ retval = -1;
+ return retval;
+ } else if (ctx->options & DHCPCD_FORKED)
+ return retval;
+#endif
if (ctx->control_fd != -1) {
eloop_event_delete(ctx->eloop, ctx->control_fd);
retval = -1;
}
- while ((l = TAILQ_FIRST(&ctx->control_fds))) {
- TAILQ_REMOVE(&ctx->control_fds, l, next);
- eloop_event_delete(ctx->eloop, l->fd);
- close(l->fd);
- control_queue_free(l);
- free(l);
- }
-
return retval;
}
{
struct fd_list *fd;
struct iovec iov[2];
+ int iov_len;
struct fd_data *data;
fd = arg;
data = TAILQ_FIRST(&fd->queue);
- iov[0].iov_base = &data->data_len;
- iov[0].iov_len = sizeof(size_t);
- iov[1].iov_base = data->data;
- iov[1].iov_len = data->data_len;
- if (writev(fd->fd, iov, 2) == -1) {
- logerr(__func__);
- if (errno != EINTR && errno != EAGAIN)
- control_delete(fd);
+
+ if (data->data_flags & FD_SENDLEN) {
+ iov[0].iov_base = &data->data_len;
+ iov[0].iov_len = sizeof(size_t);
+ iov[1].iov_base = data->data;
+ iov[1].iov_len = data->data_len;
+ iov_len = 2;
+ } else {
+ iov[0].iov_base = data->data;
+ iov[0].iov_len = data->data_len;
+ iov_len = 1;
+ }
+
+ if (writev(fd->fd, iov, iov_len) == -1) {
+ logerr("%s: write", __func__);
+ control_delete(fd);
return;
}
TAILQ_REMOVE(&fd->queue, data, next);
- fd->queue_len--;
#ifdef CTL_FREE_LIST
TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
#else
free(data);
#endif
- if (TAILQ_FIRST(&fd->queue) == NULL)
- eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
+ if (TAILQ_FIRST(&fd->queue) != NULL)
+ return;
+
+ eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
+#ifdef PRIVSEP_CONTROLLER
+ if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
+ if (ps_ctl_sendeof(fd) == -1)
+ logerr(__func__);
+ control_free(fd);
+ }
+#endif
}
int
-control_queue(struct fd_list *fd, void *data, size_t data_len, bool fit)
+control_queue(struct fd_list *fd, void *data, size_t data_len)
{
struct fd_data *d;
- if (data_len == 0)
- return 0;
+ if (data_len == 0) {
+ errno = EINVAL;
+ return -1;
+ }
#ifdef CTL_FREE_LIST
struct fd_data *df;
d = NULL;
TAILQ_FOREACH(df, &fd->free_queue, next) {
- if (!fit) {
- if (df->data_size == 0) {
- d = df;
- break;
- }
- continue;
- }
if (d == NULL || d->data_size < df->data_size) {
d = df;
if (d->data_size <= data_len)
else
#endif
{
- if (fd->queue_len == CONTROL_QUEUE_MAX) {
- errno = ENOBUFS;
- return -1;
- }
- fd->queue_len++;
d = calloc(1, sizeof(*d));
if (d == NULL)
return -1;
}
- if (!fit) {
-#ifdef CTL_FREE_LIST
- if (d->data_size != 0) {
- free(d->data);
- d->data_size = 0;
- }
-#endif
- d->data = data;
- d->data_len = data_len;
- goto queue;
- }
-
if (d->data_size == 0)
d->data = NULL;
if (d->data_size < data_len) {
}
memcpy(d->data, data, data_len);
d->data_len = data_len;
+ d->data_flags = fd->flags & FD_SENDLEN;
-queue:
TAILQ_INSERT_TAIL(&fd->queue, d, next);
eloop_event_add_w(fd->ctx->eloop, fd->fd, control_writeone, fd);
return 0;
void *data;
size_t data_size;
size_t data_len;
+ unsigned int data_flags;
};
TAILQ_HEAD(fd_data_head, fd_data);
int fd;
unsigned int flags;
struct fd_data_head queue;
- size_t queue_len;
#ifdef CTL_FREE_LIST
struct fd_data_head free_queue;
#endif
};
TAILQ_HEAD(fd_list_head, fd_list);
-#define FD_LISTEN (1<<0)
-#define FD_UNPRIV (1<<1)
+#define FD_LISTEN 0x01U
+#define FD_UNPRIV 0x02U
+#define FD_SENDLEN 0x04U
int control_start(struct dhcpcd_ctx *, const char *, sa_family_t);
int control_stop(struct dhcpcd_ctx *);
int control_open(const char *, sa_family_t, bool);
ssize_t control_send(struct dhcpcd_ctx *, int, char * const *);
-int control_queue(struct fd_list *, void *, size_t, bool);
-
+struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int);
+void control_free(struct fd_list *);
+void control_delete(struct fd_list *);
+int control_queue(struct fd_list *, void *, size_t);
+void control_recvdata(struct fd_list *fd, char *, size_t);
#endif
unsigned long long opts;
int exit_code;
+ if (ctx->options & DHCPCD_DUMPLEASE) {
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ return;
+ }
+
if (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) {
pid_t pid = pidfile_read(ctx->pidfile);
if (pid == -1) {
}
#endif
-static void
-dhcpcd_getinterfaces(void *arg)
-{
- struct fd_list *fd = arg;
- struct interface *ifp;
- size_t len;
-
- len = 0;
- TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
- if (!ifp->active)
- continue;
- len++;
-#ifdef INET
- if (D_STATE_RUNNING(ifp))
- len++;
-#endif
-#ifdef IPV4LL
- if (IPV4LL_STATE_RUNNING(ifp))
- len++;
-#endif
-#ifdef INET6
- if (IPV6_STATE_RUNNING(ifp))
- len++;
- if (RS_STATE_RUNNING(ifp))
- len++;
-#endif
-#ifdef DHCP6
- if (D6_STATE_RUNNING(ifp))
- len++;
-#endif
- }
- if (write(fd->fd, &len, sizeof(len)) != sizeof(len))
- return;
- eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
- TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
- if (!ifp->active)
- continue;
- if (send_interface(fd, ifp, AF_UNSPEC) == -1)
- logerr(__func__);
- }
-}
-
int
dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
int argc, char **argv)
struct interface *ifp;
unsigned long long opts;
int opt, oi, do_reboot, do_renew, af = AF_UNSPEC;
- size_t len, l;
+ size_t len, l, nifaces;
char *tmp, *p;
/* Special commands for our control socket
* as the other end should be blocking until it gets the
* expected reply we should be safely able just to change the
* write callback on the fd */
+ /* Make any change here in privsep-control.c as well. */
if (strcmp(*argv, "--version") == 0) {
return control_queue(fd, UNCONST(VERSION),
- strlen(VERSION) + 1, false);
+ strlen(VERSION) + 1);
} else if (strcmp(*argv, "--getconfigfile") == 0) {
return control_queue(fd, UNCONST(fd->ctx->cffile),
- strlen(fd->ctx->cffile) + 1, false);
+ strlen(fd->ctx->cffile) + 1);
} else if (strcmp(*argv, "--getinterfaces") == 0) {
- eloop_event_add_w(fd->ctx->eloop, fd->fd,
- dhcpcd_getinterfaces, fd);
- return 0;
+ optind = argc = 0;
+ goto dumplease;
} else if (strcmp(*argv, "--listen") == 0) {
fd->flags |= FD_LISTEN;
return 0;
if (opts & DHCPCD_DUMPLEASE) {
ctx->options |= DHCPCD_DUMPLEASE;
- size_t nifaces = 0;
-
+dumplease:
+ nifaces = 0;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active)
continue;
return 0;
}
-static int
-dhcpcd_readdump(struct dhcpcd_ctx *ctx)
+static void dhcpcd_readdump1(void *);
+
+static void
+dhcpcd_readdump2(void *arg)
{
- int error = 0;
- size_t nifaces, buflen = 0, dlen;
+ struct dhcpcd_ctx *ctx = arg;
ssize_t len;
- char *buf = NULL;
+ int exit_code = EXIT_FAILURE;
-again1:
- len = read(ctx->control_fd, &nifaces, sizeof(nifaces));
+ len = read(ctx->control_fd, ctx->ctl_buf + ctx->ctl_bufpos,
+ ctx->ctl_buflen - ctx->ctl_bufpos);
if (len == -1) {
- if (errno == EAGAIN)
- goto again1;
- return -1;
+ logerr(__func__);
+ goto finished;
+ } else if (len == 0)
+ goto finished;
+ if ((size_t)len + ctx->ctl_bufpos != ctx->ctl_buflen) {
+ ctx->ctl_bufpos += (size_t)len;
+ return;
}
- if (len != sizeof(nifaces)) {
- errno = EINVAL;
- return -1;
+
+ script_dump(ctx->ctl_buf, ctx->ctl_buflen);
+ fflush(stdout);
+ if (--ctx->ctl_extra != 0) {
+ putchar('\n');
+ eloop_event_add(ctx->eloop, ctx->control_fd,
+ dhcpcd_readdump1, ctx);
+ return;
}
- for (; nifaces > 0; nifaces--) {
-again2:
- len = read(ctx->control_fd, &dlen, sizeof(dlen));
- if (len == -1) {
- if (errno == EAGAIN)
- goto again2;
- error = -1;
- goto out;
- }
- if (len != sizeof(dlen)) {
+ exit_code = EXIT_SUCCESS;
+
+finished:
+ shutdown(ctx->control_fd, SHUT_RDWR);
+ eloop_exit(ctx->eloop, exit_code);
+}
+
+static void
+dhcpcd_readdump1(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ ssize_t len;
+
+ len = read(ctx->control_fd, &ctx->ctl_buflen, sizeof(ctx->ctl_buflen));
+ if (len != sizeof(ctx->ctl_buflen)) {
+ if (len != -1)
errno = EINVAL;
- goto out;
- }
- if (dlen > buflen) {
- char *nbuf = realloc(buf, dlen);
- if (nbuf == NULL) {
- error = -1;
- goto out;
- }
- buf = nbuf;
- buflen = dlen;
- }
- if (dlen == 0) {
+ goto err;
+ }
+
+ free(ctx->ctl_buf);
+ ctx->ctl_buf = malloc(ctx->ctl_buflen);
+ if (ctx->ctl_buf == NULL)
+ goto err;
+
+ ctx->ctl_bufpos = 0;
+ eloop_event_add(ctx->eloop, ctx->control_fd,
+ dhcpcd_readdump2, ctx);
+ return;
+
+err:
+ logerr(__func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
+static void
+dhcpcd_readdump0(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ ssize_t len;
+
+ len = read(ctx->control_fd, &ctx->ctl_extra, sizeof(ctx->ctl_extra));
+ if (len != sizeof(ctx->ctl_extra)) {
+ if (len != -1)
errno = EINVAL;
- error = -1;
- goto out;
- }
-again3:
- if (read(ctx->control_fd, buf, dlen) != (ssize_t)dlen) {
- if (errno == EAGAIN)
- goto again3;
- error = -1;
- goto out;
- }
- script_dump(buf, dlen);
- fflush(stdout);
- if (nifaces != 1)
- putchar('\n');
+ logerr(__func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ return;
}
-out:
- free(buf);
- return error;
+ if (ctx->ctl_extra == 0) {
+ eloop_exit(ctx->eloop, EXIT_SUCCESS);
+ return;
+ }
+
+ eloop_event_add(ctx->eloop, ctx->control_fd,
+ dhcpcd_readdump1, ctx);
+}
+
+static void
+dhcpcd_readdumptimeout(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ logerrx(__func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
+static int
+dhcpcd_readdump(struct dhcpcd_ctx *ctx)
+{
+
+ ctx->options |= DHCPCD_FORKED;
+ if (eloop_timeout_add_sec(ctx->eloop, 5,
+ dhcpcd_readdumptimeout, ctx) == -1)
+ return -1;
+ return eloop_event_add(ctx->eloop, ctx->control_fd,
+ dhcpcd_readdump0, ctx);
}
static void
#endif
#ifdef PRIVSEP
ctx.ps_root_fd = ctx.ps_data_fd = -1;
+#ifdef PRIVSEP_COMTROLLER
+ ctx.ps_ctl_fd = -1;
+#endif
TAILQ_INIT(&ctx.ps_processes);
#endif
rt_init(&ctx);
logerr("%s: dhcpcd_readdump", __func__);
goto exit_failure;
}
+ goto run_loop;
}
goto exit_success;
} else {
logerrx("dhcpcd is not running");
goto exit_failure;
}
+ if (errno == EPERM || errno == EACCES)
+ goto exit_failure;
}
ctx.options &= ~DHCPCD_FORKED;
}
}
#ifdef PRIVSEP
- if (ctx.options & DHCPCD_PRIVSEP && ps_start(&ctx) == -1) {
+ if (IN_PRIVSEP(&ctx) && ps_start(&ctx) == -1) {
logerr("ps_start");
goto exit_failure;
}
goto run_loop;
#endif
- if (!(ctx.options & DHCPCD_TEST) &&
- control_start(&ctx,
- ctx.options & DHCPCD_MASTER ? NULL : argv[optind], family) == -1)
- {
- logerr("%s: control_start", __func__);
- goto exit_failure;
+ if (!(ctx.options & DHCPCD_TEST)) {
+ if (control_start(&ctx,
+ ctx.options & DHCPCD_MASTER ?
+ NULL : argv[optind], family) == -1)
+ {
+ logerr("%s: control_start", __func__);
+ goto exit_failure;
+ }
}
#ifdef PLUGIN_DEV
loginfox(PACKAGE " exited");
logclose();
free(ctx.logfile);
+ free(ctx.ctl_buf);
#ifdef SETPROCTITLE_H
setproctitle_free();
#endif
size_t duid_len;
struct if_head *ifaces;
+ char *ctl_buf;
+ size_t ctl_buflen;
+ size_t ctl_bufpos;
+ size_t ctl_extra;
+
rb_tree_t routes; /* our routes */
#ifdef RT_FREE_ROUTE_TABLE
rb_tree_t froutes; /* free routes for re-use */
struct ps_process_head ps_processes; /* List of spawned processes */
pid_t ps_inet_pid;
int ps_inet_fd; /* Network Proxy commands and data */
+#ifdef PRIVSEP_CONTROLLER
+ pid_t ps_control_pid;
+ int ps_control_fd;
+ int ps_control_data_fd;
+ struct fd_list *ps_control;
+ struct fd_list *ps_control_client;
+#endif
#endif
#ifdef INET
--- /dev/null
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Privilege Separation for dhcpcd, control proxy
+ * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/resource.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dhcpcd.h"
+#include "control.h"
+#include "eloop.h"
+#include "logerr.h"
+#include "privsep.h"
+
+int
+ps_ctl_limitresources(struct dhcpcd_ctx *ctx)
+{
+ struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
+
+ if (ctx->ps_control_pid != getpid()) {
+ /* Prohibit new files, sockets, etc */
+ if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) {
+ logerr("setrlimit RLIMIT_NOFILE");
+ return -1;
+ }
+ }
+
+ /* Prohibit large files */
+ if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) {
+ logerr("setrlimit RLIMIT_FSIZE");
+ return -1;
+ }
+
+ /* Prohibit forks */
+ if (setrlimit(RLIMIT_NPROC, &rzero) == -1) {
+ logerr("setrlimit RLIMIT_NPROC");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ps_ctl_startcb(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ sa_family_t af;
+
+ if (ctx->options & DHCPCD_MASTER) {
+ setproctitle("[control proxy]");
+ af = AF_UNSPEC;
+ } else {
+ setproctitle("[control proxy] %s%s%s",
+ ctx->ifv[0],
+ ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
+ ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
+ if ((ctx->options &
+ (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV4)
+ af = AF_INET;
+ else if ((ctx->options &
+ (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV6)
+ af = AF_INET6;
+ else
+ af = AF_UNSPEC;
+ }
+
+ ctx->ps_control_pid = getpid();
+
+ return control_start(ctx,
+ ctx->options & DHCPCD_MASTER ? NULL : *ctx->ifv, af);
+}
+
+static ssize_t
+ps_ctl_recvmsgcb(void *arg, struct ps_msghdr *psm, __unused struct msghdr *msg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ if (psm->ps_cmd != PS_CTL_EOF) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if (ctx->ps_control_client != NULL)
+ ctx->ps_control_client = NULL;
+ return 0;
+}
+
+static void
+ps_ctl_recvmsg(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_recvmsgcb, ctx) == -1)
+ logerr(__func__);
+}
+
+static void
+ps_ctl_signalcb(int sig, void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ /* Ignore SIGINT, respect PS_STOP command or SIGTERM. */
+ if (sig == SIGINT)
+ return;
+
+ shutdown(ctx->ps_control_fd, SHUT_RDWR);
+ eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+ssize_t
+ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
+{
+
+ /* Make any change here in dhcpcd.c as well. */
+ if (strncmp(data, "--version",
+ MIN(strlen("--version"), len)) == 0) {
+ return control_queue(fd, UNCONST(VERSION),
+ strlen(VERSION) + 1);
+ } else if (strncmp(data, "--getconfigfile",
+ MIN(strlen("--getconfigfile"), len)) == 0) {
+ return control_queue(fd, UNCONST(fd->ctx->cffile),
+ strlen(fd->ctx->cffile) + 1);
+ } else if (strncmp(data, "--listen",
+ MIN(strlen("--listen"), len)) == 0) {
+ fd->flags |= FD_LISTEN;
+ return 0;
+ }
+
+ if (fd->ctx->ps_control_client != NULL &&
+ fd->ctx->ps_control_client != fd)
+ {
+ logerrx("%s: cannot handle another client", __func__);
+ return 0;
+ }
+ return 1;
+}
+
+static ssize_t
+ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ struct iovec *iov = msg->msg_iov;
+ struct fd_list *fd;
+ unsigned int fd_flags = FD_SENDLEN;
+
+ switch (psm->ps_flags) {
+ case PS_CTL_PRIV:
+ break;
+ case PS_CTL_UNPRIV:
+ fd_flags |= FD_UNPRIV;
+ break;
+ }
+
+ switch (psm->ps_cmd) {
+ case PS_CTL:
+ if (msg->msg_iovlen != 1) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (ctx->ps_control_client != NULL) {
+ logerrx("%s: cannot handle another client", __func__);
+ return 0;
+ }
+ fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags);
+ if (fd == NULL)
+ return -1;
+ ctx->ps_control_client = fd;
+ control_recvdata(fd, iov->iov_base, iov->iov_len);
+ break;
+ case PS_CTL_EOF:
+ fd = ctx->ps_control_client;
+ control_free(ctx->ps_control_client);
+ break;
+ default:
+ errno = ENOTSUP;
+ return -1;
+ }
+ return 0;
+}
+
+static void
+ps_ctl_dodispatch(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_dispatch, ctx) == -1)
+ logerr(__func__);
+}
+
+static void
+ps_ctl_recv(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ char buf[BUFSIZ];
+ ssize_t len;
+
+ errno = 0;
+ len = read(ctx->ps_control_data_fd, buf, sizeof(buf));
+ if (len == -1 || len == 0) {
+ logerr("%s: read", __func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ return;
+ }
+ if (ctx->ps_control_client == NULL) /* client disconnected */
+ return;
+ errno = 0;
+ if (control_queue(ctx->ps_control_client, buf, (size_t)len) == -1)
+ logerr("%s: control_queue", __func__);
+}
+
+static void
+ps_ctl_listen(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ char buf[BUFSIZ];
+ ssize_t len;
+ struct fd_list *fd;
+
+ errno = 0;
+ len = read(ctx->ps_control->fd, buf, sizeof(buf));
+ if (len == -1 || len == 0) {
+ logerr("%s: read", __func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ return;
+ }
+
+ /* Send to our listeners */
+ TAILQ_FOREACH(fd, &ctx->control_fds, next) {
+ if (!(fd->flags & FD_LISTEN))
+ continue;
+ if (control_queue(fd, buf, (size_t)len)== -1)
+ logerr("%s: control_queue", __func__);
+ }
+}
+
+pid_t
+ps_ctl_start(struct dhcpcd_ctx *ctx)
+{
+ int data_fd[2], listen_fd[2];
+ pid_t pid;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1)
+ return -1;
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
+ return -1;
+#ifdef PRIVSEP_RIGHTS
+ if (ps_rights_limit_fdpair(data_fd) == -1)
+ return -1;
+ if (ps_rights_limit_fdpair(listen_fd) == -1)
+ return -1;
+#endif
+
+ pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd,
+ ps_ctl_recvmsg, ps_ctl_dodispatch, ctx,
+ ps_ctl_startcb, ps_ctl_signalcb,
+ PSF_DROPPRIVS);
+
+ if (pid != 0) {
+ ctx->ps_control_data_fd = data_fd[1];
+ close(data_fd[0]);
+ ctx->ps_control = control_new(ctx,
+ listen_fd[1], FD_SENDLEN | FD_LISTEN);
+ if (ctx->ps_control == NULL)
+ return -1;
+ close(listen_fd[0]);
+ return pid;
+ } else if (pid == -1)
+ return -1;
+
+ ctx->ps_control_data_fd = data_fd[0];
+ close(data_fd[1]);
+ if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd,
+ ps_ctl_recv, ctx) == -1)
+ return -1;
+
+ ctx->ps_control = control_new(ctx,
+ listen_fd[0], 0);
+ close(listen_fd[1]);
+ if (ctx->ps_control == NULL)
+ return -1;
+ if (eloop_event_add(ctx->eloop, ctx->ps_control->fd,
+ ps_ctl_listen, ctx) == -1)
+ return -1;
+ return 0;
+}
+
+int
+ps_ctl_stop(struct dhcpcd_ctx *ctx)
+{
+
+ return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd);
+}
+
+ssize_t
+ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
+{
+ struct dhcpcd_ctx *ctx = fd->ctx;
+
+ if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
+ logerrx("%s: cannot deal with another client", __func__);
+ ctx->ps_control_client = fd;
+ return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL,
+ fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
+ data, len);
+}
+
+ssize_t
+ps_ctl_sendeof(struct fd_list *fd)
+{
+ struct dhcpcd_ctx *ctx = fd->ctx;
+
+ return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL_EOF, 0, NULL, 0);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Privilege Separation for dhcpcd
+ * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef PRIVSEP_CTL_H
+#define PRIVSEP_CTL_H
+
+#define IN_PRIVSEP_CONTROLLER(ctx) \
+ (IN_PRIVSEP((ctx)) && (ctx)->ps_control_pid == getpid())
+
+int ps_ctl_limitresources(struct dhcpcd_ctx *);
+pid_t ps_ctl_start(struct dhcpcd_ctx *);
+int ps_ctl_stop(struct dhcpcd_ctx *);
+ssize_t ps_ctl_handleargs(struct fd_list *, char *, size_t);
+ssize_t ps_ctl_sendargs(struct fd_list *, void *, size_t);
+ssize_t ps_ctl_sendeof(struct fd_list *fd);
+
+#endif
* this in a script or something.
*/
-#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
return -1;
}
-#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
- /* Resource limits are not needed for these sandboxes */
-#else
- struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
-
- /* We can't use RLIMIT_NOFILE because that breaks our control socket.
- * XXX Offload to a new process? */
-#if 0
-#ifndef __linux__ /* breaks ppoll */
- /* Prohibit new files, sockets, etc */
- if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) {
- logerr("setrlimit RLIMIT_NOFILE");
- return -1;
- }
-#endif
-#endif
-
- /* Prohibit large files */
- if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) {
- logerr("setrlimit RLIMIT_FSIZE");
+#ifdef PRIVSEP_CONTROLLER
+ if (ps_ctl_limitresources(ctx) == -1)
return -1;
- }
-
-#ifdef RLIMIT_NPROC
- /* Prohibit forks */
- if (setrlimit(RLIMIT_NPROC, &rzero) == -1) {
- logerr("setrlimit RLIMIT_NPROC");
- return -1;
- }
-#endif
+#elif !defined(HAVE_CAPSIUM) && !defined(HAVE_PLEDGE)
+#warning No sandbox support
#endif
return 0;
}
started:
+#ifdef PRIVSEP_CONTROLLER
+ if (!(ctx->options & DHCPCD_TEST)) {
+ switch (pid = ps_ctl_start(ctx)) {
+ case -1:
+ return -1;
+ case 0:
+ return 0;
+ default:
+ logdebugx("spawned controller on PID %d", pid);
+ }
+ }
+#endif
#ifdef ARC4RANDOM_H
/* Seed the random number generator early incase it needs /dev/urandom
ctx->eloop == NULL)
return 0;
+#ifdef PRIVSEP_CONTROLLER
+ r = ps_ctl_stop(ctx);
+ if (r != 0)
+ ret = r;
+#endif
+
r = ps_inet_stop(ctx);
if (r != 0)
ret = r;
#define PS_WRITEFILE 0x0015
#define PS_FILEMTIME 0x0016
#define PS_AUTH_MONORDM 0x0017
+#define PS_CTL 0x0018
+#define PS_CTL_EOF 0x0019
/* BSD Commands */
#define PS_IOCTLLINK 0x0101
#define PS_DEV_IFREMOVED 0x0002
#define PS_DEV_IFUPDATED 0x0003
+/* Control Type (via flags) */
+#define PS_CTL_PRIV 0x0301
+#define PS_CTL_UNPRIV 0x0302
+
/* Process commands */
#define PS_START 0x4000
#define PS_STOP 0x8000
#ifdef INET
#include "privsep-bpf.h"
#endif
+#ifdef PRIVSEP_CONTROLLER
+#include "privsep-control.h"
+#endif
int ps_init(struct dhcpcd_ctx *);
int ps_dropprivs(struct dhcpcd_ctx *);
len = make_env(ifp->ctx, ifp, reason);
if (len == -1)
return -1;
- return control_queue(fd, ctx->script_buf, (size_t)len, 1);
+ return control_queue(fd, ctx->script_buf, (size_t)len);
}
int
TAILQ_FOREACH(fd, &ctx->control_fds, next) {
if (!(fd->flags & FD_LISTEN))
continue;
- if (control_queue(fd, ctx->script_buf, ctx->script_buflen,
- true) == -1)
+ if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1)
logerr("%s: control_queue", __func__);
else
status = 1;