xref: /netbsd/usr.sbin/bootp/bootpd/bootpd.c (revision c4a72b64)
1 /************************************************************************
2           Copyright 1988, 1991 by Carnegie Mellon University
3 
4                           All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted, provided
8 that the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation, and that the name of Carnegie Mellon University not be used
11 in advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13 
14 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16 IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 ************************************************************************/
22 
23 #include <sys/cdefs.h>
24 #ifndef lint
25 __RCSID("$NetBSD: bootpd.c,v 1.16 2002/09/18 23:16:13 mycroft Exp $");
26 #endif
27 
28 /*
29  * BOOTP (bootstrap protocol) server daemon.
30  *
31  * Answers BOOTP request packets from booting client machines.
32  * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
33  * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
34  * See RFC 1395 for option tags 14-17.
35  * See accompanying man page -- bootpd.8
36  *
37  * HISTORY
38  *	See ./Changes
39  *
40  * BUGS
41  *	See ./ToDo
42  */
43 
44 
45 
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <sys/ioctl.h>
50 #include <sys/file.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 #include <sys/poll.h>
54 
55 #include <net/if.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>	/* inet_ntoa */
58 
59 #ifndef	NO_UNISTD
60 #include <unistd.h>
61 #endif
62 #include <stdlib.h>
63 #include <signal.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <netdb.h>
69 #include <syslog.h>
70 #include <assert.h>
71 
72 #ifdef	NO_SETSID
73 # include <fcntl.h>		/* for O_RDONLY, etc */
74 #endif
75 
76 #ifdef	SVR4
77 /* Using sigset() avoids the need to re-arm each time. */
78 #define signal sigset
79 #endif
80 
81 #ifndef	USE_BFUNCS
82 # include <memory.h>
83 /* Yes, memcpy is OK here (no overlapped copies). */
84 # define bcopy(a,b,c)    memcpy(b,a,c)
85 # define bzero(p,l)      memset(p,0,l)
86 # define bcmp(a,b,c)     memcmp(a,b,c)
87 #endif
88 
89 #include "bootp.h"
90 #include "hash.h"
91 #include "hwaddr.h"
92 #include "bootpd.h"
93 #include "dovend.h"
94 #include "getif.h"
95 #include "readfile.h"
96 #include "report.h"
97 #include "tzone.h"
98 #include "patchlevel.h"
99 
100 #ifndef CONFIG_FILE
101 #define CONFIG_FILE		"/etc/bootptab"
102 #endif
103 #ifndef DUMPTAB_FILE
104 #define DUMPTAB_FILE		"/tmp/bootpd.dump"
105 #endif
106 
107 
108 
109 /*
110  * Externals, forward declarations, and global variables
111  */
112 
113 extern void dumptab(char *);
114 
115 PRIVATE void catcher(int);
116 PRIVATE int chk_access(char *, int32 *);
117 #ifdef VEND_CMU
118 PRIVATE void dovend_cmu(struct bootp *, struct host *);
119 #endif
120 PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
121 PRIVATE void handle_reply(void);
122 PRIVATE void handle_request(void);
123 PRIVATE void sendreply(int forward, int32 dest_override);
124 PRIVATE void usage(void);
125 int main(int, char **);
126 
127 /*
128  * IP port numbers for client and server obtained from /etc/services
129  */
130 
131 u_short bootps_port, bootpc_port;
132 
133 
134 /*
135  * Internet socket and interface config structures
136  */
137 
138 struct sockaddr_in bind_addr;	/* Listening */
139 struct sockaddr_in recv_addr;	/* Packet source */
140 struct sockaddr_in send_addr;	/*  destination */
141 
142 
143 /*
144  * option defaults
145  */
146 int debug = 0;					/* Debugging flag (level) */
147 int actualtimeout = 15 * 60000;			/* fifteen minutes */
148 
149 /*
150  * General
151  */
152 
153 int s;							/* Socket file descriptor */
154 char *pktbuf;					/* Receive packet buffer */
155 int pktlen;
156 char *progname;
157 char *chdir_path;
158 char hostname[MAXHOSTNAMELEN + 1];	/* System host name */
159 struct in_addr my_ip_addr;
160 
161 /* Flags set by signal catcher. */
162 PRIVATE int do_readtab = 0;
163 PRIVATE int do_dumptab = 0;
164 
165 /*
166  * Globals below are associated with the bootp database file (bootptab).
167  */
168 
169 char *bootptab = CONFIG_FILE;
170 char *bootpd_dump = DUMPTAB_FILE;
171 
172 
173 
174 /*
175  * Initialization such as command-line processing is done and then the
176  * main server loop is started.
177  */
178 
179 int
180 main(int argc, char **argv)
181 {
182 	int timeout;
183 	struct bootp *bp;
184 	struct servent *servp;
185 	struct hostent *hep;
186 	char *stmp;
187 	int n, ba_len, ra_len;
188 	int nfound;
189 	struct pollfd set[1];
190 	int standalone;
191 
192 	progname = strrchr(argv[0], '/');
193 	if (progname)
194 		progname++;
195 	else
196 		progname = argv[0];
197 
198 	/*
199 	 * Initialize logging.
200 	 */
201 	report_init(0);				/* uses progname */
202 
203 	/*
204 	 * Log startup
205 	 */
206 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
207 
208 	/* Debugging for compilers with struct padding. */
209 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
210 
211 	/* Get space for receiving packets and composing replies. */
212 	pktbuf = malloc(MAX_MSG_SIZE);
213 	if (!pktbuf) {
214 		report(LOG_ERR, "malloc failed");
215 		exit(1);
216 	}
217 	bp = (struct bootp *) pktbuf;
218 
219 	/*
220 	 * Check to see if a socket was passed to us from inetd.
221 	 *
222 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
223 	 * (and thus we are probably a child of inetd) or if it is instead
224 	 * something else and we are running standalone.
225 	 */
226 	s = 0;
227 	ba_len = sizeof(bind_addr);
228 	bzero((char *) &bind_addr, ba_len);
229 	errno = 0;
230 	standalone = TRUE;
231 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
232 		/*
233 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
234 		 */
235 		if (bind_addr.sin_family == AF_INET) {
236 			standalone = FALSE;
237 			bootps_port = ntohs(bind_addr.sin_port);
238 		} else {
239 			/* Some other type of socket? */
240 			report(LOG_ERR, "getsockname: not an INET socket");
241 		}
242 	}
243 
244 	/*
245 	 * Set defaults that might be changed by option switches.
246 	 */
247 	stmp = NULL;
248 	timeout = actualtimeout;
249 
250 	/*
251 	 * Read switches.
252 	 */
253 	for (argc--, argv++; argc > 0; argc--, argv++) {
254 		if (argv[0][0] != '-')
255 			break;
256 		switch (argv[0][1]) {
257 
258 		case 'c':				/* chdir_path */
259 			if (argv[0][2]) {
260 				stmp = &(argv[0][2]);
261 			} else {
262 				argc--;
263 				argv++;
264 				stmp = argv[0];
265 			}
266 			if (!stmp || (stmp[0] != '/')) {
267 				fprintf(stderr,
268 						"bootpd: invalid chdir specification\n");
269 				break;
270 			}
271 			chdir_path = stmp;
272 			break;
273 
274 		case 'd':				/* debug level */
275 			if (argv[0][2]) {
276 				stmp = &(argv[0][2]);
277 			} else if (argv[1] && argv[1][0] == '-') {
278 				/*
279 				 * Backwards-compatible behavior:
280 				 * no parameter, so just increment the debug flag.
281 				 */
282 				debug++;
283 				break;
284 			} else {
285 				argc--;
286 				argv++;
287 				stmp = argv[0];
288 			}
289 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
290 				fprintf(stderr,
291 						"%s: invalid debug level\n", progname);
292 				break;
293 			}
294 			debug = n;
295 			break;
296 
297 		case 'h':				/* override hostname */
298 			if (argv[0][2]) {
299 				stmp = &(argv[0][2]);
300 			} else {
301 				argc--;
302 				argv++;
303 				stmp = argv[0];
304 			}
305 			if (!stmp) {
306 				fprintf(stderr,
307 						"bootpd: missing hostname\n");
308 				break;
309 			}
310 			strncpy(hostname, stmp, sizeof(hostname)-1);
311 			break;
312 
313 		case 'i':				/* inetd mode */
314 			standalone = FALSE;
315 			break;
316 
317 		case 's':				/* standalone mode */
318 			standalone = TRUE;
319 			break;
320 
321 		case 't':				/* timeout */
322 			if (argv[0][2]) {
323 				stmp = &(argv[0][2]);
324 			} else {
325 				argc--;
326 				argv++;
327 				stmp = argv[0];
328 			}
329 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
330 				fprintf(stderr,
331 						"%s: invalid timeout specification\n", progname);
332 				break;
333 			}
334 			actualtimeout = n * 60000;
335 			/*
336 			 * If the actual timeout is zero, pass INFTIM
337 			 * to poll so it blocks indefinitely, otherwise,
338 			 * use the actual timeout value.
339 			 */
340 			timeout = (n > 0) ? actualtimeout : INFTIM;
341 			break;
342 
343 		default:
344 			fprintf(stderr, "%s: unknown switch: -%c\n",
345 					progname, argv[0][1]);
346 			usage();
347 			break;
348 
349 		} /* switch */
350 	} /* for args */
351 
352 	/*
353 	 * Override default file names if specified on the command line.
354 	 */
355 	if (argc > 0)
356 		bootptab = argv[0];
357 
358 	if (argc > 1)
359 		bootpd_dump = argv[1];
360 
361 	/*
362 	 * Get my hostname and IP address.
363 	 */
364 	if (hostname[0] == '\0') {
365 		if (gethostname(hostname, sizeof(hostname)) == -1) {
366 			fprintf(stderr, "bootpd: can't get hostname\n");
367 			exit(1);
368 		}
369 		hostname[sizeof(hostname) - 1] = '\0';
370 	}
371 	hep = gethostbyname(hostname);
372 	if (!hep) {
373 		fprintf(stderr, "Can not get my IP address\n");
374 		exit(1);
375 	}
376 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
377 
378 	if (standalone) {
379 		/*
380 		 * Go into background and disassociate from controlling terminal.
381 		 */
382 		if (debug < 3) {
383 			if (fork())
384 				exit(0);
385 #ifdef	NO_SETSID
386 			setpgrp(0,0);
387 #ifdef TIOCNOTTY
388 			n = open("/dev/tty", O_RDWR);
389 			if (n >= 0) {
390 				ioctl(n, TIOCNOTTY, (char *) 0);
391 				(void) close(n);
392 			}
393 #endif	/* TIOCNOTTY */
394 #else	/* SETSID */
395 			if (setsid() < 0)
396 				perror("setsid");
397 #endif	/* SETSID */
398 		} /* if debug < 3 */
399 
400 		/*
401 		 * Nuke any timeout value
402 		 */
403 		timeout = INFTIM;
404 
405 	} /* if standalone (1st) */
406 
407 	/* Set the cwd (i.e. to /tftpboot) */
408 	if (chdir_path) {
409 		if (chdir(chdir_path) < 0)
410 			report(LOG_ERR, "%s: chdir failed", chdir_path);
411 	}
412 
413 	/* Get the timezone. */
414 	tzone_init();
415 
416 	/* Allocate hash tables. */
417 	rdtab_init();
418 
419 	/*
420 	 * Read the bootptab file.
421 	 */
422 	readtab(1);					/* force read */
423 
424 	if (standalone) {
425 
426 		/*
427 		 * Create a socket.
428 		 */
429 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
430 			report(LOG_ERR, "socket: %s", get_network_errmsg());
431 			exit(1);
432 		}
433 
434 		/*
435 		 * Get server's listening port number
436 		 */
437 		servp = getservbyname("bootps", "udp");
438 		if (servp) {
439 			bootps_port = ntohs((u_short) servp->s_port);
440 		} else {
441 			bootps_port = (u_short) IPPORT_BOOTPS;
442 			report(LOG_ERR,
443 				   "udp/bootps: unknown service -- assuming port %d",
444 				   bootps_port);
445 		}
446 
447 		/*
448 		 * Bind socket to BOOTPS port.
449 		 */
450 		bind_addr.sin_family = AF_INET;
451 		bind_addr.sin_addr.s_addr = INADDR_ANY;
452 		bind_addr.sin_port = htons(bootps_port);
453 		if (bind(s, (struct sockaddr *) &bind_addr,
454 				 sizeof(bind_addr)) < 0)
455 		{
456 			report(LOG_ERR, "bind: %s", get_network_errmsg());
457 			exit(1);
458 		}
459 	} /* if standalone (2nd)*/
460 
461 	/*
462 	 * Get destination port number so we can reply to client
463 	 */
464 	servp = getservbyname("bootpc", "udp");
465 	if (servp) {
466 		bootpc_port = ntohs(servp->s_port);
467 	} else {
468 		report(LOG_ERR,
469 			   "udp/bootpc: unknown service -- assuming port %d",
470 			   IPPORT_BOOTPC);
471 		bootpc_port = (u_short) IPPORT_BOOTPC;
472 	}
473 
474 	/*
475 	 * Set up signals to read or dump the table.
476 	 */
477 	if ((long) signal(SIGHUP, catcher) < 0) {
478 		report(LOG_ERR, "signal: %s", get_errmsg());
479 		exit(1);
480 	}
481 	if ((long) signal(SIGUSR1, catcher) < 0) {
482 		report(LOG_ERR, "signal: %s", get_errmsg());
483 		exit(1);
484 	}
485 
486 	/*
487 	 * Process incoming requests.
488 	 */
489 	set[0].fd = s;
490 	set[0].events = POLLIN;
491 	for (;;) {
492 		nfound = poll(set, 1, timeout);
493 		if (nfound < 0) {
494 			if (errno != EINTR) {
495 				report(LOG_ERR, "poll: %s", get_errmsg());
496 			}
497 			/*
498 			 * Call readtab() or dumptab() here to avoid the
499 			 * dangers of doing I/O from a signal handler.
500 			 */
501 			if (do_readtab) {
502 				do_readtab = 0;
503 				readtab(1);		/* force read */
504 			}
505 			if (do_dumptab) {
506 				do_dumptab = 0;
507 				dumptab(bootpd_dump);
508 			}
509 			continue;
510 		}
511 		if (nfound == 0) {
512 			if (debug > 1)
513 				report(LOG_INFO, "exiting after %d minute%s of inactivity",
514 					   actualtimeout / 60000,
515 					   actualtimeout == 60000 ? "" : "s");
516 			exit(0);
517 		}
518 		ra_len = sizeof(recv_addr);
519 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
520 					 (struct sockaddr *) &recv_addr, &ra_len);
521 		if (n <= 0) {
522 			continue;
523 		}
524 		if (debug > 1) {
525 			report(LOG_INFO, "recvd pkt from IP addr %s",
526 				   inet_ntoa(recv_addr.sin_addr));
527 		}
528 		if (n < sizeof(struct bootp)) {
529 			if (debug) {
530 				report(LOG_INFO, "received short packet");
531 			}
532 			continue;
533 		}
534 		pktlen = n;
535 
536 		readtab(0);				/* maybe re-read bootptab */
537 
538 		switch (bp->bp_op) {
539 		case BOOTREQUEST:
540 			handle_request();
541 			break;
542 		case BOOTREPLY:
543 			handle_reply();
544 			break;
545 		}
546 	}
547 }
548 
549 
550 
551 
552 /*
553  * Print "usage" message and exit
554  */
555 
556 PRIVATE void
557 usage(void)
558 {
559 	fprintf(stderr,
560 			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
561 	fprintf(stderr, "\t -c n\tset current directory\n");
562 	fprintf(stderr, "\t -d n\tset debug level\n");
563 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
564 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
565 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
566 	exit(1);
567 }
568 
569 /* Signal catchers */
570 PRIVATE void
571 catcher(int sig)
572 {
573 	if (sig == SIGHUP)
574 		do_readtab = 1;
575 	if (sig == SIGUSR1)
576 		do_dumptab = 1;
577 #ifdef	SYSV
578 	/* For older "System V" derivatives with no sigset(). */
579 	/* XXX - Should just do it the POSIX way (sigaction). */
580 	signal(sig, catcher);
581 #endif
582 }
583 
584 
585 
586 /*
587  * Process BOOTREQUEST packet.
588  *
589  * Note:  This version of the bootpd.c server never forwards
590  * a request to another server.  That is the job of a gateway
591  * program such as the "bootpgw" program included here.
592  *
593  * (Also this version does not interpret the hostname field of
594  * the request packet;  it COULD do a name->address lookup and
595  * forward the request there.)
596  */
597 PRIVATE void
598 handle_request(void)
599 {
600 	struct bootp *bp = (struct bootp *) pktbuf;
601 	struct host *hp = NULL;
602 	struct host dummyhost;
603 	int32 bootsize = 0;
604 	unsigned hlen, hashcode;
605 	int32 dest;
606 	char realpath[1024];
607 	char *clntpath;
608 	char *homedir, *bootfile;
609 	int n;
610 
611 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
612 
613 	/*
614 	 * If the servername field is set, compare it against us.
615 	 * If we're not being addressed, ignore this request.
616 	 * If the server name field is null, throw in our name.
617 	 */
618 	if (strlen(bp->bp_sname)) {
619 		if (strcmp(bp->bp_sname, hostname)) {
620 			if (debug)
621 				report(LOG_INFO, "\
622 ignoring request for server %s from client at %s address %s",
623 					   bp->bp_sname, netname(bp->bp_htype),
624 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
625 			/* XXX - Is it correct to ignore such a request? -gwr */
626 			return;
627 		}
628 	} else {
629 		strcpy(bp->bp_sname, hostname);
630 	}
631 
632 	/* If it uses an unknown network type, ignore the request.  */
633 	if (bp->bp_htype >= hwinfocnt) {
634 		if (debug)
635 			report(LOG_INFO,
636 			    "Request with unknown network type %u",
637 			    bp->bp_htype);
638 		return;
639 	}
640 
641 	/* Convert the request into a reply. */
642 	bp->bp_op = BOOTREPLY;
643 	if (bp->bp_ciaddr.s_addr == 0) {
644 		/*
645 		 * client doesnt know his IP address,
646 		 * search by hardware address.
647 		 */
648 		if (debug > 1) {
649 			report(LOG_INFO, "request from %s address %s",
650 				   netname(bp->bp_htype),
651 				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
652 		}
653 		hlen = haddrlength(bp->bp_htype);
654 		if (hlen != bp->bp_hlen) {
655 			report(LOG_NOTICE, "bad addr len from %s address %s",
656 				   netname(bp->bp_htype),
657 				   haddrtoa(bp->bp_chaddr, hlen));
658 		}
659 		dummyhost.htype = bp->bp_htype;
660 		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
661 		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
662 		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
663 										 &dummyhost);
664 		if (hp == NULL &&
665 			bp->bp_htype == HTYPE_IEEE802)
666 		{
667 			/* Try again with address in "canonical" form. */
668 			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
669 			if (debug > 1) {
670 				report(LOG_INFO, "\
671 HW addr type is IEEE 802.  convert to %s and check again\n",
672 					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
673 			}
674 			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
675 			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
676 											 hwlookcmp, &dummyhost);
677 		}
678 		if (hp == NULL) {
679 			/*
680 			 * XXX - Add dynamic IP address assignment?
681 			 */
682 			if (debug > 1)
683 				report(LOG_INFO, "unknown client %s address %s",
684 					   netname(bp->bp_htype),
685 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
686 			return; /* not found */
687 		}
688 		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
689 
690 	} else {
691 
692 		/*
693 		 * search by IP address.
694 		 */
695 		if (debug > 1) {
696 			report(LOG_INFO, "request from IP addr %s",
697 				   inet_ntoa(bp->bp_ciaddr));
698 		}
699 		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
700 		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
701 		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
702 										 &dummyhost);
703 		if (hp == NULL) {
704 			if (debug > 1) {
705 				report(LOG_NOTICE, "IP address not found: %s",
706 					   inet_ntoa(bp->bp_ciaddr));
707 			}
708 			return;
709 		}
710 	}
711 
712 	if (debug) {
713 		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
714 			   hp->hostname->string);
715 	}
716 
717 	/*
718 	 * If there is a response delay threshold, ignore requests
719 	 * with a timestamp lower than the threshold.
720 	 */
721 	if (hp->flags.min_wait) {
722 		u_int32 t = (u_int32) ntohs(bp->bp_secs);
723 		if (t < hp->min_wait) {
724 			if (debug > 1)
725 				report(LOG_INFO,
726 					   "ignoring request due to timestamp (%d < %d)",
727 					   t, hp->min_wait);
728 			return;
729 		}
730 	}
731 
732 #ifdef	YORK_EX_OPTION
733 	/*
734 	 * The need for the "ex" tag arose out of the need to empty
735 	 * shared networked drives on diskless PCs.  This solution is
736 	 * not very clean but it does work fairly well.
737 	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
738 	 *
739 	 * XXX - This could compromise security if a non-trusted user
740 	 * managed to write an entry in the bootptab with :ex=trojan:
741 	 * so I would leave this turned off unless you need it. -gwr
742 	 */
743 	/* Run a program, passing the client name as a parameter. */
744 	if (hp->flags.exec_file) {
745 		char tst[100];
746 		/* XXX - Check string lengths? -gwr */
747 		strcpy (tst, hp->exec_file->string);
748 		strcat (tst, " ");
749 		strcat (tst, hp->hostname->string);
750 		strcat (tst, " &");
751 		if (debug)
752 			report(LOG_INFO, "executing %s", tst);
753 		system(tst);	/* Hope this finishes soon... */
754 	}
755 #endif	/* YORK_EX_OPTION */
756 
757 	/*
758 	 * If a specific TFTP server address was specified in the bootptab file,
759 	 * fill it in, otherwise zero it.
760 	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
761 	 */
762 	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
763 		hp->bootserver.s_addr : 0L;
764 
765 #ifdef	STANFORD_PROM_COMPAT
766 	/*
767 	 * Stanford bootp PROMs (for a Sun?) have no way to leave
768 	 * the boot file name field blank (because the boot file
769 	 * name is automatically generated from some index).
770 	 * As a work-around, this little hack allows those PROMs to
771 	 * specify "sunboot14" with the same effect as a NULL name.
772 	 * (The user specifies boot device 14 or some such magic.)
773 	 */
774 	if (strcmp(bp->bp_file, "sunboot14") == 0)
775 		bp->bp_file[0] = '\0';	/* treat it as unspecified */
776 #endif
777 
778 	/*
779 	 * Fill in the client's proper bootfile.
780 	 *
781 	 * If the client specifies an absolute path, try that file with a
782 	 * ".host" suffix and then without.  If the file cannot be found, no
783 	 * reply is made at all.
784 	 *
785 	 * If the client specifies a null or relative file, use the following
786 	 * table to determine the appropriate action:
787 	 *
788 	 *  Homedir      Bootfile    Client's file
789 	 * specified?   specified?   specification   Action
790 	 * -------------------------------------------------------------------
791 	 *      No          No          Null         Send null filename
792 	 *      No          No          Relative     Discard request
793 	 *      No          Yes         Null         Send if absolute else null
794 	 *      No          Yes         Relative     Discard request     *XXX
795 	 *      Yes         No          Null         Send null filename
796 	 *      Yes         No          Relative     Lookup with ".host"
797 	 *      Yes         Yes         Null         Send home/boot or bootfile
798 	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
799 	 *
800 	 */
801 
802 	/*
803 	 * XXX - I don't like the policy of ignoring a client when the
804 	 * boot file is not accessible.  The TFTP server might not be
805 	 * running on the same machine as the BOOTP server, in which
806 	 * case checking accessibility of the boot file is pointless.
807 	 *
808 	 * Therefore, file accessibility is now demanded ONLY if you
809 	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
810 	 */
811 
812 	/*
813 	 * The "real" path is as seen by the BOOTP daemon on this
814 	 * machine, while the client path is relative to the TFTP
815 	 * daemon chroot directory (i.e. /tftpboot).
816 	 */
817 	if (hp->flags.tftpdir) {
818 		strncpy(realpath, hp->tftpdir->string, sizeof(realpath) - 1);
819 		realpath[sizeof(realpath) - 1] = '\0';
820 		clntpath = &realpath[strlen(realpath)];
821 	} else {
822 		realpath[0] = '\0';
823 		clntpath = realpath;
824 	}
825 
826 	/*
827 	 * Determine client's requested homedir and bootfile.
828 	 */
829 	homedir = NULL;
830 	bootfile = NULL;
831 	if (bp->bp_file[0]) {
832 		char	*t;
833 
834 		homedir = bp->bp_file;
835 
836 		/* make sure that the file is nul terminated */
837 		for (t = homedir; t - homedir < BP_FILE_LEN; t++)
838 			if (*t == '\0')
839 				break;
840 		if (t - homedir < BP_FILE_LEN) {
841 			report(LOG_INFO, "requested path length > BP_FILE_LEN  file = \"%s\", nul terminating", homedir);
842 			homedir[BP_FILE_LEN - 1] = '\0';
843 		}
844 
845 		bootfile = strrchr(homedir, '/');
846 		if (bootfile) {
847 			if (homedir == bootfile)
848 				homedir = NULL;
849 			*bootfile++ = '\0';
850 		} else {
851 			/* no "/" in the string */
852 			bootfile = homedir;
853 			homedir = NULL;
854 		}
855 		if (debug > 2) {
856 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
857 				   (homedir) ? homedir : "",
858 				   (bootfile) ? bootfile : "");
859 		}
860 	}
861 
862 	/*
863 	 * Specifications in bootptab override client requested values.
864 	 */
865 	if (hp->flags.homedir)
866 		homedir = hp->homedir->string;
867 	if (hp->flags.bootfile)
868 		bootfile = hp->bootfile->string;
869 
870 	/*
871 	 * Construct bootfile path.
872 	 */
873 	if (homedir) {
874 		if (homedir[0] != '/') {
875 			strncat(realpath, "/", sizeof(realpath) - 1);
876 			realpath[sizeof(realpath) - 1] = '\0';
877 		}
878 		strncat(realpath, homedir, sizeof(realpath) - 1);
879 		realpath[sizeof(realpath) - 1] = '\0';
880 		homedir = NULL;
881 	}
882 	if (bootfile) {
883 		if (bootfile[0] != '/') {
884 			strcat(realpath, "/");
885 			realpath[sizeof(realpath) - 1] = '\0';
886 		}
887 		strcat(realpath, bootfile);
888 		realpath[sizeof(realpath) - 1] = '\0';
889 		bootfile = NULL;
890 	}
891 
892 	/*
893 	 * First try to find the file with a ".host" suffix
894 	 */
895 	n = strlen(clntpath);
896 	strcat(clntpath, ".");
897 	strcat(clntpath, hp->hostname->string);
898 	if (chk_access(realpath, &bootsize) < 0) {
899 		clntpath[n] = 0;			/* Try it without the suffix */
900 		if (chk_access(realpath, &bootsize) < 0) {
901 			/* neither "file.host" nor "file" was found */
902 #ifdef	CHECK_FILE_ACCESS
903 
904 			if (bp->bp_file[0]) {
905 				/*
906 				 * Client wanted specific file
907 				 * and we didn't have it.
908 				 */
909 				report(LOG_NOTICE,
910 					   "requested file not found: \"%s\"", clntpath);
911 				return;
912 			}
913 			/*
914 			 * Client didn't ask for a specific file and we couldn't
915 			 * access the default file, so just zero-out the bootfile
916 			 * field in the packet and continue processing the reply.
917 			 */
918 			bzero(bp->bp_file, sizeof(bp->bp_file));
919 			goto null_file_name;
920 
921 #else	/* CHECK_FILE_ACCESS */
922 
923 			/* Complain only if boot file size was needed. */
924 			if (hp->flags.bootsize_auto) {
925 				report(LOG_ERR, "can not determine size of file \"%s\"",
926 					   clntpath);
927 			}
928 
929 #endif	/* CHECK_FILE_ACCESS */
930 		}
931 	}
932 	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
933 	if (debug > 2)
934 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
935 
936 #ifdef	CHECK_FILE_ACCESS
937 null_file_name:
938 #endif	/* CHECK_FILE_ACCESS */
939 
940 
941 	/*
942 	 * Handle vendor options based on magic number.
943 	 */
944 
945 	if (debug > 1) {
946 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
947 			   (int) ((bp->bp_vend)[0]),
948 			   (int) ((bp->bp_vend)[1]),
949 			   (int) ((bp->bp_vend)[2]),
950 			   (int) ((bp->bp_vend)[3]));
951 	}
952 	/*
953 	 * If this host isn't set for automatic vendor info then copy the
954 	 * specific cookie into the bootp packet, thus forcing a certain
955 	 * reply format.  Only force reply format if user specified it.
956 	 */
957 	if (hp->flags.vm_cookie) {
958 		/* Slam in the user specified magic number. */
959 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
960 	}
961 	/*
962 	 * Figure out the format for the vendor-specific info.
963 	 * Note that bp->bp_vend may have been set above.
964 	 */
965 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
966 		/* RFC1048 conformant bootp client */
967 		dovend_rfc1048(bp, hp, bootsize);
968 		if (debug > 1) {
969 			report(LOG_INFO, "sending reply (with RFC1048 options)");
970 		}
971 	}
972 #ifdef VEND_CMU
973 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
974 		dovend_cmu(bp, hp);
975 		if (debug > 1) {
976 			report(LOG_INFO, "sending reply (with CMU options)");
977 		}
978 	}
979 #endif
980 	else {
981 		if (debug > 1) {
982 			report(LOG_INFO, "sending reply (with no options)");
983 		}
984 	}
985 
986 	dest = (hp->flags.reply_addr) ?
987 		hp->reply_addr.s_addr : 0L;
988 
989 	/* not forwarded */
990 	sendreply(0, dest);
991 }
992 
993 
994 /*
995  * Process BOOTREPLY packet.
996  */
997 PRIVATE void
998 handle_reply(void)
999 {
1000 	if (debug) {
1001 		report(LOG_INFO, "processing boot reply");
1002 	}
1003 	/* forwarded, no destination override */
1004 	sendreply(1, 0);
1005 }
1006 
1007 
1008 /*
1009  * Send a reply packet to the client.  'forward' flag is set if we are
1010  * not the originator of this reply packet.
1011  */
1012 PRIVATE void
1013 sendreply(int forward, int32 dst_override)
1014 {
1015 	struct bootp *bp = (struct bootp *) pktbuf;
1016 	struct in_addr dst;
1017 	u_short port = bootpc_port;
1018 	unsigned char *ha;
1019 	int len;
1020 
1021 	/*
1022 	 * XXX - Should honor bp_flags "broadcast" bit here.
1023 	 * Temporary workaround: use the :ra=ADDR: option to
1024 	 * set the reply address to the broadcast address.
1025 	 */
1026 
1027 	/*
1028 	 * If the destination address was specified explicitly
1029 	 * (i.e. the broadcast address for HP compatiblity)
1030 	 * then send the response to that address.  Otherwise,
1031 	 * act in accordance with RFC951:
1032 	 *   If the client IP address is specified, use that
1033 	 * else if gateway IP address is specified, use that
1034 	 * else make a temporary arp cache entry for the client's
1035 	 * NEW IP/hardware address and use that.
1036 	 */
1037 	if (dst_override) {
1038 		dst.s_addr = dst_override;
1039 		if (debug > 1) {
1040 			report(LOG_INFO, "reply address override: %s",
1041 				   inet_ntoa(dst));
1042 		}
1043 	} else if (bp->bp_ciaddr.s_addr) {
1044 		dst = bp->bp_ciaddr;
1045 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1046 		dst = bp->bp_giaddr;
1047 		port = bootps_port;
1048 		if (debug > 1) {
1049 			report(LOG_INFO, "sending reply to gateway %s",
1050 				   inet_ntoa(dst));
1051 		}
1052 	} else {
1053 		dst = bp->bp_yiaddr;
1054 		ha = bp->bp_chaddr;
1055 		len = bp->bp_hlen;
1056 		if (len > MAXHADDRLEN)
1057 			len = MAXHADDRLEN;
1058 
1059 		if (debug > 1)
1060 			report(LOG_INFO, "setarp %s - %s",
1061 				   inet_ntoa(dst), haddrtoa(ha, len));
1062 		setarp(s, &dst, ha, len);
1063 	}
1064 
1065 	if ((forward == 0) &&
1066 		(bp->bp_siaddr.s_addr == 0))
1067 	{
1068 		struct ifreq *ifr;
1069 		struct in_addr siaddr;
1070 		/*
1071 		 * If we are originating this reply, we
1072 		 * need to find our own interface address to
1073 		 * put in the bp_siaddr field of the reply.
1074 		 * If this server is multi-homed, pick the
1075 		 * 'best' interface (the one on the same net
1076 		 * as the client).  Of course, the client may
1077 		 * be on the other side of a BOOTP gateway...
1078 		 */
1079 		ifr = getif(s, &dst);
1080 		if (ifr) {
1081 			struct sockaddr_in *sip;
1082 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1083 			siaddr = sip->sin_addr;
1084 		} else {
1085 			/* Just use my "official" IP address. */
1086 			siaddr = my_ip_addr;
1087 		}
1088 
1089 		/* XXX - No need to set bp_giaddr here. */
1090 
1091 		/* Finally, set the server address field. */
1092 		bp->bp_siaddr = siaddr;
1093 	}
1094 	/* Set up socket address for send. */
1095 	send_addr.sin_family = AF_INET;
1096 	send_addr.sin_port = htons(port);
1097 	send_addr.sin_addr = dst;
1098 
1099 	/* Send reply with same size packet as request used. */
1100 	if (sendto(s, pktbuf, pktlen, 0,
1101 			   (struct sockaddr *) &send_addr,
1102 			   sizeof(send_addr)) < 0)
1103 	{
1104 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
1105 	}
1106 } /* sendreply */
1107 
1108 
1109 /* nmatch() - now in getif.c */
1110 /* setarp() - now in hwaddr.c */
1111 
1112 
1113 /*
1114  * This call checks read access to a file.  It returns 0 if the file given
1115  * by "path" exists and is publically readable.  A value of -1 is returned if
1116  * access is not permitted or an error occurs.  Successful calls also
1117  * return the file size in bytes using the long pointer "filesize".
1118  *
1119  * The read permission bit for "other" users is checked.  This bit must be
1120  * set for tftpd(8) to allow clients to read the file.
1121  */
1122 
1123 PRIVATE int
1124 chk_access(char *path, int32 *filesize)
1125 {
1126 	struct stat st;
1127 
1128 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1129 		*filesize = (int32) st.st_size;
1130 		return 0;
1131 	} else {
1132 		return -1;
1133 	}
1134 }
1135 
1136 
1137 /*
1138  * Now in dumptab.c :
1139  *	dumptab()
1140  *	dump_host()
1141  *	list_ipaddresses()
1142  */
1143 
1144 #ifdef VEND_CMU
1145 
1146 /*
1147  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1148  * bootp packet pointed to by "bp".
1149  */
1150 
1151 PRIVATE void
1152 dovend_cmu(struct bootp *bp, struct host *hp)
1153 {
1154 	struct cmu_vend *vendp;
1155 	struct in_addr_list *taddr;
1156 
1157 	/*
1158 	 * Initialize the entire vendor field to zeroes.
1159 	 */
1160 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1161 
1162 	/*
1163 	 * Fill in vendor information. Subnet mask, default gateway,
1164 	 * domain name server, ien name server, time server
1165 	 */
1166 	vendp = (struct cmu_vend *) bp->bp_vend;
1167 	strcpy(vendp->v_magic, (char *)vm_cmu);
1168 	if (hp->flags.subnet_mask) {
1169 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1170 		(vendp->v_flags) |= VF_SMASK;
1171 		if (hp->flags.gateway) {
1172 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1173 		}
1174 	}
1175 	if (hp->flags.domain_server) {
1176 		taddr = hp->domain_server;
1177 		if (taddr->addrcount > 0) {
1178 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1179 			if (taddr->addrcount > 1) {
1180 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1181 			}
1182 		}
1183 	}
1184 	if (hp->flags.name_server) {
1185 		taddr = hp->name_server;
1186 		if (taddr->addrcount > 0) {
1187 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1188 			if (taddr->addrcount > 1) {
1189 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1190 			}
1191 		}
1192 	}
1193 	if (hp->flags.time_server) {
1194 		taddr = hp->time_server;
1195 		if (taddr->addrcount > 0) {
1196 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1197 			if (taddr->addrcount > 1) {
1198 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1199 			}
1200 		}
1201 	}
1202 	/* Log message now done by caller. */
1203 } /* dovend_cmu */
1204 
1205 #endif /* VEND_CMU */
1206 
1207 
1208 
1209 /*
1210  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1211  * bootp packet pointed to by "bp".
1212  */
1213 #define	NEED(LEN, MSG) do \
1214 	if (bytesleft < (LEN)) { \
1215 		report(LOG_NOTICE, noroom, \
1216 			   hp->hostname->string, MSG); \
1217 		return; \
1218 	} while (0)
1219 PRIVATE void
1220 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
1221 {
1222 	int bytesleft, len;
1223 	byte *vp;
1224 
1225 	static const char noroom[] = "%s: No room for \"%s\" option";
1226 
1227 	vp = bp->bp_vend;
1228 
1229 	if (hp->flags.msg_size) {
1230 		pktlen = hp->msg_size;
1231 	} else {
1232 		/*
1233 		 * If the request was longer than the official length, build
1234 		 * a response of that same length where the additional length
1235 		 * is assumed to be part of the bp_vend (options) area.
1236 		 */
1237 		if (pktlen > sizeof(*bp)) {
1238 			if (debug > 1)
1239 				report(LOG_INFO, "request message length=%d", pktlen);
1240 		}
1241 		/*
1242 		 * Check whether the request contains the option:
1243 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1244 		 * and if so, override the response length with its value.
1245 		 * This request must lie within the first BP_VEND_LEN
1246 		 * bytes of the option space.
1247 		 */
1248 		{
1249 			byte *p, *ep;
1250 			byte tag, len;
1251 			short msgsz = 0;
1252 
1253 			p = vp + 4;
1254 			ep = p + BP_VEND_LEN - 4;
1255 			while (p < ep) {
1256 				tag = *p++;
1257 				/* Check for tags with no data first. */
1258 				if (tag == TAG_PAD)
1259 					continue;
1260 				if (tag == TAG_END)
1261 					break;
1262 				/* Now scan the length byte. */
1263 				len = *p++;
1264 				switch (tag) {
1265 				case TAG_MAX_MSGSZ:
1266 					if (len == 2) {
1267 						bcopy(p, (char*)&msgsz, 2);
1268 						msgsz = ntohs(msgsz);
1269 					}
1270 					break;
1271 				case TAG_SUBNET_MASK:
1272 					/* XXX - Should preserve this if given... */
1273 					break;
1274 				} /* swtich */
1275 				p += len;
1276 			}
1277 
1278 			if (msgsz > sizeof(*bp)) {
1279 				if (debug > 1)
1280 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1281 				pktlen = msgsz;
1282 			}
1283 		}
1284 	}
1285 
1286 	if (pktlen < sizeof(*bp)) {
1287 		report(LOG_ERR, "invalid response length=%d", pktlen);
1288 		pktlen = sizeof(*bp);
1289 	}
1290 	bytesleft = ((byte*)bp + pktlen) - vp;
1291 	if (pktlen > sizeof(*bp)) {
1292 		if (debug > 1)
1293 			report(LOG_INFO, "extended reply, length=%d, options=%d",
1294 				   pktlen, bytesleft);
1295 	}
1296 
1297 	/* Copy in the magic cookie */
1298 	bcopy(vm_rfc1048, vp, 4);
1299 	vp += 4;
1300 	bytesleft -= 4;
1301 
1302 	if (hp->flags.subnet_mask) {
1303 		/* always enough room here. */
1304 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1305 		*vp++ = 4;				/* -1 byte  */
1306 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1307 		bytesleft -= 6;			/* Fix real count */
1308 		if (hp->flags.gateway) {
1309 			(void) insert_ip(TAG_GATEWAY,
1310 							 hp->gateway,
1311 							 &vp, &bytesleft);
1312 		}
1313 	}
1314 	if (hp->flags.bootsize) {
1315 		/* always enough room here */
1316 		bootsize = (hp->flags.bootsize_auto) ?
1317 			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1318 		*vp++ = TAG_BOOT_SIZE;
1319 		*vp++ = 2;
1320 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1321 		*vp++ = (byte) (bootsize & 0xFF);
1322 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1323 	}
1324 	/*
1325 	 * This one is special: Remaining options go in the ext file.
1326 	 * Only the subnet_mask, bootsize, and gateway should precede.
1327 	 */
1328 	if (hp->flags.exten_file) {
1329 		/*
1330 		 * Check for room for exten_file.  Add 3 to account for
1331 		 * TAG_EXTEN_FILE, length, and TAG_END.
1332 		 */
1333 		len = strlen(hp->exten_file->string);
1334 		NEED((len + 3), "ef");
1335 		*vp++ = TAG_EXTEN_FILE;
1336 		*vp++ = (byte) (len & 0xFF);
1337 		bcopy(hp->exten_file->string, vp, len);
1338 		vp += len;
1339 		*vp++ = TAG_END;
1340 		bytesleft -= len + 3;
1341 		return;					/* no more options here. */
1342 	}
1343 	/*
1344 	 * The remaining options are inserted by the following
1345 	 * function (which is shared with bootpef.c).
1346 	 * Keep back one byte for the TAG_END.
1347 	 */
1348 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1349 	vp += len;
1350 	bytesleft -= len;
1351 
1352 	/* There should be at least one byte left. */
1353 	NEED(1, "(end)");
1354 	*vp++ = TAG_END;
1355 	bytesleft--;
1356 
1357 	/* Log message done by caller. */
1358 	if (bytesleft > 0) {
1359 		/*
1360 		 * Zero out any remaining part of the vendor area.
1361 		 */
1362 		bzero(vp, bytesleft);
1363 	}
1364 } /* dovend_rfc1048 */
1365 #undef	NEED
1366 
1367 
1368 /*
1369  * Now in readfile.c:
1370  * 	hwlookcmp()
1371  *	iplookcmp()
1372  */
1373 
1374 /* haddrtoa() - now in hwaddr.c */
1375 /*
1376  * Now in dovend.c:
1377  * insert_ip()
1378  * insert_generic()
1379  * insert_u_long()
1380  */
1381 
1382 /* get_errmsg() - now in report.c */
1383 
1384 /*
1385  * Local Variables:
1386  * tab-width: 4
1387  * c-indent-level: 4
1388  * c-argdecl-indent: 4
1389  * c-continued-statement-offset: 4
1390  * c-continued-brace-offset: -4
1391  * c-label-offset: -4
1392  * c-brace-offset: 0
1393  * End:
1394  */
1395