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