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