1 /* $NetBSD: ftpcmd.y,v 1.90 2009/07/13 19:05:40 roy 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.90 2009/07/13 19:05:40 roy 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 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 mlst(NULL); 859 } 860 861 | MLSD check_login SP pathname CRLF 862 { 863 if ($2 && $4 != NULL) 864 mlsd($4); 865 if ($4 != NULL) 866 free($4); 867 } 868 869 | MLSD check_login CRLF 870 { 871 mlsd(NULL); 872 } 873 874 | error CRLF 875 { 876 yyerrok; 877 } 878 ; 879 880 rcmd 881 : REST check_login SP NUMBER CRLF 882 { 883 if ($2) { 884 REASSIGN(fromname, NULL); 885 restart_point = (off_t)$4.ll; 886 reply(350, 887 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 888 (LLT)restart_point); 889 } 890 } 891 892 | RNFR SP pathname CRLF 893 { 894 restart_point = (off_t) 0; 895 if (check_write($3, 0)) { 896 REASSIGN(fromname, NULL); 897 fromname = renamefrom($3); 898 } 899 if ($3 != NULL) 900 free($3); 901 } 902 ; 903 904 username 905 : STRING 906 ; 907 908 password 909 : /* empty */ 910 { 911 $$ = (char *)calloc(1, sizeof(char)); 912 } 913 914 | STRING 915 ; 916 917 byte_size 918 : NUMBER 919 { 920 $$ = $1.i; 921 } 922 ; 923 924 host_port 925 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 926 NUMBER COMMA NUMBER 927 { 928 char *a, *p; 929 930 memset(&data_dest, 0, sizeof(data_dest)); 931 data_dest.su_len = sizeof(struct sockaddr_in); 932 data_dest.su_family = AF_INET; 933 p = (char *)&data_dest.su_port; 934 p[0] = $9.i; p[1] = $11.i; 935 a = (char *)&data_dest.su_addr; 936 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 937 } 938 ; 939 940 host_long_port4 941 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 942 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 943 NUMBER 944 { 945 char *a, *p; 946 947 memset(&data_dest, 0, sizeof(data_dest)); 948 data_dest.su_len = sizeof(struct sockaddr_in); 949 data_dest.su_family = AF_INET; 950 p = (char *)&data_dest.su_port; 951 p[0] = $15.i; p[1] = $17.i; 952 a = (char *)&data_dest.su_addr; 953 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 954 955 /* reject invalid LPRT command */ 956 if ($1.i != 4 || $3.i != 4 || $13.i != 2) 957 memset(&data_dest, 0, sizeof(data_dest)); 958 } 959 ; 960 961 host_long_port6 962 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 963 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 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 968 { 969 #ifdef INET6 970 char *a, *p; 971 972 memset(&data_dest, 0, sizeof(data_dest)); 973 data_dest.su_len = sizeof(struct sockaddr_in6); 974 data_dest.su_family = AF_INET6; 975 p = (char *)&data_dest.su_port; 976 p[0] = $39.i; p[1] = $41.i; 977 a = (char *)&data_dest.si_su.su_sin6.sin6_addr; 978 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 979 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 980 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 981 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 982 if (his_addr.su_family == AF_INET6) { 983 /* XXX: more sanity checks! */ 984 data_dest.su_scope_id = his_addr.su_scope_id; 985 } 986 #else 987 memset(&data_dest, 0, sizeof(data_dest)); 988 #endif /* INET6 */ 989 /* reject invalid LPRT command */ 990 if ($1.i != 6 || $3.i != 16 || $37.i != 2) 991 memset(&data_dest, 0, sizeof(data_dest)); 992 } 993 ; 994 995 form_code 996 : N 997 { 998 $$ = FORM_N; 999 } 1000 1001 | T 1002 { 1003 $$ = FORM_T; 1004 } 1005 1006 | C 1007 { 1008 $$ = FORM_C; 1009 } 1010 ; 1011 1012 type_code 1013 : A 1014 { 1015 cmd_type = TYPE_A; 1016 cmd_form = FORM_N; 1017 } 1018 1019 | A SP form_code 1020 { 1021 cmd_type = TYPE_A; 1022 cmd_form = $3; 1023 } 1024 1025 | E 1026 { 1027 cmd_type = TYPE_E; 1028 cmd_form = FORM_N; 1029 } 1030 1031 | E SP form_code 1032 { 1033 cmd_type = TYPE_E; 1034 cmd_form = $3; 1035 } 1036 1037 | I 1038 { 1039 cmd_type = TYPE_I; 1040 } 1041 1042 | L 1043 { 1044 cmd_type = TYPE_L; 1045 cmd_bytesz = NBBY; 1046 } 1047 1048 | L SP byte_size 1049 { 1050 cmd_type = TYPE_L; 1051 cmd_bytesz = $3; 1052 } 1053 1054 /* this is for a bug in the BBN ftp */ 1055 | L byte_size 1056 { 1057 cmd_type = TYPE_L; 1058 cmd_bytesz = $2; 1059 } 1060 ; 1061 1062 struct_code 1063 : F 1064 { 1065 $$ = STRU_F; 1066 } 1067 1068 | R 1069 { 1070 $$ = STRU_R; 1071 } 1072 1073 | P 1074 { 1075 $$ = STRU_P; 1076 } 1077 ; 1078 1079 mode_code 1080 : S 1081 { 1082 $$ = MODE_S; 1083 } 1084 1085 | B 1086 { 1087 $$ = MODE_B; 1088 } 1089 1090 | C 1091 { 1092 $$ = MODE_C; 1093 } 1094 ; 1095 1096 pathname 1097 : pathstring 1098 { 1099 /* 1100 * Problem: this production is used for all pathname 1101 * processing, but only gives a 550 error reply. 1102 * This is a valid reply in some cases but not in 1103 * others. 1104 */ 1105 if (logged_in && $1 && *$1 == '~') { 1106 char *path, *home, *result; 1107 size_t len; 1108 1109 path = strchr($1 + 1, '/'); 1110 if (path != NULL) 1111 *path++ = '\0'; 1112 if ($1[1] == '\0') 1113 home = homedir; 1114 else { 1115 struct passwd *hpw; 1116 1117 if ((hpw = getpwnam($1 + 1)) != NULL) 1118 home = hpw->pw_dir; 1119 else 1120 home = $1; 1121 } 1122 len = strlen(home) + 1; 1123 if (path != NULL) 1124 len += strlen(path) + 1; 1125 if ((result = malloc(len)) == NULL) 1126 fatal("Local resource failure: malloc"); 1127 strlcpy(result, home, len); 1128 if (path != NULL) { 1129 strlcat(result, "/", len); 1130 strlcat(result, path, len); 1131 } 1132 $$ = result; 1133 free($1); 1134 } else 1135 $$ = $1; 1136 } 1137 ; 1138 1139 pathstring 1140 : STRING 1141 ; 1142 1143 octal_number 1144 : NUMBER 1145 { 1146 int ret, dec, multby, digit; 1147 1148 /* 1149 * Convert a number that was read as decimal number 1150 * to what it would be if it had been read as octal. 1151 */ 1152 dec = $1.i; 1153 multby = 1; 1154 ret = 0; 1155 while (dec) { 1156 digit = dec%10; 1157 if (digit > 7) { 1158 ret = -1; 1159 break; 1160 } 1161 ret += digit * multby; 1162 multby *= 8; 1163 dec /= 10; 1164 } 1165 $$ = ret; 1166 } 1167 ; 1168 1169 mechanism_name 1170 : STRING 1171 ; 1172 1173 base64data 1174 : STRING 1175 ; 1176 1177 prot_code 1178 : STRING 1179 ; 1180 1181 decimal_integer 1182 : NUMBER 1183 { 1184 $$ = $1.i; 1185 } 1186 ; 1187 1188 check_login 1189 : /* empty */ 1190 { 1191 if (logged_in) 1192 $$ = 1; 1193 else { 1194 reply(530, "Please login with USER and PASS."); 1195 $$ = 0; 1196 hasyyerrored = 1; 1197 } 1198 } 1199 ; 1200 1201 %% 1202 1203 #define CMD 0 /* beginning of command */ 1204 #define ARGS 1 /* expect miscellaneous arguments */ 1205 #define STR1 2 /* expect SP followed by STRING */ 1206 #define STR2 3 /* expect STRING */ 1207 #define OSTR 4 /* optional SP then STRING */ 1208 #define ZSTR1 5 /* SP then optional STRING */ 1209 #define ZSTR2 6 /* optional STRING after SP */ 1210 #define SITECMD 7 /* SITE command */ 1211 #define NSTR 8 /* Number followed by a string */ 1212 #define NOARGS 9 /* No arguments allowed */ 1213 #define EOLN 10 /* End of line */ 1214 1215 struct tab cmdtab[] = { 1216 /* From RFC 959, in order defined (5.3.1) */ 1217 { "USER", USER, STR1, 1, "<sp> username", 0, }, 1218 { "PASS", PASS, ZSTR1, 1, "<sp> password", 0, }, 1219 { "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, 1220 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 1221 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, 1222 { "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, 1223 { "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, 1224 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, 1225 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5", 0, }, 1226 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, 1227 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|", 0, }, 1228 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, 1229 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, 1230 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]", 0, }, 1231 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]", 0, }, 1232 { "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, 1233 { "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, 1234 { "RETR", RETR, STR1, 1, "<sp> file-name", 0, }, 1235 { "STOR", STOR, STR1, 1, "<sp> file-name", 0, }, 1236 { "STOU", STOU, STR1, 1, "<sp> file-name", 0, }, 1237 { "APPE", APPE, STR1, 1, "<sp> file-name", 0, }, 1238 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, 1239 { "REST", REST, ARGS, 1, "<sp> offset (restart command)", 0, }, 1240 { "RNFR", RNFR, STR1, 1, "<sp> file-name", 0, }, 1241 { "RNTO", RNTO, STR1, 1, "<sp> file-name", 0, }, 1242 { "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, 1243 { "DELE", DELE, STR1, 1, "<sp> file-name", 0, }, 1244 { "RMD", RMD, STR1, 1, "<sp> path-name", 0, }, 1245 { "MKD", MKD, STR1, 1, "<sp> path-name", 0, }, 1246 { "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, 1247 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]", 0, }, 1248 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]", 0, }, 1249 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]", 0, }, 1250 { "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, 1251 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]", 0, }, 1252 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, 1253 { "NOOP", NOOP, NOARGS, 2, "", 0, }, 1254 1255 /* From RFC 2228, in order defined */ 1256 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name", 0, }, 1257 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data", 0, }, 1258 { "PROT", PROT, STR1, 1, "<sp> prot-code", 0, }, 1259 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer", 0, }, 1260 { "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, 1261 { "MIC", MIC, STR1, 4, "<sp> base64data", 0, }, 1262 { "CONF", CONF, STR1, 4, "<sp> base64data", 0, }, 1263 { "ENC", ENC, STR1, 4, "<sp> base64data", 0, }, 1264 1265 /* From RFC 2389, in order defined */ 1266 { "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, 1267 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]", 0, }, 1268 1269 /* From RFC 3659, in order defined */ 1270 { "MDTM", MDTM, OSTR, 1, "<sp> path-name", 0, }, 1271 { "SIZE", SIZE, OSTR, 1, "<sp> path-name", 0, }, 1272 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]", 0, }, 1273 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 1274 1275 /* obsolete commands */ 1276 { "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, 1277 { "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, 1278 { "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, 1279 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, 1280 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, 1281 { "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, 1282 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, 1283 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, 1284 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 1285 { "XMKD", MKD, STR1, 1, "<sp> path-name", 0, }, 1286 { "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, 1287 { "XRMD", RMD, STR1, 1, "<sp> path-name", 0, }, 1288 1289 { NULL, 0, 0, 0, 0, 0, } 1290 }; 1291 1292 struct tab sitetab[] = { 1293 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name", 0, }, 1294 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, 1295 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]", 0, }, 1296 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]", 0, }, 1297 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]", 0, }, 1298 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]", 0, }, 1299 { NULL, 0, 0, 0, 0, 0, } 1300 }; 1301 1302 /* 1303 * Check if a filename is allowed to be modified (isupload == 0) or 1304 * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 1305 * If the filename is NULL, fail. 1306 * If the filename is "", don't do the sane name check. 1307 */ 1308 static int 1309 check_write(const char *file, int isupload) 1310 { 1311 if (file == NULL) 1312 return (0); 1313 if (! logged_in) { 1314 reply(530, "Please login with USER and PASS."); 1315 return (0); 1316 } 1317 /* checking modify */ 1318 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 1319 reply(502, "No permission to use this command."); 1320 return (0); 1321 } 1322 /* checking upload */ 1323 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 1324 reply(502, "No permission to use this command."); 1325 return (0); 1326 } 1327 1328 /* checking sanenames */ 1329 if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { 1330 const char *p; 1331 1332 if (file[0] == '.') 1333 goto insane_name; 1334 for (p = file; *p; p++) { 1335 if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || 1336 *p == ',' || *p == '.' || *p == '_') 1337 continue; 1338 insane_name: 1339 reply(553, "File name `%s' not allowed.", file); 1340 return (0); 1341 } 1342 } 1343 return (1); 1344 } 1345 1346 struct tab * 1347 lookup(struct tab *p, const char *cmd) 1348 { 1349 1350 for (; p->name != NULL; p++) 1351 if (strcasecmp(cmd, p->name) == 0) 1352 return (p); 1353 return (0); 1354 } 1355 1356 #include <arpa/telnet.h> 1357 1358 /* 1359 * get_line - a hacked up version of fgets to ignore TELNET escape codes. 1360 * `s' is the buffer to read into. 1361 * `n' is the 1 less than the size of the buffer, to allow trailing NUL 1362 * `iop' is the FILE to read from. 1363 * Returns 0 on success, -1 on EOF, -2 if the command was too long. 1364 */ 1365 int 1366 get_line(char *s, int n, FILE *iop) 1367 { 1368 int c; 1369 char *cs; 1370 1371 cs = s; 1372 /* tmpline may contain saved command from urgent mode interruption */ 1373 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1374 *cs++ = tmpline[c]; 1375 if (tmpline[c] == '\n') { 1376 *cs++ = '\0'; 1377 if (ftpd_debug) 1378 syslog(LOG_DEBUG, "command: %s", s); 1379 tmpline[0] = '\0'; 1380 return(0); 1381 } 1382 if (c == 0) 1383 tmpline[0] = '\0'; 1384 } 1385 while ((c = getc(iop)) != EOF) { 1386 total_bytes++; 1387 total_bytes_in++; 1388 c &= 0377; 1389 if (c == IAC) { 1390 if ((c = getc(iop)) != EOF) { 1391 total_bytes++; 1392 total_bytes_in++; 1393 c &= 0377; 1394 switch (c) { 1395 case WILL: 1396 case WONT: 1397 c = getc(iop); 1398 total_bytes++; 1399 total_bytes_in++; 1400 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 1401 (void) fflush(stdout); 1402 continue; 1403 case DO: 1404 case DONT: 1405 c = getc(iop); 1406 total_bytes++; 1407 total_bytes_in++; 1408 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 1409 (void) fflush(stdout); 1410 continue; 1411 case IAC: 1412 break; 1413 default: 1414 continue; /* ignore command */ 1415 } 1416 } 1417 } 1418 *cs++ = c; 1419 if (--n <= 0) { 1420 /* 1421 * If command doesn't fit into buffer, discard the 1422 * rest of the command and indicate truncation. 1423 * This prevents the command to be split up into 1424 * multiple commands. 1425 */ 1426 if (ftpd_debug) 1427 syslog(LOG_DEBUG, 1428 "command too long, last char: %d", c); 1429 while (c != '\n' && (c = getc(iop)) != EOF) 1430 continue; 1431 return (-2); 1432 } 1433 if (c == '\n') 1434 break; 1435 } 1436 if (c == EOF && cs == s) 1437 return (-1); 1438 *cs++ = '\0'; 1439 if (ftpd_debug) { 1440 if ((curclass.type != CLASS_GUEST && 1441 strncasecmp(s, "PASS ", 5) == 0) || 1442 strncasecmp(s, "ACCT ", 5) == 0) { 1443 /* Don't syslog passwords */ 1444 syslog(LOG_DEBUG, "command: %.4s ???", s); 1445 } else { 1446 char *cp; 1447 int len; 1448 1449 /* Don't syslog trailing CR-LF */ 1450 len = strlen(s); 1451 cp = s + len - 1; 1452 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1453 --cp; 1454 --len; 1455 } 1456 syslog(LOG_DEBUG, "command: %.*s", len, s); 1457 } 1458 } 1459 return (0); 1460 } 1461 1462 void 1463 ftp_handle_line(char *cp) 1464 { 1465 1466 cmdp = cp; 1467 yyparse(); 1468 } 1469 1470 void 1471 ftp_loop(void) 1472 { 1473 int ret; 1474 1475 while (1) { 1476 (void) alarm(curclass.timeout); 1477 ret = get_line(cbuf, sizeof(cbuf)-1, stdin); 1478 (void) alarm(0); 1479 if (ret == -1) { 1480 reply(221, "You could at least say goodbye."); 1481 dologout(0); 1482 } else if (ret == -2) { 1483 reply(500, "Command too long."); 1484 } else { 1485 ftp_handle_line(cbuf); 1486 } 1487 } 1488 /*NOTREACHED*/ 1489 } 1490 1491 int 1492 yylex(void) 1493 { 1494 static int cpos, state; 1495 char *cp, *cp2; 1496 struct tab *p; 1497 int n; 1498 char c; 1499 1500 switch (state) { 1501 1502 case CMD: 1503 hasyyerrored = 0; 1504 if ((cp = strchr(cmdp, '\r'))) { 1505 *cp = '\0'; 1506 #if defined(HAVE_SETPROCTITLE) 1507 if (strncasecmp(cmdp, "PASS", 4) != 0 && 1508 strncasecmp(cmdp, "ACCT", 4) != 0) 1509 setproctitle("%s: %s", proctitle, cmdp); 1510 #endif /* defined(HAVE_SETPROCTITLE) */ 1511 *cp++ = '\n'; 1512 *cp = '\0'; 1513 } 1514 if ((cp = strpbrk(cmdp, " \n"))) 1515 cpos = cp - cmdp; 1516 if (cpos == 0) 1517 cpos = 4; 1518 c = cmdp[cpos]; 1519 cmdp[cpos] = '\0'; 1520 p = lookup(cmdtab, cmdp); 1521 cmdp[cpos] = c; 1522 if (p != NULL) { 1523 if (is_oob && ! CMD_OOB(p)) { 1524 /* command will be handled in-band */ 1525 return (0); 1526 } else if (! CMD_IMPLEMENTED(p)) { 1527 reply(502, "%s command not implemented.", 1528 p->name); 1529 hasyyerrored = 1; 1530 break; 1531 } 1532 state = p->state; 1533 yylval.cs = p->name; 1534 return (p->token); 1535 } 1536 break; 1537 1538 case SITECMD: 1539 if (cmdp[cpos] == ' ') { 1540 cpos++; 1541 return (SP); 1542 } 1543 cp = &cmdp[cpos]; 1544 if ((cp2 = strpbrk(cp, " \n"))) 1545 cpos = cp2 - cmdp; 1546 c = cmdp[cpos]; 1547 cmdp[cpos] = '\0'; 1548 p = lookup(sitetab, cp); 1549 cmdp[cpos] = c; 1550 if (p != NULL) { 1551 if (!CMD_IMPLEMENTED(p)) { 1552 reply(502, "SITE %s command not implemented.", 1553 p->name); 1554 hasyyerrored = 1; 1555 break; 1556 } 1557 state = p->state; 1558 yylval.cs = p->name; 1559 return (p->token); 1560 } 1561 break; 1562 1563 case OSTR: 1564 if (cmdp[cpos] == '\n') { 1565 state = EOLN; 1566 return (CRLF); 1567 } 1568 /* FALLTHROUGH */ 1569 1570 case STR1: 1571 case ZSTR1: 1572 dostr1: 1573 if (cmdp[cpos] == ' ') { 1574 cpos++; 1575 state = state == OSTR ? STR2 : state+1; 1576 return (SP); 1577 } 1578 break; 1579 1580 case ZSTR2: 1581 if (cmdp[cpos] == '\n') { 1582 state = EOLN; 1583 return (CRLF); 1584 } 1585 /* FALLTHROUGH */ 1586 1587 case STR2: 1588 cp = &cmdp[cpos]; 1589 n = strlen(cp); 1590 cpos += n - 1; 1591 /* 1592 * Make sure the string is nonempty and \n terminated. 1593 */ 1594 if (n > 1 && cmdp[cpos] == '\n') { 1595 cmdp[cpos] = '\0'; 1596 yylval.s = ftpd_strdup(cp); 1597 cmdp[cpos] = '\n'; 1598 state = ARGS; 1599 return (STRING); 1600 } 1601 break; 1602 1603 case NSTR: 1604 if (cmdp[cpos] == ' ') { 1605 cpos++; 1606 return (SP); 1607 } 1608 if (isdigit((unsigned char)cmdp[cpos])) { 1609 cp = &cmdp[cpos]; 1610 while (isdigit((unsigned char)cmdp[++cpos])) 1611 ; 1612 c = cmdp[cpos]; 1613 cmdp[cpos] = '\0'; 1614 yylval.u.i = atoi(cp); 1615 cmdp[cpos] = c; 1616 state = STR1; 1617 return (NUMBER); 1618 } 1619 state = STR1; 1620 goto dostr1; 1621 1622 case ARGS: 1623 if (isdigit((unsigned char)cmdp[cpos])) { 1624 cp = &cmdp[cpos]; 1625 while (isdigit((unsigned char)cmdp[++cpos])) 1626 ; 1627 c = cmdp[cpos]; 1628 cmdp[cpos] = '\0'; 1629 yylval.u.i = atoi(cp); 1630 yylval.u.ll = STRTOLL(cp, (char **)NULL, 10); 1631 cmdp[cpos] = c; 1632 return (NUMBER); 1633 } 1634 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 1635 && !isalnum((unsigned char)cmdp[cpos + 3])) { 1636 cpos += 3; 1637 return (ALL); 1638 } 1639 switch (cmdp[cpos++]) { 1640 1641 case '\n': 1642 state = EOLN; 1643 return (CRLF); 1644 1645 case ' ': 1646 return (SP); 1647 1648 case ',': 1649 return (COMMA); 1650 1651 case 'A': 1652 case 'a': 1653 return (A); 1654 1655 case 'B': 1656 case 'b': 1657 return (B); 1658 1659 case 'C': 1660 case 'c': 1661 return (C); 1662 1663 case 'E': 1664 case 'e': 1665 return (E); 1666 1667 case 'F': 1668 case 'f': 1669 return (F); 1670 1671 case 'I': 1672 case 'i': 1673 return (I); 1674 1675 case 'L': 1676 case 'l': 1677 return (L); 1678 1679 case 'N': 1680 case 'n': 1681 return (N); 1682 1683 case 'P': 1684 case 'p': 1685 return (P); 1686 1687 case 'R': 1688 case 'r': 1689 return (R); 1690 1691 case 'S': 1692 case 's': 1693 return (S); 1694 1695 case 'T': 1696 case 't': 1697 return (T); 1698 1699 } 1700 break; 1701 1702 case NOARGS: 1703 if (cmdp[cpos] == '\n') { 1704 state = EOLN; 1705 return (CRLF); 1706 } 1707 c = cmdp[cpos]; 1708 cmdp[cpos] = '\0'; 1709 reply(501, "'%s' command does not take any arguments.", cmdp); 1710 hasyyerrored = 1; 1711 cmdp[cpos] = c; 1712 break; 1713 1714 case EOLN: 1715 state = CMD; 1716 return (0); 1717 1718 default: 1719 fatal("Unknown state in scanner."); 1720 } 1721 yyerror(NULL); 1722 state = CMD; 1723 return (0); 1724 } 1725 1726 /* ARGSUSED */ 1727 void 1728 yyerror(const char *s) 1729 { 1730 char *cp; 1731 1732 if (hasyyerrored || is_oob) 1733 return; 1734 if ((cp = strchr(cmdp,'\n')) != NULL) 1735 *cp = '\0'; 1736 reply(500, "'%s': command not understood.", cmdp); 1737 hasyyerrored = 1; 1738 } 1739 1740 static void 1741 help(struct tab *ctab, const char *s) 1742 { 1743 struct tab *c; 1744 int width, NCMDS; 1745 const char *htype; 1746 1747 if (ctab == sitetab) 1748 htype = "SITE "; 1749 else 1750 htype = ""; 1751 width = 0, NCMDS = 0; 1752 for (c = ctab; c->name != NULL; c++) { 1753 int len = strlen(c->name); 1754 1755 if (len > width) 1756 width = len; 1757 NCMDS++; 1758 } 1759 width = (width + 8) &~ 7; 1760 if (s == 0) { 1761 int i, j, w; 1762 int columns, lines; 1763 1764 reply(-214, "%s", ""); 1765 reply(0, "The following %scommands are recognized.", htype); 1766 reply(0, "(`-' = not implemented, `+' = supports options)"); 1767 columns = 76 / width; 1768 if (columns == 0) 1769 columns = 1; 1770 lines = (NCMDS + columns - 1) / columns; 1771 for (i = 0; i < lines; i++) { 1772 cprintf(stdout, " "); 1773 for (j = 0; j < columns; j++) { 1774 c = ctab + j * lines + i; 1775 cprintf(stdout, "%s", c->name); 1776 w = strlen(c->name); 1777 if (! CMD_IMPLEMENTED(c)) { 1778 CPUTC('-', stdout); 1779 w++; 1780 } 1781 if (CMD_HAS_OPTIONS(c)) { 1782 CPUTC('+', stdout); 1783 w++; 1784 } 1785 if (c + lines >= &ctab[NCMDS]) 1786 break; 1787 while (w < width) { 1788 CPUTC(' ', stdout); 1789 w++; 1790 } 1791 } 1792 cprintf(stdout, "\r\n"); 1793 } 1794 (void) fflush(stdout); 1795 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1796 return; 1797 } 1798 c = lookup(ctab, s); 1799 if (c == (struct tab *)0) { 1800 reply(502, "Unknown command '%s'.", s); 1801 return; 1802 } 1803 if (CMD_IMPLEMENTED(c)) 1804 reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 1805 else 1806 reply(504, "%s%-*s\t%s; not implemented.", htype, width, 1807 c->name, c->help); 1808 } 1809 1810 /* 1811 * Check that the structures used for a PORT, LPRT or EPRT command are 1812 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 1813 * If family != -1 check that his_addr.su_family == family. 1814 */ 1815 static void 1816 port_check(const char *cmd, int family) 1817 { 1818 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 1819 char s1[NI_MAXHOST], s2[NI_MAXHOST]; 1820 #ifdef NI_WITHSCOPEID 1821 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 1822 #else 1823 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 1824 #endif 1825 1826 if (epsvall) { 1827 reply(501, "%s disallowed after EPSV ALL", cmd); 1828 return; 1829 } 1830 1831 if (family != -1 && his_addr.su_family != family) { 1832 port_check_fail: 1833 reply(500, "Illegal %s command rejected", cmd); 1834 return; 1835 } 1836 1837 if (data_dest.su_family != his_addr.su_family) 1838 goto port_check_fail; 1839 1840 /* be paranoid, if told so */ 1841 if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 1842 #ifdef INET6 1843 /* 1844 * be paranoid, there are getnameinfo implementation that does 1845 * not present scopeid portion 1846 */ 1847 if (data_dest.su_family == AF_INET6 && 1848 data_dest.su_scope_id != his_addr.su_scope_id) 1849 goto port_check_fail; 1850 #endif 1851 1852 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 1853 h1, sizeof(h1), s1, sizeof(s1), niflags)) 1854 goto port_check_fail; 1855 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 1856 h2, sizeof(h2), s2, sizeof(s2), niflags)) 1857 goto port_check_fail; 1858 1859 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 1860 goto port_check_fail; 1861 } 1862 1863 usedefault = 0; 1864 if (pdata >= 0) { 1865 (void) close(pdata); 1866 pdata = -1; 1867 } 1868 reply(200, "%s command successful.", cmd); 1869 } 1870