1/* $NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $ */ 2 3/*- 4 * Copyright (c) 1997-2005 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39/* 40 * Copyright (c) 1985, 1988, 1993, 1994 41 * The Regents of the University of California. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. Neither the name of the University nor the names of its contributors 52 * may be used to endorse or promote products derived from this software 53 * without specific prior written permission. 54 * 55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 65 * SUCH DAMAGE. 66 * 67 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 68 */ 69 70/* 71 * Grammar for FTP commands. 72 * See RFC 959. 73 */ 74 75%{ 76#include <sys/cdefs.h> 77 78#ifndef lint 79#if 0 80static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 81#else 82__RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $"); 83#endif 84#endif /* not lint */ 85 86#include <sys/param.h> 87#include <sys/socket.h> 88#include <sys/stat.h> 89 90#include <netinet/in.h> 91#include <arpa/ftp.h> 92#include <arpa/inet.h> 93 94#include <ctype.h> 95#include <errno.h> 96#include <pwd.h> 97#include <stdio.h> 98#include <stdlib.h> 99#include <string.h> 100#include <syslog.h> 101#include <time.h> 102#include <tzfile.h> 103#include <unistd.h> 104#include <netdb.h> 105 106#ifdef KERBEROS5 107#include <krb5/krb5.h> 108#endif 109 110#include "extern.h" 111#include "version.h" 112 113static int cmd_type; 114static int cmd_form; 115static int cmd_bytesz; 116 117char cbuf[FTP_BUFLEN]; 118char *cmdp; 119char *fromname; 120 121extern int epsvall; 122struct tab sitetab[]; 123 124static int check_write(const char *, int); 125static void help(struct tab *, const char *); 126static void port_check(const char *, int); 127 int yylex(void); 128 129%} 130 131%union { 132 struct { 133 LLT ll; 134 int i; 135 } u; 136 char *s; 137} 138 139%token 140 A B C E F I 141 L N P R S T 142 143 SP CRLF COMMA ALL 144 145 USER PASS ACCT CWD CDUP SMNT 146 QUIT REIN PORT PASV TYPE STRU 147 MODE RETR STOR STOU APPE ALLO 148 REST RNFR RNTO ABOR DELE RMD 149 MKD PWD LIST NLST SITE SYST 150 STAT HELP NOOP 151 152 AUTH ADAT PROT PBSZ CCC MIC 153 CONF ENC 154 155 FEAT OPTS 156 157 SIZE MDTM MLST MLSD 158 159 LPRT LPSV EPRT EPSV 160 161 MAIL MLFL MRCP MRSQ MSAM MSND 162 MSOM 163 164 CHMOD IDLE RATEGET RATEPUT UMASK 165 166 LEXERR 167 168%token <s> STRING 169%token <u> NUMBER 170 171%type <u.i> check_login octal_number byte_size 172%type <u.i> struct_code mode_code type_code form_code decimal_integer 173%type <s> pathstring pathname password username 174%type <s> mechanism_name base64data prot_code 175 176%start cmd_sel 177 178%% 179 180cmd_sel 181 : cmd 182 { 183 REASSIGN(fromname, NULL); 184 restart_point = (off_t) 0; 185 } 186 187 | rcmd 188 189 ; 190 191cmd 192 /* RFC 959 */ 193 : USER SP username CRLF 194 { 195 user($3); 196 free($3); 197 } 198 199 | PASS SP password CRLF 200 { 201 pass($3); 202 memset($3, 0, strlen($3)); 203 free($3); 204 } 205 206 | CWD check_login CRLF 207 { 208 if ($2) 209 cwd(homedir); 210 } 211 212 | CWD check_login SP pathname CRLF 213 { 214 if ($2 && $4 != NULL) 215 cwd($4); 216 if ($4 != NULL) 217 free($4); 218 } 219 220 | CDUP check_login CRLF 221 { 222 if ($2) 223 cwd(".."); 224 } 225 226 | QUIT CRLF 227 { 228 if (logged_in) { 229 reply(-221, "%s", ""); 230 reply(0, 231 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 232 (LLT)total_data, PLURAL(total_data), 233 (LLT)total_files, PLURAL(total_files)); 234 reply(0, 235 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 236 (LLT)total_bytes, PLURAL(total_bytes), 237 (LLT)total_xfers, PLURAL(total_xfers)); 238 } 239 reply(221, 240 "Thank you for using the FTP service on %s.", 241 hostname); 242 if (logged_in && logging) { 243 syslog(LOG_INFO, 244 "Data traffic: " LLF " byte%s in " LLF " file%s", 245 (LLT)total_data, PLURAL(total_data), 246 (LLT)total_files, PLURAL(total_files)); 247 syslog(LOG_INFO, 248 "Total traffic: " LLF " byte%s in " LLF " transfer%s", 249 (LLT)total_bytes, PLURAL(total_bytes), 250 (LLT)total_xfers, PLURAL(total_xfers)); 251 } 252 253 dologout(0); 254 } 255 256 | PORT check_login SP host_port CRLF 257 { 258 if ($2) 259 port_check("PORT", AF_INET); 260 } 261 262 | LPRT check_login SP host_long_port4 CRLF 263 { 264 if ($2) 265 port_check("LPRT", AF_INET); 266 } 267 268 | LPRT check_login SP host_long_port6 CRLF 269 { 270#ifdef INET6 271 if ($2) 272 port_check("LPRT", AF_INET6); 273#else 274 reply(500, "IPv6 support not available."); 275#endif 276 } 277 278 | EPRT check_login SP STRING CRLF 279 { 280 if ($2) { 281 if (extended_port($4) == 0) 282 port_check("EPRT", -1); 283 } 284 free($4); 285 } 286 287 | PASV check_login CRLF 288 { 289 if ($2) { 290 if (CURCLASS_FLAGS_ISSET(passive)) 291 passive(); 292 else 293 reply(500, "PASV mode not available."); 294 } 295 } 296 297 | LPSV check_login CRLF 298 { 299 if ($2) { 300 if (CURCLASS_FLAGS_ISSET(passive)) { 301 if (epsvall) 302 reply(501, 303 "LPSV disallowed after EPSV ALL"); 304 else 305 long_passive("LPSV", PF_UNSPEC); 306 } else 307 reply(500, "LPSV mode not available."); 308 } 309 } 310 311 | EPSV check_login SP NUMBER CRLF 312 { 313 if ($2) { 314 if (CURCLASS_FLAGS_ISSET(passive)) 315 long_passive("EPSV", 316 epsvproto2af($4.i)); 317 else 318 reply(500, "EPSV mode not available."); 319 } 320 } 321 322 | EPSV check_login SP ALL CRLF 323 { 324 if ($2) { 325 if (CURCLASS_FLAGS_ISSET(passive)) { 326 reply(200, 327 "EPSV ALL command successful."); 328 epsvall++; 329 } else 330 reply(500, "EPSV mode not available."); 331 } 332 } 333 334 | EPSV check_login CRLF 335 { 336 if ($2) { 337 if (CURCLASS_FLAGS_ISSET(passive)) 338 long_passive("EPSV", PF_UNSPEC); 339 else 340 reply(500, "EPSV mode not available."); 341 } 342 } 343 344 | TYPE check_login SP type_code CRLF 345 { 346 if ($2) { 347 348 switch (cmd_type) { 349 350 case TYPE_A: 351 if (cmd_form == FORM_N) { 352 reply(200, "Type set to A."); 353 type = cmd_type; 354 form = cmd_form; 355 } else 356 reply(504, "Form must be N."); 357 break; 358 359 case TYPE_E: 360 reply(504, "Type E not implemented."); 361 break; 362 363 case TYPE_I: 364 reply(200, "Type set to I."); 365 type = cmd_type; 366 break; 367 368 case TYPE_L: 369#if NBBY == 8 370 if (cmd_bytesz == 8) { 371 reply(200, 372 "Type set to L (byte size 8)."); 373 type = cmd_type; 374 } else 375 reply(504, "Byte size must be 8."); 376#else /* NBBY == 8 */ 377 UNIMPLEMENTED for NBBY != 8 378#endif /* NBBY == 8 */ 379 } 380 381 } 382 } 383 384 | STRU check_login SP struct_code CRLF 385 { 386 if ($2) { 387 switch ($4) { 388 389 case STRU_F: 390 reply(200, "STRU F ok."); 391 break; 392 393 default: 394 reply(504, "Unimplemented STRU type."); 395 } 396 } 397 } 398 399 | MODE check_login SP mode_code CRLF 400 { 401 if ($2) { 402 switch ($4) { 403 404 case MODE_S: 405 reply(200, "MODE S ok."); 406 break; 407 408 default: 409 reply(502, "Unimplemented MODE type."); 410 } 411 } 412 } 413 414 | RETR check_login SP pathname CRLF 415 { 416 if ($2 && $4 != NULL) 417 retrieve(NULL, $4); 418 if ($4 != NULL) 419 free($4); 420 } 421 422 | STOR SP pathname CRLF 423 { 424 if (check_write($3, 1)) 425 store($3, "w", 0); 426 if ($3 != NULL) 427 free($3); 428 } 429 430 | STOU SP pathname CRLF 431 { 432 if (check_write($3, 1)) 433 store($3, "w", 1); 434 if ($3 != NULL) 435 free($3); 436 } 437 438 | APPE SP pathname CRLF 439 { 440 if (check_write($3, 1)) 441 store($3, "a", 0); 442 if ($3 != NULL) 443 free($3); 444 } 445 446 | ALLO check_login SP NUMBER CRLF 447 { 448 if ($2) 449 reply(202, "ALLO command ignored."); 450 } 451 452 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 453 { 454 if ($2) 455 reply(202, "ALLO command ignored."); 456 } 457 458 | RNTO SP pathname CRLF 459 { 460 if (check_write($3, 0)) { 461 if (fromname) { 462 renamecmd(fromname, $3); 463 REASSIGN(fromname, NULL); 464 } else { 465 reply(503, "Bad sequence of commands."); 466 } 467 } 468 if ($3 != NULL) 469 free($3); 470 } 471 472 | ABOR check_login CRLF 473 { 474 if (is_oob) 475 abor(); 476 else if ($2) 477 reply(225, "ABOR command successful."); 478 } 479 480 | DELE SP pathname CRLF 481 { 482 if (check_write($3, 0)) 483 delete($3); 484 if ($3 != NULL) 485 free($3); 486 } 487 488 | RMD SP pathname CRLF 489 { 490 if (check_write($3, 0)) 491 removedir($3); 492 if ($3 != NULL) 493 free($3); 494 } 495 496 | MKD SP pathname CRLF 497 { 498 if (check_write($3, 0)) 499 makedir($3); 500 if ($3 != NULL) 501 free($3); 502 } 503 504 | PWD check_login CRLF 505 { 506 if ($2) 507 pwd(); 508 } 509 510 | LIST check_login CRLF 511 { 512 char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 513 514 if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 515 argv[1] = "-LlgA"; 516 if ($2) 517 retrieve(argv, ""); 518 } 519 520 | LIST check_login SP pathname CRLF 521 { 522 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 523 524 if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 525 argv[1] = "-LlgA"; 526 if ($2 && $4 != NULL) { 527 argv[2] = $4; 528 retrieve(argv, $4); 529 } 530 if ($4 != NULL) 531 free($4); 532 } 533 534 | NLST check_login CRLF 535 { 536 if ($2) 537 send_file_list("."); 538 } 539 540 | NLST check_login SP pathname CRLF 541 { 542 if ($2) 543 send_file_list($4); 544 free($4); 545 } 546 547 | SITE SP HELP CRLF 548 { 549 help(sitetab, NULL); 550 } 551 552 | SITE SP CHMOD SP octal_number SP pathname CRLF 553 { 554 if (check_write($7, 0)) { 555 if (($5 == -1) || ($5 > 0777)) 556 reply(501, 557 "CHMOD: Mode value must be between 0 and 0777"); 558 else if (chmod($7, $5) < 0) 559 perror_reply(550, $7); 560 else 561 reply(200, "CHMOD command successful."); 562 } 563 if ($7 != NULL) 564 free($7); 565 } 566 567 | SITE SP HELP SP STRING CRLF 568 { 569 help(sitetab, $5); 570 free($5); 571 } 572 573 | SITE SP IDLE check_login CRLF 574 { 575 if ($4) { 576 reply(200, 577 "Current IDLE time limit is " LLF 578 " seconds; max " LLF, 579 (LLT)curclass.timeout, 580 (LLT)curclass.maxtimeout); 581 } 582 } 583 584 | SITE SP IDLE check_login SP NUMBER CRLF 585 { 586 if ($4) { 587 if ($6.i < 30 || $6.i > curclass.maxtimeout) { 588 reply(501, 589 "IDLE time limit must be between 30 and " 590 LLF " seconds", 591 (LLT)curclass.maxtimeout); 592 } else { 593 curclass.timeout = $6.i; 594 (void) alarm(curclass.timeout); 595 reply(200, 596 "IDLE time limit set to " 597 LLF " seconds", 598 (LLT)curclass.timeout); 599 } 600 } 601 } 602 603 | SITE SP RATEGET check_login CRLF 604 { 605 if ($4) { 606 reply(200, 607 "Current RATEGET is " LLF " bytes/sec", 608 (LLT)curclass.rateget); 609 } 610 } 611 612 | SITE SP RATEGET check_login SP STRING CRLF 613 { 614 char errbuf[100]; 615 char *p = $6; 616 LLT rate; 617 618 if ($4) { 619 rate = strsuftollx("RATEGET", p, 0, 620 curclass.maxrateget 621 ? curclass.maxrateget 622 : LLTMAX, errbuf, sizeof(errbuf)); 623 if (errbuf[0]) 624 reply(501, "%s", errbuf); 625 else { 626 curclass.rateget = rate; 627 reply(200, 628 "RATEGET set to " LLF " bytes/sec", 629 (LLT)curclass.rateget); 630 } 631 } 632 free($6); 633 } 634 635 | SITE SP RATEPUT check_login CRLF 636 { 637 if ($4) { 638 reply(200, 639 "Current RATEPUT is " LLF " bytes/sec", 640 (LLT)curclass.rateput); 641 } 642 } 643 644 | SITE SP RATEPUT check_login SP STRING CRLF 645 { 646 char errbuf[100]; 647 char *p = $6; 648 LLT rate; 649 650 if ($4) { 651 rate = strsuftollx("RATEPUT", p, 0, 652 curclass.maxrateput 653 ? curclass.maxrateput 654 : LLTMAX, errbuf, sizeof(errbuf)); 655 if (errbuf[0]) 656 reply(501, "%s", errbuf); 657 else { 658 curclass.rateput = rate; 659 reply(200, 660 "RATEPUT set to " LLF " bytes/sec", 661 (LLT)curclass.rateput); 662 } 663 } 664 free($6); 665 } 666 667 | SITE SP UMASK check_login CRLF 668 { 669 int oldmask; 670 671 if ($4) { 672 oldmask = umask(0); 673 (void) umask(oldmask); 674 reply(200, "Current UMASK is %03o", oldmask); 675 } 676 } 677 678 | SITE SP UMASK check_login SP octal_number CRLF 679 { 680 int oldmask; 681 682 if ($4 && check_write("", 0)) { 683 if (($6 == -1) || ($6 > 0777)) { 684 reply(501, "Bad UMASK value"); 685 } else { 686 oldmask = umask($6); 687 reply(200, 688 "UMASK set to %03o (was %03o)", 689 $6, oldmask); 690 } 691 } 692 } 693 694 | SYST CRLF 695 { 696 if (EMPTYSTR(version)) 697 reply(215, "UNIX Type: L%d", NBBY); 698 else 699 reply(215, "UNIX Type: L%d Version: %s", NBBY, 700 version); 701 } 702 703 | STAT check_login SP pathname CRLF 704 { 705 if ($2 && $4 != NULL) 706 statfilecmd($4); 707 if ($4 != NULL) 708 free($4); 709 } 710 711 | STAT CRLF 712 { 713 if (is_oob) 714 statxfer(); 715 else 716 statcmd(); 717 } 718 719 | HELP CRLF 720 { 721 help(cmdtab, NULL); 722 } 723 724 | HELP SP STRING CRLF 725 { 726 char *cp = $3; 727 728 if (strncasecmp(cp, "SITE", 4) == 0) { 729 cp = $3 + 4; 730 if (*cp == ' ') 731 cp++; 732 if (*cp) 733 help(sitetab, cp); 734 else 735 help(sitetab, NULL); 736 } else 737 help(cmdtab, $3); 738 free($3); 739 } 740 741 | NOOP CRLF 742 { 743 reply(200, "NOOP command successful."); 744 } 745 746 /* RFC 2228 */ 747 | AUTH SP mechanism_name CRLF 748 { 749 reply(502, "RFC 2228 authentication not implemented."); 750 free($3); 751 } 752 753 | ADAT SP base64data CRLF 754 { 755 reply(503, 756 "Please set authentication state with AUTH."); 757 free($3); 758 } 759 760 | PROT SP prot_code CRLF 761 { 762 reply(503, 763 "Please set protection buffer size with PBSZ."); 764 free($3); 765 } 766 767 | PBSZ SP decimal_integer CRLF 768 { 769 reply(503, 770 "Please set authentication state with AUTH."); 771 } 772 773 | CCC CRLF 774 { 775 reply(533, "No protection enabled."); 776 } 777 778 | MIC SP base64data CRLF 779 { 780 reply(502, "RFC 2228 authentication not implemented."); 781 free($3); 782 } 783 784 | CONF SP base64data CRLF 785 { 786 reply(502, "RFC 2228 authentication not implemented."); 787 free($3); 788 } 789 790 | ENC SP base64data CRLF 791 { 792 reply(502, "RFC 2228 authentication not implemented."); 793 free($3); 794 } 795 796 /* RFC 2389 */ 797 | FEAT CRLF 798 { 799 800 feat(); 801 } 802 803 | OPTS SP STRING CRLF 804 { 805 806 opts($3); 807 free($3); 808 } 809 810 811 /* extensions from draft-ietf-ftpext-mlst-11 */ 812 813 /* 814 * Return size of file in a format suitable for 815 * using with RESTART (we just count bytes). 816 */ 817 | SIZE check_login SP pathname CRLF 818 { 819 if ($2 && $4 != NULL) 820 sizecmd($4); 821 if ($4 != NULL) 822 free($4); 823 } 824 825 /* 826 * Return modification time of file as an ISO 3307 827 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 828 * where xxx is the fractional second (of any precision, 829 * not necessarily 3 digits) 830 */ 831 | MDTM check_login SP pathname CRLF 832 { 833 if ($2 && $4 != NULL) { 834 struct stat stbuf; 835 if (stat($4, &stbuf) < 0) 836 perror_reply(550, $4); 837 else if (!S_ISREG(stbuf.st_mode)) { 838 reply(550, "%s: not a plain file.", $4); 839 } else { 840 struct tm *t; 841 842 t = gmtime(&stbuf.st_mtime); 843 reply(213, 844 "%04d%02d%02d%02d%02d%02d", 845 TM_YEAR_BASE + t->tm_year, 846 t->tm_mon+1, t->tm_mday, 847 t->tm_hour, t->tm_min, t->tm_sec); 848 } 849 } 850 if ($4 != NULL) 851 free($4); 852 } 853 854 | MLST check_login SP pathname CRLF 855 { 856 if ($2 && $4 != NULL) 857 mlst($4); 858 if ($4 != NULL) 859 free($4); 860 } 861 862 | MLST check_login CRLF 863 { 864 mlst(NULL); 865 } 866 867 | MLSD check_login SP pathname CRLF 868 { 869 if ($2 && $4 != NULL) 870 mlsd($4); 871 if ($4 != NULL) 872 free($4); 873 } 874 875 | MLSD check_login CRLF 876 { 877 mlsd(NULL); 878 } 879 880 | error CRLF 881 { 882 yyerrok; 883 } 884 ; 885 886rcmd 887 : REST check_login SP NUMBER CRLF 888 { 889 if ($2) { 890 REASSIGN(fromname, NULL); 891 restart_point = (off_t)$4.ll; 892 reply(350, 893 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 894 (LLT)restart_point); 895 } 896 } 897 898 | RNFR SP pathname CRLF 899 { 900 restart_point = (off_t) 0; 901 if (check_write($3, 0)) { 902 REASSIGN(fromname, NULL); 903 fromname = renamefrom($3); 904 } 905 if ($3 != NULL) 906 free($3); 907 } 908 ; 909 910username 911 : STRING 912 ; 913 914password 915 : /* empty */ 916 { 917 $$ = (char *)calloc(1, sizeof(char)); 918 } 919 920 | STRING 921 ; 922 923byte_size 924 : NUMBER 925 { 926 $$ = $1.i; 927 } 928 ; 929 930host_port 931 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 932 NUMBER COMMA NUMBER 933 { 934 char *a, *p; 935 936 memset(&data_dest, 0, sizeof(data_dest)); 937 data_dest.su_len = sizeof(struct sockaddr_in); 938 data_dest.su_family = AF_INET; 939 p = (char *)&data_dest.su_port; 940 p[0] = $9.i; p[1] = $11.i; 941 a = (char *)&data_dest.su_addr; 942 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 943 } 944 ; 945 946host_long_port4 947 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 948 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 949 NUMBER 950 { 951 char *a, *p; 952 953 memset(&data_dest, 0, sizeof(data_dest)); 954 data_dest.su_len = sizeof(struct sockaddr_in); 955 data_dest.su_family = AF_INET; 956 p = (char *)&data_dest.su_port; 957 p[0] = $15.i; p[1] = $17.i; 958 a = (char *)&data_dest.su_addr; 959 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 960 961 /* reject invalid LPRT command */ 962 if ($1.i != 4 || $3.i != 4 || $13.i != 2) 963 memset(&data_dest, 0, sizeof(data_dest)); 964 } 965 ; 966 967host_long_port6 968 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 969 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 970 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 971 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 972 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 973 NUMBER 974 { 975#ifdef INET6 976 char *a, *p; 977 978 memset(&data_dest, 0, sizeof(data_dest)); 979 data_dest.su_len = sizeof(struct sockaddr_in6); 980 data_dest.su_family = AF_INET6; 981 p = (char *)&data_dest.su_port; 982 p[0] = $39.i; p[1] = $41.i; 983 a = (char *)&data_dest.si_su.su_sin6.sin6_addr; 984 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 985 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 986 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 987 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 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 1001form_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 1018type_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 1068struct_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 1085mode_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 1102pathname 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 1145pathstring 1146 : STRING 1147 ; 1148 1149octal_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 1175mechanism_name 1176 : STRING 1177 ; 1178 1179base64data 1180 : STRING 1181 ; 1182 1183prot_code 1184 : STRING 1185 ; 1186 1187decimal_integer 1188 : NUMBER 1189 { 1190 $$ = $1.i; 1191 } 1192 ; 1193 1194check_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 1221struct tab cmdtab[] = { 1222 /* From RFC 959, in order defined (5.3.1) */ 1223 { "USER", USER, STR1, 1, "<sp> username" }, 1224 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1225 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1226 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1227 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 1228 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1229 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, 1230 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, 1231 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 1232 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1233 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1234 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, 1235 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1236 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1237 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1238 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1239 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1240 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1241 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1242 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1243 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1244 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1245 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1246 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1247 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1248 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" }, 1249 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1250 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1251 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1252 { "PWD", PWD, NOARGS, 1, "(return current directory)" }, 1253 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1254 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1255 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1256 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, 1257 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" }, 1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1259 { "NOOP", NOOP, NOARGS, 2, "" }, 1260 1261 /* From RFC 2228, in order defined */ 1262 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" }, 1263 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" }, 1264 { "PROT", PROT, STR1, 1, "<sp> prot-code" }, 1265 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" }, 1266 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, 1267 { "MIC", MIC, STR1, 4, "<sp> base64data" }, 1268 { "CONF", CONF, STR1, 4, "<sp> base64data" }, 1269 { "ENC", ENC, STR1, 4, "<sp> base64data" }, 1270 1271 /* From RFC 2389, in order defined */ 1272 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, 1273 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" }, 1274 1275 /* from draft-ietf-ftpext-mlst-11 */ 1276 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1277 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1278 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" }, 1279 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" }, 1280 1281 /* obsolete commands */ 1282 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1283 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1284 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1285 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1286 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1287 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1288 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1289 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 1290 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1291 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1292 { "XPWD", PWD, NOARGS, 1, "(return current directory)" }, 1293 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1294 1295 { NULL, 0, 0, 0, 0 } 1296}; 1297 1298struct tab sitetab[] = { 1299 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1300 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1301 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1302 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" }, 1303 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" }, 1304 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1305 { NULL, 0, 0, 0, NULL } 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 */ 1314static int 1315check_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 1352struct tab * 1353lookup(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 * getline - 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 */ 1371int 1372getline(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 1468void 1469ftp_handle_line(char *cp) 1470{ 1471 1472 cmdp = cp; 1473 yyparse(); 1474} 1475 1476void 1477ftp_loop(void) 1478{ 1479 int ret; 1480 1481 while (1) { 1482 (void) alarm(curclass.timeout); 1483 ret = getline(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 1497int 1498yylex(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 HAVE_SETPROCTITLE 1513 if (strncasecmp(cmdp, "PASS", 4) != 0 && 1514 strncasecmp(cmdp, "ACCT", 4) != 0) 1515 setproctitle("%s: %s", proctitle, cmdp); 1516#endif /* 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.s = 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.s = 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, (char **)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 */ 1733void 1734yyerror(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 1746static void 1747help(struct tab *ctab, const char *s) 1748{ 1749 struct tab *c; 1750 int width, NCMDS; 1751 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 */ 1821static void 1822port_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