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 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 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 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 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 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 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 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 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 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