tftpd.c revision 18458
1/* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static char copyright[] = 36"@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; 42#endif /* not lint */ 43 44/* 45 * Trivial file transfer protocol server. 46 * 47 * This version includes many modifications by Jim Guyton 48 * <guyton@rand-unix>. 49 */ 50 51#include <sys/param.h> 52#include <sys/ioctl.h> 53#include <sys/stat.h> 54#include <sys/socket.h> 55#include <sys/types.h> 56 57#include <netinet/in.h> 58#include <arpa/tftp.h> 59#include <arpa/inet.h> 60 61#include <ctype.h> 62#include <errno.h> 63#include <fcntl.h> 64#include <netdb.h> 65#include <setjmp.h> 66#include <signal.h> 67#include <stdio.h> 68#include <stdlib.h> 69#include <string.h> 70#include <syslog.h> 71#include <unistd.h> 72#include <pwd.h> 73 74#include "tftpsubs.h" 75 76#define TIMEOUT 5 77 78int peer; 79int rexmtval = TIMEOUT; 80int maxtimeout = 5*TIMEOUT; 81 82#define PKTSIZE SEGSIZE+4 83char buf[PKTSIZE]; 84char ackbuf[PKTSIZE]; 85struct sockaddr_in from; 86int fromlen; 87 88void tftp __P((struct tftphdr *, int)); 89 90/* 91 * Null-terminated directory prefix list for absolute pathname requests and 92 * search list for relative pathname requests. 93 * 94 * MAXDIRS should be at least as large as the number of arguments that 95 * inetd allows (currently 20). 96 */ 97#define MAXDIRS 20 98static struct dirlist { 99 char *name; 100 int len; 101} dirs[MAXDIRS+1]; 102static int suppress_naks; 103static int logging; 104 105static char *errtomsg __P((int)); 106static void nak __P((int)); 107static char *verifyhost __P((struct sockaddr_in *)); 108 109int 110main(argc, argv) 111 int argc; 112 char *argv[]; 113{ 114 register struct tftphdr *tp; 115 register int n; 116 int ch, on; 117 struct sockaddr_in sin; 118 char *chroot_dir = NULL; 119 struct passwd *nobody; 120 121 openlog("tftpd", LOG_PID, LOG_FTP); 122 while ((ch = getopt(argc, argv, "lns:")) != EOF) { 123 switch (ch) { 124 case 'l': 125 logging = 1; 126 break; 127 case 'n': 128 suppress_naks = 1; 129 break; 130 case 's': 131 chroot_dir = optarg; 132 break; 133 default: 134 syslog(LOG_WARNING, "ignoring unknown option -%c", ch); 135 } 136 } 137 if (optind < argc) { 138 struct dirlist *dirp; 139 140 /* Get list of directory prefixes. Skip relative pathnames. */ 141 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 142 optind++) { 143 if (argv[optind][0] == '/') { 144 dirp->name = argv[optind]; 145 dirp->len = strlen(dirp->name); 146 dirp++; 147 } 148 } 149 } 150 else if (chroot_dir) { 151 dirs->name = "/"; 152 dirs->len = 1; 153 } 154 155 on = 1; 156 if (ioctl(0, FIONBIO, &on) < 0) { 157 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); 158 exit(1); 159 } 160 fromlen = sizeof (from); 161 n = recvfrom(0, buf, sizeof (buf), 0, 162 (struct sockaddr *)&from, &fromlen); 163 if (n < 0) { 164 syslog(LOG_ERR, "recvfrom: %m\n"); 165 exit(1); 166 } 167 /* 168 * Now that we have read the message out of the UDP 169 * socket, we fork and exit. Thus, inetd will go back 170 * to listening to the tftp port, and the next request 171 * to come in will start up a new instance of tftpd. 172 * 173 * We do this so that inetd can run tftpd in "wait" mode. 174 * The problem with tftpd running in "nowait" mode is that 175 * inetd may get one or more successful "selects" on the 176 * tftp port before we do our receive, so more than one 177 * instance of tftpd may be started up. Worse, if tftpd 178 * break before doing the above "recvfrom", inetd would 179 * spawn endless instances, clogging the system. 180 */ 181 { 182 int pid; 183 int i, j; 184 185 for (i = 1; i < 20; i++) { 186 pid = fork(); 187 if (pid < 0) { 188 sleep(i); 189 /* 190 * flush out to most recently sent request. 191 * 192 * This may drop some request, but those 193 * will be resent by the clients when 194 * they timeout. The positive effect of 195 * this flush is to (try to) prevent more 196 * than one tftpd being started up to service 197 * a single request from a single client. 198 */ 199 j = sizeof from; 200 i = recvfrom(0, buf, sizeof (buf), 0, 201 (struct sockaddr *)&from, &j); 202 if (i > 0) { 203 n = i; 204 fromlen = j; 205 } 206 } else { 207 break; 208 } 209 } 210 if (pid < 0) { 211 syslog(LOG_ERR, "fork: %m\n"); 212 exit(1); 213 } else if (pid != 0) { 214 exit(0); 215 } 216 } 217 218 /* 219 * Since we exit here, we should do that only after the above 220 * recvfrom to keep inetd from constantly forking should there 221 * be a problem. See the above comment about system clogging. 222 */ 223 if (chroot_dir) { 224 /* Must get this before chroot because /etc might go away */ 225 if ((nobody = getpwnam("nobody")) == NULL) { 226 syslog(LOG_ERR, "nobody: no such user"); 227 exit(1); 228 } 229 if (chroot(chroot_dir)) { 230 syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); 231 exit(1); 232 } 233 chdir( "/" ); 234 setuid(nobody->pw_uid); 235 } 236 237 from.sin_family = AF_INET; 238 alarm(0); 239 close(0); 240 close(1); 241 peer = socket(AF_INET, SOCK_DGRAM, 0); 242 if (peer < 0) { 243 syslog(LOG_ERR, "socket: %m\n"); 244 exit(1); 245 } 246 memset(&sin, 0, sizeof(sin)); 247 sin.sin_family = AF_INET; 248 if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 249 syslog(LOG_ERR, "bind: %m\n"); 250 exit(1); 251 } 252 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 253 syslog(LOG_ERR, "connect: %m\n"); 254 exit(1); 255 } 256 tp = (struct tftphdr *)buf; 257 tp->th_opcode = ntohs(tp->th_opcode); 258 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 259 tftp(tp, n); 260 exit(1); 261} 262 263struct formats; 264int validate_access __P((char **, int)); 265void sendfile __P((struct formats *)); 266void recvfile __P((struct formats *)); 267 268struct formats { 269 char *f_mode; 270 int (*f_validate) __P((char **, int)); 271 void (*f_send) __P((struct formats *)); 272 void (*f_recv) __P((struct formats *)); 273 int f_convert; 274} formats[] = { 275 { "netascii", validate_access, sendfile, recvfile, 1 }, 276 { "octet", validate_access, sendfile, recvfile, 0 }, 277#ifdef notdef 278 { "mail", validate_user, sendmail, recvmail, 1 }, 279#endif 280 { 0 } 281}; 282 283/* 284 * Handle initial connection protocol. 285 */ 286void 287tftp(tp, size) 288 struct tftphdr *tp; 289 int size; 290{ 291 register char *cp; 292 int first = 1, ecode; 293 register struct formats *pf; 294 char *filename, *mode; 295 296 filename = cp = tp->th_stuff; 297again: 298 while (cp < buf + size) { 299 if (*cp == '\0') 300 break; 301 cp++; 302 } 303 if (*cp != '\0') { 304 nak(EBADOP); 305 exit(1); 306 } 307 if (first) { 308 mode = ++cp; 309 first = 0; 310 goto again; 311 } 312 for (cp = mode; *cp; cp++) 313 if (isupper(*cp)) 314 *cp = tolower(*cp); 315 for (pf = formats; pf->f_mode; pf++) 316 if (strcmp(pf->f_mode, mode) == 0) 317 break; 318 if (pf->f_mode == 0) { 319 nak(EBADOP); 320 exit(1); 321 } 322 ecode = (*pf->f_validate)(&filename, tp->th_opcode); 323 if (logging) { 324 syslog(LOG_INFO, "%s: %s request for %s: %s", 325 verifyhost(&from), 326 tp->th_opcode == WRQ ? "write" : "read", 327 filename, errtomsg(ecode)); 328 } 329 if (ecode) { 330 /* 331 * Avoid storms of naks to a RRQ broadcast for a relative 332 * bootfile pathname from a diskless Sun. 333 */ 334 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 335 exit(0); 336 nak(ecode); 337 exit(1); 338 } 339 if (tp->th_opcode == WRQ) 340 (*pf->f_recv)(pf); 341 else 342 (*pf->f_send)(pf); 343 exit(0); 344} 345 346 347FILE *file; 348 349/* 350 * Validate file access. Since we 351 * have no uid or gid, for now require 352 * file to exist and be publicly 353 * readable/writable. 354 * If we were invoked with arguments 355 * from inetd then the file must also be 356 * in one of the given directory prefixes. 357 * Note also, full path name must be 358 * given as we have no login directory. 359 */ 360int 361validate_access(filep, mode) 362 char **filep; 363 int mode; 364{ 365 struct stat stbuf; 366 int fd; 367 struct dirlist *dirp; 368 static char pathname[MAXPATHLEN]; 369 char *filename = *filep; 370 371 /* 372 * Prevent tricksters from getting around the directory restrictions 373 */ 374 if (strstr(filename, "/../")) 375 return (EACCESS); 376 377 if (*filename == '/') { 378 /* 379 * Allow the request if it's in one of the approved locations. 380 * Special case: check the null prefix ("/") by looking 381 * for length = 1 and relying on the arg. processing that 382 * it's a /. 383 */ 384 for (dirp = dirs; dirp->name != NULL; dirp++) { 385 if (dirp->len == 1 || 386 (!strncmp(filename, dirp->name, dirp->len) && 387 filename[dirp->len] == '/')) 388 break; 389 } 390 /* If directory list is empty, allow access to any file */ 391 if (dirp->name == NULL && dirp != dirs) 392 return (EACCESS); 393 if (stat(filename, &stbuf) < 0) 394 return (errno == ENOENT ? ENOTFOUND : EACCESS); 395 if ((stbuf.st_mode & S_IFMT) != S_IFREG) 396 return (ENOTFOUND); 397 if (mode == RRQ) { 398 if ((stbuf.st_mode & S_IROTH) == 0) 399 return (EACCESS); 400 } else { 401 if ((stbuf.st_mode & S_IWOTH) == 0) 402 return (EACCESS); 403 } 404 } else { 405 int err; 406 407 /* 408 * Relative file name: search the approved locations for it. 409 * Don't allow write requests that avoid directory 410 * restrictions. 411 */ 412 413 if (!strncmp(filename, "../", 3)) 414 return (EACCESS); 415 416 /* 417 * If the file exists in one of the directories and isn't 418 * readable, continue looking. However, change the error code 419 * to give an indication that the file exists. 420 */ 421 err = ENOTFOUND; 422 for (dirp = dirs; dirp->name != NULL; dirp++) { 423 sprintf(pathname, "%s/%s", dirp->name, filename); 424 if (stat(pathname, &stbuf) == 0 && 425 (stbuf.st_mode & S_IFMT) == S_IFREG) { 426 if ((stbuf.st_mode & S_IROTH) != 0) { 427 break; 428 } 429 err = EACCESS; 430 } 431 } 432 if (dirp->name == NULL) 433 return (err); 434 *filep = filename = pathname; 435 } 436 fd = open(filename, mode == RRQ ? 0 : 1); 437 if (fd < 0) 438 return (errno + 100); 439 file = fdopen(fd, (mode == RRQ)? "r":"w"); 440 if (file == NULL) { 441 return errno+100; 442 } 443 return (0); 444} 445 446int timeout; 447jmp_buf timeoutbuf; 448 449void 450timer() 451{ 452 453 timeout += rexmtval; 454 if (timeout >= maxtimeout) 455 exit(1); 456 longjmp(timeoutbuf, 1); 457} 458 459/* 460 * Send the requested file. 461 */ 462void 463sendfile(pf) 464 struct formats *pf; 465{ 466 struct tftphdr *dp, *r_init(); 467 register struct tftphdr *ap; /* ack packet */ 468 register int size, n; 469 volatile int block; 470 471 signal(SIGALRM, timer); 472 dp = r_init(); 473 ap = (struct tftphdr *)ackbuf; 474 block = 1; 475 do { 476 size = readit(file, &dp, pf->f_convert); 477 if (size < 0) { 478 nak(errno + 100); 479 goto abort; 480 } 481 dp->th_opcode = htons((u_short)DATA); 482 dp->th_block = htons((u_short)block); 483 timeout = 0; 484 (void)setjmp(timeoutbuf); 485 486send_data: 487 if (send(peer, dp, size + 4, 0) != size + 4) { 488 syslog(LOG_ERR, "tftpd: write: %m\n"); 489 goto abort; 490 } 491 read_ahead(file, pf->f_convert); 492 for ( ; ; ) { 493 alarm(rexmtval); /* read the ack */ 494 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 495 alarm(0); 496 if (n < 0) { 497 syslog(LOG_ERR, "tftpd: read: %m\n"); 498 goto abort; 499 } 500 ap->th_opcode = ntohs((u_short)ap->th_opcode); 501 ap->th_block = ntohs((u_short)ap->th_block); 502 503 if (ap->th_opcode == ERROR) 504 goto abort; 505 506 if (ap->th_opcode == ACK) { 507 if (ap->th_block == block) 508 break; 509 /* Re-synchronize with the other side */ 510 (void) synchnet(peer); 511 if (ap->th_block == (block -1)) 512 goto send_data; 513 } 514 515 } 516 block++; 517 } while (size == SEGSIZE); 518abort: 519 (void) fclose(file); 520} 521 522void 523justquit() 524{ 525 exit(0); 526} 527 528 529/* 530 * Receive a file. 531 */ 532void 533recvfile(pf) 534 struct formats *pf; 535{ 536 struct tftphdr *dp, *w_init(); 537 register struct tftphdr *ap; /* ack buffer */ 538 register int n, size; 539 volatile int block; 540 541 signal(SIGALRM, timer); 542 dp = w_init(); 543 ap = (struct tftphdr *)ackbuf; 544 block = 0; 545 do { 546 timeout = 0; 547 ap->th_opcode = htons((u_short)ACK); 548 ap->th_block = htons((u_short)block); 549 block++; 550 (void) setjmp(timeoutbuf); 551send_ack: 552 if (send(peer, ackbuf, 4, 0) != 4) { 553 syslog(LOG_ERR, "tftpd: write: %m\n"); 554 goto abort; 555 } 556 write_behind(file, pf->f_convert); 557 for ( ; ; ) { 558 alarm(rexmtval); 559 n = recv(peer, dp, PKTSIZE, 0); 560 alarm(0); 561 if (n < 0) { /* really? */ 562 syslog(LOG_ERR, "tftpd: read: %m\n"); 563 goto abort; 564 } 565 dp->th_opcode = ntohs((u_short)dp->th_opcode); 566 dp->th_block = ntohs((u_short)dp->th_block); 567 if (dp->th_opcode == ERROR) 568 goto abort; 569 if (dp->th_opcode == DATA) { 570 if (dp->th_block == block) { 571 break; /* normal */ 572 } 573 /* Re-synchronize with the other side */ 574 (void) synchnet(peer); 575 if (dp->th_block == (block-1)) 576 goto send_ack; /* rexmit */ 577 } 578 } 579 /* size = write(file, dp->th_data, n - 4); */ 580 size = writeit(file, &dp, n - 4, pf->f_convert); 581 if (size != (n-4)) { /* ahem */ 582 if (size < 0) nak(errno + 100); 583 else nak(ENOSPACE); 584 goto abort; 585 } 586 } while (size == SEGSIZE); 587 write_behind(file, pf->f_convert); 588 (void) fclose(file); /* close data file */ 589 590 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 591 ap->th_block = htons((u_short)(block)); 592 (void) send(peer, ackbuf, 4, 0); 593 594 signal(SIGALRM, justquit); /* just quit on timeout */ 595 alarm(rexmtval); 596 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 597 alarm(0); 598 if (n >= 4 && /* if read some data */ 599 dp->th_opcode == DATA && /* and got a data block */ 600 block == dp->th_block) { /* then my last ack was lost */ 601 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 602 } 603abort: 604 return; 605} 606 607struct errmsg { 608 int e_code; 609 char *e_msg; 610} errmsgs[] = { 611 { EUNDEF, "Undefined error code" }, 612 { ENOTFOUND, "File not found" }, 613 { EACCESS, "Access violation" }, 614 { ENOSPACE, "Disk full or allocation exceeded" }, 615 { EBADOP, "Illegal TFTP operation" }, 616 { EBADID, "Unknown transfer ID" }, 617 { EEXISTS, "File already exists" }, 618 { ENOUSER, "No such user" }, 619 { -1, 0 } 620}; 621 622static char * 623errtomsg(error) 624 int error; 625{ 626 static char buf[20]; 627 register struct errmsg *pe; 628 if (error == 0) 629 return "success"; 630 for (pe = errmsgs; pe->e_code >= 0; pe++) 631 if (pe->e_code == error) 632 return pe->e_msg; 633 sprintf(buf, "error %d", error); 634 return buf; 635} 636 637/* 638 * Send a nak packet (error message). 639 * Error code passed in is one of the 640 * standard TFTP codes, or a UNIX errno 641 * offset by 100. 642 */ 643static void 644nak(error) 645 int error; 646{ 647 register struct tftphdr *tp; 648 int length; 649 register struct errmsg *pe; 650 651 tp = (struct tftphdr *)buf; 652 tp->th_opcode = htons((u_short)ERROR); 653 tp->th_code = htons((u_short)error); 654 for (pe = errmsgs; pe->e_code >= 0; pe++) 655 if (pe->e_code == error) 656 break; 657 if (pe->e_code < 0) { 658 pe->e_msg = strerror(error - 100); 659 tp->th_code = EUNDEF; /* set 'undef' errorcode */ 660 } 661 strcpy(tp->th_msg, pe->e_msg); 662 length = strlen(pe->e_msg); 663 tp->th_msg[length] = '\0'; 664 length += 5; 665 if (send(peer, buf, length, 0) != length) 666 syslog(LOG_ERR, "nak: %m\n"); 667} 668 669static char * 670verifyhost(fromp) 671 struct sockaddr_in *fromp; 672{ 673 struct hostent *hp; 674 675 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), 676 fromp->sin_family); 677 if (hp) 678 return hp->h_name; 679 else 680 return inet_ntoa(fromp->sin_addr); 681} 682