1 #ifndef lint 2 static char sccsid[] = "@(#)ftpd.c 4.22 (Berkeley) 06/19/83"; 3 #endif 4 5 /* 6 * FTP server. 7 */ 8 #include <sys/param.h> 9 #include <sys/stat.h> 10 #include <sys/ioctl.h> 11 #include <sys/socket.h> 12 13 #include <netinet/in.h> 14 15 #include <arpa/ftp.h> 16 #include <arpa/inet.h> 17 18 #include <stdio.h> 19 #include <signal.h> 20 #include <wait.h> 21 #include <pwd.h> 22 #include <setjmp.h> 23 #include <netdb.h> 24 #include <errno.h> 25 26 /* 27 * File containing login names 28 * NOT to be used on this machine. 29 * Commonly used to disallow uucp. 30 */ 31 #define FTPUSERS "/etc/ftpusers" 32 33 extern int errno; 34 extern char *sys_errlist[]; 35 extern char *crypt(); 36 extern char version[]; 37 extern char *home; /* pointer to home directory for glob */ 38 extern FILE *popen(), *fopen(); 39 extern int pclose(), fclose(); 40 41 struct sockaddr_in ctrl_addr; 42 struct sockaddr_in data_source; 43 struct sockaddr_in data_dest; 44 struct sockaddr_in his_addr; 45 46 struct hostent *hp; 47 48 int data; 49 jmp_buf errcatch; 50 int logged_in; 51 struct passwd *pw; 52 int debug; 53 int timeout; 54 int logging; 55 int guest; 56 int type; 57 int form; 58 int stru; /* avoid C keyword */ 59 int mode; 60 int usedefault = 1; /* for data transfers */ 61 char hostname[32]; 62 char *remotehost; 63 struct servent *sp; 64 65 /* 66 * Timeout intervals for retrying connections 67 * to hosts that don't accept PORT cmds. This 68 * is a kludge, but given the problems with TCP... 69 */ 70 #define SWAITMAX 90 /* wait at most 90 seconds */ 71 #define SWAITINT 5 /* interval between retries */ 72 73 int swaitmax = SWAITMAX; 74 int swaitint = SWAITINT; 75 76 int lostconn(); 77 int reapchild(); 78 FILE *getdatasock(), *dataconn(); 79 char *ntoa(); 80 81 main(argc, argv) 82 int argc; 83 char *argv[]; 84 { 85 int ctrl, s, options = 0; 86 char *cp; 87 88 sp = getservbyname("ftp", "tcp"); 89 if (sp == 0) { 90 fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 91 exit(1); 92 } 93 ctrl_addr.sin_port = sp->s_port; 94 data_source.sin_port = htons(ntohs(sp->s_port) - 1); 95 signal(SIGPIPE, lostconn); 96 debug = 0; 97 argc--, argv++; 98 while (argc > 0 && *argv[0] == '-') { 99 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 100 101 case 'v': 102 debug = 1; 103 break; 104 105 case 'd': 106 debug = 1; 107 options |= SO_DEBUG; 108 break; 109 110 case 'l': 111 logging = 1; 112 break; 113 114 case 't': 115 timeout = atoi(++cp); 116 goto nextopt; 117 break; 118 119 default: 120 fprintf(stderr, "Unknown flag -%c ignored.\n", *cp); 121 break; 122 } 123 nextopt: 124 argc--, argv++; 125 } 126 #ifndef DEBUG 127 if (fork()) 128 exit(0); 129 for (s = 0; s < 10; s++) 130 if (!logging || (s != 2)) 131 (void) close(s); 132 (void) open("/", 0); 133 (void) dup2(0, 1); 134 if (!logging) 135 (void) dup2(0, 2); 136 { int tt = open("/dev/tty", 2); 137 if (tt > 0) { 138 ioctl(tt, TIOCNOTTY, 0); 139 close(tt); 140 } 141 } 142 #endif 143 while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 144 perror("ftpd: socket"); 145 sleep(5); 146 } 147 if (options & SO_DEBUG) 148 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 149 perror("ftpd: setsockopt (SO_DEBUG)"); 150 #ifdef notdef 151 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 152 perror("ftpd: setsockopt (SO_KEEPALIVE)"); 153 #endif 154 while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 155 perror("ftpd: bind"); 156 sleep(5); 157 } 158 signal(SIGCHLD, reapchild); 159 listen(s, 10); 160 for (;;) { 161 int hisaddrlen = sizeof (his_addr); 162 163 ctrl = accept(s, &his_addr, &hisaddrlen, 0); 164 if (ctrl < 0) { 165 if (errno == EINTR) 166 continue; 167 perror("ftpd: accept"); 168 continue; 169 } 170 if (fork() == 0) { 171 signal (SIGCHLD, SIG_IGN); 172 if (logging) 173 dolog(&his_addr); 174 close(s); 175 dup2(ctrl, 0), close(ctrl), dup2(0, 1); 176 /* do telnet option negotiation here */ 177 /* 178 * Set up default state 179 */ 180 logged_in = 0; 181 data = -1; 182 type = TYPE_A; 183 form = FORM_N; 184 stru = STRU_F; 185 mode = MODE_S; 186 (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr)); 187 gethostname(hostname, sizeof (hostname)); 188 reply(220, "%s FTP server (%s) ready.", 189 hostname, version); 190 for (;;) { 191 setjmp(errcatch); 192 yyparse(); 193 } 194 } 195 close(ctrl); 196 } 197 } 198 199 reapchild() 200 { 201 union wait status; 202 203 while (wait3(&status, WNOHANG, 0) > 0) 204 ; 205 } 206 207 lostconn() 208 { 209 210 fatal("Connection closed."); 211 } 212 213 pass(passwd) 214 char *passwd; 215 { 216 char *xpasswd, *savestr(); 217 static struct passwd save; 218 219 if (logged_in || pw == NULL) { 220 reply(503, "Login with USER first."); 221 return; 222 } 223 if (!guest) { /* "ftp" is only account allowed no password */ 224 xpasswd = crypt(passwd, pw->pw_passwd); 225 if (strcmp(xpasswd, pw->pw_passwd) != 0) { 226 reply(530, "Login incorrect."); 227 pw = NULL; 228 return; 229 } 230 } 231 setegid(pw->pw_gid); 232 initgroups(pw->pw_name, pw->pw_gid); 233 if (chdir(pw->pw_dir)) { 234 reply(550, "User %s: can't change directory to $s.", 235 pw->pw_name, pw->pw_dir); 236 goto bad; 237 } 238 if (guest && chroot(pw->pw_dir) < 0) { 239 reply(550, "Can't set guest privileges."); 240 goto bad; 241 } 242 if (!guest) 243 reply(230, "User %s logged in.", pw->pw_name); 244 else 245 reply(230, "Guest login ok, access restrictions apply."); 246 logged_in = 1; 247 seteuid(pw->pw_uid); 248 /* 249 * Save everything so globbing doesn't 250 * clobber the fields. 251 */ 252 save = *pw; 253 save.pw_name = savestr(pw->pw_name); 254 save.pw_passwd = savestr(pw->pw_passwd); 255 save.pw_comment = savestr(pw->pw_comment); 256 save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 257 save.pw_dir = savestr(pw->pw_dir); 258 save.pw_shell = savestr(pw->pw_shell); 259 pw = &save; 260 home = pw->pw_dir; /* home dir for globbing */ 261 return; 262 bad: 263 seteuid(0); 264 pw = NULL; 265 } 266 267 char * 268 savestr(s) 269 char *s; 270 { 271 char *malloc(); 272 char *new = malloc(strlen(s) + 1); 273 274 if (new != NULL) 275 strcpy(new, s); 276 return (new); 277 } 278 279 retrieve(cmd, name) 280 char *cmd, *name; 281 { 282 FILE *fin, *dout; 283 struct stat st; 284 int (*closefunc)(); 285 286 if (cmd == 0) { 287 #ifdef notdef 288 /* no remote command execution -- it's a security hole */ 289 if (*name == '|') 290 fin = popen(name + 1, "r"), closefunc = pclose; 291 else 292 #endif 293 fin = fopen(name, "r"), closefunc = fclose; 294 } else { 295 char line[BUFSIZ]; 296 297 sprintf(line, cmd, name), name = line; 298 fin = popen(line, "r"), closefunc = pclose; 299 } 300 if (fin == NULL) { 301 if (errno != 0) 302 reply(550, "%s: %s.", name, sys_errlist[errno]); 303 return; 304 } 305 st.st_size = 0; 306 if (cmd == 0 && 307 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 308 reply(550, "%s: not a plain file.", name); 309 goto done; 310 } 311 dout = dataconn(name, st.st_size, "w"); 312 if (dout == NULL) 313 goto done; 314 if (send_data(fin, dout) || ferror(dout)) 315 reply(550, "%s: %s.", name, sys_errlist[errno]); 316 else 317 reply(226, "Transfer complete."); 318 fclose(dout), data = -1; 319 done: 320 (*closefunc)(fin); 321 } 322 323 store(name, mode) 324 char *name, *mode; 325 { 326 FILE *fout, *din; 327 int (*closefunc)(), dochown = 0; 328 329 #ifdef notdef 330 /* no remote command execution -- it's a security hole */ 331 if (name[0] == '|') 332 fout = popen(&name[1], "w"), closefunc = pclose; 333 else 334 #endif 335 { 336 struct stat st; 337 338 if (stat(name, &st) < 0) 339 dochown++; 340 fout = fopen(name, mode), closefunc = fclose; 341 } 342 if (fout == NULL) { 343 reply(550, "%s: %s.", name, sys_errlist[errno]); 344 return; 345 } 346 din = dataconn(name, (off_t)-1, "r"); 347 if (din == NULL) 348 goto done; 349 if (receive_data(din, fout) || ferror(fout)) 350 reply(550, "%s: %s.", name, sys_errlist[errno]); 351 else 352 reply(226, "Transfer complete."); 353 fclose(din), data = -1; 354 done: 355 if (dochown) 356 (void) chown(name, pw->pw_uid, -1); 357 (*closefunc)(fout); 358 } 359 360 FILE * 361 getdatasock(mode) 362 char *mode; 363 { 364 int s, linger; 365 366 if (data >= 0) 367 return (fdopen(data, mode)); 368 s = socket(AF_INET, SOCK_STREAM, 0, 0); 369 if (s < 0) 370 return (NULL); 371 seteuid(0); 372 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 373 goto bad; 374 /* anchor socket to avoid multi-homing problems */ 375 data_source.sin_family = AF_INET; 376 data_source.sin_addr = ctrl_addr.sin_addr; 377 if (bind(s, &data_source, sizeof (data_source), 0) < 0) 378 goto bad; 379 linger = 60; /* value ignored by system */ 380 (void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)); 381 seteuid(pw->pw_uid); 382 return (fdopen(s, mode)); 383 bad: 384 seteuid(pw->pw_uid); 385 close(s); 386 return (NULL); 387 } 388 389 FILE * 390 dataconn(name, size, mode) 391 char *name; 392 off_t size; 393 char *mode; 394 { 395 char sizebuf[32]; 396 FILE *file; 397 int retry = 0; 398 399 if (size >= 0) 400 sprintf (sizebuf, " (%ld bytes)", size); 401 else 402 (void) strcpy(sizebuf, ""); 403 if (data >= 0) { 404 reply(125, "Using existing data connection for %s%s.", 405 name, sizebuf); 406 usedefault = 1; 407 return (fdopen(data, mode)); 408 } 409 if (usedefault) 410 data_dest = his_addr; 411 usedefault = 1; 412 file = getdatasock(mode); 413 if (file == NULL) { 414 reply(425, "Can't create data socket (%s,%d): %s.", 415 ntoa(data_source.sin_addr), 416 ntohs(data_source.sin_port), 417 sys_errlist[errno]); 418 return (NULL); 419 } 420 reply(150, "Opening data connection for %s (%s,%d)%s.", 421 name, ntoa(data_dest.sin_addr.s_addr), 422 ntohs(data_dest.sin_port), sizebuf); 423 data = fileno(file); 424 while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 425 if (errno == EADDRINUSE && retry < swaitmax) { 426 sleep(swaitint); 427 retry += swaitint; 428 continue; 429 } 430 reply(425, "Can't build data connection: %s.", 431 sys_errlist[errno]); 432 (void) fclose(file); 433 data = -1; 434 return (NULL); 435 } 436 return (file); 437 } 438 439 /* 440 * Tranfer the contents of "instr" to 441 * "outstr" peer using the appropriate 442 * encapulation of the date subject 443 * to Mode, Structure, and Type. 444 * 445 * NB: Form isn't handled. 446 */ 447 send_data(instr, outstr) 448 FILE *instr, *outstr; 449 { 450 register int c; 451 int netfd, filefd, cnt; 452 char buf[BUFSIZ]; 453 454 switch (type) { 455 456 case TYPE_A: 457 while ((c = getc(instr)) != EOF) { 458 if (c == '\n') { 459 if (ferror (outstr)) 460 return (1); 461 putc('\r', outstr); 462 } 463 putc(c, outstr); 464 if (c == '\r') 465 putc ('\0', outstr); 466 } 467 if (ferror (instr) || ferror (outstr)) 468 return (1); 469 return (0); 470 471 case TYPE_I: 472 case TYPE_L: 473 netfd = fileno(outstr); 474 filefd = fileno(instr); 475 476 while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 477 if (write(netfd, buf, cnt) < 0) 478 return (1); 479 return (cnt < 0); 480 } 481 reply(504,"Unimplemented TYPE %d in send_data", type); 482 return (1); 483 } 484 485 /* 486 * Transfer data from peer to 487 * "outstr" using the appropriate 488 * encapulation of the data subject 489 * to Mode, Structure, and Type. 490 * 491 * N.B.: Form isn't handled. 492 */ 493 receive_data(instr, outstr) 494 FILE *instr, *outstr; 495 { 496 register int c; 497 int cnt; 498 char buf[BUFSIZ]; 499 500 501 switch (type) { 502 503 case TYPE_I: 504 case TYPE_L: 505 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 506 if (write(fileno(outstr), buf, cnt) < 0) 507 return (1); 508 return (cnt < 0); 509 510 case TYPE_E: 511 reply(504, "TYPE E not implemented."); 512 return (1); 513 514 case TYPE_A: 515 while ((c = getc(instr)) != EOF) { 516 if (c == '\r') { 517 if (ferror (outstr)) 518 return (1); 519 if ((c = getc(instr)) != '\n') 520 putc ('\r', outstr); 521 if (c == '\0') 522 continue; 523 } 524 putc (c, outstr); 525 } 526 if (ferror (instr) || ferror (outstr)) 527 return (1); 528 return (0); 529 } 530 fatal("Unknown type in receive_data."); 531 /*NOTREACHED*/ 532 } 533 534 fatal(s) 535 char *s; 536 { 537 reply(451, "Error in server: %s\n", s); 538 reply(221, "Closing connection due to server error."); 539 exit(0); 540 } 541 542 reply(n, s, args) 543 int n; 544 char *s; 545 { 546 547 printf("%d ", n); 548 _doprnt(s, &args, stdout); 549 printf("\r\n"); 550 fflush(stdout); 551 if (debug) { 552 fprintf(stderr, "<--- %d ", n); 553 _doprnt(s, &args, stderr); 554 fprintf(stderr, "\n"); 555 fflush(stderr); 556 } 557 } 558 559 lreply(n, s, args) 560 int n; 561 char *s; 562 { 563 printf("%d-", n); 564 _doprnt(s, &args, stdout); 565 printf("\r\n"); 566 fflush(stdout); 567 if (debug) { 568 fprintf(stderr, "<--- %d-", n); 569 _doprnt(s, &args, stderr); 570 fprintf(stderr, "\n"); 571 } 572 } 573 574 replystr(s) 575 char *s; 576 { 577 printf("%s\r\n", s); 578 fflush(stdout); 579 if (debug) 580 fprintf(stderr, "<--- %s\n", s); 581 } 582 583 ack(s) 584 char *s; 585 { 586 reply(200, "%s command okay.", s); 587 } 588 589 nack(s) 590 char *s; 591 { 592 reply(502, "%s command not implemented.", s); 593 } 594 595 yyerror() 596 { 597 reply(500, "Command not understood."); 598 } 599 600 delete(name) 601 char *name; 602 { 603 struct stat st; 604 605 if (stat(name, &st) < 0) { 606 reply(550, "%s: %s.", name, sys_errlist[errno]); 607 return; 608 } 609 if ((st.st_mode&S_IFMT) == S_IFDIR) { 610 if (rmdir(name) < 0) { 611 reply(550, "%s: %s.", name, sys_errlist[errno]); 612 return; 613 } 614 goto done; 615 } 616 if (unlink(name) < 0) { 617 reply(550, "%s: %s.", name, sys_errlist[errno]); 618 return; 619 } 620 done: 621 ack("DELE"); 622 } 623 624 cwd(path) 625 char *path; 626 { 627 628 if (chdir(path) < 0) { 629 reply(550, "%s: %s.", path, sys_errlist[errno]); 630 return; 631 } 632 ack("CWD"); 633 } 634 635 makedir(name) 636 char *name; 637 { 638 struct stat st; 639 int dochown = stat(name, &st) < 0; 640 641 if (mkdir(name, 0777) < 0) { 642 reply(550, "%s: %s.", name, sys_errlist[errno]); 643 return; 644 } 645 if (dochown) 646 (void) chown(name, pw->pw_uid, -1); 647 ack("MKDIR"); 648 } 649 650 removedir(name) 651 char *name; 652 { 653 654 if (rmdir(name) < 0) { 655 reply(550, "%s: %s.", name, sys_errlist[errno]); 656 return; 657 } 658 ack("RMDIR"); 659 } 660 661 pwd() 662 { 663 char path[MAXPATHLEN + 1]; 664 665 if (getwd(path) == NULL) { 666 reply(451, "%s.", path); 667 return; 668 } 669 reply(251, "\"%s\" is current directory.", path); 670 } 671 672 char * 673 renamefrom(name) 674 char *name; 675 { 676 struct stat st; 677 678 if (stat(name, &st) < 0) { 679 reply(550, "%s: %s.", name, sys_errlist[errno]); 680 return ((char *)0); 681 } 682 reply(350, "File exists, ready for destination name"); 683 return (name); 684 } 685 686 renamecmd(from, to) 687 char *from, *to; 688 { 689 690 if (rename(from, to) < 0) { 691 reply(550, "rename: %s.", sys_errlist[errno]); 692 return; 693 } 694 ack("RNTO"); 695 } 696 697 /* 698 * Convert network-format internet address 699 * to base 256 d.d.d.d representation. 700 */ 701 char * 702 ntoa(in) 703 struct in_addr in; 704 { 705 static char b[18]; 706 register char *p; 707 708 p = (char *)∈ 709 #define UC(b) (((int)b)&0xff) 710 sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 711 return (b); 712 } 713 714 dolog(sin) 715 struct sockaddr_in *sin; 716 { 717 struct hostent *hp = gethostbyaddr(&sin->sin_addr, 718 sizeof (struct in_addr), AF_INET); 719 char *remotehost; 720 time_t t; 721 722 if (hp) 723 remotehost = hp->h_name; 724 else 725 remotehost = inet_ntoa(sin->sin_addr); 726 t = time(0); 727 fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 728 fflush(stderr); 729 } 730 731 /* 732 * Special version of popen which avoids 733 * call to shell. This insures noone may 734 * create a pipe to a hidden program as a side 735 * effect of a list or dir command. 736 */ 737 #define tst(a,b) (*mode == 'r'? (b) : (a)) 738 #define RDR 0 739 #define WTR 1 740 static int popen_pid[5]; 741 742 static char * 743 nextarg(cpp) 744 char *cpp; 745 { 746 register char *cp = cpp; 747 748 if (cp == 0) 749 return (cp); 750 while (*cp && *cp != ' ' && *cp != '\t') 751 cp++; 752 if (*cp == ' ' || *cp == '\t') { 753 *cp++ = '\0'; 754 while (*cp == ' ' || *cp == '\t') 755 cp++; 756 } 757 if (cp == cpp) 758 return ((char *)0); 759 return (cp); 760 } 761 762 FILE * 763 popen(cmd, mode) 764 char *cmd, *mode; 765 { 766 int p[2], ac, gac; 767 register myside, hisside, pid; 768 char *av[20], *gav[512]; 769 register char *cp; 770 771 if (pipe(p) < 0) 772 return (NULL); 773 cp = cmd, ac = 0; 774 /* break up string into pieces */ 775 do { 776 av[ac++] = cp; 777 cp = nextarg(cp); 778 } while (cp && *cp && ac < 20); 779 av[ac] = (char *)0; 780 gav[0] = av[0]; 781 /* glob each piece */ 782 for (gac = ac = 1; av[ac] != NULL; ac++) { 783 char **pop; 784 extern char **glob(); 785 786 pop = glob(av[ac]); 787 if (pop) { 788 av[ac] = (char *)pop; /* save to free later */ 789 while (*pop && gac < 512) 790 gav[gac++] = *pop++; 791 } 792 } 793 gav[gac] = (char *)0; 794 myside = tst(p[WTR], p[RDR]); 795 hisside = tst(p[RDR], p[WTR]); 796 if ((pid = fork()) == 0) { 797 /* myside and hisside reverse roles in child */ 798 close(myside); 799 dup2(hisside, tst(0, 1)); 800 close(hisside); 801 execv(gav[0], gav); 802 _exit(1); 803 } 804 for (ac = 1; av[ac] != NULL; ac++) 805 blkfree((char **)av[ac]); 806 if (pid == -1) 807 return (NULL); 808 popen_pid[myside] = pid; 809 close(hisside); 810 return (fdopen(myside, mode)); 811 } 812 813 pclose(ptr) 814 FILE *ptr; 815 { 816 register f, r, (*hstat)(), (*istat)(), (*qstat)(); 817 int status; 818 819 f = fileno(ptr); 820 fclose(ptr); 821 istat = signal(SIGINT, SIG_IGN); 822 qstat = signal(SIGQUIT, SIG_IGN); 823 hstat = signal(SIGHUP, SIG_IGN); 824 while ((r = wait(&status)) != popen_pid[f] && r != -1) 825 ; 826 if (r == -1) 827 status = -1; 828 signal(SIGINT, istat); 829 signal(SIGQUIT, qstat); 830 signal(SIGHUP, hstat); 831 return (status); 832 } 833 834 /* 835 * Check user requesting login priviledges. 836 * Disallow anyone mentioned in the file FTPUSERS 837 * to allow people such as uucp to be avoided. 838 */ 839 checkuser(name) 840 register char *name; 841 { 842 char line[BUFSIZ], *index(); 843 FILE *fd; 844 int found = 0; 845 846 fd = fopen(FTPUSERS, "r"); 847 if (fd == NULL) 848 return (1); 849 while (fgets(line, sizeof (line), fd) != NULL) { 850 register char *cp = index(line, '\n'); 851 852 if (cp) 853 *cp = '\0'; 854 if (strcmp(line, name) == 0) { 855 found++; 856 break; 857 } 858 } 859 fclose(fd); 860 return (!found); 861 } 862