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