]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Only set RLIMIT_NOFILES for supported platforms
authorRoy Marples <roy@marples.name>
Sat, 13 Nov 2021 14:08:54 +0000 (14:08 +0000)
committerRoy Marples <roy@marples.name>
Sat, 13 Nov 2021 14:08:54 +0000 (14:08 +0000)
Basically only for NetBSD, DragonFlyBSD, kqueue or epoll platforms.
All others will use poll(2) and return EINVAL if nfds > RLIMIT_NOFILES.

Trying to work out our nfs limit relies on already listening to
all FD's which may not be the case and was problematic.
Luckily all platforms aside from Solaris have new file creation
sandboxed away aside from accept(2) so this should still be safe.

src/privsep.c

index d574a2bcaee7ad02a15870d47c389b15c77511f5..82394e0fbf0df10bd25d78d642113b92aedc937f 100644 (file)
@@ -144,33 +144,27 @@ ps_dropprivs(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 defined(__linux__) || defined(__sun) || defined(__OpenBSD__)
-               /*
-                * If poll(2) is called with nfds > RLIMIT_NOFILE
-                * then it returns EINVAL.
-                * This blows.
-                * Do the best we can and limit to what we need.
-                * An attacker could potentially close a file and
-                * open a new one still, but that cannot be helped.
-                */
-               unsigned long maxfd;
-               maxfd = (unsigned long)eloop_event_count(ctx->eloop);
-               if (IN_PRIVSEP_SE(ctx))
-                       maxfd++; /* XXX why? */
-
-               struct rlimit rmaxfd = {
-                   .rlim_cur = maxfd,
-                   .rlim_max = maxfd
-               };
-               if (setrlimit(RLIMIT_NOFILE, &rmaxfd) == -1)
-                       logerr("setrlimit RLIMIT_NOFILE");
-#else
+       /* Prohibit new files, sockets, etc */
+       /*
+        * If poll(2) is called with nfds>RLIMIT_NOFILE then it returns EINVAL.
+        * We don't know the final value of nfds at this point *easily*.
+        * Sadly, this is a POSIX limitation and most platforms adhere to it.
+        * However, some are not that strict and are whitelisted below.
+        * Also, if we're not using poll then we can be restrictive.
+        *
+        * For the non whitelisted platforms there should be a sandbox to
+        * fallback to where we don't allow new files, etc:
+        *      Linux:seccomp, FreeBSD:capsicum, OpenBSD:pledge
+        * Solaris users are sadly out of luck on both counts.
+        */
+#if defined(__NetBSD__) || defined(__DragonFly__) || \
+    defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+       /* The control proxy *does* need to create new fd's via accept(2). */
+       if (ctx->ps_ctl == NULL || ctx->ps_ctl->psp_pid != getpid()) {
                if (setrlimit(RLIMIT_NOFILE, &rzero) == -1)
                        logerr("setrlimit RLIMIT_NOFILE");
-#endif
        }
+#endif
 
 #define DHC_NOCHKIO    (DHCPCD_STARTED | DHCPCD_DAEMONISE)
        /* Prohibit writing to files.