All fd's in network facing processes are fully limited.
Capability mode is only enabled for BPF processes because
it's too restrictive otherwise - the reasons are noted
in the commit.
echo "EMBEDDEDINSTALL= _embeddedinstall" >>$CONFIG_MK
fi
+if [ "$PRIVSEP" = yes ]; then
+ printf "Testing for capsicum ... "
+ cat <<EOF >_capsicum.c
+#include <sys/capsicum.h>
+int main(void) {
+ return cap_enter();
+}
+EOF
+ if $XCC _capsicum.c -o _capsicum 2>&3; then
+ echo "yes"
+ echo "#define HAVE_CAPSICUM" >>$CONFIG_H
+ else
+ echo "no"
+ fi
+ rm -f _capsicum.c _capsicum
+fi
+
if [ "$OS" = linux ]; then
printf "Testing for nl80211 ... "
cat <<EOF >_nl80211.c
dev_start(&ctx);
#ifdef PRIVSEP
- if (ctx.options & DHCPCD_PRIVSEP && ps_dropprivs(&ctx) == -1) {
- logerr("ps_dropprivs");
- goto exit_failure;
+ if (ctx.options & DHCPCD_PRIVSEP) {
+ /*
+ * PSF_CAP_ENTER is not set because the following functions
+ * won't work in it:
+ * getifaddrs(3), gethostname(3), uname(3).
+ */
+ if (ps_dropprivs(&ctx, 0) == -1) {
+ logerr("ps_dropprivs");
+ goto exit_failure;
+ }
}
#endif
#include "logerr.h"
#include "privsep.h"
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+
static void
ps_bpf_recvbpf(void *arg)
{
{
struct ps_process *psp = arg;
struct dhcpcd_ctx *ctx = psp->psp_ctx;
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+
+ /* We need CAP_IOCTL so we can change the BPF filter when we
+ * need to. */
+ cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_IOCTL);
+#endif
setproctitle("[BPF %s] %s", psp->psp_protostr, psp->psp_ifname);
psp->psp_work_fd = bpf_open(&psp->psp_ifp, psp->psp_filter);
if (psp->psp_work_fd == -1)
logerr("%s: bpf_open",__func__);
+#ifdef HAVE_CAPSICUM
+ else if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+ errno != ENOSYS)
+ logerr("%s: cap_rights_limit", __func__);
+#endif
else if (eloop_event_add(ctx->eloop,
psp->psp_work_fd, ps_bpf_recvbpf, psp) == -1)
logerr("%s: eloop_event_add", __func__);
start = ps_dostart(ctx,
&psp->psp_pid, &psp->psp_fd,
ps_bpf_recvmsg, NULL, psp,
- ps_bpf_start_bpf, ps_bpf_signal_bpfcb, PSF_DROPPRIVS);
+ ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
+ PSF_DROPPRIVS | PSF_CAP_ENTER);
switch (start) {
case -1:
ps_freeprocess(psp);
#include "logerr.h"
#include "privsep.h"
+#ifdef HAVE_CAPSICUM
+/* We never call ps_dostart with PSF_CAP_ENTER because
+ * our sockets require the use of CAP_CONNECT which does not
+ * work in capabilities mode according to rights(4). */
+#include <sys/capsicum.h>
+#endif
+
#ifdef INET
static void
ps_inet_recvbootp(void *arg)
{
struct dhcpcd_ctx *ctx = arg;
int ret = 0;
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
if (ctx->options & DHCPCD_MASTER)
setproctitle("[network proxy]");
ctx->udp_fd = dhcp_openudp(NULL);
if (ctx->udp_fd == -1)
logerr("%s: dhcp_open", __func__);
+#ifdef HAVE_CAPSICUM
+ else if (cap_rights_limit(ctx->udp_fd, &rights) == -1
+ && errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ close(ctx->udp_fd);
+ ctx->udp_fd = -1;
+ }
+#endif
else if (eloop_event_add(ctx->eloop, ctx->udp_fd,
ps_inet_recvbootp, ctx) == -1)
{
if (ctx->options & DHCPCD_IPV6) {
if (ipv6nd_open(ctx) == -1)
logerr("%s: ipv6nd_open", __func__);
+#ifdef HAVE_CAPSICUM
+ else if (cap_rights_limit(ctx->nd_fd, &rights) == -1
+ && errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ close(ctx->nd_fd);
+ ctx->nd_fd = -1;
+ }
+#endif
else if (eloop_event_add(ctx->eloop, ctx->nd_fd,
ps_inet_recvra, ctx) == -1)
{
ctx->dhcp6_fd = dhcp6_openudp(0, NULL);
if (ctx->dhcp6_fd == -1)
logerr("%s: dhcp6_open", __func__);
+#ifdef HAVE_CAPSICUM
+ else if (cap_rights_limit(ctx->dhcp6_fd, &rights) == -1
+ && errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ close(ctx->dhcp6_fd);
+ ctx->dhcp6_fd = -1;
+ }
+#endif
else if (eloop_event_add(ctx->eloop, ctx->dhcp6_fd,
ps_inet_recvdhcp6, ctx) == -1)
{
struct ps_process *psp = arg;
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
char buf[INET_ADDRSTRLEN];
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
inet_ntop(AF_INET, ia, buf, sizeof(buf));
setproctitle("[network proxy] %s", buf);
return -1;
}
+#ifdef HAVE_CAPSICUM
+ if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+ errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ return -1;
+ }
+#endif
+
if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
ps_inet_recvinbootp, psp) == -1)
{
return -1;
}
-#ifdef PRIVSEP_DEBUG
logdebugx("spawned listener %s on PID %d", buf, getpid());
-#endif
return 0;
}
#endif
ps_inet_listennd(void *arg)
{
struct ps_process *psp = arg;
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
setproctitle("[ND network proxy]");
return -1;
}
+#ifdef HAVE_CAPSICUM
+ if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+ errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ return -1;
+ }
+#endif
+
if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
ps_inet_recvin6nd, psp) == -1)
{
struct ps_process *psp = arg;
struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;
char buf[INET6_ADDRSTRLEN];
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT);
+#endif
inet_ntop(AF_INET6, ia, buf, sizeof(buf));
setproctitle("[network proxy] %s", buf);
return -1;
}
+#ifdef HAVE_CAPSICUM
+ if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 &&
+ errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ return -1;
+ }
+#endif
+
if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
ps_inet_recvin6dhcp6, psp) == -1)
{
#include "logerr.h"
#include "privsep.h"
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
}
int
-ps_dropprivs(struct dhcpcd_ctx *ctx)
+ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags)
{
struct passwd *pw = ctx->ps_user;
return -1;
}
+#ifdef HAVE_CAPSICUM
+ if (flags & PSF_CAP_ENTER) {
+ if (cap_enter() == -1 && errno != ENOSYS) {
+ logerr("%s: cap_enter", __func__);
+ return -1;
+ }
+ }
+#endif
return 0;
}
int stype;
int fd[2];
pid_t pid;
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);
+#endif
stype = SOCK_CLOEXEC | SOCK_NONBLOCK;
if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) {
*priv_pid = pid;
*priv_fd = fd[0];
close(fd[1]);
- if (recv_unpriv_msg != NULL &&
- eloop_event_add(ctx->eloop, *priv_fd,
+ if (recv_unpriv_msg == NULL)
+ ;
+#ifdef HAVE_CAPSICUM
+ else if (cap_rights_limit(*priv_fd, &rights) == -1
+ && errno != ENOSYS)
+ {
+ logerr("%s: cap_rights_limit", __func__);
+ return -1;
+ }
+#endif
+ else if (eloop_event_add(ctx->eloop, *priv_fd,
recv_unpriv_msg, recv_ctx) == -1)
{
logerr("%s: eloop_event_add", __func__);
goto errexit;
}
+#ifdef HAVE_CAPSICUM
+ if (cap_rights_limit(*priv_fd, &rights) == -1 && errno != ENOSYS)
+ goto errexit;
+#endif
+
if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
{
logerr("%s: eloop_event_add", __func__);
}
if (flags & PSF_DROPPRIVS)
- ps_dropprivs(ctx);
+ ps_dropprivs(ctx, flags);
return 0;
/* Start flags */
#define PSF_DROPPRIVS 0x01
+#define PSF_CAP_ENTER 0x02
/* Commands */
#define PS_BOOTP 0x01
int ps_mkdir(char *);
int ps_init(struct dhcpcd_ctx *);
-int ps_dropprivs(struct dhcpcd_ctx *);
+int ps_dropprivs(struct dhcpcd_ctx *, unsigned int);
int ps_start(struct dhcpcd_ctx *);
int ps_stop(struct dhcpcd_ctx *);