GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/dhcpd.c Lines: 0 157 0.0 %
Date: 2017-11-13 Branches: 0 108 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dhcpd.c,v 1.56 2017/02/16 00:24:43 krw 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 "log.h"
64
#include "sync.h"
65
66
67
__dead void usage(void);
68
69
time_t cur_time, last_scan;
70
struct group root_group;
71
72
u_int16_t server_port;
73
u_int16_t client_port;
74
75
struct passwd *pw;
76
int log_priority;
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
88
int
89
main(int argc, char *argv[])
90
{
91
	int ch, cftest = 0, daemonize = 1, rdomain = -1, udpsockmode = 0;
92
	char *sync_iface = NULL;
93
	char *sync_baddr = NULL;
94
	u_short sync_port = 0;
95
	struct servent *ent;
96
	struct in_addr udpaddr;
97
98
	log_init(1, LOG_DAEMON);	/* log to stderr until daemonized */
99
	log_setverbose(1);
100
101
	opterr = 0;
102
	while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1)
103
		switch (ch) {
104
		case 'Y':
105
			syncsend = 1;
106
			break;
107
		case 'y':
108
			syncrecv = 1;
109
			break;
110
		}
111
	if (syncsend || syncrecv) {
112
		if ((ent = getservbyname("dhcpd-sync", "udp")) == NULL)
113
			errx(1, "Can't find service \"dhcpd-sync\" in "
114
			    "/etc/services");
115
		sync_port = ntohs(ent->s_port);
116
	}
117
118
	udpaddr.s_addr = htonl(INADDR_BROADCAST);
119
120
	optreset = optind = opterr = 1;
121
	while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nu::Y:y:")) != -1)
122
		switch (ch) {
123
		case 'A':
124
			abandoned_tab = optarg;
125
			break;
126
		case 'C':
127
			changedmac_tab = optarg;
128
			break;
129
		case 'L':
130
			leased_tab = optarg;
131
			break;
132
		case 'c':
133
			path_dhcpd_conf = optarg;
134
			break;
135
		case 'd':
136
			daemonize = 0;
137
			break;
138
		case 'f':
139
			daemonize = 0;
140
			break;
141
		case 'l':
142
			path_dhcpd_db = optarg;
143
			break;
144
		case 'n':
145
			daemonize = 0;
146
			cftest = 1;
147
			break;
148
		case 'u':
149
			udpsockmode = 1;
150
			if (optarg != NULL) {
151
				if (inet_aton(optarg, &udpaddr) != 1)
152
					errx(1, "Cannot parse binding IP "
153
					    "address: %s", optarg);
154
			}
155
			break;
156
		case 'Y':
157
			if (sync_addhost(optarg, sync_port) != 0)
158
				sync_iface = optarg;
159
			syncsend = 1;
160
			break;
161
		case 'y':
162
			sync_baddr = optarg;
163
			syncrecv = 1;
164
			break;
165
		default:
166
			usage();
167
		}
168
169
	argc -= optind;
170
	argv += optind;
171
172
	while (argc > 0) {
173
		struct interface_info *tmp = calloc(1, sizeof(*tmp));
174
		if (!tmp)
175
			fatalx("calloc");
176
		strlcpy(tmp->name, argv[0], sizeof(tmp->name));
177
		tmp->next = interfaces;
178
		interfaces = tmp;
179
		argc--;
180
		argv++;
181
	}
182
183
	/* Default DHCP/BOOTP ports. */
184
	server_port = htons(SERVER_PORT);
185
	client_port = htons(CLIENT_PORT);
186
187
	tzset();
188
189
	time(&cur_time);
190
	if (!readconf())
191
		fatalx("Configuration file errors encountered");
192
193
	if (cftest)
194
		exit(0);
195
196
	db_startup();
197
	if (!udpsockmode || argc > 0)
198
		discover_interfaces(&rdomain);
199
200
	if (rdomain != -1)
201
		if (setrtable(rdomain) == -1)
202
			fatal("setrtable");
203
204
	if (syncsend || syncrecv) {
205
		syncfd = sync_init(sync_iface, sync_baddr, sync_port);
206
		if (syncfd == -1)
207
			err(1, "sync init");
208
	}
209
210
	if (daemonize)
211
		daemon(0, 0);
212
213
	log_init(0, LOG_DAEMON);	/* stop logging to stderr */
214
	log_setverbose(0);
215
216
	if ((pw = getpwnam("_dhcp")) == NULL)
217
		fatalx("user \"_dhcp\" not found");
218
219
	/* don't go near /dev/pf unless we actually intend to use it */
220
	if ((abandoned_tab != NULL) ||
221
	    (changedmac_tab != NULL) ||
222
	    (leased_tab != NULL)){
223
		if (pipe(pfpipe) == -1)
224
			fatal("pipe");
225
		switch (pfproc_pid = fork()){
226
		case -1:
227
			fatal("fork");
228
			/* NOTREACHED */
229
			exit(1);
230
		case 0:
231
			/* child process. start up table engine */
232
			close(pfpipe[1]);
233
			pftable_handler();
234
			/* NOTREACHED */
235
			exit(1);
236
		default:
237
			close(pfpipe[0]);
238
			gotpipe = 1;
239
			break;
240
		}
241
	}
242
243
	if (udpsockmode)
244
		udpsock_startup(udpaddr);
245
246
	icmp_startup(1, lease_pinged);
247
248
	if (chroot(_PATH_VAREMPTY) == -1)
249
		fatal("chroot %s", _PATH_VAREMPTY);
250
	if (chdir("/") == -1)
251
		fatal("chdir(\"/\")");
252
	if (setgroups(1, &pw->pw_gid) ||
253
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
254
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
255
		fatal("can't drop privileges");
256
257
	if (udpsockmode) {
258
		if (pledge("stdio inet route sendfd flock rpath cpath wpath", NULL) == -1)
259
			err(1, "pledge");
260
	} else {
261
		if (pledge("stdio inet sendfd flock rpath cpath wpath", NULL) == -1)
262
			err(1, "pledge");
263
	}
264
265
	add_timeout(cur_time + 5, periodic_scan, NULL);
266
	dispatch();
267
268
	/* not reached */
269
	exit(0);
270
}
271
272
__dead void
273
usage(void)
274
{
275
	extern char *__progname;
276
277
	fprintf(stderr, "usage: %s [-dfn] [-A abandoned_ip_table]",
278
	    __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
		log_info("unexpected ICMP Echo Reply from %s", piaddr(from));
304
		return;
305
	}
306
307
	if (!lp->state && !lp->releasing) {
308
		log_warnx("ICMP Echo Reply for %s arrived late or is "
309
		    "spurious.", 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
		log_warnx("IP address %s answers a ping after sending a "
323
		    "release", piaddr(lp->ip_addr));
324
		log_warnx("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
}