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