1 /* 2 * Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <sys/types.h> 18 #include <sys/ioctl.h> 19 #include <sys/param.h> 20 #include <sys/socket.h> 21 #include <sys/time.h> 22 23 #include <net/if.h> 24 #include <net/pfvar.h> 25 #include <arpa/inet.h> 26 27 #include <ctype.h> 28 #include <err.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <poll.h> 32 #include <pwd.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "dhcpd.h" 39 40 extern struct passwd *pw; 41 extern int pfpipe[2]; 42 extern char *abandoned_tab; 43 extern char *changedmac_tab; 44 45 __dead void 46 pftable_handler() 47 { 48 struct pf_cmd cmd; 49 struct pollfd pfd[1]; 50 int l, r, fd, nfds; 51 52 if ((fd = open(_PATH_DEV_PF, O_RDWR|O_NOFOLLOW, 0660)) == -1) 53 error("can't open pf device: %m"); 54 if (chroot(_PATH_VAREMPTY) == -1) 55 error("chroot %s: %m", _PATH_VAREMPTY); 56 if (chdir("/") == -1) 57 error("chdir(\"/\"): %m"); 58 if (setgroups(1, &pw->pw_gid) || 59 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 60 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 61 error("can't drop privileges: %m"); 62 63 setproctitle("pf table handler"); 64 l = sizeof(struct pf_cmd); 65 66 for(;;){ 67 pfd[0].fd = fd; 68 pfd[0].events = POLLIN; 69 if ((nfds = poll(pfd, 1, -1)) == -1) 70 if (errno != EINTR) 71 error("poll: %m"); 72 73 if (nfds > 0 && (pfd[0].revents & POLLIN)){ 74 bzero(&cmd, l); 75 r = atomicio(read, pfpipe[0], &cmd, l); 76 77 if (r != l) 78 error("pf pipe error: %m"); 79 80 switch (cmd.type){ 81 case 'A': 82 pf_change_table(fd, 1, cmd.ip, abandoned_tab); 83 pf_kill_state(fd, cmd.ip); 84 break; 85 case 'C': 86 pf_change_table(fd, 0, cmd.ip, abandoned_tab); 87 pf_change_table(fd, 0, cmd.ip, changedmac_tab); 88 break; 89 case 'L': 90 pf_change_table(fd, 0, cmd.ip, abandoned_tab); 91 break; 92 default: 93 break; 94 } 95 } 96 } 97 /* not reached */ 98 exit(1); 99 } 100 101 /* inspired by ("stolen") from usr.sbin/authpf/authpf.c */ 102 void 103 pf_change_table(int fd, int op, struct in_addr ip, char *table) 104 { 105 struct pfioc_table io; 106 struct pfr_addr addr; 107 108 bzero(&io, sizeof(io)); 109 strlcpy(io.pfrio_table.pfrt_name, table, 110 sizeof(io.pfrio_table.pfrt_name)); 111 io.pfrio_buffer = &addr; 112 io.pfrio_esize = sizeof(addr); 113 io.pfrio_size = 1; 114 115 bzero(&addr, sizeof(addr)); 116 bcopy(&ip, &addr.pfra_ip4addr, 4); 117 addr.pfra_af = AF_INET; 118 addr.pfra_net = 32; 119 120 if (ioctl(fd, op ? DIOCRADDADDRS : DIOCRDELADDRS, &io) && 121 errno != ESRCH) { 122 warning( "DIOCR%sADDRS on table %s: %s", 123 op ? "ADD" : "DEL", table, strerror(errno)); 124 } 125 } 126 127 void 128 pf_kill_state(int fd, struct in_addr ip) 129 { 130 struct pfioc_state_kill psk; 131 struct pf_addr target; 132 133 bzero(&psk, sizeof(psk)); 134 bzero(&target, sizeof(target)); 135 136 bcopy(&ip.s_addr, &target.v4, 4); 137 psk.psk_af = AF_INET; 138 139 /* Kill all states from target */ 140 bcopy(&target, &psk.psk_src.addr.v.a.addr, 141 sizeof(psk.psk_src.addr.v.a.addr)); 142 memset(&psk.psk_src.addr.v.a.mask, 0xff, 143 sizeof(psk.psk_src.addr.v.a.mask)); 144 if (ioctl(fd, DIOCKILLSTATES, &psk)){ 145 warning("DIOCKILLSTATES failed (%s)", strerror(errno)); 146 } 147 148 /* Kill all states to target */ 149 bzero(&psk.psk_src, sizeof(psk.psk_src)); 150 bcopy(&target, &psk.psk_dst.addr.v.a.addr, 151 sizeof(psk.psk_dst.addr.v.a.addr)); 152 memset(&psk.psk_dst.addr.v.a.mask, 0xff, 153 sizeof(psk.psk_dst.addr.v.a.mask)); 154 if (ioctl(fd, DIOCKILLSTATES, &psk)){ 155 warning("DIOCKILLSTATES failed (%s)", strerror(errno)); 156 } 157 } 158 159 /* inspired by ("stolen") from usr.bin/ssh/atomicio.c */ 160 size_t 161 atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) 162 { 163 char *s = _s; 164 size_t pos = 0; 165 ssize_t res; 166 167 while (n > pos) { 168 res = (f) (fd, s + pos, n - pos); 169 switch (res) { 170 case -1: 171 if (errno == EINTR || errno == EAGAIN) 172 continue; 173 return 0; 174 case 0: 175 errno = EPIPE; 176 return pos; 177 default: 178 pos += (size_t)res; 179 } 180 } 181 return (pos); 182 } 183