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