1 /* $NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $ */ 2 3 /* 4 * Copyright (c) 1985, 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 36 */ 37 38 /* 39 * Grammar for FTP commands. 40 * See RFC 959. 41 */ 42 43 %{ 44 45 #include "ftpd_locl.h" 46 RCSID("$Id: ftpcmd.y,v 1.61.10.2 2004/08/20 15:15:46 lha Exp $"); 47 48 off_t restart_point; 49 50 static int hasyyerrored; 51 52 53 static int cmd_type; 54 static int cmd_form; 55 static int cmd_bytesz; 56 char cbuf[64*1024]; 57 char *fromname; 58 59 struct tab { 60 char *name; 61 short token; 62 short state; 63 short implemented; /* 1 if command is implemented */ 64 char *help; 65 }; 66 67 extern struct tab cmdtab[]; 68 extern struct tab sitetab[]; 69 70 static char *copy (char *); 71 static void help (struct tab *, char *); 72 static struct tab * 73 lookup (struct tab *, char *); 74 static void sizecmd (char *); 75 static RETSIGTYPE toolong (int); 76 static int yylex (void); 77 78 /* This is for bison */ 79 80 #if !defined(alloca) && !defined(HAVE_ALLOCA) 81 #define alloca(x) malloc(x) 82 #endif 83 84 %} 85 86 %union { 87 int i; 88 char *s; 89 } 90 91 %token 92 A B C E F I 93 L N P R S T 94 95 SP CRLF COMMA 96 97 USER PASS ACCT REIN QUIT PORT 98 PASV TYPE STRU MODE RETR STOR 99 APPE MLFL MAIL MSND MSOM MSAM 100 MRSQ MRCP ALLO REST RNFR RNTO 101 ABOR DELE CWD LIST NLST SITE 102 sTAT HELP NOOP MKD RMD PWD 103 CDUP STOU SMNT SYST SIZE MDTM 104 EPRT EPSV 105 106 UMASK IDLE CHMOD 107 108 AUTH ADAT PROT PBSZ CCC MIC 109 CONF ENC 110 111 KAUTH KLIST KDESTROY KRBTKFILE AFSLOG 112 LOCATE URL 113 114 FEAT OPTS 115 116 LEXERR 117 118 %token <s> STRING 119 %token <i> NUMBER 120 121 %type <i> check_login check_login_no_guest check_secure octal_number byte_size 122 %type <i> struct_code mode_code type_code form_code 123 %type <s> pathstring pathname password username 124 125 %start cmd_list 126 127 %% 128 129 cmd_list 130 : /* empty */ 131 | cmd_list cmd 132 { 133 fromname = (char *) 0; 134 restart_point = (off_t) 0; 135 } 136 | cmd_list rcmd 137 ; 138 139 cmd 140 : USER SP username CRLF 141 { 142 user($3); 143 free($3); 144 } 145 | PASS SP password CRLF 146 { 147 pass($3); 148 memset ($3, 0, strlen($3)); 149 free($3); 150 } 151 | PORT SP host_port CRLF 152 { 153 usedefault = 0; 154 if (pdata >= 0) { 155 close(pdata); 156 pdata = -1; 157 } 158 reply(200, "PORT command successful."); 159 } 160 | EPRT SP STRING CRLF 161 { 162 eprt ($3); 163 free ($3); 164 } 165 | PASV CRLF check_login 166 { 167 if($3) 168 pasv (); 169 } 170 | EPSV CRLF check_login 171 { 172 if($3) 173 epsv (NULL); 174 } 175 | EPSV SP STRING CRLF check_login 176 { 177 if($5) 178 epsv ($3); 179 free ($3); 180 } 181 | TYPE SP type_code CRLF 182 { 183 switch (cmd_type) { 184 185 case TYPE_A: 186 if (cmd_form == FORM_N) { 187 reply(200, "Type set to A."); 188 type = cmd_type; 189 form = cmd_form; 190 } else 191 reply(504, "Form must be N."); 192 break; 193 194 case TYPE_E: 195 reply(504, "Type E not implemented."); 196 break; 197 198 case TYPE_I: 199 reply(200, "Type set to I."); 200 type = cmd_type; 201 break; 202 203 case TYPE_L: 204 #if NBBY == 8 205 if (cmd_bytesz == 8) { 206 reply(200, 207 "Type set to L (byte size 8)."); 208 type = cmd_type; 209 } else 210 reply(504, "Byte size must be 8."); 211 #else /* NBBY == 8 */ 212 UNIMPLEMENTED for NBBY != 8 213 #endif /* NBBY == 8 */ 214 } 215 } 216 | STRU SP struct_code CRLF 217 { 218 switch ($3) { 219 220 case STRU_F: 221 reply(200, "STRU F ok."); 222 break; 223 224 default: 225 reply(504, "Unimplemented STRU type."); 226 } 227 } 228 | MODE SP mode_code CRLF 229 { 230 switch ($3) { 231 232 case MODE_S: 233 reply(200, "MODE S ok."); 234 break; 235 236 default: 237 reply(502, "Unimplemented MODE type."); 238 } 239 } 240 | ALLO SP NUMBER CRLF 241 { 242 reply(202, "ALLO command ignored."); 243 } 244 | ALLO SP NUMBER SP R SP NUMBER CRLF 245 { 246 reply(202, "ALLO command ignored."); 247 } 248 | RETR SP pathname CRLF check_login 249 { 250 char *name = $3; 251 252 if ($5 && name != NULL) 253 retrieve(0, name); 254 if (name != NULL) 255 free(name); 256 } 257 | STOR SP pathname CRLF check_login 258 { 259 char *name = $3; 260 261 if ($5 && name != NULL) 262 do_store(name, "w", 0); 263 if (name != NULL) 264 free(name); 265 } 266 | APPE SP pathname CRLF check_login 267 { 268 char *name = $3; 269 270 if ($5 && name != NULL) 271 do_store(name, "a", 0); 272 if (name != NULL) 273 free(name); 274 } 275 | NLST CRLF check_login 276 { 277 if ($3) 278 send_file_list("."); 279 } 280 | NLST SP STRING CRLF check_login 281 { 282 char *name = $3; 283 284 if ($5 && name != NULL) 285 send_file_list(name); 286 if (name != NULL) 287 free(name); 288 } 289 | LIST CRLF check_login 290 { 291 if($3) 292 list_file("."); 293 } 294 | LIST SP pathname CRLF check_login 295 { 296 if($5) 297 list_file($3); 298 free($3); 299 } 300 | sTAT SP pathname CRLF check_login 301 { 302 if ($5 && $3 != NULL) 303 statfilecmd($3); 304 if ($3 != NULL) 305 free($3); 306 } 307 | sTAT CRLF 308 { 309 statcmd(); 310 } 311 | DELE SP pathname CRLF check_login_no_guest 312 { 313 if ($5 && $3 != NULL) 314 do_delete($3); 315 if ($3 != NULL) 316 free($3); 317 } 318 | RNTO SP pathname CRLF check_login_no_guest 319 { 320 if($5){ 321 if (fromname) { 322 renamecmd(fromname, $3); 323 free(fromname); 324 fromname = (char *) 0; 325 } else { 326 reply(503, "Bad sequence of commands."); 327 } 328 } 329 if ($3 != NULL) 330 free($3); 331 } 332 | ABOR CRLF 333 { 334 reply(225, "ABOR command successful."); 335 } 336 | CWD CRLF check_login 337 { 338 if ($3) 339 cwd(pw->pw_dir); 340 } 341 | CWD SP pathname CRLF check_login 342 { 343 if ($5 && $3 != NULL) 344 cwd($3); 345 if ($3 != NULL) 346 free($3); 347 } 348 | HELP CRLF 349 { 350 help(cmdtab, (char *) 0); 351 } 352 | HELP SP STRING CRLF 353 { 354 char *cp = $3; 355 356 if (strncasecmp(cp, "SITE", 4) == 0) { 357 cp = $3 + 4; 358 if (*cp == ' ') 359 cp++; 360 if (*cp) 361 help(sitetab, cp); 362 else 363 help(sitetab, (char *) 0); 364 } else 365 help(cmdtab, $3); 366 } 367 | NOOP CRLF 368 { 369 reply(200, "NOOP command successful."); 370 } 371 | MKD SP pathname CRLF check_login 372 { 373 if ($5 && $3 != NULL) 374 makedir($3); 375 if ($3 != NULL) 376 free($3); 377 } 378 | RMD SP pathname CRLF check_login_no_guest 379 { 380 if ($5 && $3 != NULL) 381 removedir($3); 382 if ($3 != NULL) 383 free($3); 384 } 385 | PWD CRLF check_login 386 { 387 if ($3) 388 pwd(); 389 } 390 | CDUP CRLF check_login 391 { 392 if ($3) 393 cwd(".."); 394 } 395 | FEAT CRLF 396 { 397 lreply(211, "Supported features:"); 398 lreply(0, " MDTM"); 399 lreply(0, " REST STREAM"); 400 lreply(0, " SIZE"); 401 reply(211, "End"); 402 } 403 | OPTS SP STRING CRLF 404 { 405 free ($3); 406 reply(501, "Bad options"); 407 } 408 409 | SITE SP HELP CRLF 410 { 411 help(sitetab, (char *) 0); 412 } 413 | SITE SP HELP SP STRING CRLF 414 { 415 help(sitetab, $5); 416 } 417 | SITE SP UMASK CRLF check_login 418 { 419 if ($5) { 420 int oldmask = umask(0); 421 umask(oldmask); 422 reply(200, "Current UMASK is %03o", oldmask); 423 } 424 } 425 | SITE SP UMASK SP octal_number CRLF check_login_no_guest 426 { 427 if ($7) { 428 if (($5 == -1) || ($5 > 0777)) { 429 reply(501, "Bad UMASK value"); 430 } else { 431 int oldmask = umask($5); 432 reply(200, 433 "UMASK set to %03o (was %03o)", 434 $5, oldmask); 435 } 436 } 437 } 438 | SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest 439 { 440 if ($9 && $7 != NULL) { 441 if ($5 > 0777) 442 reply(501, 443 "CHMOD: Mode value must be between 0 and 0777"); 444 else if (chmod($7, $5) < 0) 445 perror_reply(550, $7); 446 else 447 reply(200, "CHMOD command successful."); 448 } 449 if ($7 != NULL) 450 free($7); 451 } 452 | SITE SP IDLE CRLF 453 { 454 reply(200, 455 "Current IDLE time limit is %d seconds; max %d", 456 ftpd_timeout, maxtimeout); 457 } 458 | SITE SP IDLE SP NUMBER CRLF 459 { 460 if ($5 < 30 || $5 > maxtimeout) { 461 reply(501, 462 "Maximum IDLE time must be between 30 and %d seconds", 463 maxtimeout); 464 } else { 465 ftpd_timeout = $5; 466 alarm((unsigned) ftpd_timeout); 467 reply(200, 468 "Maximum IDLE time set to %d seconds", 469 ftpd_timeout); 470 } 471 } 472 473 | SITE SP KAUTH SP STRING CRLF check_login 474 { 475 #ifdef KRB4 476 char *p; 477 478 if(guest) 479 reply(500, "Can't be done as guest."); 480 else{ 481 if($7 && $5 != NULL){ 482 p = strpbrk($5, " \t"); 483 if(p){ 484 *p++ = 0; 485 kauth($5, p + strspn(p, " \t")); 486 }else 487 kauth($5, NULL); 488 } 489 } 490 if($5 != NULL) 491 free($5); 492 #else 493 reply(500, "Command not implemented."); 494 #endif 495 } 496 | SITE SP KLIST CRLF check_login 497 { 498 #ifdef KRB4 499 if($5) 500 klist(); 501 #else 502 reply(500, "Command not implemented."); 503 #endif 504 } 505 | SITE SP KDESTROY CRLF check_login 506 { 507 #ifdef KRB4 508 if($5) 509 kdestroy(); 510 #else 511 reply(500, "Command not implemented."); 512 #endif 513 } 514 | SITE SP KRBTKFILE SP STRING CRLF check_login 515 { 516 #ifdef KRB4 517 if(guest) 518 reply(500, "Can't be done as guest."); 519 else if($7 && $5) 520 krbtkfile($5); 521 if($5) 522 free($5); 523 #else 524 reply(500, "Command not implemented."); 525 #endif 526 } 527 | SITE SP AFSLOG CRLF check_login 528 { 529 #ifdef KRB4 530 if(guest) 531 reply(500, "Can't be done as guest."); 532 else if($5) 533 afslog(NULL); 534 #else 535 reply(500, "Command not implemented."); 536 #endif 537 } 538 | SITE SP AFSLOG SP STRING CRLF check_login 539 { 540 #ifdef KRB4 541 if(guest) 542 reply(500, "Can't be done as guest."); 543 else if($7) 544 afslog($5); 545 if($5) 546 free($5); 547 #else 548 reply(500, "Command not implemented."); 549 #endif 550 } 551 | SITE SP LOCATE SP STRING CRLF check_login 552 { 553 if($7 && $5 != NULL) 554 find($5); 555 if($5 != NULL) 556 free($5); 557 } 558 | SITE SP URL CRLF 559 { 560 reply(200, "http://www.pdc.kth.se/kth-krb/"); 561 } 562 | STOU SP pathname CRLF check_login 563 { 564 if ($5 && $3 != NULL) 565 do_store($3, "w", 1); 566 if ($3 != NULL) 567 free($3); 568 } 569 | SYST CRLF 570 { 571 #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__) 572 reply(215, "UNIX Type: L%d", NBBY); 573 #else 574 reply(215, "UNKNOWN Type: L%d", NBBY); 575 #endif 576 } 577 578 /* 579 * SIZE is not in RFC959, but Postel has blessed it and 580 * it will be in the updated RFC. 581 * 582 * Return size of file in a format suitable for 583 * using with RESTART (we just count bytes). 584 */ 585 | SIZE SP pathname CRLF check_login 586 { 587 if ($5 && $3 != NULL) 588 sizecmd($3); 589 if ($3 != NULL) 590 free($3); 591 } 592 593 /* 594 * MDTM is not in RFC959, but Postel has blessed it and 595 * it will be in the updated RFC. 596 * 597 * Return modification time of file as an ISO 3307 598 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 599 * where xxx is the fractional second (of any precision, 600 * not necessarily 3 digits) 601 */ 602 | MDTM SP pathname CRLF check_login 603 { 604 if ($5 && $3 != NULL) { 605 struct stat stbuf; 606 if (stat($3, &stbuf) < 0) 607 reply(550, "%s: %s", 608 $3, strerror(errno)); 609 else if (!S_ISREG(stbuf.st_mode)) { 610 reply(550, 611 "%s: not a plain file.", $3); 612 } else { 613 struct tm *t; 614 time_t mtime = stbuf.st_mtime; 615 616 t = gmtime(&mtime); 617 reply(213, 618 "%04d%02d%02d%02d%02d%02d", 619 t->tm_year + 1900, 620 t->tm_mon + 1, 621 t->tm_mday, 622 t->tm_hour, 623 t->tm_min, 624 t->tm_sec); 625 } 626 } 627 if ($3 != NULL) 628 free($3); 629 } 630 | QUIT CRLF 631 { 632 reply(221, "Goodbye."); 633 dologout(0); 634 } 635 | error CRLF 636 { 637 yyerrok; 638 } 639 ; 640 rcmd 641 : RNFR SP pathname CRLF check_login_no_guest 642 { 643 restart_point = (off_t) 0; 644 if ($5 && $3) { 645 fromname = renamefrom($3); 646 if (fromname == (char *) 0 && $3) { 647 free($3); 648 } 649 } 650 } 651 | REST SP byte_size CRLF 652 { 653 fromname = (char *) 0; 654 restart_point = $3; /* XXX $3 is only "int" */ 655 reply(350, "Restarting at %ld. %s", 656 (long)restart_point, 657 "Send STORE or RETRIEVE to initiate transfer."); 658 } 659 | AUTH SP STRING CRLF 660 { 661 auth($3); 662 free($3); 663 } 664 | ADAT SP STRING CRLF 665 { 666 adat($3); 667 free($3); 668 } 669 | PBSZ SP NUMBER CRLF 670 { 671 pbsz($3); 672 } 673 | PROT SP STRING CRLF 674 { 675 prot($3); 676 } 677 | CCC CRLF 678 { 679 ccc(); 680 } 681 | MIC SP STRING CRLF 682 { 683 mec($3, prot_safe); 684 free($3); 685 } 686 | CONF SP STRING CRLF 687 { 688 mec($3, prot_confidential); 689 free($3); 690 } 691 | ENC SP STRING CRLF 692 { 693 mec($3, prot_private); 694 free($3); 695 } 696 ; 697 698 username 699 : STRING 700 ; 701 702 password 703 : /* empty */ 704 { 705 $$ = (char *)calloc(1, sizeof(char)); 706 } 707 | STRING 708 ; 709 710 byte_size 711 : NUMBER 712 ; 713 714 host_port 715 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 716 NUMBER COMMA NUMBER 717 { 718 struct sockaddr_in *sin = (struct sockaddr_in *)data_dest; 719 720 sin->sin_family = AF_INET; 721 sin->sin_port = htons($9 * 256 + $11); 722 sin->sin_addr.s_addr = 723 htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7); 724 } 725 ; 726 727 form_code 728 : N 729 { 730 $$ = FORM_N; 731 } 732 | T 733 { 734 $$ = FORM_T; 735 } 736 | C 737 { 738 $$ = FORM_C; 739 } 740 ; 741 742 type_code 743 : A 744 { 745 cmd_type = TYPE_A; 746 cmd_form = FORM_N; 747 } 748 | A SP form_code 749 { 750 cmd_type = TYPE_A; 751 cmd_form = $3; 752 } 753 | E 754 { 755 cmd_type = TYPE_E; 756 cmd_form = FORM_N; 757 } 758 | E SP form_code 759 { 760 cmd_type = TYPE_E; 761 cmd_form = $3; 762 } 763 | I 764 { 765 cmd_type = TYPE_I; 766 } 767 | L 768 { 769 cmd_type = TYPE_L; 770 cmd_bytesz = NBBY; 771 } 772 | L SP byte_size 773 { 774 cmd_type = TYPE_L; 775 cmd_bytesz = $3; 776 } 777 /* this is for a bug in the BBN ftp */ 778 | L byte_size 779 { 780 cmd_type = TYPE_L; 781 cmd_bytesz = $2; 782 } 783 ; 784 785 struct_code 786 : F 787 { 788 $$ = STRU_F; 789 } 790 | R 791 { 792 $$ = STRU_R; 793 } 794 | P 795 { 796 $$ = STRU_P; 797 } 798 ; 799 800 mode_code 801 : S 802 { 803 $$ = MODE_S; 804 } 805 | B 806 { 807 $$ = MODE_B; 808 } 809 | C 810 { 811 $$ = MODE_C; 812 } 813 ; 814 815 pathname 816 : pathstring 817 { 818 /* 819 * Problem: this production is used for all pathname 820 * processing, but only gives a 550 error reply. 821 * This is a valid reply in some cases but not in others. 822 */ 823 if (logged_in && $1 && *$1 == '~') { 824 glob_t gl; 825 int flags = 826 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 827 828 memset(&gl, 0, sizeof(gl)); 829 if (glob($1, flags, NULL, &gl) || 830 gl.gl_pathc == 0) { 831 reply(550, "not found"); 832 $$ = NULL; 833 } else { 834 $$ = strdup(gl.gl_pathv[0]); 835 } 836 globfree(&gl); 837 free($1); 838 } else 839 $$ = $1; 840 } 841 ; 842 843 pathstring 844 : STRING 845 ; 846 847 octal_number 848 : NUMBER 849 { 850 int ret, dec, multby, digit; 851 852 /* 853 * Convert a number that was read as decimal number 854 * to what it would be if it had been read as octal. 855 */ 856 dec = $1; 857 multby = 1; 858 ret = 0; 859 while (dec) { 860 digit = dec%10; 861 if (digit > 7) { 862 ret = -1; 863 break; 864 } 865 ret += digit * multby; 866 multby *= 8; 867 dec /= 10; 868 } 869 $$ = ret; 870 } 871 ; 872 873 874 check_login_no_guest : check_login 875 { 876 $$ = $1 && !guest; 877 if($1 && !$$) 878 reply(550, "Permission denied"); 879 } 880 ; 881 882 check_login : check_secure 883 { 884 if($1) { 885 if(($$ = logged_in) == 0) 886 reply(530, "Please login with USER and PASS."); 887 } else 888 $$ = 0; 889 } 890 ; 891 892 check_secure : /* empty */ 893 { 894 $$ = 1; 895 if(sec_complete && !secure_command()) { 896 $$ = 0; 897 reply(533, "Command protection level denied " 898 "for paranoid reasons."); 899 } 900 } 901 ; 902 903 %% 904 905 #define CMD 0 /* beginning of command */ 906 #define ARGS 1 /* expect miscellaneous arguments */ 907 #define STR1 2 /* expect SP followed by STRING */ 908 #define STR2 3 /* expect STRING */ 909 #define OSTR 4 /* optional SP then STRING */ 910 #define ZSTR1 5 /* SP then optional STRING */ 911 #define ZSTR2 6 /* optional STRING after SP */ 912 #define SITECMD 7 /* SITE command */ 913 #define NSTR 8 /* Number followed by a string */ 914 915 struct tab cmdtab[] = { /* In order defined in RFC 765 */ 916 { "USER", USER, STR1, 1, "<sp> username" }, 917 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 918 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 919 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 920 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 921 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 922 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 923 { "EPRT", EPRT, STR1, 1, "<sp> string" }, 924 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 925 { "EPSV", EPSV, OSTR, 1, "[<sp> foo]" }, 926 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 927 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 928 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 929 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 930 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 931 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 932 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 933 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 934 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 935 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 936 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 937 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 938 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 939 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 940 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 941 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 942 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 943 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 944 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 945 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 946 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 947 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 948 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 949 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 950 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 951 { "STAT", sTAT, OSTR, 1, "[ <sp> path-name ]" }, 952 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 953 { "NOOP", NOOP, ARGS, 1, "" }, 954 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 955 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 956 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 957 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 958 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 959 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 960 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 961 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 962 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 963 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 964 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 965 966 /* extensions from RFC2228 */ 967 { "AUTH", AUTH, STR1, 1, "<sp> auth-type" }, 968 { "ADAT", ADAT, STR1, 1, "<sp> auth-data" }, 969 { "PBSZ", PBSZ, ARGS, 1, "<sp> buffer-size" }, 970 { "PROT", PROT, STR1, 1, "<sp> prot-level" }, 971 { "CCC", CCC, ARGS, 1, "" }, 972 { "MIC", MIC, STR1, 1, "<sp> integrity command" }, 973 { "CONF", CONF, STR1, 1, "<sp> confidentiality command" }, 974 { "ENC", ENC, STR1, 1, "<sp> privacy command" }, 975 976 /* RFC2389 */ 977 { "FEAT", FEAT, ARGS, 1, "" }, 978 { "OPTS", OPTS, ARGS, 1, "<sp> command [<sp> options]" }, 979 980 { NULL, 0, 0, 0, 0 } 981 }; 982 983 struct tab sitetab[] = { 984 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 985 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 986 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 987 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 988 989 { "KAUTH", KAUTH, STR1, 1, "<sp> principal [ <sp> ticket ]" }, 990 { "KLIST", KLIST, ARGS, 1, "(show ticket file)" }, 991 { "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" }, 992 { "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" }, 993 { "AFSLOG", AFSLOG, OSTR, 1, "[<sp> cell]" }, 994 995 { "LOCATE", LOCATE, STR1, 1, "<sp> globexpr" }, 996 { "FIND", LOCATE, STR1, 1, "<sp> globexpr" }, 997 998 { "URL", URL, ARGS, 1, "?" }, 999 1000 { NULL, 0, 0, 0, 0 } 1001 }; 1002 1003 static struct tab * 1004 lookup(struct tab *p, char *cmd) 1005 { 1006 1007 for (; p->name != NULL; p++) 1008 if (strcmp(cmd, p->name) == 0) 1009 return (p); 1010 return (0); 1011 } 1012 1013 /* 1014 * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes. 1015 */ 1016 char * 1017 ftpd_getline(char *s, int n) 1018 { 1019 int c; 1020 char *cs; 1021 1022 cs = s; 1023 1024 /* might still be data within the security MIC/CONF/ENC */ 1025 if(ftp_command){ 1026 strlcpy(s, ftp_command, n); 1027 if (debug) 1028 syslog(LOG_DEBUG, "command: %s", s); 1029 return s; 1030 } 1031 while ((c = getc(stdin)) != EOF) { 1032 c &= 0377; 1033 if (c == IAC) { 1034 if ((c = getc(stdin)) != EOF) { 1035 c &= 0377; 1036 switch (c) { 1037 case WILL: 1038 case WONT: 1039 c = getc(stdin); 1040 printf("%c%c%c", IAC, DONT, 0377&c); 1041 fflush(stdout); 1042 continue; 1043 case DO: 1044 case DONT: 1045 c = getc(stdin); 1046 printf("%c%c%c", IAC, WONT, 0377&c); 1047 fflush(stdout); 1048 continue; 1049 case IAC: 1050 break; 1051 default: 1052 continue; /* ignore command */ 1053 } 1054 } 1055 } 1056 *cs++ = c; 1057 if (--n <= 0 || c == '\n') 1058 break; 1059 } 1060 if (c == EOF && cs == s) 1061 return (NULL); 1062 *cs++ = '\0'; 1063 if (debug) { 1064 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1065 /* Don't syslog passwords */ 1066 syslog(LOG_DEBUG, "command: %.5s ???", s); 1067 } else { 1068 char *cp; 1069 int len; 1070 1071 /* Don't syslog trailing CR-LF */ 1072 len = strlen(s); 1073 cp = s + len - 1; 1074 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1075 --cp; 1076 --len; 1077 } 1078 syslog(LOG_DEBUG, "command: %.*s", len, s); 1079 } 1080 } 1081 #ifdef XXX 1082 fprintf(stderr, "%s\n", s); 1083 #endif 1084 return (s); 1085 } 1086 1087 static RETSIGTYPE 1088 toolong(int signo) 1089 { 1090 1091 reply(421, 1092 "Timeout (%d seconds): closing control connection.", 1093 ftpd_timeout); 1094 if (logging) 1095 syslog(LOG_INFO, "User %s timed out after %d seconds", 1096 (pw ? pw -> pw_name : "unknown"), ftpd_timeout); 1097 dologout(1); 1098 SIGRETURN(0); 1099 } 1100 1101 static int 1102 yylex(void) 1103 { 1104 static int cpos, state; 1105 char *cp, *cp2; 1106 struct tab *p; 1107 int n; 1108 char c; 1109 1110 for (;;) { 1111 switch (state) { 1112 1113 case CMD: 1114 hasyyerrored = 0; 1115 1116 signal(SIGALRM, toolong); 1117 alarm((unsigned) ftpd_timeout); 1118 if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) { 1119 reply(221, "You could at least say goodbye."); 1120 dologout(0); 1121 } 1122 alarm(0); 1123 #ifdef HAVE_SETPROCTITLE 1124 if (strncasecmp(cbuf, "PASS", 4) != 0) 1125 setproctitle("%s: %s", proctitle, cbuf); 1126 #endif /* HAVE_SETPROCTITLE */ 1127 if ((cp = strchr(cbuf, '\r'))) { 1128 *cp++ = '\n'; 1129 *cp = '\0'; 1130 } 1131 if ((cp = strpbrk(cbuf, " \n"))) 1132 cpos = cp - cbuf; 1133 if (cpos == 0) 1134 cpos = 4; 1135 c = cbuf[cpos]; 1136 cbuf[cpos] = '\0'; 1137 strupr(cbuf); 1138 p = lookup(cmdtab, cbuf); 1139 cbuf[cpos] = c; 1140 if (p != 0) { 1141 if (p->implemented == 0) { 1142 nack(p->name); 1143 hasyyerrored = 1; 1144 break; 1145 } 1146 state = p->state; 1147 yylval.s = p->name; 1148 return (p->token); 1149 } 1150 break; 1151 1152 case SITECMD: 1153 if (cbuf[cpos] == ' ') { 1154 cpos++; 1155 return (SP); 1156 } 1157 cp = &cbuf[cpos]; 1158 if ((cp2 = strpbrk(cp, " \n"))) 1159 cpos = cp2 - cbuf; 1160 c = cbuf[cpos]; 1161 cbuf[cpos] = '\0'; 1162 strupr(cp); 1163 p = lookup(sitetab, cp); 1164 cbuf[cpos] = c; 1165 if (p != 0) { 1166 if (p->implemented == 0) { 1167 state = CMD; 1168 nack(p->name); 1169 hasyyerrored = 1; 1170 break; 1171 } 1172 state = p->state; 1173 yylval.s = p->name; 1174 return (p->token); 1175 } 1176 state = CMD; 1177 break; 1178 1179 case OSTR: 1180 if (cbuf[cpos] == '\n') { 1181 state = CMD; 1182 return (CRLF); 1183 } 1184 /* FALLTHROUGH */ 1185 1186 case STR1: 1187 case ZSTR1: 1188 dostr1: 1189 if (cbuf[cpos] == ' ') { 1190 cpos++; 1191 if(state == OSTR) 1192 state = STR2; 1193 else 1194 state++; 1195 return (SP); 1196 } 1197 break; 1198 1199 case ZSTR2: 1200 if (cbuf[cpos] == '\n') { 1201 state = CMD; 1202 return (CRLF); 1203 } 1204 /* FALLTHROUGH */ 1205 1206 case STR2: 1207 cp = &cbuf[cpos]; 1208 n = strlen(cp); 1209 cpos += n - 1; 1210 /* 1211 * Make sure the string is nonempty and \n terminated. 1212 */ 1213 if (n > 1 && cbuf[cpos] == '\n') { 1214 cbuf[cpos] = '\0'; 1215 yylval.s = copy(cp); 1216 cbuf[cpos] = '\n'; 1217 state = ARGS; 1218 return (STRING); 1219 } 1220 break; 1221 1222 case NSTR: 1223 if (cbuf[cpos] == ' ') { 1224 cpos++; 1225 return (SP); 1226 } 1227 if (isdigit((unsigned char)cbuf[cpos])) { 1228 cp = &cbuf[cpos]; 1229 while (isdigit((unsigned char)cbuf[++cpos])) 1230 ; 1231 c = cbuf[cpos]; 1232 cbuf[cpos] = '\0'; 1233 yylval.i = atoi(cp); 1234 cbuf[cpos] = c; 1235 state = STR1; 1236 return (NUMBER); 1237 } 1238 state = STR1; 1239 goto dostr1; 1240 1241 case ARGS: 1242 if (isdigit((unsigned char)cbuf[cpos])) { 1243 cp = &cbuf[cpos]; 1244 while (isdigit((unsigned char)cbuf[++cpos])) 1245 ; 1246 c = cbuf[cpos]; 1247 cbuf[cpos] = '\0'; 1248 yylval.i = atoi(cp); 1249 cbuf[cpos] = c; 1250 return (NUMBER); 1251 } 1252 switch (cbuf[cpos++]) { 1253 1254 case '\n': 1255 state = CMD; 1256 return (CRLF); 1257 1258 case ' ': 1259 return (SP); 1260 1261 case ',': 1262 return (COMMA); 1263 1264 case 'A': 1265 case 'a': 1266 return (A); 1267 1268 case 'B': 1269 case 'b': 1270 return (B); 1271 1272 case 'C': 1273 case 'c': 1274 return (C); 1275 1276 case 'E': 1277 case 'e': 1278 return (E); 1279 1280 case 'F': 1281 case 'f': 1282 return (F); 1283 1284 case 'I': 1285 case 'i': 1286 return (I); 1287 1288 case 'L': 1289 case 'l': 1290 return (L); 1291 1292 case 'N': 1293 case 'n': 1294 return (N); 1295 1296 case 'P': 1297 case 'p': 1298 return (P); 1299 1300 case 'R': 1301 case 'r': 1302 return (R); 1303 1304 case 'S': 1305 case 's': 1306 return (S); 1307 1308 case 'T': 1309 case 't': 1310 return (T); 1311 1312 } 1313 break; 1314 1315 default: 1316 fatal("Unknown state in scanner."); 1317 } 1318 yyerror(NULL); 1319 state = CMD; 1320 return (0); 1321 } 1322 } 1323 1324 /* ARGSUSED */ 1325 void 1326 yyerror(char *s) 1327 { 1328 char *cp; 1329 1330 if (hasyyerrored) 1331 return; 1332 1333 if ((cp = strchr(cbuf,'\n'))) 1334 *cp = '\0'; 1335 reply(500, "'%s': command not understood.", cbuf); 1336 hasyyerrored = 1; 1337 } 1338 1339 static char * 1340 copy(char *s) 1341 { 1342 char *p; 1343 1344 p = strdup(s); 1345 if (p == NULL) 1346 fatal("Ran out of memory."); 1347 return p; 1348 } 1349 1350 static void 1351 help(struct tab *ctab, char *s) 1352 { 1353 struct tab *c; 1354 int width, NCMDS; 1355 char *type; 1356 char buf[1024]; 1357 1358 if (ctab == sitetab) 1359 type = "SITE "; 1360 else 1361 type = ""; 1362 width = 0, NCMDS = 0; 1363 for (c = ctab; c->name != NULL; c++) { 1364 int len = strlen(c->name); 1365 1366 if (len > width) 1367 width = len; 1368 NCMDS++; 1369 } 1370 width = (width + 8) &~ 7; 1371 if (s == 0) { 1372 int i, j, w; 1373 int columns, lines; 1374 1375 lreply(214, "The following %scommands are recognized %s.", 1376 type, "(* =>'s unimplemented)"); 1377 columns = 76 / width; 1378 if (columns == 0) 1379 columns = 1; 1380 lines = (NCMDS + columns - 1) / columns; 1381 for (i = 0; i < lines; i++) { 1382 strlcpy (buf, " ", sizeof(buf)); 1383 for (j = 0; j < columns; j++) { 1384 c = ctab + j * lines + i; 1385 snprintf (buf + strlen(buf), 1386 sizeof(buf) - strlen(buf), 1387 "%s%c", 1388 c->name, 1389 c->implemented ? ' ' : '*'); 1390 if (c + lines >= &ctab[NCMDS]) 1391 break; 1392 w = strlen(c->name) + 1; 1393 while (w < width) { 1394 strlcat (buf, 1395 " ", 1396 sizeof(buf)); 1397 w++; 1398 } 1399 } 1400 lreply(214, "%s", buf); 1401 } 1402 reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se"); 1403 return; 1404 } 1405 strupr(s); 1406 c = lookup(ctab, s); 1407 if (c == (struct tab *)0) { 1408 reply(502, "Unknown command %s.", s); 1409 return; 1410 } 1411 if (c->implemented) 1412 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1413 else 1414 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1415 c->name, c->help); 1416 } 1417 1418 static void 1419 sizecmd(char *filename) 1420 { 1421 switch (type) { 1422 case TYPE_L: 1423 case TYPE_I: { 1424 struct stat stbuf; 1425 if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 1426 reply(550, "%s: not a plain file.", filename); 1427 else 1428 reply(213, "%lu", (unsigned long)stbuf.st_size); 1429 break; 1430 } 1431 case TYPE_A: { 1432 FILE *fin; 1433 int c; 1434 size_t count; 1435 struct stat stbuf; 1436 fin = fopen(filename, "r"); 1437 if (fin == NULL) { 1438 perror_reply(550, filename); 1439 return; 1440 } 1441 if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 1442 reply(550, "%s: not a plain file.", filename); 1443 fclose(fin); 1444 return; 1445 } 1446 1447 count = 0; 1448 while((c=getc(fin)) != EOF) { 1449 if (c == '\n') /* will get expanded to \r\n */ 1450 count++; 1451 count++; 1452 } 1453 fclose(fin); 1454 1455 reply(213, "%lu", (unsigned long)count); 1456 break; 1457 } 1458 default: 1459 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1460 } 1461 } 1462