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