1 /* $OpenBSD: ftpcmd.y,v 1.69 2020/03/04 20:17:48 millert Exp $ */ 2 /* $NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp $ */ 3 4 /* 5 * Copyright (c) 1985, 1988, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 33 */ 34 35 /* 36 * Grammar for FTP commands. 37 * See RFC 959. 38 */ 39 40 %{ 41 42 #include <sys/types.h> 43 #include <sys/socket.h> 44 #include <sys/stat.h> 45 46 #include <netinet/in.h> 47 #include <arpa/ftp.h> 48 49 #include <ctype.h> 50 #include <errno.h> 51 #include <glob.h> 52 #include <pwd.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <time.h> 59 #include <unistd.h> 60 #include <netdb.h> 61 #include <limits.h> 62 63 #include "monitor.h" 64 #include "extern.h" 65 66 extern union sockunion data_dest; 67 extern int logged_in; 68 extern struct passwd *pw; 69 extern int guest; 70 extern int logging; 71 extern int type; 72 extern int form; 73 extern int debug; 74 extern int timeout; 75 extern int maxtimeout; 76 extern int pdata; 77 extern char hostname[], remotehost[]; 78 extern char proctitle[]; 79 extern int usedefault; 80 extern int transflag; 81 extern char tmpline[]; 82 extern int portcheck; 83 extern union sockunion his_addr; 84 extern int umaskchange; 85 86 off_t restart_point; 87 88 static int cmd_type; 89 static int cmd_form; 90 static int cmd_bytesz; 91 static int state; 92 static int quit; 93 char cbuf[512]; 94 char *fromname; 95 96 %} 97 98 %union { 99 int i; 100 off_t o; 101 char *s; 102 } 103 104 %token 105 A B C E F I 106 L N P R S T 107 108 SP CRLF COMMA ALL 109 110 USER PASS ACCT REIN QUIT PORT 111 PASV TYPE STRU MODE RETR STOR 112 APPE MLFL MAIL MSND MSOM MSAM 113 MRSQ MRCP ALLO REST RNFR RNTO 114 ABOR DELE CWD LIST NLST SITE 115 STAT HELP NOOP MKD RMD PWD 116 CDUP STOU SMNT SYST SIZE MDTM 117 118 LPRT LPSV EPRT EPSV 119 120 UMASK IDLE CHMOD 121 122 LEXERR 123 124 %token <s> STRING 125 %token <i> NUMBER 126 %token <o> BIGNUM 127 128 %type <i> check_login check_login_epsvall octal_number byte_size 129 %type <i> struct_code mode_code type_code form_code 130 %type <i> host_port host_long_port4 host_long_port6 131 %type <o> file_size 132 %type <s> pathstring pathname password username 133 134 %start cmd_list 135 136 %% 137 138 cmd_list 139 : /* empty */ 140 | cmd_list cmd 141 { 142 if (fromname) { 143 free(fromname); 144 fromname = NULL; 145 } 146 restart_point = 0; 147 } 148 | cmd_list rcmd 149 ; 150 151 cmd 152 : USER SP username CRLF 153 { 154 monitor_user($3); 155 free($3); 156 } 157 | PASS SP password CRLF 158 { 159 quit = monitor_pass($3); 160 explicit_bzero($3, strlen($3)); 161 free($3); 162 163 /* Terminate unprivileged pre-auth slave */ 164 if (quit) 165 _exit(0); 166 } 167 | PORT check_login_epsvall SP host_port CRLF 168 { 169 if ($2) { 170 if ($4) { 171 usedefault = 1; 172 reply(500, 173 "Illegal PORT rejected (range errors)."); 174 } else if (portcheck && 175 ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) { 176 usedefault = 1; 177 reply(500, 178 "Illegal PORT rejected (reserved port)."); 179 } else if (portcheck && 180 memcmp(&data_dest.su_sin.sin_addr, 181 &his_addr.su_sin.sin_addr, 182 sizeof data_dest.su_sin.sin_addr)) { 183 usedefault = 1; 184 reply(500, 185 "Illegal PORT rejected (address wrong)."); 186 } else { 187 usedefault = 0; 188 if (pdata >= 0) { 189 (void) close(pdata); 190 pdata = -1; 191 } 192 reply(200, "PORT command successful."); 193 } 194 } 195 } 196 | LPRT check_login_epsvall SP host_long_port4 CRLF 197 { 198 if ($2) { 199 /* reject invalid host_long_port4 */ 200 if ($4) { 201 reply(500, 202 "Illegal LPRT command rejected"); 203 usedefault = 1; 204 } else { 205 usedefault = 0; 206 if (pdata >= 0) { 207 (void) close(pdata); 208 pdata = -1; 209 } 210 reply(200, "LPRT command successful."); 211 } 212 } 213 } 214 215 | LPRT check_login_epsvall SP host_long_port6 CRLF 216 { 217 if ($2) { 218 /* reject invalid host_long_port6 */ 219 if ($4) { 220 reply(500, 221 "Illegal LPRT command rejected"); 222 usedefault = 1; 223 } else { 224 usedefault = 0; 225 if (pdata >= 0) { 226 (void) close(pdata); 227 pdata = -1; 228 } 229 reply(200, "LPRT command successful."); 230 } 231 } 232 } 233 234 | EPRT check_login_epsvall SP STRING CRLF 235 { 236 if ($2) 237 extended_port($4); 238 free($4); 239 } 240 241 | PASV check_login_epsvall CRLF 242 { 243 if ($2) 244 passive(); 245 } 246 | LPSV check_login_epsvall CRLF 247 { 248 if ($2) 249 long_passive("LPSV", PF_UNSPEC); 250 } 251 | EPSV check_login SP NUMBER CRLF 252 { 253 if ($2) 254 long_passive("EPSV", epsvproto2af($4)); 255 } 256 | EPSV check_login SP ALL CRLF 257 { 258 if ($2) { 259 reply(200, "EPSV ALL command successful."); 260 epsvall++; 261 } 262 } 263 | EPSV check_login CRLF 264 { 265 if ($2) 266 long_passive("EPSV", PF_UNSPEC); 267 } 268 | TYPE check_login SP type_code CRLF 269 { 270 if ($2) { 271 switch (cmd_type) { 272 273 case TYPE_A: 274 if (cmd_form == FORM_N) { 275 reply(200, "Type set to A."); 276 type = cmd_type; 277 form = cmd_form; 278 } else 279 reply(504, "Form must be N."); 280 break; 281 282 case TYPE_E: 283 reply(504, "Type E not implemented."); 284 break; 285 286 case TYPE_I: 287 reply(200, "Type set to I."); 288 type = cmd_type; 289 break; 290 291 case TYPE_L: 292 if (cmd_bytesz == 8) { 293 reply(200, 294 "Type set to L (byte size 8)."); 295 type = cmd_type; 296 } else 297 reply(504, "Byte size must be 8."); 298 299 } 300 } 301 } 302 | STRU check_login SP struct_code CRLF 303 { 304 if ($2) { 305 switch ($4) { 306 307 case STRU_F: 308 reply(200, "STRU F ok."); 309 break; 310 311 default: 312 reply(504, "Unimplemented STRU type."); 313 } 314 } 315 } 316 | MODE check_login SP mode_code CRLF 317 { 318 if ($2) { 319 switch ($4) { 320 321 case MODE_S: 322 reply(200, "MODE S ok."); 323 break; 324 325 default: 326 reply(502, "Unimplemented MODE type."); 327 } 328 } 329 } 330 | ALLO check_login SP NUMBER CRLF 331 { 332 if ($2) { 333 reply(202, "ALLO command ignored."); 334 } 335 } 336 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 337 { 338 if ($2) { 339 reply(202, "ALLO command ignored."); 340 } 341 } 342 | RETR check_login SP pathname CRLF 343 { 344 if ($2 && $4 != NULL) 345 retrieve(RET_FILE, $4); 346 if ($4 != NULL) 347 free($4); 348 } 349 | STOR check_login SP pathname CRLF 350 { 351 if ($2 && $4 != NULL) 352 store($4, "w", 0); 353 if ($4 != NULL) 354 free($4); 355 } 356 | APPE check_login SP pathname CRLF 357 { 358 if ($2 && $4 != NULL) 359 store($4, "a", 0); 360 if ($4 != NULL) 361 free($4); 362 } 363 | NLST check_login CRLF 364 { 365 if ($2) 366 send_file_list("."); 367 } 368 | NLST check_login SP STRING CRLF 369 { 370 if ($2 && $4 != NULL) 371 send_file_list($4); 372 free($4); 373 } 374 | LIST check_login CRLF 375 { 376 if ($2) 377 retrieve(RET_LIST, "."); 378 } 379 | LIST check_login SP pathname CRLF 380 { 381 if ($2 && $4 != NULL) 382 retrieve(RET_LIST, $4); 383 if ($4 != NULL) 384 free($4); 385 } 386 | STAT check_login SP pathname CRLF 387 { 388 if ($2 && $4 != NULL) 389 statfilecmd($4); 390 if ($4 != NULL) 391 free($4); 392 } 393 | STAT check_login CRLF 394 { 395 if ($2) 396 statcmd(); 397 } 398 | DELE check_login SP pathname CRLF 399 { 400 if ($2 && $4 != NULL) 401 delete($4); 402 if ($4 != NULL) 403 free($4); 404 } 405 | RNTO check_login SP pathname CRLF 406 { 407 if ($2 && $4 != NULL) { 408 if (fromname) { 409 renamecmd(fromname, $4); 410 free(fromname); 411 fromname = NULL; 412 } else { 413 reply(503, 414 "Bad sequence of commands."); 415 } 416 } 417 if ($4 != NULL) 418 free($4); 419 } 420 | ABOR check_login CRLF 421 { 422 if ($2) 423 reply(225, "ABOR command successful."); 424 } 425 | CWD check_login CRLF 426 { 427 if ($2) 428 cwd(pw->pw_dir); 429 } 430 | CWD check_login SP pathname CRLF 431 { 432 if ($2 && $4 != NULL) 433 cwd($4); 434 if ($4 != NULL) 435 free($4); 436 } 437 | HELP CRLF 438 { 439 help(cmdtab, NULL); 440 } 441 | HELP SP STRING CRLF 442 { 443 char *cp = $3; 444 445 if (strncasecmp(cp, "SITE", 4) == 0) { 446 cp = $3 + 4; 447 if (*cp == ' ') 448 cp++; 449 if (*cp) 450 help(sitetab, cp); 451 else 452 help(sitetab, NULL); 453 } else 454 help(cmdtab, $3); 455 free ($3); 456 } 457 | NOOP CRLF 458 { 459 reply(200, "NOOP command successful."); 460 } 461 | MKD check_login SP pathname CRLF 462 { 463 if ($2 && $4 != NULL) 464 makedir($4); 465 if ($4 != NULL) 466 free($4); 467 } 468 | RMD check_login SP pathname CRLF 469 { 470 if ($2 && $4 != NULL) 471 removedir($4); 472 if ($4 != NULL) 473 free($4); 474 } 475 | PWD check_login CRLF 476 { 477 if ($2) 478 pwd(); 479 } 480 | CDUP check_login CRLF 481 { 482 if ($2) 483 cwd(".."); 484 } 485 | SITE SP HELP CRLF 486 { 487 help(sitetab, NULL); 488 } 489 | SITE SP HELP SP STRING CRLF 490 { 491 help(sitetab, $5); 492 free ($5); 493 } 494 | SITE SP UMASK check_login CRLF 495 { 496 mode_t oldmask; 497 498 if ($4) { 499 oldmask = umask(0); 500 (void) umask(oldmask); 501 reply(200, "Current UMASK is %03o", oldmask); 502 } 503 } 504 | SITE SP UMASK check_login SP octal_number CRLF 505 { 506 mode_t oldmask; 507 508 if ($4) { 509 if (($6 == -1) || ($6 > 0777)) { 510 reply(501, "Bad UMASK value"); 511 } else if (!umaskchange) { 512 reply(550, 513 "No permission to change umask."); 514 } else { 515 oldmask = umask($6); 516 reply(200, 517 "UMASK set to %03o (was %03o)", 518 $6, oldmask); 519 } 520 } 521 } 522 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 523 { 524 if ($4 && ($8 != NULL)) { 525 if (($6 == -1) || ($6 > 0777)) 526 reply(501, 527 "CHMOD: Mode value must be between " 528 "0 and 0777"); 529 else if (!umaskchange) 530 reply(550, 531 "No permission to change mode of %s.", 532 $8); 533 else if (chmod($8, $6) == -1) 534 perror_reply(550, $8); 535 else 536 reply(200, 537 "CHMOD command successful."); 538 } 539 if ($8 != NULL) 540 free($8); 541 } 542 | SITE SP check_login IDLE CRLF 543 { 544 if ($3) 545 reply(200, 546 "Current IDLE time limit is %d " 547 "seconds; max %d", 548 timeout, maxtimeout); 549 } 550 | SITE SP check_login IDLE SP NUMBER CRLF 551 { 552 if ($3) { 553 if ($6 < 30 || $6 > maxtimeout) { 554 reply(501, 555 "Maximum IDLE time must be between " 556 "30 and %d seconds", 557 maxtimeout); 558 } else { 559 timeout = $6; 560 (void) alarm((unsigned) timeout); 561 reply(200, 562 "Maximum IDLE time set to %d seconds", 563 timeout); 564 } 565 } 566 } 567 | STOU check_login SP pathname CRLF 568 { 569 if ($2 && $4 != NULL) 570 store($4, "w", 1); 571 if ($4 != NULL) 572 free($4); 573 } 574 | SYST check_login CRLF 575 { 576 if ($2) 577 reply(215, "UNIX Type: L8"); 578 } 579 580 /* 581 * SIZE is not in RFC959, but Postel has blessed it and 582 * it will be in the updated RFC. 583 * 584 * Return size of file in a format suitable for 585 * using with RESTART (we just count bytes). 586 */ 587 | SIZE check_login SP pathname CRLF 588 { 589 if ($2 && $4 != NULL) 590 sizecmd($4); 591 if ($4 != NULL) 592 free($4); 593 } 594 595 /* 596 * MDTM is not in RFC959, but Postel has blessed it and 597 * it will be in the updated RFC. 598 * 599 * Return modification time of file as an ISO 3307 600 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 601 * where xxx is the fractional second (of any precision, 602 * not necessarily 3 digits) 603 */ 604 | MDTM check_login SP pathname CRLF 605 { 606 if ($2 && $4 != NULL) { 607 struct stat stbuf; 608 if (stat($4, &stbuf) == -1) 609 reply(550, "%s: %s", 610 $4, strerror(errno)); 611 else if (!S_ISREG(stbuf.st_mode)) { 612 reply(550, "%s: not a plain file.", $4); 613 } else { 614 struct tm *t; 615 t = gmtime(&stbuf.st_mtime); 616 reply(213, 617 "%04d%02d%02d%02d%02d%02d", 618 1900 + t->tm_year, 619 t->tm_mon+1, t->tm_mday, 620 t->tm_hour, t->tm_min, t->tm_sec); 621 } 622 } 623 if ($4 != NULL) 624 free($4); 625 } 626 | QUIT CRLF 627 { 628 reply(221, "Goodbye."); 629 dologout(0); 630 } 631 | error 632 { 633 yyclearin; /* discard lookahead data */ 634 yyerrok; /* clear error condition */ 635 state = 0; /* reset lexer state */ 636 } 637 ; 638 rcmd 639 : RNFR check_login SP pathname CRLF 640 { 641 restart_point = 0; 642 if ($2 && $4) { 643 if (fromname) 644 free(fromname); 645 fromname = renamefrom($4); 646 if (fromname == NULL) 647 free($4); 648 } else if ($4) { 649 free ($4); 650 } 651 } 652 653 | REST check_login SP file_size CRLF 654 { 655 if ($2) { 656 if (fromname) { 657 free(fromname); 658 fromname = NULL; 659 } 660 restart_point = $4; 661 reply(350, "Restarting at %lld. %s", 662 (long long)restart_point, 663 "Send STORE or RETRIEVE to initiate transfer."); 664 } 665 } 666 ; 667 668 username 669 : STRING 670 ; 671 672 password 673 : /* empty */ 674 { 675 $$ = calloc(1, sizeof(char)); 676 } 677 | STRING 678 ; 679 680 byte_size 681 : NUMBER 682 ; 683 684 file_size 685 : NUMBER 686 { 687 $$ = $1; 688 } 689 | BIGNUM 690 { 691 $$ = $1; 692 } 693 ; 694 695 host_port 696 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 697 NUMBER COMMA NUMBER 698 { 699 char *a, *p; 700 701 if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 || 702 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 703 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { 704 $$ = 1; 705 } else { 706 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 707 data_dest.su_sin.sin_family = AF_INET; 708 p = (char *)&data_dest.su_sin.sin_port; 709 p[0] = $9; p[1] = $11; 710 a = (char *)&data_dest.su_sin.sin_addr; 711 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 712 $$ = 0; 713 } 714 } 715 ; 716 717 host_long_port4 718 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 719 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 720 NUMBER 721 { 722 char *a, *p; 723 724 /* reject invalid LPRT command */ 725 if ($1 != 4 || $3 != 4 || 726 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 727 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 || 728 $13 != 2 || 729 $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) { 730 $$ = 1; 731 } else { 732 data_dest.su_sin.sin_len = 733 sizeof(struct sockaddr_in); 734 data_dest.su_family = AF_INET; 735 p = (char *)&data_dest.su_port; 736 p[0] = $15; p[1] = $17; 737 a = (char *)&data_dest.su_sin.sin_addr; 738 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 739 $$ = 0; 740 } 741 } 742 ; 743 744 host_long_port6 745 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 746 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 747 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 748 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 749 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 750 NUMBER 751 { 752 char *a, *p; 753 754 /* reject invalid LPRT command */ 755 if ($1 != 6 || $3 != 16 || 756 $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || 757 $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 || 758 $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 || 759 $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 || 760 $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 || 761 $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 || 762 $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 || 763 $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 || 764 $37 != 2 || 765 $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) { 766 $$ = 1; 767 } else { 768 data_dest.su_sin6.sin6_len = 769 sizeof(struct sockaddr_in6); 770 data_dest.su_family = AF_INET6; 771 p = (char *)&data_dest.su_port; 772 p[0] = $39; p[1] = $41; 773 a = (char *)&data_dest.su_sin6.sin6_addr; 774 a[0] = $5; a[1] = $7; 775 a[2] = $9; a[3] = $11; 776 a[4] = $13; a[5] = $15; 777 a[6] = $17; a[7] = $19; 778 a[8] = $21; a[9] = $23; 779 a[10] = $25; a[11] = $27; 780 a[12] = $29; a[13] = $31; 781 a[14] = $33; a[15] = $35; 782 if (his_addr.su_family == AF_INET6) { 783 /* XXX more sanity checks! */ 784 data_dest.su_sin6.sin6_scope_id = 785 his_addr.su_sin6.sin6_scope_id; 786 } 787 788 $$ = 0; 789 } 790 } 791 ; 792 793 form_code 794 : N 795 { 796 $$ = FORM_N; 797 } 798 | T 799 { 800 $$ = FORM_T; 801 } 802 | C 803 { 804 $$ = FORM_C; 805 } 806 ; 807 808 type_code 809 : A 810 { 811 cmd_type = TYPE_A; 812 cmd_form = FORM_N; 813 } 814 | A SP form_code 815 { 816 cmd_type = TYPE_A; 817 cmd_form = $3; 818 } 819 | E 820 { 821 cmd_type = TYPE_E; 822 cmd_form = FORM_N; 823 } 824 | E SP form_code 825 { 826 cmd_type = TYPE_E; 827 cmd_form = $3; 828 } 829 | I 830 { 831 cmd_type = TYPE_I; 832 } 833 | L 834 { 835 cmd_type = TYPE_L; 836 cmd_bytesz = 8; 837 } 838 | L SP byte_size 839 { 840 cmd_type = TYPE_L; 841 cmd_bytesz = $3; 842 } 843 /* this is for a bug in the BBN ftp */ 844 | L byte_size 845 { 846 cmd_type = TYPE_L; 847 cmd_bytesz = $2; 848 } 849 ; 850 851 struct_code 852 : F 853 { 854 $$ = STRU_F; 855 } 856 | R 857 { 858 $$ = STRU_R; 859 } 860 | P 861 { 862 $$ = STRU_P; 863 } 864 ; 865 866 mode_code 867 : S 868 { 869 $$ = MODE_S; 870 } 871 | B 872 { 873 $$ = MODE_B; 874 } 875 | C 876 { 877 $$ = MODE_C; 878 } 879 ; 880 881 pathname 882 : pathstring 883 { 884 /* 885 * Problem: this production is used for all pathname 886 * processing, but only gives a 550 error reply. 887 * This is a valid reply in some cases but not in others. 888 */ 889 if (logged_in && $1 && strchr($1, '~') != NULL) { 890 glob_t gl; 891 int flags = 892 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 893 char *pptr = $1; 894 895 /* 896 * glob() will only find a leading ~, but 897 * Netscape kindly puts a slash in front of 898 * it for publish URLs. There needs to be 899 * a flag for glob() that expands tildes 900 * anywhere in the string. 901 */ 902 if ((pptr[0] == '/') && (pptr[1] == '~')) 903 pptr++; 904 905 memset(&gl, 0, sizeof(gl)); 906 if (glob(pptr, flags, NULL, &gl) || 907 gl.gl_pathc == 0) { 908 reply(550, "not found"); 909 $$ = NULL; 910 } else { 911 $$ = strdup(gl.gl_pathv[0]); 912 } 913 globfree(&gl); 914 free($1); 915 } else 916 $$ = $1; 917 } 918 ; 919 920 pathstring 921 : STRING 922 ; 923 924 octal_number 925 : NUMBER 926 { 927 int ret, dec, multby, digit; 928 929 /* 930 * Convert a number that was read as decimal number 931 * to what it would be if it had been read as octal. 932 */ 933 dec = $1; 934 multby = 1; 935 ret = 0; 936 while (dec) { 937 digit = dec%10; 938 if (digit > 7) { 939 ret = -1; 940 break; 941 } 942 ret += digit * multby; 943 multby *= 8; 944 dec /= 10; 945 } 946 $$ = ret; 947 } 948 ; 949 950 951 check_login 952 : /* empty */ 953 { 954 if (logged_in) 955 $$ = 1; 956 else { 957 reply(530, "Please login with USER and PASS."); 958 $$ = 0; 959 state = 0; 960 YYABORT; 961 } 962 } 963 ; 964 965 check_login_epsvall 966 : /* empty */ 967 { 968 if (!logged_in) { 969 reply(530, "Please login with USER and PASS."); 970 $$ = 0; 971 state = 0; 972 YYABORT; 973 } else if (epsvall) { 974 reply(501, "the command is disallowed " 975 "after EPSV ALL"); 976 usedefault = 1; 977 $$ = 0; 978 } else 979 $$ = 1; 980 } 981 ; 982 983 %% 984 985 #define CMD 0 /* beginning of command */ 986 #define ARGS 1 /* expect miscellaneous arguments */ 987 #define STR1 2 /* expect SP followed by STRING */ 988 #define STR2 3 /* expect STRING */ 989 #define OSTR 4 /* optional SP then STRING */ 990 #define ZSTR1 5 /* SP then optional STRING */ 991 #define ZSTR2 6 /* optional STRING after SP */ 992 #define SITECMD 7 /* SITE command */ 993 #define NSTR 8 /* Number followed by a string */ 994 995 struct tab { 996 char *name; 997 short token; 998 short state; 999 short implemented; /* 1 if command is implemented */ 1000 char *help; 1001 }; 1002 1003 struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1004 { "USER", USER, STR1, 1, "<sp> username" }, 1005 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1006 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1007 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1008 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1009 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1010 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 1011 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1012 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1013 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 1014 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1015 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1016 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1017 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1018 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1019 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1020 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1021 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1022 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1023 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1024 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1025 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1026 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1027 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1028 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1029 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1030 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1031 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1032 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1033 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1034 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1035 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1036 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1037 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1038 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1039 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1040 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1041 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1042 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1043 { "NOOP", NOOP, ARGS, 1, "" }, 1044 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1045 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1046 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1047 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1048 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1049 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1050 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1051 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1052 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1053 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1054 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1055 { NULL, 0, 0, 0, 0 } 1056 }; 1057 1058 struct tab sitetab[] = { 1059 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1060 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1061 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1062 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1063 { NULL, 0, 0, 0, 0 } 1064 }; 1065 1066 static void help(struct tab *, char *); 1067 static struct tab * 1068 lookup(struct tab *, char *); 1069 static void sizecmd(char *); 1070 static int yylex(void); 1071 1072 extern int epsvall; 1073 1074 static struct tab * 1075 lookup(p, cmd) 1076 struct tab *p; 1077 char *cmd; 1078 { 1079 1080 for (; p->name != NULL; p++) 1081 if (strcmp(cmd, p->name) == 0) 1082 return (p); 1083 return (NULL); 1084 } 1085 1086 #include <arpa/telnet.h> 1087 1088 /* 1089 * get_line - a hacked up version of fgets to ignore TELNET escape codes. 1090 */ 1091 int 1092 get_line(s, n, iop) 1093 char *s; 1094 int n; 1095 FILE *iop; 1096 { 1097 int c; 1098 char *cs; 1099 1100 cs = s; 1101 /* tmpline may contain saved command from urgent mode interruption */ 1102 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1103 *cs++ = tmpline[c]; 1104 if (tmpline[c] == '\n') { 1105 *cs++ = '\0'; 1106 if (debug) 1107 syslog(LOG_DEBUG, "command: %s", s); 1108 tmpline[0] = '\0'; 1109 return(0); 1110 } 1111 if (c == 0) 1112 tmpline[0] = '\0'; 1113 } 1114 while ((c = getc(iop)) != EOF) { 1115 c &= 0377; 1116 if (c == IAC) { 1117 if ((c = getc(iop)) != EOF) { 1118 c &= 0377; 1119 switch (c) { 1120 case WILL: 1121 case WONT: 1122 c = getc(iop); 1123 printf("%c%c%c", IAC, DONT, 0377&c); 1124 (void) fflush(stdout); 1125 continue; 1126 case DO: 1127 case DONT: 1128 c = getc(iop); 1129 printf("%c%c%c", IAC, WONT, 0377&c); 1130 (void) fflush(stdout); 1131 continue; 1132 case IAC: 1133 break; 1134 default: 1135 continue; /* ignore command */ 1136 } 1137 } 1138 } 1139 *cs++ = c; 1140 if (--n <= 0) { 1141 /* 1142 * If command doesn't fit into buffer, discard the 1143 * rest of the command and indicate truncation. 1144 * This prevents the command to be split up into 1145 * multiple commands. 1146 */ 1147 while (c != '\n' && (c = getc(iop)) != EOF) 1148 ; 1149 return (-2); 1150 } 1151 if (c == '\n') 1152 break; 1153 } 1154 if (c == EOF && cs == s) 1155 return (-1); 1156 *cs++ = '\0'; 1157 if (debug) { 1158 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1159 /* Don't syslog passwords */ 1160 syslog(LOG_DEBUG, "command: %.5s ???", s); 1161 } else { 1162 char *cp; 1163 int len; 1164 1165 /* Don't syslog trailing CR-LF */ 1166 len = strlen(s); 1167 cp = s + len - 1; 1168 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1169 --cp; 1170 --len; 1171 } 1172 syslog(LOG_DEBUG, "command: %.*s", len, s); 1173 } 1174 } 1175 return (0); 1176 } 1177 1178 /*ARGSUSED*/ 1179 void 1180 toolong(signo) 1181 int signo; 1182 { 1183 struct syslog_data sdata = SYSLOG_DATA_INIT; 1184 1185 reply_r(421, 1186 "Timeout (%d seconds): closing control connection.", timeout); 1187 if (logging) 1188 syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds", 1189 (pw ? pw -> pw_name : "unknown"), timeout); 1190 dologout(1); 1191 } 1192 1193 static int 1194 yylex() 1195 { 1196 static int cpos; 1197 char *cp, *cp2; 1198 struct tab *p; 1199 int n; 1200 char c; 1201 1202 for (;;) { 1203 switch (state) { 1204 1205 case CMD: 1206 (void) alarm((unsigned) timeout); 1207 n = get_line(cbuf, sizeof(cbuf)-1, stdin); 1208 if (n == -1) { 1209 reply(221, "You could at least say goodbye."); 1210 dologout(0); 1211 } else if (n == -2) { 1212 reply(500, "Command too long."); 1213 alarm(0); 1214 continue; 1215 } 1216 (void) alarm(0); 1217 if ((cp = strchr(cbuf, '\r'))) { 1218 *cp++ = '\n'; 1219 *cp = '\0'; 1220 } 1221 if (strncasecmp(cbuf, "PASS", 4) != 0) { 1222 if ((cp = strpbrk(cbuf, "\n"))) { 1223 c = *cp; 1224 *cp = '\0'; 1225 setproctitle("%s: %s", proctitle, cbuf); 1226 *cp = c; 1227 } 1228 } 1229 if ((cp = strpbrk(cbuf, " \n"))) 1230 cpos = cp - cbuf; 1231 if (cpos == 0) 1232 cpos = 4; 1233 c = cbuf[cpos]; 1234 cbuf[cpos] = '\0'; 1235 upper(cbuf); 1236 p = lookup(cmdtab, cbuf); 1237 cbuf[cpos] = c; 1238 if (p != NULL) { 1239 if (p->implemented == 0) { 1240 nack(p->name); 1241 return (LEXERR); 1242 } 1243 state = p->state; 1244 yylval.s = p->name; 1245 return (p->token); 1246 } 1247 break; 1248 1249 case SITECMD: 1250 if (cbuf[cpos] == ' ') { 1251 cpos++; 1252 return (SP); 1253 } 1254 cp = &cbuf[cpos]; 1255 if ((cp2 = strpbrk(cp, " \n"))) 1256 cpos = cp2 - cbuf; 1257 c = cbuf[cpos]; 1258 cbuf[cpos] = '\0'; 1259 upper(cp); 1260 p = lookup(sitetab, cp); 1261 cbuf[cpos] = c; 1262 if (p != NULL) { 1263 if (p->implemented == 0) { 1264 state = CMD; 1265 nack(p->name); 1266 return (LEXERR); 1267 } 1268 state = p->state; 1269 yylval.s = p->name; 1270 return (p->token); 1271 } 1272 state = CMD; 1273 break; 1274 1275 case OSTR: 1276 if (cbuf[cpos] == '\n') { 1277 state = CMD; 1278 return (CRLF); 1279 } 1280 /* FALLTHROUGH */ 1281 1282 case STR1: 1283 case ZSTR1: 1284 dostr1: 1285 if (cbuf[cpos] == ' ') { 1286 cpos++; 1287 state = state == OSTR ? STR2 : state+1; 1288 return (SP); 1289 } 1290 break; 1291 1292 case ZSTR2: 1293 if (cbuf[cpos] == '\n') { 1294 state = CMD; 1295 return (CRLF); 1296 } 1297 /* FALLTHROUGH */ 1298 1299 case STR2: 1300 cp = &cbuf[cpos]; 1301 n = strlen(cp); 1302 cpos += n - 1; 1303 /* 1304 * Make sure the string is nonempty and \n terminated. 1305 */ 1306 if (n > 1 && cbuf[cpos] == '\n') { 1307 cbuf[cpos] = '\0'; 1308 yylval.s = strdup(cp); 1309 if (yylval.s == NULL) 1310 fatal("Ran out of memory."); 1311 cbuf[cpos] = '\n'; 1312 state = ARGS; 1313 return (STRING); 1314 } 1315 break; 1316 1317 case NSTR: 1318 if (cbuf[cpos] == ' ') { 1319 cpos++; 1320 return (SP); 1321 } 1322 if (isdigit((unsigned char)cbuf[cpos])) { 1323 cp = &cbuf[cpos]; 1324 while (isdigit((unsigned char)cbuf[++cpos])) 1325 ; 1326 c = cbuf[cpos]; 1327 cbuf[cpos] = '\0'; 1328 yylval.i = atoi(cp); 1329 cbuf[cpos] = c; 1330 state = STR1; 1331 return (NUMBER); 1332 } 1333 state = STR1; 1334 goto dostr1; 1335 1336 case ARGS: 1337 if (isdigit((unsigned char)cbuf[cpos])) { 1338 long long llval; 1339 1340 cp = &cbuf[cpos]; 1341 errno = 0; 1342 llval = strtoll(cp, &cp2, 10); 1343 if (llval < 0 || 1344 (errno == ERANGE && llval == LLONG_MAX)) 1345 break; 1346 1347 cpos = (int)(cp2 - cbuf); 1348 if (llval > INT_MAX) { 1349 yylval.o = llval; 1350 return (BIGNUM); 1351 } else { 1352 yylval.i = (int)llval; 1353 return (NUMBER); 1354 } 1355 } 1356 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 && 1357 !isalnum((unsigned char)cbuf[cpos + 3])) { 1358 cpos += 3; 1359 return ALL; 1360 } 1361 switch (cbuf[cpos++]) { 1362 1363 case '\n': 1364 state = CMD; 1365 return (CRLF); 1366 1367 case ' ': 1368 return (SP); 1369 1370 case ',': 1371 return (COMMA); 1372 1373 case 'A': 1374 case 'a': 1375 return (A); 1376 1377 case 'B': 1378 case 'b': 1379 return (B); 1380 1381 case 'C': 1382 case 'c': 1383 return (C); 1384 1385 case 'E': 1386 case 'e': 1387 return (E); 1388 1389 case 'F': 1390 case 'f': 1391 return (F); 1392 1393 case 'I': 1394 case 'i': 1395 return (I); 1396 1397 case 'L': 1398 case 'l': 1399 return (L); 1400 1401 case 'N': 1402 case 'n': 1403 return (N); 1404 1405 case 'P': 1406 case 'p': 1407 return (P); 1408 1409 case 'R': 1410 case 'r': 1411 return (R); 1412 1413 case 'S': 1414 case 's': 1415 return (S); 1416 1417 case 'T': 1418 case 't': 1419 return (T); 1420 1421 } 1422 break; 1423 1424 default: 1425 fatal("Unknown state in scanner."); 1426 } 1427 state = CMD; 1428 return (LEXERR); 1429 } 1430 } 1431 1432 void 1433 upper(s) 1434 char *s; 1435 { 1436 char *p; 1437 1438 for (p = s; *p; p++) { 1439 if (islower((unsigned char)*p)) 1440 *p = (char)toupper((unsigned char)*p); 1441 } 1442 } 1443 1444 static void 1445 help(ctab, s) 1446 struct tab *ctab; 1447 char *s; 1448 { 1449 struct tab *c; 1450 int width, NCMDS; 1451 char *type; 1452 1453 if (ctab == sitetab) 1454 type = "SITE "; 1455 else 1456 type = ""; 1457 width = 0, NCMDS = 0; 1458 for (c = ctab; c->name != NULL; c++) { 1459 int len = strlen(c->name); 1460 1461 if (len > width) 1462 width = len; 1463 NCMDS++; 1464 } 1465 width = (width + 8) &~ 7; 1466 if (s == NULL) { 1467 int i, j, w; 1468 int columns, lines; 1469 1470 lreply(214, "The following %scommands are recognized %s.", 1471 type, "(* =>'s unimplemented)"); 1472 columns = 76 / width; 1473 if (columns == 0) 1474 columns = 1; 1475 lines = (NCMDS + columns - 1) / columns; 1476 for (i = 0; i < lines; i++) { 1477 printf(" "); 1478 for (j = 0; j < columns; j++) { 1479 c = ctab + j * lines + i; 1480 printf("%s%c", c->name, 1481 c->implemented ? ' ' : '*'); 1482 if (c + lines >= &ctab[NCMDS]) 1483 break; 1484 w = strlen(c->name) + 1; 1485 while (w < width) { 1486 putchar(' '); 1487 w++; 1488 } 1489 } 1490 printf("\r\n"); 1491 } 1492 (void) fflush(stdout); 1493 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1494 return; 1495 } 1496 upper(s); 1497 c = lookup(ctab, s); 1498 if (c == NULL) { 1499 reply(502, "Unknown command %s.", s); 1500 return; 1501 } 1502 if (c->implemented) 1503 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1504 else 1505 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1506 c->name, c->help); 1507 } 1508 1509 static void 1510 sizecmd(filename) 1511 char *filename; 1512 { 1513 switch (type) { 1514 case TYPE_L: 1515 case TYPE_I: { 1516 struct stat stbuf; 1517 if (stat(filename, &stbuf) == -1 || !S_ISREG(stbuf.st_mode)) 1518 reply(550, "%s: not a plain file.", filename); 1519 else 1520 reply(213, "%lld", (long long)stbuf.st_size); 1521 break; } 1522 case TYPE_A: { 1523 FILE *fin; 1524 int c; 1525 off_t count; 1526 struct stat stbuf; 1527 fin = fopen(filename, "r"); 1528 if (fin == NULL) { 1529 perror_reply(550, filename); 1530 return; 1531 } 1532 if (fstat(fileno(fin), &stbuf) == -1 || !S_ISREG(stbuf.st_mode)) { 1533 reply(550, "%s: not a plain file.", filename); 1534 (void) fclose(fin); 1535 return; 1536 } 1537 if (stbuf.st_size > 10240) { 1538 reply(550, "%s: file too large for SIZE.", filename); 1539 (void) fclose(fin); 1540 return; 1541 } 1542 1543 count = 0; 1544 while((c = getc(fin)) != EOF) { 1545 if (c == '\n') /* will get expanded to \r\n */ 1546 count++; 1547 count++; 1548 } 1549 (void) fclose(fin); 1550 1551 reply(213, "%lld", (long long)count); 1552 break; } 1553 default: 1554 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1555 } 1556 } 1557