1 /* $NetBSD: ftpcmd.y,v 1.96 2024/02/16 19:32:38 jkoshy Exp $ */ 2 3 /*- 4 * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1985, 1988, 1993, 1994 34 * The Regents of the University of California. All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 * 60 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 61 */ 62 63 /* 64 * Grammar for FTP commands. 65 * See RFC 959. 66 */ 67 68 %{ 69 #include <sys/cdefs.h> 70 71 #ifndef lint 72 #if 0 73 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 74 #else 75 __RCSID("$NetBSD: ftpcmd.y,v 1.96 2024/02/16 19:32:38 jkoshy Exp $"); 76 #endif 77 #endif /* not lint */ 78 79 #include <sys/param.h> 80 #include <sys/socket.h> 81 #include <sys/stat.h> 82 83 #include <netinet/in.h> 84 #include <arpa/ftp.h> 85 #include <arpa/inet.h> 86 87 #include <ctype.h> 88 #include <errno.h> 89 #include <pwd.h> 90 #include <stdio.h> 91 #include <stdlib.h> 92 #include <string.h> 93 #include <syslog.h> 94 #include <time.h> 95 #include <tzfile.h> 96 #include <unistd.h> 97 #include <netdb.h> 98 99 #ifdef KERBEROS5 100 #include <krb5/krb5.h> 101 #endif 102 103 #include "extern.h" 104 #include "version.h" 105 106 static int cmd_type; 107 static int cmd_form; 108 static int cmd_bytesz; 109 110 char cbuf[FTP_BUFLEN]; 111 char *cmdp; 112 char *fromname; 113 114 extern int epsvall; 115 struct tab sitetab[]; 116 117 static int check_write(const char *, int); 118 static void help(struct tab *, const char *); 119 static void port_check(const char *, int); 120 int yylex(void); 121 122 %} 123 124 %union { 125 struct { 126 LLT ll; 127 int i; 128 } u; 129 char *s; 130 const char *cs; 131 } 132 133 %token 134 A B C E F I 135 L N P R S T 136 137 SP CRLF COMMA ALL 138 139 USER PASS ACCT CWD CDUP SMNT 140 QUIT REIN PORT PASV TYPE STRU 141 MODE RETR STOR STOU APPE ALLO 142 REST RNFR RNTO ABOR DELE RMD 143 MKD PWD LIST NLST SITE SYST 144 STAT HELP NOOP 145 146 AUTH ADAT PROT PBSZ CCC MIC 147 CONF ENC 148 149 FEAT OPTS 150 151 SIZE MDTM MLST MLSD 152 153 LPRT LPSV EPRT EPSV 154 155 MAIL MLFL MRCP MRSQ MSAM MSND 156 MSOM 157 158 CHMOD IDLE RATEGET RATEPUT UMASK 159 160 LEXERR 161 162 %token <s> STRING 163 %token <u> NUMBER 164 165 %type <u.i> check_login octal_number byte_size 166 %type <u.i> struct_code mode_code type_code form_code decimal_integer 167 %type <s> pathstring pathname password username 168 %type <s> mechanism_name base64data prot_code 169 170 %start cmd_sel 171 172 %% 173 174 cmd_sel 175 : cmd 176 { 177 REASSIGN(fromname, NULL); 178 restart_point = (off_t) 0; 179 } 180 181 | rcmd 182 183 ; 184 185 cmd 186 /* RFC 959 */ 187 : USER SP username CRLF 188 { 189 user($3); 190 free($3); 191 } 192 193 | PASS SP password CRLF 194 { 195 pass($3); 196 explicit_memset($3, 0, strlen($3)); 197 free($3); 198 } 199 200 | CWD check_login CRLF 201 { 202 if ($2) 203 cwd(homedir); 204 } 205 206 | CWD check_login SP pathname CRLF 207 { 208 if ($2 && $4 != NULL) 209 cwd($4); 210 if ($4 != NULL) 211 free($4); 212 } 213 214 | CDUP check_login CRLF 215 { 216 if ($2) 217 cwd(".."); 218 } 219 220 | QUIT CRLF 221 { 222 if (logged_in) { 223 reply(-221, "%s", ""); 224 reply(0, 225 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 226 (LLT)total_data, PLURAL(total_data), 227 (LLT)total_files, PLURAL(total_files)); 228 reply(0, 229 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 230 (LLT)total_bytes, PLURAL(total_bytes), 231 (LLT)total_xfers, PLURAL(total_xfers)); 232 } 233 reply(221, 234 "Thank you for using the FTP service on %s.", 235 hostname); 236 if (logged_in && logging) { 237 syslog(LOG_INFO, 238 "Data traffic: " LLF " byte%s in " LLF " file%s", 239 (LLT)total_data, PLURAL(total_data), 240 (LLT)total_files, PLURAL(total_files)); 241 syslog(LOG_INFO, 242 "Total traffic: " LLF " byte%s in " LLF " transfer%s", 243 (LLT)total_bytes, PLURAL(total_bytes), 244 (LLT)total_xfers, PLURAL(total_xfers)); 245 } 246 247 dologout(0); 248 } 249 250 | PORT check_login SP host_port CRLF 251 { 252 if ($2) 253 port_check("PORT", AF_INET); 254 } 255 256 | LPRT check_login SP host_long_port4 CRLF 257 { 258 if ($2) 259 port_check("LPRT", AF_INET); 260 } 261 262 | LPRT check_login SP host_long_port6 CRLF 263 { 264 #ifdef INET6 265 if ($2) 266 port_check("LPRT", AF_INET6); 267 #else 268 reply(500, "IPv6 support not available."); 269 #endif 270 } 271 272 | EPRT check_login SP STRING CRLF 273 { 274 if ($2) { 275 if (extended_port($4) == 0) 276 port_check("EPRT", -1); 277 } 278 free($4); 279 } 280 281 | PASV check_login CRLF 282 { 283 if ($2) { 284 if (CURCLASS_FLAGS_ISSET(passive)) 285 passive(); 286 else 287 reply(500, "PASV mode not available."); 288 } 289 } 290 291 | LPSV check_login CRLF 292 { 293 if ($2) { 294 if (CURCLASS_FLAGS_ISSET(passive)) { 295 if (epsvall) 296 reply(501, 297 "LPSV disallowed after EPSV ALL"); 298 else 299 long_passive("LPSV", PF_UNSPEC); 300 } else 301 reply(500, "LPSV mode not available."); 302 } 303 } 304 305 | EPSV check_login SP NUMBER CRLF 306 { 307 if ($2) { 308 if (CURCLASS_FLAGS_ISSET(passive)) 309 long_passive("EPSV", 310 epsvproto2af($4.i)); 311 else 312 reply(500, "EPSV mode not available."); 313 } 314 } 315 316 | EPSV check_login SP ALL CRLF 317 { 318 if ($2) { 319 if (CURCLASS_FLAGS_ISSET(passive)) { 320 reply(200, 321 "EPSV ALL command successful."); 322 epsvall++; 323 } else 324 reply(500, "EPSV mode not available."); 325 } 326 } 327 328 | EPSV check_login CRLF 329 { 330 if ($2) { 331 if (CURCLASS_FLAGS_ISSET(passive)) 332 long_passive("EPSV", PF_UNSPEC); 333 else 334 reply(500, "EPSV mode not available."); 335 } 336 } 337 338 | TYPE check_login SP type_code CRLF 339 { 340 if ($2) { 341 342 switch (cmd_type) { 343 344 case TYPE_A: 345 if (cmd_form == FORM_N) { 346 reply(200, "Type set to A."); 347 type = cmd_type; 348 form = cmd_form; 349 } else 350 reply(504, "Form must be N."); 351 break; 352 353 case TYPE_E: 354 reply(504, "Type E not implemented."); 355 break; 356 357 case TYPE_I: 358 reply(200, "Type set to I."); 359 type = cmd_type; 360 break; 361 362 case TYPE_L: 363 #if NBBY == 8 364 if (cmd_bytesz == 8) { 365 reply(200, 366 "Type set to L (byte size 8)."); 367 type = cmd_type; 368 } else 369 reply(504, "Byte size must be 8."); 370 #else /* NBBY == 8 */ 371 UNIMPLEMENTED for NBBY != 8 372 #endif /* NBBY == 8 */ 373 } 374 375 } 376 } 377 378 | STRU check_login SP struct_code CRLF 379 { 380 if ($2) { 381 switch ($4) { 382 383 case STRU_F: 384 reply(200, "STRU F ok."); 385 break; 386 387 default: 388 reply(504, "Unimplemented STRU type."); 389 } 390 } 391 } 392 393 | MODE check_login SP mode_code CRLF 394 { 395 if ($2) { 396 switch ($4) { 397 398 case MODE_S: 399 reply(200, "MODE S ok."); 400 break; 401 402 default: 403 reply(502, "Unimplemented MODE type."); 404 } 405 } 406 } 407 408 | RETR check_login SP pathname CRLF 409 { 410 if ($2 && $4 != NULL) 411 retrieve(NULL, $4); 412 if ($4 != NULL) 413 free($4); 414 } 415 416 | STOR SP pathname CRLF 417 { 418 if (check_write($3, 1)) 419 store($3, "w", 0); 420 if ($3 != NULL) 421 free($3); 422 } 423 424 | STOU SP pathname CRLF 425 { 426 if (check_write($3, 1)) 427 store($3, "w", 1); 428 if ($3 != NULL) 429 free($3); 430 } 431 432 | APPE SP pathname CRLF 433 { 434 if (check_write($3, 1)) 435 store($3, "a", 0); 436 if ($3 != NULL) 437 free($3); 438 } 439 440 | ALLO check_login SP NUMBER CRLF 441 { 442 if ($2) 443 reply(202, "ALLO command ignored."); 444 } 445 446 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 447 { 448 if ($2) 449 reply(202, "ALLO command ignored."); 450 } 451 452 | RNTO SP pathname CRLF 453 { 454 if (check_write($3, 0)) { 455 if (fromname) { 456 renamecmd(fromname, $3); 457 REASSIGN(fromname, NULL); 458 } else { 459 reply(503, "Bad sequence of commands."); 460 } 461 } 462 if ($3 != NULL) 463 free($3); 464 } 465 466 | ABOR check_login CRLF 467 { 468 if (is_oob) 469 abor(); 470 else if ($2) 471 reply(225, "ABOR command successful."); 472 } 473 474 | DELE SP pathname CRLF 475 { 476 if (check_write($3, 0)) 477 delete($3); 478 if ($3 != NULL) 479 free($3); 480 } 481 482 | RMD SP pathname CRLF 483 { 484 if (check_write($3, 0)) 485 removedir($3); 486 if ($3 != NULL) 487 free($3); 488 } 489 490 | MKD SP pathname CRLF 491 { 492 if (check_write($3, 0)) 493 makedir($3); 494 if ($3 != NULL) 495 free($3); 496 } 497 498 | PWD check_login CRLF 499 { 500 if ($2) 501 pwd(); 502 } 503 504 | LIST check_login CRLF 505 { 506 const char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 507 508 if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 509 argv[1] = "-LlgA"; 510 if ($2) 511 retrieve(argv, ""); 512 } 513 514 | LIST check_login SP pathname CRLF 515 { 516 const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 517 518 if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 519 argv[1] = "-LlgA"; 520 if ($2 && $4 != NULL) { 521 argv[2] = $4; 522 retrieve(argv, $4); 523 } 524 if ($4 != NULL) 525 free($4); 526 } 527 528 | NLST check_login CRLF 529 { 530 if ($2) 531 send_file_list("."); 532 } 533 534 | NLST check_login SP pathname CRLF 535 { 536 if ($2) 537 send_file_list($4); 538 free($4); 539 } 540 541 | SITE SP HELP CRLF 542 { 543 help(sitetab, NULL); 544 } 545 546 | SITE SP CHMOD SP octal_number SP pathname CRLF 547 { 548 if (check_write($7, 0)) { 549 if (($5 == -1) || ($5 > 0777)) 550 reply(501, 551 "CHMOD: Mode value must be between 0 and 0777"); 552 else if (chmod($7, $5) < 0) 553 perror_reply(550, $7); 554 else 555 reply(200, "CHMOD command successful."); 556 } 557 if ($7 != NULL) 558 free($7); 559 } 560 561 | SITE SP HELP SP STRING CRLF 562 { 563 help(sitetab, $5); 564 free($5); 565 } 566 567 | SITE SP IDLE check_login CRLF 568 { 569 if ($4) { 570 reply(200, 571 "Current IDLE time limit is " LLF 572 " seconds; max " LLF, 573 (LLT)curclass.timeout, 574 (LLT)curclass.maxtimeout); 575 } 576 } 577 578 | SITE SP IDLE check_login SP NUMBER CRLF 579 { 580 if ($4) { 581 if ($6.i < 30 || $6.i > curclass.maxtimeout) { 582 reply(501, 583 "IDLE time limit must be between 30 and " 584 LLF " seconds", 585 (LLT)curclass.maxtimeout); 586 } else { 587 curclass.timeout = $6.i; 588 (void) alarm(curclass.timeout); 589 reply(200, 590 "IDLE time limit set to " 591 LLF " seconds", 592 (LLT)curclass.timeout); 593 } 594 } 595 } 596 597 | SITE SP RATEGET check_login CRLF 598 { 599 if ($4) { 600 reply(200, 601 "Current RATEGET is " LLF " bytes/sec", 602 (LLT)curclass.rateget); 603 } 604 } 605 606 | SITE SP RATEGET check_login SP STRING CRLF 607 { 608 char errbuf[100]; 609 char *p = $6; 610 LLT rate; 611 612 if ($4) { 613 rate = strsuftollx("RATEGET", p, 0, 614 curclass.maxrateget 615 ? curclass.maxrateget 616 : LLTMAX, errbuf, sizeof(errbuf)); 617 if (errbuf[0]) 618 reply(501, "%s", errbuf); 619 else { 620 curclass.rateget = rate; 621 reply(200, 622 "RATEGET set to " LLF " bytes/sec", 623 (LLT)curclass.rateget); 624 } 625 } 626 free($6); 627 } 628 629 | SITE SP RATEPUT check_login CRLF 630 { 631 if ($4) { 632 reply(200, 633 "Current RATEPUT is " LLF " bytes/sec", 634 (LLT)curclass.rateput); 635 } 636 } 637 638 | SITE SP RATEPUT check_login SP STRING CRLF 639 { 640 char errbuf[100]; 641 char *p = $6; 642 LLT rate; 643 644 if ($4) { 645 rate = strsuftollx("RATEPUT", p, 0, 646 curclass.maxrateput 647 ? curclass.maxrateput 648 : LLTMAX, errbuf, sizeof(errbuf)); 649 if (errbuf[0]) 650 reply(501, "%s", errbuf); 651 else { 652 curclass.rateput = rate; 653 reply(200, 654 "RATEPUT set to " LLF " bytes/sec", 655 (LLT)curclass.rateput); 656 } 657 } 658 free($6); 659 } 660 661 | SITE SP UMASK check_login CRLF 662 { 663 int oldmask; 664 665 if ($4) { 666 oldmask = umask(0); 667 (void) umask(oldmask); 668 reply(200, "Current UMASK is %03o", oldmask); 669 } 670 } 671 672 | SITE SP UMASK check_login SP octal_number CRLF 673 { 674 int oldmask; 675 676 if ($4 && check_write("", 0)) { 677 if (($6 == -1) || ($6 > 0777)) { 678 reply(501, "Bad UMASK value"); 679 } else { 680 oldmask = umask($6); 681 reply(200, 682 "UMASK set to %03o (was %03o)", 683 $6, oldmask); 684 } 685 } 686 } 687 688 | SYST CRLF 689 { 690 if (EMPTYSTR(version)) 691 reply(215, "UNIX Type: L%d", NBBY); 692 else 693 reply(215, "UNIX Type: L%d Version: %s", NBBY, 694 version); 695 } 696 697 | STAT check_login SP pathname CRLF 698 { 699 if ($2 && $4 != NULL) 700 statfilecmd($4); 701 if ($4 != NULL) 702 free($4); 703 } 704 705 | STAT CRLF 706 { 707 if (is_oob) 708 statxfer(); 709 else 710 statcmd(); 711 } 712 713 | HELP CRLF 714 { 715 help(cmdtab, NULL); 716 } 717 718 | HELP SP STRING CRLF 719 { 720 char *cp = $3; 721 722 if (strncasecmp(cp, "SITE", 4) == 0) { 723 cp = $3 + 4; 724 if (*cp == ' ') 725 cp++; 726 if (*cp) 727 help(sitetab, cp); 728 else 729 help(sitetab, NULL); 730 } else 731 help(cmdtab, $3); 732 free($3); 733 } 734 735 | NOOP CRLF 736 { 737 reply(200, "NOOP command successful."); 738 } 739 740 /* RFC 2228 */ 741 | AUTH SP mechanism_name CRLF 742 { 743 reply(502, "RFC 2228 authentication not implemented."); 744 free($3); 745 } 746 747 | ADAT SP base64data CRLF 748 { 749 reply(503, 750 "Please set authentication state with AUTH."); 751 free($3); 752 } 753 754 | PROT SP prot_code CRLF 755 { 756 reply(503, 757 "Please set protection buffer size with PBSZ."); 758 free($3); 759 } 760 761 | PBSZ SP decimal_integer CRLF 762 { 763 reply(503, 764 "Please set authentication state with AUTH."); 765 } 766 767 | CCC CRLF 768 { 769 reply(533, "No protection enabled."); 770 } 771 772 | MIC SP base64data CRLF 773 { 774 reply(502, "RFC 2228 authentication not implemented."); 775 free($3); 776 } 777 778 | CONF SP base64data CRLF 779 { 780 reply(502, "RFC 2228 authentication not implemented."); 781 free($3); 782 } 783 784 | ENC SP base64data CRLF 785 { 786 reply(502, "RFC 2228 authentication not implemented."); 787 free($3); 788 } 789 790 /* RFC 2389 */ 791 | FEAT CRLF 792 { 793 794 feat(); 795 } 796 797 | OPTS SP STRING CRLF 798 { 799 800 opts($3); 801 free($3); 802 } 803 804 805 /* RFC 3659 */ 806 807 /* 808 * Return size of file in a format suitable for 809 * using with RESTART (we just count bytes). 810 */ 811 | SIZE check_login SP pathname CRLF 812 { 813 if ($2 && $4 != NULL) 814 sizecmd($4); 815 if ($4 != NULL) 816 free($4); 817 } 818 819 /* 820 * Return modification time of file as an ISO 3307 821 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 822 * where xxx is the fractional second (of any precision, 823 * not necessarily 3 digits) 824 */ 825 | MDTM check_login SP pathname CRLF 826 { 827 if ($2 && $4 != NULL) { 828 struct stat stbuf; 829 if (stat($4, &stbuf) < 0) 830 perror_reply(550, $4); 831 else if (!S_ISREG(stbuf.st_mode)) { 832 reply(550, "%s: not a plain file.", $4); 833 } else { 834 struct tm *t; 835 836 t = gmtime(&stbuf.st_mtime); 837 reply(213, 838 "%04d%02d%02d%02d%02d%02d", 839 TM_YEAR_BASE + t->tm_year, 840 t->tm_mon+1, t->tm_mday, 841 t->tm_hour, t->tm_min, t->tm_sec); 842 } 843 } 844 if ($4 != NULL) 845 free($4); 846 } 847 848 | MLST check_login SP pathname CRLF 849 { 850 if ($2 && $4 != NULL) 851 mlst($4); 852 if ($4 != NULL) 853 free($4); 854 } 855 856 | MLST check_login CRLF 857 { 858 if ($2) 859 mlst(NULL); 860 } 861 862 | MLSD check_login SP pathname CRLF 863 { 864 if ($2 && $4 != NULL) 865 mlsd($4); 866 if ($4 != NULL) 867 free($4); 868 } 869 870 | MLSD check_login CRLF 871 { 872 if ($2) 873 mlsd(NULL); 874 } 875 876 | error CRLF 877 { 878 yyerrok; 879 } 880 ; 881 882 rcmd 883 : REST check_login SP NUMBER CRLF 884 { 885 if ($2) { 886 REASSIGN(fromname, NULL); 887 restart_point = (off_t)$4.ll; 888 reply(350, 889 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 890 (LLT)restart_point); 891 } 892 } 893 894 | RNFR SP pathname CRLF 895 { 896 restart_point = (off_t) 0; 897 if (check_write($3, 0)) { 898 REASSIGN(fromname, NULL); 899 fromname = renamefrom($3); 900 } 901 if ($3 != NULL) 902 free($3); 903 } 904 ; 905 906 username 907 : STRING 908 ; 909 910 password 911 : /* empty */ 912 { 913 $$ = (char *)calloc(1, sizeof(char)); 914 } 915 916 | STRING 917 ; 918 919 byte_size 920 : NUMBER 921 { 922 $$ = $1.i; 923 } 924 ; 925 926 host_port 927 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 928 NUMBER COMMA NUMBER 929 { 930 char *a, *p; 931 932 memset(&data_dest, 0, sizeof(data_dest)); 933 data_dest.su_len = sizeof(struct sockaddr_in); 934 data_dest.su_family = AF_INET; 935 p = (char *)&data_dest.su_port; 936 p[0] = $9.i; p[1] = $11.i; 937 a = (char *)&data_dest.su_addr; 938 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 939 } 940 ; 941 942 host_long_port4 943 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 944 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 945 NUMBER 946 { 947 char *a, *p; 948 949 memset(&data_dest, 0, sizeof(data_dest)); 950 data_dest.su_len = sizeof(struct sockaddr_in); 951 data_dest.su_family = AF_INET; 952 p = (char *)&data_dest.su_port; 953 p[0] = $15.i; p[1] = $17.i; 954 a = (char *)&data_dest.su_addr; 955 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 956 957 /* reject invalid LPRT command */ 958 if ($1.i != 4 || $3.i != 4 || $13.i != 2) 959 memset(&data_dest, 0, sizeof(data_dest)); 960 } 961 ; 962 963 host_long_port6 964 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 965 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 966 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 967 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 968 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 969 NUMBER 970 { 971 #ifdef INET6 972 unsigned char buf[16]; 973 974 (void)memset(&data_dest, 0, sizeof(data_dest)); 975 data_dest.su_len = sizeof(struct sockaddr_in6); 976 data_dest.su_family = AF_INET6; 977 buf[0] = $39.i; buf[1] = $41.i; 978 (void)memcpy(&data_dest.su_port, buf, 979 sizeof(data_dest.su_port)); 980 buf[0] = $5.i; buf[1] = $7.i; 981 buf[2] = $9.i; buf[3] = $11.i; 982 buf[4] = $13.i; buf[5] = $15.i; 983 buf[6] = $17.i; buf[7] = $19.i; 984 buf[8] = $21.i; buf[9] = $23.i; 985 buf[10] = $25.i; buf[11] = $27.i; 986 buf[12] = $29.i; buf[13] = $31.i; 987 buf[14] = $33.i; buf[15] = $35.i; 988 (void)memcpy(&data_dest.si_su.su_sin6.sin6_addr, 989 buf, sizeof(data_dest.si_su.su_sin6.sin6_addr)); 990 if (his_addr.su_family == AF_INET6) { 991 /* XXX: more sanity checks! */ 992 data_dest.su_scope_id = his_addr.su_scope_id; 993 } 994 #else 995 memset(&data_dest, 0, sizeof(data_dest)); 996 #endif /* INET6 */ 997 /* reject invalid LPRT command */ 998 if ($1.i != 6 || $3.i != 16 || $37.i != 2) 999 memset(&data_dest, 0, sizeof(data_dest)); 1000 } 1001 ; 1002 1003 form_code 1004 : N 1005 { 1006 $$ = FORM_N; 1007 } 1008 1009 | T 1010 { 1011 $$ = FORM_T; 1012 } 1013 1014 | C 1015 { 1016 $$ = FORM_C; 1017 } 1018 ; 1019 1020 type_code 1021 : A 1022 { 1023 cmd_type = TYPE_A; 1024 cmd_form = FORM_N; 1025 } 1026 1027 | A SP form_code 1028 { 1029 cmd_type = TYPE_A; 1030 cmd_form = $3; 1031 } 1032 1033 | E 1034 { 1035 cmd_type = TYPE_E; 1036 cmd_form = FORM_N; 1037 } 1038 1039 | E SP form_code 1040 { 1041 cmd_type = TYPE_E; 1042 cmd_form = $3; 1043 } 1044 1045 | I 1046 { 1047 cmd_type = TYPE_I; 1048 } 1049 1050 | L 1051 { 1052 cmd_type = TYPE_L; 1053 cmd_bytesz = NBBY; 1054 } 1055 1056 | L SP byte_size 1057 { 1058 cmd_type = TYPE_L; 1059 cmd_bytesz = $3; 1060 } 1061 1062 /* this is for a bug in the BBN ftp */ 1063 | L byte_size 1064 { 1065 cmd_type = TYPE_L; 1066 cmd_bytesz = $2; 1067 } 1068 ; 1069 1070 struct_code 1071 : F 1072 { 1073 $$ = STRU_F; 1074 } 1075 1076 | R 1077 { 1078 $$ = STRU_R; 1079 } 1080 1081 | P 1082 { 1083 $$ = STRU_P; 1084 } 1085 ; 1086 1087 mode_code 1088 : S 1089 { 1090 $$ = MODE_S; 1091 } 1092 1093 | B 1094 { 1095 $$ = MODE_B; 1096 } 1097 1098 | C 1099 { 1100 $$ = MODE_C; 1101 } 1102 ; 1103 1104 pathname 1105 : pathstring 1106 { 1107 /* 1108 * Problem: this production is used for all pathname 1109 * processing, but only gives a 550 error reply. 1110 * This is a valid reply in some cases but not in 1111 * others. 1112 */ 1113 if (logged_in && $1 && *$1 == '~') { 1114 char *path, *home, *result; 1115 size_t len; 1116 1117 path = strchr($1 + 1, '/'); 1118 if (path != NULL) 1119 *path++ = '\0'; 1120 if ($1[1] == '\0') 1121 home = homedir; 1122 else { 1123 struct passwd *hpw; 1124 1125 if ((hpw = getpwnam($1 + 1)) != NULL) 1126 home = hpw->pw_dir; 1127 else 1128 home = $1; 1129 } 1130 len = strlen(home) + 1; 1131 if (path != NULL) 1132 len += strlen(path) + 1; 1133 if ((result = malloc(len)) == NULL) 1134 fatal("Local resource failure: malloc"); 1135 strlcpy(result, home, len); 1136 if (path != NULL) { 1137 strlcat(result, "/", len); 1138 strlcat(result, path, len); 1139 } 1140 $$ = result; 1141 free($1); 1142 } else 1143 $$ = $1; 1144 } 1145 ; 1146 1147 pathstring 1148 : STRING 1149 ; 1150 1151 octal_number 1152 : NUMBER 1153 { 1154 int ret, dec, multby, digit; 1155 1156 /* 1157 * Convert a number that was read as decimal number 1158 * to what it would be if it had been read as octal. 1159 */ 1160 dec = $1.i; 1161 multby = 1; 1162 ret = 0; 1163 while (dec) { 1164 digit = dec%10; 1165 if (digit > 7) { 1166 ret = -1; 1167 break; 1168 } 1169 ret += digit * multby; 1170 multby *= 8; 1171 dec /= 10; 1172 } 1173 $$ = ret; 1174 } 1175 ; 1176 1177 mechanism_name 1178 : STRING 1179 ; 1180 1181 base64data 1182 : STRING 1183 ; 1184 1185 prot_code 1186 : STRING 1187 ; 1188 1189 decimal_integer 1190 : NUMBER 1191 { 1192 $$ = $1.i; 1193 } 1194 ; 1195 1196 check_login 1197 : /* empty */ 1198 { 1199 if (logged_in) 1200 $$ = 1; 1201 else { 1202 reply(530, "Please login with USER and PASS."); 1203 $$ = 0; 1204 hasyyerrored = 1; 1205 } 1206 } 1207 ; 1208 1209 %% 1210 1211 #define CMD 0 /* beginning of command */ 1212 #define ARGS 1 /* expect miscellaneous arguments */ 1213 #define STR1 2 /* expect SP followed by STRING */ 1214 #define STR2 3 /* expect STRING */ 1215 #define OSTR 4 /* optional SP then STRING */ 1216 #define ZSTR1 5 /* SP then optional STRING */ 1217 #define ZSTR2 6 /* optional STRING after SP */ 1218 #define SITECMD 7 /* SITE command */ 1219 #define NSTR 8 /* Number followed by a string */ 1220 #define NOARGS 9 /* No arguments allowed */ 1221 #define EOLN 10 /* End of line */ 1222 1223 struct tab cmdtab[] = { 1224 /* From RFC 959, in order defined (5.3.1) */ 1225 { "USER", USER, STR1, 1, "<sp> username", 0, }, 1226 { "PASS", PASS, ZSTR1, 1, "<sp> password", 0, }, 1227 { "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, 1228 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 1229 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, 1230 { "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, 1231 { "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, 1232 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, 1233 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5", 0, }, 1234 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, 1235 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|", 0, }, 1236 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, 1237 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, 1238 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]", 0, }, 1239 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]", 0, }, 1240 { "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, 1241 { "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, 1242 { "RETR", RETR, STR1, 1, "<sp> file-name", 0, }, 1243 { "STOR", STOR, STR1, 1, "<sp> file-name", 0, }, 1244 { "STOU", STOU, STR1, 1, "<sp> file-name", 0, }, 1245 { "APPE", APPE, STR1, 1, "<sp> file-name", 0, }, 1246 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, 1247 { "REST", REST, ARGS, 1, "<sp> offset (restart command)", 0, }, 1248 { "RNFR", RNFR, STR1, 1, "<sp> file-name", 0, }, 1249 { "RNTO", RNTO, STR1, 1, "<sp> file-name", 0, }, 1250 { "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, 1251 { "DELE", DELE, STR1, 1, "<sp> file-name", 0, }, 1252 { "RMD", RMD, STR1, 1, "<sp> path-name", 0, }, 1253 { "MKD", MKD, STR1, 1, "<sp> path-name", 0, }, 1254 { "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, 1255 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]", 0, }, 1256 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]", 0, }, 1257 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]", 0, }, 1258 { "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, 1259 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]", 0, }, 1260 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, 1261 { "NOOP", NOOP, NOARGS, 2, "", 0, }, 1262 1263 /* From RFC 2228, in order defined */ 1264 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name", 0, }, 1265 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data", 0, }, 1266 { "PROT", PROT, STR1, 1, "<sp> prot-code", 0, }, 1267 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer", 0, }, 1268 { "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, 1269 { "MIC", MIC, STR1, 4, "<sp> base64data", 0, }, 1270 { "CONF", CONF, STR1, 4, "<sp> base64data", 0, }, 1271 { "ENC", ENC, STR1, 4, "<sp> base64data", 0, }, 1272 1273 /* From RFC 2389, in order defined */ 1274 { "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, 1275 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]", 0, }, 1276 1277 /* From RFC 3659, in order defined */ 1278 { "MDTM", MDTM, OSTR, 1, "<sp> path-name", 0, }, 1279 { "SIZE", SIZE, OSTR, 1, "<sp> path-name", 0, }, 1280 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]", 0, }, 1281 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 1282 1283 /* obsolete commands */ 1284 { "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, 1285 { "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, 1286 { "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, 1287 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, 1288 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, 1289 { "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, 1290 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, 1291 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, 1292 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 1293 { "XMKD", MKD, STR1, 1, "<sp> path-name", 0, }, 1294 { "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, 1295 { "XRMD", RMD, STR1, 1, "<sp> path-name", 0, }, 1296 1297 { NULL, 0, 0, 0, 0, 0, } 1298 }; 1299 1300 struct tab sitetab[] = { 1301 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name", 0, }, 1302 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, 1303 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]", 0, }, 1304 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]", 0, }, 1305 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]", 0, }, 1306 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]", 0, }, 1307 { NULL, 0, 0, 0, 0, 0, } 1308 }; 1309 1310 /* 1311 * Check if a filename is allowed to be modified (isupload == 0) or 1312 * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 1313 * If the filename is NULL, fail. 1314 * If the filename is "", don't do the sane name check. 1315 */ 1316 static int 1317 check_write(const char *file, int isupload) 1318 { 1319 if (file == NULL) 1320 return (0); 1321 if (! logged_in) { 1322 reply(530, "Please login with USER and PASS."); 1323 return (0); 1324 } 1325 /* checking modify */ 1326 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 1327 reply(502, "No permission to use this command."); 1328 return (0); 1329 } 1330 /* checking upload */ 1331 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 1332 reply(502, "No permission to use this command."); 1333 return (0); 1334 } 1335 1336 /* checking sanenames */ 1337 if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { 1338 const char *p; 1339 1340 if (file[0] == '.') 1341 goto insane_name; 1342 for (p = file; *p; p++) { 1343 if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || 1344 *p == ',' || *p == '.' || *p == '_') 1345 continue; 1346 insane_name: 1347 reply(553, "File name `%s' not allowed.", file); 1348 return (0); 1349 } 1350 } 1351 return (1); 1352 } 1353 1354 struct tab * 1355 lookup(struct tab *p, const char *cmd) 1356 { 1357 1358 for (; p->name != NULL; p++) 1359 if (strcasecmp(cmd, p->name) == 0) 1360 return (p); 1361 return (0); 1362 } 1363 1364 #include <arpa/telnet.h> 1365 1366 /* 1367 * get_line - a hacked up version of fgets to ignore TELNET escape codes. 1368 * `s' is the buffer to read into. 1369 * `n' is the 1 less than the size of the buffer, to allow trailing NUL 1370 * `iop' is the FILE to read from. 1371 * Returns 0 on success, -1 on EOF, -2 if the command was too long. 1372 */ 1373 int 1374 get_line(char *s, int n, FILE *iop) 1375 { 1376 int c; 1377 char *cs; 1378 1379 cs = s; 1380 /* tmpline may contain saved command from urgent mode interruption */ 1381 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1382 *cs++ = tmpline[c]; 1383 if (tmpline[c] == '\n') { 1384 *cs++ = '\0'; 1385 if (ftpd_debug) 1386 syslog(LOG_DEBUG, "command: %s", s); 1387 tmpline[0] = '\0'; 1388 return(0); 1389 } 1390 if (c == 0) 1391 tmpline[0] = '\0'; 1392 } 1393 while ((c = getc(iop)) != EOF) { 1394 total_bytes++; 1395 total_bytes_in++; 1396 c &= 0377; 1397 if (c == IAC) { 1398 if ((c = getc(iop)) != EOF) { 1399 total_bytes++; 1400 total_bytes_in++; 1401 c &= 0377; 1402 switch (c) { 1403 case WILL: 1404 case WONT: 1405 c = getc(iop); 1406 total_bytes++; 1407 total_bytes_in++; 1408 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 1409 (void) fflush(stdout); 1410 continue; 1411 case DO: 1412 case DONT: 1413 c = getc(iop); 1414 total_bytes++; 1415 total_bytes_in++; 1416 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 1417 (void) fflush(stdout); 1418 continue; 1419 case IAC: 1420 break; 1421 default: 1422 continue; /* ignore command */ 1423 } 1424 } 1425 } 1426 *cs++ = c; 1427 if (--n <= 0) { 1428 /* 1429 * If command doesn't fit into buffer, discard the 1430 * rest of the command and indicate truncation. 1431 * This prevents the command to be split up into 1432 * multiple commands. 1433 */ 1434 if (ftpd_debug) 1435 syslog(LOG_DEBUG, 1436 "command too long, last char: %d", c); 1437 while (c != '\n' && (c = getc(iop)) != EOF) 1438 continue; 1439 return (-2); 1440 } 1441 if (c == '\n') 1442 break; 1443 } 1444 if (c == EOF && cs == s) 1445 return (-1); 1446 *cs++ = '\0'; 1447 if (ftpd_debug) { 1448 if ((curclass.type != CLASS_GUEST && 1449 strncasecmp(s, "PASS ", 5) == 0) || 1450 strncasecmp(s, "ACCT ", 5) == 0) { 1451 /* Don't syslog passwords */ 1452 syslog(LOG_DEBUG, "command: %.4s ???", s); 1453 } else { 1454 char *cp; 1455 int len; 1456 1457 /* Don't syslog trailing CR-LF */ 1458 len = strlen(s); 1459 cp = s + len - 1; 1460 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1461 --cp; 1462 --len; 1463 } 1464 syslog(LOG_DEBUG, "command: %.*s", len, s); 1465 } 1466 } 1467 return (0); 1468 } 1469 1470 void 1471 ftp_handle_line(char *cp) 1472 { 1473 1474 cmdp = cp; 1475 yyparse(); 1476 } 1477 1478 void 1479 ftp_loop(void) 1480 { 1481 int ret; 1482 1483 while (1) { 1484 (void) alarm(curclass.timeout); 1485 ret = get_line(cbuf, sizeof(cbuf)-1, stdin); 1486 (void) alarm(0); 1487 if (ret == -1) { 1488 reply(221, "You could at least say goodbye."); 1489 dologout(0); 1490 } else if (ret == -2) { 1491 reply(500, "Command too long."); 1492 } else { 1493 ftp_handle_line(cbuf); 1494 } 1495 } 1496 /*NOTREACHED*/ 1497 } 1498 1499 int 1500 yylex(void) 1501 { 1502 static int cpos, state; 1503 char *cp, *cp2; 1504 struct tab *p; 1505 int n; 1506 char c; 1507 1508 switch (state) { 1509 1510 case CMD: 1511 hasyyerrored = 0; 1512 if ((cp = strchr(cmdp, '\r'))) { 1513 *cp = '\0'; 1514 #if defined(HAVE_SETPROCTITLE) 1515 if (strncasecmp(cmdp, "PASS", 4) != 0 && 1516 strncasecmp(cmdp, "ACCT", 4) != 0) 1517 setproctitle("%s: %s", proctitle, cmdp); 1518 #endif /* defined(HAVE_SETPROCTITLE) */ 1519 *cp++ = '\n'; 1520 *cp = '\0'; 1521 } 1522 if ((cp = strpbrk(cmdp, " \n"))) 1523 cpos = cp - cmdp; 1524 if (cpos == 0) 1525 cpos = 4; 1526 c = cmdp[cpos]; 1527 cmdp[cpos] = '\0'; 1528 p = lookup(cmdtab, cmdp); 1529 cmdp[cpos] = c; 1530 if (p != NULL) { 1531 if (is_oob && ! CMD_OOB(p)) { 1532 /* command will be handled in-band */ 1533 return (0); 1534 } else if (! CMD_IMPLEMENTED(p)) { 1535 reply(502, "%s command not implemented.", 1536 p->name); 1537 hasyyerrored = 1; 1538 break; 1539 } 1540 state = p->state; 1541 yylval.cs = p->name; 1542 return (p->token); 1543 } 1544 break; 1545 1546 case SITECMD: 1547 if (cmdp[cpos] == ' ') { 1548 cpos++; 1549 return (SP); 1550 } 1551 cp = &cmdp[cpos]; 1552 if ((cp2 = strpbrk(cp, " \n"))) 1553 cpos = cp2 - cmdp; 1554 c = cmdp[cpos]; 1555 cmdp[cpos] = '\0'; 1556 p = lookup(sitetab, cp); 1557 cmdp[cpos] = c; 1558 if (p != NULL) { 1559 if (!CMD_IMPLEMENTED(p)) { 1560 reply(502, "SITE %s command not implemented.", 1561 p->name); 1562 hasyyerrored = 1; 1563 break; 1564 } 1565 state = p->state; 1566 yylval.cs = p->name; 1567 return (p->token); 1568 } 1569 break; 1570 1571 case OSTR: 1572 if (cmdp[cpos] == '\n') { 1573 state = EOLN; 1574 return (CRLF); 1575 } 1576 /* FALLTHROUGH */ 1577 1578 case STR1: 1579 case ZSTR1: 1580 dostr1: 1581 if (cmdp[cpos] == ' ') { 1582 cpos++; 1583 state = state == OSTR ? STR2 : state+1; 1584 return (SP); 1585 } 1586 break; 1587 1588 case ZSTR2: 1589 if (cmdp[cpos] == '\n') { 1590 state = EOLN; 1591 return (CRLF); 1592 } 1593 /* FALLTHROUGH */ 1594 1595 case STR2: 1596 cp = &cmdp[cpos]; 1597 n = strlen(cp); 1598 cpos += n - 1; 1599 /* 1600 * Make sure the string is nonempty and \n terminated. 1601 */ 1602 if (n > 1 && cmdp[cpos] == '\n') { 1603 cmdp[cpos] = '\0'; 1604 yylval.s = ftpd_strdup(cp); 1605 cmdp[cpos] = '\n'; 1606 state = ARGS; 1607 return (STRING); 1608 } 1609 break; 1610 1611 case NSTR: 1612 if (cmdp[cpos] == ' ') { 1613 cpos++; 1614 return (SP); 1615 } 1616 if (isdigit((unsigned char)cmdp[cpos])) { 1617 cp = &cmdp[cpos]; 1618 while (isdigit((unsigned char)cmdp[++cpos])) 1619 ; 1620 c = cmdp[cpos]; 1621 cmdp[cpos] = '\0'; 1622 yylval.u.i = atoi(cp); 1623 cmdp[cpos] = c; 1624 state = STR1; 1625 return (NUMBER); 1626 } 1627 state = STR1; 1628 goto dostr1; 1629 1630 case ARGS: 1631 if (isdigit((unsigned char)cmdp[cpos])) { 1632 cp = &cmdp[cpos]; 1633 while (isdigit((unsigned char)cmdp[++cpos])) 1634 ; 1635 c = cmdp[cpos]; 1636 cmdp[cpos] = '\0'; 1637 yylval.u.i = atoi(cp); 1638 yylval.u.ll = STRTOLL(cp, NULL, 10); 1639 cmdp[cpos] = c; 1640 return (NUMBER); 1641 } 1642 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 1643 && !isalnum((unsigned char)cmdp[cpos + 3])) { 1644 cpos += 3; 1645 return (ALL); 1646 } 1647 switch (cmdp[cpos++]) { 1648 1649 case '\n': 1650 state = EOLN; 1651 return (CRLF); 1652 1653 case ' ': 1654 return (SP); 1655 1656 case ',': 1657 return (COMMA); 1658 1659 case 'A': 1660 case 'a': 1661 return (A); 1662 1663 case 'B': 1664 case 'b': 1665 return (B); 1666 1667 case 'C': 1668 case 'c': 1669 return (C); 1670 1671 case 'E': 1672 case 'e': 1673 return (E); 1674 1675 case 'F': 1676 case 'f': 1677 return (F); 1678 1679 case 'I': 1680 case 'i': 1681 return (I); 1682 1683 case 'L': 1684 case 'l': 1685 return (L); 1686 1687 case 'N': 1688 case 'n': 1689 return (N); 1690 1691 case 'P': 1692 case 'p': 1693 return (P); 1694 1695 case 'R': 1696 case 'r': 1697 return (R); 1698 1699 case 'S': 1700 case 's': 1701 return (S); 1702 1703 case 'T': 1704 case 't': 1705 return (T); 1706 1707 } 1708 break; 1709 1710 case NOARGS: 1711 if (cmdp[cpos] == '\n') { 1712 state = EOLN; 1713 return (CRLF); 1714 } 1715 c = cmdp[cpos]; 1716 cmdp[cpos] = '\0'; 1717 reply(501, "'%s' command does not take any arguments.", cmdp); 1718 hasyyerrored = 1; 1719 cmdp[cpos] = c; 1720 break; 1721 1722 case EOLN: 1723 state = CMD; 1724 return (0); 1725 1726 default: 1727 fatal("Unknown state in scanner."); 1728 } 1729 yyerror(NULL); 1730 state = CMD; 1731 return (0); 1732 } 1733 1734 /* ARGSUSED */ 1735 void 1736 yyerror(const char *s) 1737 { 1738 char *cp; 1739 1740 if (hasyyerrored || is_oob) 1741 return; 1742 if ((cp = strchr(cmdp,'\n')) != NULL) 1743 *cp = '\0'; 1744 reply(500, "'%s': command not understood.", cmdp); 1745 hasyyerrored = 1; 1746 } 1747 1748 static void 1749 help(struct tab *ctab, const char *s) 1750 { 1751 struct tab *c; 1752 int width, NCMDS; 1753 const char *htype; 1754 1755 if (ctab == sitetab) 1756 htype = "SITE "; 1757 else 1758 htype = ""; 1759 width = 0, NCMDS = 0; 1760 for (c = ctab; c->name != NULL; c++) { 1761 int len = strlen(c->name); 1762 1763 if (len > width) 1764 width = len; 1765 NCMDS++; 1766 } 1767 width = (width + 8) &~ 7; 1768 if (s == 0) { 1769 int i, j, w; 1770 int columns, lines; 1771 1772 reply(-214, "%s", ""); 1773 reply(0, "The following %scommands are recognized.", htype); 1774 reply(0, "(`-' = not implemented, `+' = supports options)"); 1775 columns = 76 / width; 1776 if (columns == 0) 1777 columns = 1; 1778 lines = (NCMDS + columns - 1) / columns; 1779 for (i = 0; i < lines; i++) { 1780 cprintf(stdout, " "); 1781 for (j = 0; j < columns; j++) { 1782 c = ctab + j * lines + i; 1783 cprintf(stdout, "%s", c->name); 1784 w = strlen(c->name); 1785 if (! CMD_IMPLEMENTED(c)) { 1786 CPUTC('-', stdout); 1787 w++; 1788 } 1789 if (CMD_HAS_OPTIONS(c)) { 1790 CPUTC('+', stdout); 1791 w++; 1792 } 1793 if (c + lines >= &ctab[NCMDS]) 1794 break; 1795 while (w < width) { 1796 CPUTC(' ', stdout); 1797 w++; 1798 } 1799 } 1800 cprintf(stdout, "\r\n"); 1801 } 1802 (void) fflush(stdout); 1803 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1804 return; 1805 } 1806 c = lookup(ctab, s); 1807 if (c == (struct tab *)0) { 1808 reply(502, "Unknown command '%s'.", s); 1809 return; 1810 } 1811 if (CMD_IMPLEMENTED(c)) 1812 reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 1813 else 1814 reply(504, "%s%-*s\t%s; not implemented.", htype, width, 1815 c->name, c->help); 1816 } 1817 1818 /* 1819 * Check that the structures used for a PORT, LPRT or EPRT command are 1820 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 1821 * If family != -1 check that his_addr.su_family == family. 1822 */ 1823 static void 1824 port_check(const char *cmd, int family) 1825 { 1826 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 1827 char s1[NI_MAXHOST], s2[NI_MAXHOST]; 1828 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 1829 1830 if (epsvall) { 1831 reply(501, "%s disallowed after EPSV ALL", cmd); 1832 return; 1833 } 1834 1835 if (family != -1 && his_addr.su_family != family) { 1836 port_check_fail: 1837 reply(500, "Illegal %s command rejected", cmd); 1838 return; 1839 } 1840 1841 if (data_dest.su_family != his_addr.su_family) 1842 goto port_check_fail; 1843 1844 /* be paranoid, if told so */ 1845 if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 1846 #ifdef INET6 1847 /* 1848 * be paranoid, there are getnameinfo implementation that does 1849 * not present scopeid portion 1850 */ 1851 if (data_dest.su_family == AF_INET6 && 1852 data_dest.su_scope_id != his_addr.su_scope_id) 1853 goto port_check_fail; 1854 #endif 1855 1856 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 1857 h1, sizeof(h1), s1, sizeof(s1), niflags)) 1858 goto port_check_fail; 1859 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 1860 h2, sizeof(h2), s2, sizeof(s2), niflags)) 1861 goto port_check_fail; 1862 1863 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 1864 goto port_check_fail; 1865 } 1866 1867 usedefault = 0; 1868 if (pdata >= 0) { 1869 (void) close(pdata); 1870 pdata = -1; 1871 } 1872 reply(200, "%s command successful.", cmd); 1873 } 1874