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