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