Hi tech@ This is a revised version of pledging dhcpd(8) with earler pledging.
Hoist up sync_init() due to a multicast setsockopt(2) (IP_MULTICAST_TTL) that pledge doesn't allow, also hoist up the daemon(3) section, getpwnam(3) and the check if arguments -A, -C or -L were used (pf table handling) since it calls 2 ioctl(2)'s that pledge pf doesn't allow. After this if !udpsockmode then apply the following annotations: "rpath": icmp_startup()->getprotobyname(3)->read /etc/protocols "inet": icmp_startup()->socket(2) "sendfd": for sendmsg(2) in ICMP echo request "proc/id" chroot(2) and privdrop section If in udpsockmode then the pledge needs to happen inside udpsock_startup() instead of main() since setsockopt(2) IP_RECVIF is not allowed by pledge. After that happens then apply the same pledge with the annotations above, although additionally this code path also needs "route" for ioctl(2) SIOCGIFADDR. Just before the main loop of the program then it can drop to "stdio inet route sendfd" if in udpsockmode or else just to "stdio inet sendfd". Any comments with this implementation? Specifically for the UDP code path since I don't have at the moment a way to test DHCPINFORM requests on non Ethernet packets? Index: dhcpd.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.c,v retrieving revision 1.48 diff -u -p -u -r1.48 dhcpd.c --- dhcpd.c 10 Feb 2015 23:06:13 -0000 1.48 +++ dhcpd.c 2 Dec 2015 11:28:50 -0000 @@ -45,7 +45,7 @@ #include <err.h> #include <pwd.h> -void usage(void); +__dead void usage(void); time_t cur_time, last_scan; struct group root_group; @@ -187,22 +187,18 @@ main(int argc, char *argv[]) if (setrtable(rdomain) == -1) error("setrtable (%m)"); - if (udpsockmode) - udpsock_startup(udpaddr); - icmp_startup(1, lease_pinged); - if (syncsend || syncrecv) { syncfd = sync_init(sync_iface, sync_baddr, sync_port); if (syncfd == -1) err(1, "sync init"); } - if ((pw = getpwnam("_dhcp")) == NULL) - error("user \"_dhcp\" not found"); - if (daemonize) daemon(0, 0); + if ((pw = getpwnam("_dhcp")) == NULL) + error("user \"_dhcp\" not found"); + /* don't go near /dev/pf unless we actually intend to use it */ if ((abandoned_tab != NULL) || (changedmac_tab != NULL) || @@ -227,6 +223,15 @@ main(int argc, char *argv[]) } } + if (udpsockmode) { + udpsock_startup(udpaddr); + } else { + if (pledge("stdio rpath inet sendfd proc id", NULL) == -1) + err(1, "pledge"); + } + + icmp_startup(1, lease_pinged); + if (chroot(_PATH_VAREMPTY) == -1) error("chroot %s: %m", _PATH_VAREMPTY); if (chdir("/") == -1) @@ -236,6 +241,14 @@ main(int argc, char *argv[]) setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) error("can't drop privileges: %m"); + if (udpsockmode) { + if (pledge("stdio inet route sendfd", NULL) == -1) + err(1, "pledge"); + } else { + if (pledge("stdio inet sendfd", NULL) == -1) + err(1, "pledge"); + } + add_timeout(cur_time + 5, periodic_scan, NULL); dispatch(); @@ -243,7 +256,7 @@ main(int argc, char *argv[]) exit(0); } -void +__dead void usage(void) { extern char *__progname; Index: udpsock.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/udpsock.c,v retrieving revision 1.2 diff -u -p -u -r1.2 udpsock.c --- udpsock.c 16 Jan 2015 06:40:16 -0000 1.2 +++ udpsock.c 2 Dec 2015 11:28:55 -0000 @@ -56,6 +56,9 @@ udpsock_startup(struct in_addr bindaddr) error("setsocketopt IP_RECVIF failed for udp: %s", strerror(errno)); + if (pledge("stdio rpath inet route sendfd proc id", NULL) == -1) + error("pledge: %s", strerror(errno)); + sin4.sin_family = AF_INET; sin4.sin_len = sizeof(sin4); sin4.sin_addr = bindaddr;