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