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