Mercurial > hg > dhcpcd
changeset 5321:41b99a2a12cf draft
privsep: Limit rights generically rather than Capsicum specifc
You never know when another sandbox tech comes around.
While here, add limits for every socket in the unpriviledged
processes. Some were absent before.
Also, note that RLIMIT_NOFILE breaks our control socket so
temporary disable that.
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Fri, 05 Jun 2020 20:24:21 +0100 |
| parents | 6b1dead2555e |
| children | 3dd079b1d37b |
| files | src/control.c src/dhcpcd.c src/if-bsd.c src/if.c src/privsep-inet.c src/privsep-root.c src/privsep.c src/privsep.h |
| diffstat | 8 files changed, 182 insertions(+), 97 deletions(-) [+] |
line wrap: on
line diff
--- a/src/control.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/control.c Fri Jun 05 20:24:21 2020 +0100 @@ -46,6 +46,7 @@ #include "eloop.h" #include "if.h" #include "logerr.h" +#include "privsep.h" #ifndef SUN_LEN #define SUN_LEN(su) \ @@ -252,6 +253,14 @@ return -1; } +#ifdef PRIVSEP_RIGHTS + if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) { + close(fd); + unlink(sa.sun_path); + return -1; + } +#endif + if ((fmode & S_UNPRIV) != S_UNPRIV) strlcpy(ctx->control_sock, sa.sun_path, sizeof(ctx->control_sock)); @@ -287,7 +296,7 @@ errno = 0; #ifdef PRIVSEP - if (ctx->options & DHCPCD_PRIVSEP) + if (IN_PRIVSEP(ctx)) retval = (int)ps_root_unlink(ctx, file); else #else
--- a/src/dhcpcd.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/dhcpcd.c Fri Jun 05 20:24:21 2020 +0100 @@ -2198,6 +2198,12 @@ logerr("pipe"); goto exit_failure; } +#ifdef HAVE_CAPSICUM + if (ps_rights_limit_fdpair(sigpipe) == -1) { + logerr("ps_rights_limit_fdpair"); + goto exit_failure; + } +#endif switch (pid = fork()) { case -1: logerr("fork"); @@ -2300,24 +2306,8 @@ logerrx("dhcp_vendor"); #ifdef PRIVSEP - if (ctx.options & DHCPCD_PRIVSEP) { - if (ps_dropprivs(&ctx) == -1) { - logerr("ps_dropprivs"); - goto exit_failure; - } -#ifdef HAVE_CAPSICUM - if (cap_enter() == -1 && errno != ENOSYS) { - logerr("%s: cap_enter", __func__); - goto exit_failure; - } -#endif -#ifdef HAVE_PLEDGE - if (pledge("stdio route", NULL) == -1) { - logerr("%s: pledge", __func__); - goto exit_failure; - } -#endif - } + if (IN_PRIVSEP(&ctx) && ps_mastersandbox(&ctx) == -1) + goto exit_failure; #endif /* When running dhcpcd against a single interface, we need to retain
--- a/src/if-bsd.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/if-bsd.c Fri Jun 05 20:24:21 2020 +0100 @@ -163,6 +163,10 @@ #ifdef INET6 priv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); +#ifdef PRIVSEP_RIGHTS + if (IN_PRIVSEP(ctx)) + ps_rights_limit_ioctl(priv->pf_inet6_fd); +#endif /* Don't return an error so we at least work on kernels witout INET6 * even though we expect INET6 support. * We will fail noisily elsewhere anyway. */
--- a/src/if.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/if.c Fri Jun 05 20:24:21 2020 +0100 @@ -111,6 +111,10 @@ ctx->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (ctx->pf_link_fd == -1) return -1; +#ifdef HAVE_CAPSICUM + if (ps_rights_limit_ioctl(ctx->pf_link_fd) == -1) + return -1; +#endif #endif /* We use this socket for some operations without INET. */
--- a/src/privsep-inet.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/privsep-inet.c Fri Jun 05 20:24:21 2020 +0100 @@ -99,11 +99,6 @@ { struct dhcpcd_ctx *ctx = arg; int ret = 0; -#ifdef HAVE_CAPSICUM - cap_rights_t rights; - - cap_rights_init(&rights, CAP_RECV, CAP_EVENT); -#endif if (ctx->options & DHCPCD_MASTER) setproctitle("[network proxy]"); @@ -126,11 +121,9 @@ ctx->udp_rfd = dhcp_openudp(NULL); if (ctx->udp_rfd == -1) logerr("%s: dhcp_open", __func__); -#ifdef HAVE_CAPSICUM - else if (cap_rights_limit(ctx->udp_rfd, &rights) == -1 - && errno != ENOSYS) - { - logerr("%s: cap_rights_limit", __func__); +#ifdef PRIVSEP_RIGHTS + else if (ps_rights_limit_fd_rdonly(ctx->udp_rfd) == -1) { + logerr("%s: ps_rights_limit_fd_rdonly", __func__); close(ctx->udp_rfd); ctx->udp_rfd = -1; } @@ -150,11 +143,9 @@ ctx->nd_fd = ipv6nd_open(true); if (ctx->nd_fd == -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__); +#ifdef PRIVSEP_RIGHTS + else if (ps_rights_limit_fd_rdonly(ctx->nd_fd) == -1) { + logerr("%s: ps_rights_limit_fd_rdonly", __func__); close(ctx->nd_fd); ctx->nd_fd = -1; } @@ -176,11 +167,9 @@ ctx->dhcp6_rfd = dhcp6_openudp(0, NULL); if (ctx->dhcp6_rfd == -1) logerr("%s: dhcp6_open", __func__); -#ifdef HAVE_CAPSICUM - else if (cap_rights_limit(ctx->dhcp6_rfd, &rights) == -1 - && errno != ENOSYS) - { - logerr("%s: cap_rights_limit", __func__); +#ifdef PRIVSEP_RIGHTS + else if (ps_rights_limit_fd_rdonly(ctx->dhcp6_rfd) == -1) { + logerr("%s: ps_rights_limit_fd_rdonly", __func__); close(ctx->dhcp6_rfd); ctx->dhcp6_rfd = -1; } @@ -398,11 +387,6 @@ 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_EVENT); -#endif inet_ntop(AF_INET, ia, buf, sizeof(buf)); setproctitle("[network proxy] %s", buf); @@ -413,11 +397,9 @@ return -1; } -#ifdef HAVE_CAPSICUM - if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && - errno != ENOSYS) - { - logerr("%s: cap_rights_limit", __func__); +#ifdef PRIVSEP_RIGHTS + if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { + logerr("%s: ps_rights_limit_fd_rdonly", __func__); return -1; } #endif @@ -449,11 +431,6 @@ ps_inet_listennd(void *arg) { struct ps_process *psp = arg; -#ifdef HAVE_CAPSICUM - cap_rights_t rights; - - cap_rights_init(&rights, CAP_RECV, CAP_EVENT); -#endif setproctitle("[ND network proxy]"); @@ -463,11 +440,9 @@ return -1; } -#ifdef HAVE_CAPSICUM - if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && - errno != ENOSYS) - { - logerr("%s: cap_rights_limit", __func__); +#ifdef PRIVSEP_RIGHTS + if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { + logerr("%s: ps_rights_limit_fd_rdonly", __func__); return -1; } #endif @@ -501,11 +476,6 @@ 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_EVENT); -#endif inet_ntop(AF_INET6, ia, buf, sizeof(buf)); setproctitle("[network proxy] %s", buf); @@ -516,11 +486,9 @@ return -1; } -#ifdef HAVE_CAPSICUM - if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && - errno != ENOSYS) - { - logerr("%s: cap_rights_limit", __func__); +#ifdef PRIVSEP_RIGHTS + if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { + logerr("%s: ps_rights_limit_fd_rdonly", __func__); return -1; } #endif
--- a/src/privsep-root.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/privsep-root.c Fri Jun 05 20:24:21 2020 +0100 @@ -777,6 +777,12 @@ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) return -1; + if (ps_setbuf_fdpair(fd) == -1) + return -1; +#ifdef PRIVSEP_RIGHTS + if (ps_rights_limit_fdpair(fd) == -1) + return -1; +#endif pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd, ps_root_recvmsg, NULL, ctx,
--- a/src/privsep.c Fri Jun 05 14:52:35 2020 +0100 +++ b/src/privsep.c Fri Jun 05 20:24:21 2020 +0100 @@ -113,9 +113,6 @@ ps_dropprivs(struct dhcpcd_ctx *ctx) { struct passwd *pw = ctx->ps_user; -#if !defined(HAVE_PLEDGE) - struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 }; -#endif if (!(ctx->options & DHCPCD_FORKED)) logdebugx("chrooting to `%s' as %s", pw->pw_dir, pw->pw_name); @@ -132,9 +129,14 @@ return -1; } -#if defined(HAVE_PLEDGE) - /* None of these resource limits work with pledge. */ +#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) { @@ -142,14 +144,13 @@ return -1; } #endif +#endif -#ifndef HAVE_CAPSICUM /* breaks sending over our IPC */ /* Prohibit large files */ if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) { logerr("setrlimit RLIMIT_FSIZE"); return -1; } -#endif #ifdef RLIMIT_NPROC /* Prohibit forks */ @@ -198,6 +199,71 @@ return 0; } +int +ps_setbuf_fdpair(int fd[]) +{ + + if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1) + return -1; + return 0; +} + +#ifdef PRIVSEP_RIGHTS +int +ps_rights_limit_ioctl(int fd) +{ + cap_rights_t rights; + + cap_rights_init(&rights, CAP_IOCTL); + if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) + return -1; + return 0; +} + +int +ps_rights_limit_fd_fctnl(int fd) +{ + cap_rights_t rights; + + cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, + CAP_ACCEPT, CAP_FCNTL); + if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) + return -1; + return 0; +} + +int +ps_rights_limit_fd(int fd) +{ + cap_rights_t rights; + + cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN); + if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) + return -1; + return 0; +} + +int +ps_rights_limit_fd_rdonly(int fd) +{ + cap_rights_t rights; + + cap_rights_init(&rights, CAP_READ, CAP_EVENT); + if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS) + return -1; + return 0; +} + +int +ps_rights_limit_fdpair(int fd[]) +{ + + if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1) + return -1; + return 0; +} +#endif + pid_t ps_dostart(struct dhcpcd_ctx *ctx, pid_t *priv_pid, int *priv_fd, @@ -208,17 +274,22 @@ 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) { - logerr("socketpair"); + logerr("%s: socketpair", __func__); + return -1; + } + if (ps_setbuf_fdpair(fd) == -1) { + logerr("%s: ps_setbuf_fdpair", __func__); return -1; } +#ifdef PRIVSEP_RIGHTS + if (ps_rights_limit_fdpair(fd) == -1) { + logerr("%s: ps_rights_limit_fdpair", __func__); + return -1; + } +#endif switch (pid = fork()) { case -1: @@ -227,23 +298,13 @@ 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 - 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) { @@ -284,11 +345,6 @@ 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__); @@ -392,6 +448,40 @@ } int +ps_mastersandbox(struct dhcpcd_ctx *ctx) +{ + + if (ps_dropprivs(ctx) == -1) { + logerr("%s: ps_dropprivs", __func__); + return -1; + } + +#ifdef PRIVSEP_RIGHTS + if ((ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1 || + ps_rights_limit_fd(ctx->link_fd) == -1) && + errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + return -1; + } +#endif +#ifdef HAVE_CAPSICUM + if (cap_enter() == -1 && errno != ENOSYS) { + logerr("%s: cap_enter", __func__); + return -1; + } +#endif +#ifdef HAVE_PLEDGE + if (pledge("stdio route", NULL) == -1) { + logerr("%s: pledge", __func__); + return -1; + } +#endif + + return 0; +} + +int ps_stop(struct dhcpcd_ctx *ctx) { int r, ret = 0;
--- a/src/privsep.h Fri Jun 05 14:52:35 2020 +0100 +++ b/src/privsep.h Fri Jun 05 20:24:21 2020 +0100 @@ -86,6 +86,11 @@ #define IN_PRIVSEP_SE(ctx) \ (((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP) + +#if defined(PRIVSEP) && defined(HAVE_CAPSICUM) +#define PRIVSEP_RIGHTS +#endif + #include "config.h" #include "arp.h" #include "dhcp.h" @@ -157,6 +162,7 @@ int ps_dropprivs(struct dhcpcd_ctx *); int ps_start(struct dhcpcd_ctx *); int ps_stop(struct dhcpcd_ctx *); +int ps_mastersandbox(struct dhcpcd_ctx *); int ps_unrollmsg(struct msghdr *, struct ps_msghdr *, const void *, size_t); ssize_t ps_sendpsmmsg(struct dhcpcd_ctx *, int, @@ -172,6 +178,14 @@ ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *); /* Internal privsep functions. */ +int ps_setbuf_fdpair(int []); +#ifdef PRIVSEP_RIGHTS +int ps_rights_limit_ioctl(int); +int ps_rights_limit_fd_fctnl(int); +int ps_rights_limit_fd_rdonly(int); +int ps_rights_limit_fd(int); +int ps_rights_limit_fdpair(int []); +#endif pid_t ps_dostart(struct dhcpcd_ctx * ctx, pid_t *priv_pid, int *priv_fd, void (*recv_msg)(void *), void (*recv_unpriv_msg),
