1 #ifndef lint 2 static char sccsid[] = "@(#)ftpd.c 4.4 (Berkeley) 83/01/15"; 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 <stdio.h> 16 #include <signal.h> 17 #include <wait.h> 18 #include <pwd.h> 19 #include <setjmp.h> 20 #include <netdb.h> 21 22 #include "ftp.h" 23 24 extern int errno; 25 extern char *sys_errlist[]; 26 extern char *crypt(); 27 extern char version[]; 28 extern char *home; /* pointer to home directory for glob */ 29 extern FILE *popen(), *fopen(); 30 extern int pclose(), fclose(); 31 32 struct sockaddr_in ctrl_addr; 33 struct sockaddr_in data_source; 34 struct sockaddr_in data_dest; 35 struct sockaddr_in his_addr; 36 37 struct hostent *hp; 38 39 int data; 40 jmp_buf errcatch; 41 int logged_in; 42 struct passwd *pw; 43 int debug; 44 int logging = 1; 45 int guest; 46 int type; 47 int form; 48 int stru; /* avoid C keyword */ 49 int mode; 50 char hostname[32]; 51 char *remotehost; 52 53 int lostconn(); 54 FILE *getdatasock(), *dataconn(); 55 char *ntoa(); 56 57 main(argc, argv) 58 int argc; 59 char *argv[]; 60 { 61 int ctrl, s, options = 0; 62 struct servent *sp; 63 union wait status; 64 char *cp; 65 66 sp = getservbyname("ftp", "tcp"); 67 if (sp == 0) { 68 fprintf(stderr, "ftpd: fpt/tcp: unknown service\n"); 69 exit(1); 70 } 71 ctrl_addr.sin_port = sp->s_port; 72 data_source.sin_port = htons(ntohs(sp->s_port) - 1); 73 signal(SIGPIPE, lostconn); 74 debug = 0; 75 argc--, argv++; 76 while (argc > 0 && *argv[0] == '-') { 77 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 78 79 case 'd': 80 debug = 1; 81 options |= SO_DEBUG; 82 break; 83 84 default: 85 fprintf(stderr, "Unknown flag -%c ignored.\n", cp); 86 break; 87 } 88 argc--, argv++; 89 } 90 #ifndef DEBUG 91 if (fork()) 92 exit(0); 93 for (s = 0; s < 10; s++) 94 if (s != 2 && debug) /* don't screw stderr */ 95 (void) close(s); 96 (void) open("/dev/null", 0); 97 (void) dup2(0, 1); 98 { int tt = open("/dev/tty", 2); 99 if (tt > 0) { 100 ioctl(tt, TIOCNOTTY, 0); 101 close(tt); 102 } 103 } 104 #endif 105 while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 106 perror("ftpd: socket"); 107 sleep(5); 108 } 109 while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 110 perror("ftpd: bind"); 111 sleep(5); 112 } 113 listen(s, 10); 114 for (;;) { 115 int hisaddrlen = sizeof (his_addr); 116 117 ctrl = accept(s, &his_addr, &hisaddrlen, 0); 118 if (ctrl < 0) { 119 perror("ftpd: accept"); 120 sleep(5); 121 continue; 122 } 123 data_dest = his_addr; 124 if (fork() == 0) { 125 if (logging) 126 dolog(&his_addr); 127 close(s); 128 dup2(ctrl, 0), close(ctrl), dup2(0, 1); 129 /* do telnet option negotiation here */ 130 /* 131 * Set up default state 132 */ 133 logged_in = 0; 134 data = -1; 135 type = TYPE_A; 136 form = FORM_N; 137 stru = STRU_F; 138 mode = MODE_S; 139 gethostname(hostname, sizeof (hostname)); 140 reply(220, "%s FTP server (%s) ready.", 141 hostname, version); 142 for (;;) { 143 setjmp(errcatch); 144 yyparse(); 145 } 146 } 147 close(ctrl); 148 while (wait3(status, WNOHANG, 0) > 0) 149 continue; 150 } 151 } 152 153 lostconn() 154 { 155 156 fatal("Connection closed."); 157 } 158 159 pass(passwd) 160 char *passwd; 161 { 162 char *xpasswd, *savestr(); 163 static struct passwd save; 164 165 if (logged_in || pw == NULL) { 166 reply(503, "Login with USER first."); 167 return; 168 } 169 if (!guest) { /* "ftp" is only account allowed no password */ 170 xpasswd = crypt(passwd, pw->pw_passwd); 171 if (strcmp(xpasswd, pw->pw_passwd) != 0) { 172 reply(530, "Login incorrect."); 173 pw = NULL; 174 return; 175 } 176 } 177 setegid(pw->pw_gid); 178 initgroups(pw->pw_name, pw->pw_gid); 179 if (chdir(pw->pw_dir)) { 180 reply(550, "User %s: can't change directory to $s.", 181 pw->pw_name, pw->pw_dir); 182 goto bad; 183 } 184 if (guest && chroot(pw->pw_dir) < 0) { 185 reply(550, "Can't set guest privileges."); 186 goto bad; 187 } 188 if (!guest) 189 reply(230, "User %s logged in.", pw->pw_name); 190 else 191 reply(230, "Guest login ok, access restrictions apply."); 192 logged_in = 1; 193 seteuid(pw->pw_uid); 194 /* 195 * Save everything so globbing doesn't 196 * clobber the fields. 197 */ 198 save = *pw; 199 save.pw_name = savestr(pw->pw_name); 200 save.pw_passwd = savestr(pw->pw_passwd); 201 save.pw_comment = savestr(pw->pw_comment); 202 save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 203 save.pw_dir = savestr(pw->pw_dir); 204 save.pw_shell = savestr(pw->pw_shell); 205 pw = &save; 206 home = pw->pw_dir; /* home dir for globbing */ 207 return; 208 bad: 209 seteuid(0); 210 pw = NULL; 211 } 212 213 char * 214 savestr(s) 215 char *s; 216 { 217 char *malloc(); 218 char *new = malloc(strlen(s) + 1); 219 220 if (new != NULL) 221 strcpy(new, s); 222 return(new); 223 } 224 225 retrieve(cmd, name) 226 char *cmd, *name; 227 { 228 FILE *fin, *dout; 229 struct stat st; 230 int (*closefunc)(); 231 232 if (cmd == 0) { 233 if (*name == '!') 234 fin = popen(name + 1, "r"), closefunc = pclose; 235 else 236 fin = fopen(name, "r"), closefunc = fclose; 237 } else { 238 char line[BUFSIZ]; 239 240 sprintf(line, cmd, name); 241 fin = popen(line, "r"), closefunc = pclose; 242 } 243 if (fin == NULL) { 244 reply(550, "%s: %s.", name, sys_errlist[errno]); 245 return; 246 } 247 st.st_size = 0; 248 if (cmd == 0 && 249 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 250 reply(550, "%s: not a plain file.", name); 251 goto done; 252 } 253 dout = dataconn(name, st.st_size, "w"); 254 if (dout == NULL) 255 goto done; 256 if (send_data(fin, dout) || ferror(dout)) 257 reply(550, "%s: %s.", name, sys_errlist[errno]); 258 else 259 reply(226, "Transfer complete."); 260 fclose(dout), data = -1; 261 done: 262 (*closefunc)(fin); 263 } 264 265 store(name, mode) 266 char *name, *mode; 267 { 268 FILE *fout, *din; 269 int (*closefunc)(), dochown = 0; 270 271 if (name[0] == '!') 272 fout = popen(&name[1], "w"), closefunc = pclose; 273 else { 274 struct stat st; 275 276 if (stat(name, &st) < 0) 277 dochown++; 278 fout = fopen(name, mode), closefunc = fclose; 279 } 280 if (fout == NULL) { 281 reply(550, "%s: %s.", name, sys_errlist[errno]); 282 return; 283 } 284 din = dataconn(name, -1, "r"); 285 if (din == NULL) 286 goto done; 287 if (receive_data(din, fout) || ferror(fout)) 288 reply(550, "%s: %s.", name, sys_errlist[errno]); 289 else 290 reply(226, "Transfer complete."); 291 fclose(din), data = -1; 292 done: 293 if (dochown) 294 (void) chown(name, pw->pw_uid, -1); 295 (*closefunc)(fout); 296 } 297 298 FILE * 299 getdatasock(mode) 300 char *mode; 301 { 302 int retrytime, s; 303 304 if (data >= 0) 305 return (fdopen(data, mode)); 306 retrytime = 1; 307 while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 308 if (retrytime < 5) { 309 sleep(retrytime); 310 retrytime <<= 1; 311 continue; 312 } 313 return (NULL); 314 } 315 retrytime = 1; 316 seteuid(0); 317 while (bind(s, &data_source, sizeof (data_source), 0) < 0) { 318 if (retrytime < 5) { 319 sleep(retrytime); 320 retrytime <<= 1; 321 continue; 322 } 323 seteuid(pw->pw_uid); 324 close(s); 325 return (NULL); 326 } 327 seteuid(pw->pw_uid); 328 return (fdopen(s, mode)); 329 } 330 331 FILE * 332 dataconn(name, size, mode) 333 char *name; 334 int size; 335 char *mode; 336 { 337 char sizebuf[32]; 338 FILE *file; 339 340 if (size >= 0) 341 sprintf(sizebuf, " (%d bytes)", size); 342 else 343 (void) strcpy(sizebuf, ""); 344 if (data >= 0) { 345 reply(125, "Using existing data connection for %s%s.", 346 name, sizebuf); 347 return (fdopen(data, mode)); 348 } 349 reply(150, "Opening data connection for %s (%s,%d)%s.", 350 name, ntoa(data_dest.sin_addr.s_addr), 351 ntohs(data_dest.sin_port), sizebuf); 352 file = getdatasock(mode); 353 if (file == NULL) { 354 reply(425, "Can't create data socket (%s,%d): %s.", 355 ntoa(data_source.sin_addr), 356 ntohs(data_source.sin_port), 357 sys_errlist[errno]); 358 return (NULL); 359 } 360 data = fileno(file); 361 if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 362 reply(425, "Can't build data connection: %s.", 363 sys_errlist[errno]); 364 (void) fclose(file); 365 data = -1; 366 return (NULL); 367 } 368 return (file); 369 } 370 371 /* 372 * Tranfer the contents of "instr" to 373 * "outstr" peer using the appropriate 374 * encapulation of the date subject 375 * to Mode, Structure, and Type. 376 * 377 * NB: Form isn't handled. 378 */ 379 send_data(instr, outstr) 380 FILE *instr, *outstr; 381 { 382 register int c; 383 int netfd, filefd, cnt; 384 char buf[BUFSIZ]; 385 386 switch (type) { 387 388 case TYPE_A: 389 while ((c = getc(instr)) != EOF) { 390 if (c == '\n') 391 putc('\r', outstr); 392 if (putc(c, outstr) == EOF) 393 return (1); 394 } 395 return (0); 396 397 case TYPE_I: 398 case TYPE_L: 399 netfd = fileno(outstr); 400 filefd = fileno(instr); 401 402 while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 403 if (write(netfd, buf, cnt) < 0) 404 return (1); 405 return (cnt < 0); 406 } 407 reply(504,"Unimplemented TYPE %d in send_data", type); 408 return (1); 409 } 410 411 /* 412 * Transfer data from peer to 413 * "outstr" using the appropriate 414 * encapulation of the data subject 415 * to Mode, Structure, and Type. 416 * 417 * N.B.: Form isn't handled. 418 */ 419 receive_data(instr, outstr) 420 FILE *instr, *outstr; 421 { 422 register int c; 423 int cr, escape, eof; 424 int netfd, filefd, cnt; 425 char buf[BUFSIZ]; 426 427 428 switch (type) { 429 430 case TYPE_I: 431 case TYPE_L: 432 netfd = fileno(instr); 433 netfd = fileno(outstr); 434 while ((cnt = read(netfd, buf, sizeof buf)) > 0) 435 if (write(filefd, buf, cnt) < 0) 436 return (1); 437 return (cnt < 0); 438 439 case TYPE_E: 440 reply(504, "TYPE E not implemented."); 441 return (1); 442 443 case TYPE_A: 444 cr = 0; 445 while ((c = getc(instr)) != EOF) { 446 if (cr) { 447 if (c != '\r' && c != '\n') 448 putc('\r', outstr); 449 putc(c, outstr); 450 cr = c == '\r'; 451 continue; 452 } 453 if (c == '\r') { 454 cr = 1; 455 continue; 456 } 457 putc(c, outstr); 458 } 459 if (cr) 460 putc('\r', outstr); 461 return (0); 462 } 463 fatal("Unknown type in receive_data."); 464 /*NOTREACHED*/ 465 } 466 467 fatal(s) 468 char *s; 469 { 470 reply(451, "Error in server: %s\n", s); 471 reply(221, "Closing connection due to server error."); 472 exit(0); 473 } 474 475 reply(n, s, args) 476 int n; 477 char *s; 478 { 479 480 printf("%d ", n); 481 _doprnt(s, &args, stdout); 482 printf("\r\n"); 483 fflush(stdout); 484 if (debug) { 485 fprintf(stderr, "<--- %d ", n); 486 _doprnt(s, &args, stderr); 487 fprintf(stderr, "\n"); 488 fflush(stderr); 489 } 490 } 491 492 lreply(n, s, args) 493 int n; 494 char *s; 495 { 496 printf("%d-", n); 497 _doprnt(s, &args, stdout); 498 printf("\r\n"); 499 fflush(stdout); 500 if (debug) { 501 fprintf(stderr, "<--- %d-", n); 502 _doprnt(s, &args, stderr); 503 fprintf(stderr, "\n"); 504 } 505 } 506 507 replystr(s) 508 char *s; 509 { 510 printf("%s\r\n", s); 511 fflush(stdout); 512 if (debug) 513 fprintf(stderr, "<--- %s\n", s); 514 } 515 516 ack(s) 517 char *s; 518 { 519 reply(200, "%s command okay.", s); 520 } 521 522 nack(s) 523 char *s; 524 { 525 reply(502, "%s command not implemented.", s); 526 } 527 528 yyerror() 529 { 530 reply(500, "Command not understood."); 531 } 532 533 delete(name) 534 char *name; 535 { 536 struct stat st; 537 538 if (stat(name, &st) < 0) { 539 reply(550, "%s: %s.", name, sys_errlist[errno]); 540 return; 541 } 542 if ((st.st_mode&S_IFMT) == S_IFDIR) { 543 if (rmdir(name) < 0) { 544 reply(550, "%s: %s.", name, sys_errlist[errno]); 545 return; 546 } 547 goto done; 548 } 549 if (unlink(name) < 0) { 550 reply(550, "%s: %s.", name, sys_errlist[errno]); 551 return; 552 } 553 done: 554 ack("DELE"); 555 } 556 557 cwd(path) 558 char *path; 559 { 560 561 if (chdir(path) < 0) { 562 reply(550, "%s: %s.", path, sys_errlist[errno]); 563 return; 564 } 565 ack("CWD"); 566 } 567 568 makedir(name) 569 char *name; 570 { 571 struct stat st; 572 int dochown = stat(name, &st) < 0; 573 574 if (mkdir(name, 0777) < 0) { 575 reply(550, "%s: %s.", name, sys_errlist[errno]); 576 return; 577 } 578 if (dochown) 579 (void) chown(name, pw->pw_uid, -1); 580 ack("MKDIR"); 581 } 582 583 removedir(name) 584 char *name; 585 { 586 587 if (rmdir(name) < 0) { 588 reply(550, "%s: %s.", name, sys_errlist[errno]); 589 return; 590 } 591 ack("RMDIR"); 592 } 593 594 pwd() 595 { 596 char path[MAXPATHLEN + 1]; 597 char *p; 598 599 if (getwd(path) == NULL) { 600 reply(451, "%s.", path); 601 return; 602 } 603 reply(251, "\"%s\" is current directory.", path); 604 } 605 606 char * 607 renamefrom(name) 608 char *name; 609 { 610 struct stat st; 611 612 if (stat(name, &st) < 0) { 613 reply(550, "%s: %s.", name, sys_errlist[errno]); 614 return ((char *)0); 615 } 616 reply(350, "File exists, ready for destination name"); 617 return (name); 618 } 619 620 renamecmd(from, to) 621 char *from, *to; 622 { 623 624 if (rename(from, to) < 0) { 625 reply(550, "rename: %s.", sys_errlist[errno]); 626 return; 627 } 628 ack("RNTO"); 629 } 630 631 int guest; 632 /* 633 * Test pathname for guest-user safety. 634 */ 635 inappropriate_request(name) 636 char *name; 637 { 638 int bogus = 0, depth = 0, length = strlen(name); 639 char *p, *s; 640 641 if (!guest) 642 return (0); 643 if (name[0] == '/' || name[0] == '|') 644 bogus = 1; 645 for (p = name; p < name+length;) { 646 s = p; /* start of token */ 647 while ( *p && *p!= '/') 648 p++; 649 *p = 0; 650 if (strcmp(s, "..") == 0) 651 depth -= 1; /* backing up */ 652 else if (strcmp(s, ".") == 0) 653 depth += 0; /* no change */ 654 else 655 depth += 1; /* descending */ 656 if (depth < 0) { 657 bogus = 1; 658 break; 659 } 660 } 661 if (bogus) 662 reply(553, "%s: pathname disallowed guest users", name); 663 return (bogus); 664 } 665 666 /* 667 * Convert network-format internet address 668 * to base 256 d.d.d.d representation. 669 */ 670 char * 671 ntoa(in) 672 struct in_addr in; 673 { 674 static char b[18]; 675 register char *p; 676 677 in.s_addr = ntohl(in.s_addr); 678 p = (char *)∈ 679 #define UC(b) (((int)b)&0xff) 680 sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 681 return (b); 682 } 683 684 dolog(sin) 685 struct sockaddr_in *sin; 686 { 687 struct hostent *hp = gethostbyaddr(&sin->sin_addr, 688 sizeof (struct in_addr), AF_INET); 689 char *remotehost; 690 time_t t; 691 692 if (hp) 693 remotehost = hp->h_name; 694 else 695 remotehost = "UNKNOWNHOST"; 696 t = time(0); 697 fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 698 fflush(stderr); 699 } 700