From: Roy Marples Date: Fri, 5 Jun 2020 19:24:21 +0000 (+0100) Subject: privsep: Limit rights generically rather than Capsicum specifc X-Git-Tag: v9.1.2~36 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ef3d6a703f5c3b1fa537ae324f7d79f085f31f9;p=thirdparty%2Fdhcpcd.git 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. --- diff --git a/src/control.c b/src/control.c index 4dc8a16a..6b4ecd21 100644 --- a/src/control.c +++ b/src/control.c @@ -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 @@ control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family, 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 @@ control_unlink(struct dhcpcd_ctx *ctx, const char *file) errno = 0; #ifdef PRIVSEP - if (ctx->options & DHCPCD_PRIVSEP) + if (IN_PRIVSEP(ctx)) retval = (int)ps_root_unlink(ctx, file); else #else diff --git a/src/dhcpcd.c b/src/dhcpcd.c index c0f3a0d6..905a1029 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -2198,6 +2198,12 @@ printpidfile: 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 @@ printpidfile: 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 diff --git a/src/if-bsd.c b/src/if-bsd.c index 6f2746d0..e87c787d 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -163,6 +163,10 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) #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. */ diff --git a/src/if.c b/src/if.c index fca65c3f..c9327384 100644 --- a/src/if.c +++ b/src/if.c @@ -111,6 +111,10 @@ if_opensockets(struct dhcpcd_ctx *ctx) 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. */ diff --git a/src/privsep-inet.c b/src/privsep-inet.c index 48fb19a2..0ac2b39a 100644 --- a/src/privsep-inet.c +++ b/src/privsep-inet.c @@ -99,11 +99,6 @@ ps_inet_startcb(void *arg) { 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 @@ ps_inet_startcb(void *arg) 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 @@ ps_inet_startcb(void *arg) 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 @@ ps_inet_startcb(void *arg) 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 @@ ps_inet_listenin(void *arg) 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 @@ ps_inet_listenin(void *arg) 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 @@ static int 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 @@ ps_inet_listennd(void *arg) 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 @@ ps_inet_listenin6(void *arg) 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 @@ ps_inet_listenin6(void *arg) 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 diff --git a/src/privsep-root.c b/src/privsep-root.c index 5f5e2861..8ff32303 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -777,6 +777,12 @@ ps_root_start(struct dhcpcd_ctx *ctx) 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, diff --git a/src/privsep.c b/src/privsep.c index 271c0c39..3b836893 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -113,9 +113,6 @@ int 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,24 +129,28 @@ ps_dropprivs(struct dhcpcd_ctx *ctx) 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) { logerr("setrlimit RLIMIT_NOFILE"); 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 @@ ps_setbuf(int fd) 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 @@ ps_dostart(struct dhcpcd_ctx *ctx, 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 @@ 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 - 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 @@ ps_dostart(struct dhcpcd_ctx *ctx, 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__); @@ -391,6 +447,40 @@ started: return 1; } +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) { diff --git a/src/privsep.h b/src/privsep.h index b22dfd9f..5d7b4ec0 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -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_init(struct dhcpcd_ctx *); 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 ps_recvpsmsg(struct dhcpcd_ctx *, int, 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),