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