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