1 /* $OpenBSD: dhcpd.c,v 1.52 2016/08/27 01:26:22 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> 5 * Copyright (c) 1995, 1996, 1997, 1998, 1999 6 * The Internet Software Consortium. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of The Internet Software Consortium nor the names 18 * of its contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 22 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 29 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * This software has been written for the Internet Software Consortium 36 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 37 * Enterprises. To learn more about the Internet Software Consortium, 38 * see ``http://www.vix.com/isc''. To learn more about Vixie 39 * Enterprises, see ``http://www.vix.com''. 40 */ 41 42 #include <sys/types.h> 43 #include <sys/socket.h> 44 45 #include <net/if.h> 46 47 #include <arpa/inet.h> 48 49 #include <err.h> 50 #include <netdb.h> 51 #include <paths.h> 52 #include <pwd.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <syslog.h> 57 #include <time.h> 58 #include <unistd.h> 59 60 #include "dhcp.h" 61 #include "tree.h" 62 #include "dhcpd.h" 63 #include "sync.h" 64 65 66 __dead void usage(void); 67 68 time_t cur_time, last_scan; 69 struct group root_group; 70 71 u_int16_t server_port; 72 u_int16_t client_port; 73 74 struct passwd *pw; 75 int log_priority; 76 int log_perror = 0; 77 int pfpipe[2]; 78 int gotpipe = 0; 79 int syncrecv; 80 int syncsend; 81 pid_t pfproc_pid = -1; 82 char *path_dhcpd_conf = _PATH_DHCPD_CONF; 83 char *path_dhcpd_db = _PATH_DHCPD_DB; 84 char *abandoned_tab = NULL; 85 char *changedmac_tab = NULL; 86 char *leased_tab = NULL; 87 struct syslog_data sdata = SYSLOG_DATA_INIT; 88 89 int 90 main(int argc, char *argv[]) 91 { 92 int ch, cftest = 0, daemonize = 1, rdomain = -1, udpsockmode = 0; 93 extern char *__progname; 94 char *sync_iface = NULL; 95 char *sync_baddr = NULL; 96 u_short sync_port = 0; 97 struct servent *ent; 98 struct in_addr udpaddr; 99 100 /* Initially, log errors to stderr as well as to syslogd. */ 101 openlog_r(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY, &sdata); 102 103 opterr = 0; 104 while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1) 105 switch (ch) { 106 case 'Y': 107 syncsend = 1; 108 break; 109 case 'y': 110 syncrecv = 1; 111 break; 112 } 113 if (syncsend || syncrecv) { 114 if ((ent = getservbyname("dhcpd-sync", "udp")) == NULL) 115 errx(1, "Can't find service \"dhcpd-sync\" in " 116 "/etc/services"); 117 sync_port = ntohs(ent->s_port); 118 } 119 120 udpaddr.s_addr = htonl(INADDR_BROADCAST); 121 122 optreset = optind = opterr = 1; 123 while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1) 124 switch (ch) { 125 case 'A': 126 abandoned_tab = optarg; 127 break; 128 case 'C': 129 changedmac_tab = optarg; 130 break; 131 case 'L': 132 leased_tab = optarg; 133 break; 134 case 'c': 135 path_dhcpd_conf = optarg; 136 break; 137 case 'd': 138 daemonize = 0; 139 log_perror = 1; 140 break; 141 case 'f': 142 daemonize = 0; 143 break; 144 case 'l': 145 path_dhcpd_db = optarg; 146 break; 147 case 'n': 148 daemonize = 0; 149 cftest = 1; 150 log_perror = 1; 151 break; 152 case 'u': 153 udpsockmode = 1; 154 if (optarg != NULL) { 155 if (inet_aton(optarg, &udpaddr) != 1) 156 errx(1, "Cannot parse binding IP " 157 "address: %s", optarg); 158 } 159 break; 160 case 'Y': 161 if (sync_addhost(optarg, sync_port) != 0) 162 sync_iface = optarg; 163 syncsend = 1; 164 break; 165 case 'y': 166 sync_baddr = optarg; 167 syncrecv = 1; 168 break; 169 default: 170 usage(); 171 } 172 173 argc -= optind; 174 argv += optind; 175 176 while (argc > 0) { 177 struct interface_info *tmp = calloc(1, sizeof(*tmp)); 178 if (!tmp) 179 error("calloc"); 180 strlcpy(tmp->name, argv[0], sizeof(tmp->name)); 181 tmp->next = interfaces; 182 interfaces = tmp; 183 argc--; 184 argv++; 185 } 186 187 /* Default DHCP/BOOTP ports. */ 188 server_port = htons(SERVER_PORT); 189 client_port = htons(CLIENT_PORT); 190 191 tzset(); 192 193 time(&cur_time); 194 if (!readconf()) 195 error("Configuration file errors encountered"); 196 197 if (cftest) 198 exit(0); 199 200 db_startup(); 201 if (!udpsockmode || argc > 0) 202 discover_interfaces(&rdomain); 203 204 if (rdomain != -1) 205 if (setrtable(rdomain) == -1) 206 error("setrtable (%m)"); 207 208 if (syncsend || syncrecv) { 209 syncfd = sync_init(sync_iface, sync_baddr, sync_port); 210 if (syncfd == -1) 211 err(1, "sync init"); 212 } 213 214 if (daemonize) 215 daemon(0, 0); 216 217 if ((pw = getpwnam("_dhcp")) == NULL) 218 error("user \"_dhcp\" not found"); 219 220 /* don't go near /dev/pf unless we actually intend to use it */ 221 if ((abandoned_tab != NULL) || 222 (changedmac_tab != NULL) || 223 (leased_tab != NULL)){ 224 if (pipe(pfpipe) == -1) 225 error("pipe (%m)"); 226 switch (pfproc_pid = fork()){ 227 case -1: 228 error("fork (%m)"); 229 /* NOTREACHED */ 230 exit(1); 231 case 0: 232 /* child process. start up table engine */ 233 close(pfpipe[1]); 234 pftable_handler(); 235 /* NOTREACHED */ 236 exit(1); 237 default: 238 close(pfpipe[0]); 239 gotpipe = 1; 240 break; 241 } 242 } 243 244 if (udpsockmode) 245 udpsock_startup(udpaddr); 246 247 icmp_startup(1, lease_pinged); 248 249 if (chroot(_PATH_VAREMPTY) == -1) 250 error("chroot %s: %m", _PATH_VAREMPTY); 251 if (chdir("/") == -1) 252 error("chdir(\"/\"): %m"); 253 if (setgroups(1, &pw->pw_gid) || 254 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 255 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 256 error("can't drop privileges: %m"); 257 258 if (udpsockmode) { 259 if (pledge("stdio inet route sendfd", NULL) == -1) 260 err(1, "pledge"); 261 } else { 262 if (pledge("stdio inet sendfd", NULL) == -1) 263 err(1, "pledge"); 264 } 265 266 add_timeout(cur_time + 5, periodic_scan, NULL); 267 dispatch(); 268 269 /* not reached */ 270 exit(0); 271 } 272 273 __dead void 274 usage(void) 275 { 276 extern char *__progname; 277 278 fprintf(stderr, "usage: %s [-dfn] [-A abandoned_ip_table]", __progname); 279 fprintf(stderr, " [-C changed_ip_table]\n"); 280 fprintf(stderr, "\t[-c config-file] [-L leased_ip_table]"); 281 fprintf(stderr, " [-l lease-file] [-u[bind_address]]\n"); 282 fprintf(stderr, "\t[-Y synctarget] [-y synclisten] [if0 [... ifN]]\n"); 283 exit(1); 284 } 285 286 void 287 lease_pinged(struct iaddr from, u_int8_t *packet, int length) 288 { 289 struct lease *lp; 290 291 /* 292 * Don't try to look up a pinged lease if we aren't trying to 293 * ping one - otherwise somebody could easily make us churn by 294 * just forging repeated ICMP EchoReply packets for us to look 295 * up. 296 */ 297 if (!outstanding_pings) 298 return; 299 300 lp = find_lease_by_ip_addr(from); 301 302 if (!lp) { 303 note("unexpected ICMP Echo Reply from %s", piaddr(from)); 304 return; 305 } 306 307 if (!lp->state && !lp->releasing) { 308 warning("ICMP Echo Reply for %s arrived late or is spurious.", 309 piaddr(from)); 310 return; 311 } 312 313 /* At this point it looks like we pinged a lease and got a 314 * response, which shouldn't have happened. 315 * if it did it's either one of two two cases: 316 * 1 - we pinged this lease before offering it and 317 * something answered, so we abandon it. 318 * 2 - we pinged this lease before releasing it 319 * and something answered, so we don't release it. 320 */ 321 if (lp->releasing) { 322 warning("IP address %s answers a ping after sending a release", 323 piaddr(lp->ip_addr)); 324 warning("Possible release spoof - Not releasing address %s", 325 piaddr(lp->ip_addr)); 326 lp->releasing = 0; 327 } else { 328 free_lease_state(lp->state, "lease_pinged"); 329 lp->state = NULL; 330 abandon_lease(lp, "pinged before offer"); 331 } 332 cancel_timeout(lease_ping_timeout, lp); 333 --outstanding_pings; 334 } 335 336 void 337 lease_ping_timeout(void *vlp) 338 { 339 struct lease *lp = vlp; 340 341 --outstanding_pings; 342 if (lp->releasing) { 343 lp->releasing = 0; 344 release_lease(lp); 345 } else 346 dhcp_reply(lp); 347 } 348 349 /* from memory.c - needed to be able to walk the lease table */ 350 extern struct subnet *subnets; 351 352 #define MINIMUM(a,b) (((a)<(b))?(a):(b)) 353 354 void 355 periodic_scan(void *p) 356 { 357 time_t x, y; 358 struct subnet *n; 359 struct group *g; 360 struct shared_network *s; 361 struct lease *l; 362 363 /* find the shortest lease this server gives out */ 364 x = MINIMUM(root_group.default_lease_time, root_group.max_lease_time); 365 for (n = subnets; n; n = n->next_subnet) 366 for (g = n->group; g; g = g->next) 367 x = MINIMUM(x, g->default_lease_time); 368 369 /* use half of the shortest lease as the scan interval */ 370 y = x / 2; 371 if (y < 1) 372 y = 1; 373 374 /* walk across all leases to find the exired ones */ 375 for (n = subnets; n; n = n->next_subnet) 376 for (g = n->group; g; g = g->next) 377 for (s = g->shared_network; s; s = s->next) 378 for (l = s->leases; l && l->ends; l = l->next) 379 if (cur_time >= l->ends) 380 if (l->ends > last_scan) 381 pfmsg('R', l); 382 383 last_scan = cur_time; 384 add_timeout(cur_time + y, periodic_scan, NULL); 385 } 386