26*/ 27/* 28 * Copyright (c) 1985, 1988 Regents of the University of California. 29 * All rights reserved. 30 * 31 * Redistribution and use in source and binary forms, with or without 32 * modification, are permitted provided that the following conditions 33 * are met: 34 * 1. Redistributions of source code must retain the above copyright 35 * notice, this list of conditions and the following disclaimer. 36 * 2. Redistributions in binary form must reproduce the above copyright 37 * notice, this list of conditions and the following disclaimer in the 38 * documentation and/or other materials provided with the distribution. 39 * 3. All advertising materials mentioning features or use of this software 40 * must display the following acknowledgement: 41 * This product includes software developed by the University of 42 * California, Berkeley and its contributors. 43 * 4. Neither the name of the University nor the names of its contributors 44 * may be used to endorse or promote products derived from this software 45 * without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 57 * SUCH DAMAGE. 58 * 59 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91 60 */ 61 62/* 63 * Grammar for FTP commands. 64 * See RFC 959. 65 */ 66 67%{ 68#include "opie_cfg.h" 69 70#include <sys/param.h> 71#include <sys/types.h> 72#include <sys/socket.h> 73#include <sys/stat.h> 74#include <netinet/in.h> 75#include <arpa/ftp.h> 76#include <signal.h> 77#include <setjmp.h> 78#include <syslog.h> 79#if TM_IN_SYS_TIME 80#include <sys/time.h> 81#else /* TM_IN_SYS_TIME */ 82#include <time.h> 83#endif /* TM_IN_SYS_TIME */ 84#include <pwd.h> 85#include <unistd.h> 86#include <stdio.h> 87#include <ctype.h> 88#include <stdlib.h> 89#include <string.h> 90 91#include "opie.h" 92 93#if HAVE_LS_G_FLAG 94#define LS_COMMAND "/bin/ls -lgA" 95#else /* HAVE_LS_G_FLAG */ 96#define LS_COMMAND "/bin/ls -lA" 97#endif /* HAVE_LS_G_FLAG */ 98 99extern struct sockaddr_in data_dest; 100extern struct sockaddr_in his_addr; 101extern int logged_in; 102extern struct passwd *pw; 103extern int guest; 104extern int type; 105extern int form; 106extern int debug; 107extern int timeout; 108extern int maxtimeout; 109extern int pdata; 110extern char *remotehost; 111extern char *proctitle; 112extern char *globerr; 113extern int usedefault; 114extern int transflag; 115extern char tmpline[]; 116char **ftpglob(); 117 118VOIDRET dologout __P((int)); 119VOIDRET upper __P((char *)); 120VOIDRET nack __P((char *)); 121VOIDRET opiefatal __P((char *)); 122 123VOIDRET pass __P((char *)); 124int user __P((char *)); 125VOIDRET passive __P((void)); 126VOIDRET retrieve __P((char *, char *)); 127VOIDRET store __P((char *, char *, int)); 128VOIDRET send_file_list __P((char *)); 129VOIDRET statfilecmd __P((char *)); 130VOIDRET statcmd __P((void)); 131VOIDRET delete __P((char *)); 132VOIDRET renamecmd __P((char *, char *)); 133VOIDRET cwd __P((char *)); 134VOIDRET makedir __P((char *)); 135VOIDRET removedir __P((char *)); 136VOIDRET pwd __P((void)); 137 138VOIDRET sizecmd __P((char *)); 139 140off_t restart_point; 141 142static int cmd_type; 143static int cmd_form; 144static int cmd_bytesz; 145static unsigned short cliport = 0; 146char cbuf[512]; 147char *fromname; 148 149struct tab { 150 char *name; 151 short token; 152 short state; 153 short implemented; /* 1 if command is implemented */ 154 char *help; 155}; 156 157VOIDRET help __P((struct tab *, char *)); 158 159struct tab cmdtab[], sitetab[]; 160 161%} 162 163%token 164 A B C E F I 165 L N P R S T 166 167 SP CRLF COMMA STRING NUMBER 168 169 USER PASS ACCT REIN QUIT PORT 170 PASV TYPE STRU MODE RETR STOR 171 APPE MLFL MAIL MSND MSOM MSAM 172 MRSQ MRCP ALLO REST RNFR RNTO 173 ABOR DELE CWD LIST NLST SITE 174 STAT HELP NOOP MKD RMD PWD 175 CDUP STOU SMNT SYST SIZE MDTM 176 177 UMASK IDLE CHMOD 178 179 LEXERR 180 181%start cmd_list 182 183%% 184 185cmd_list: /* empty */ 186 | cmd_list cmd 187 = { 188 fromname = (char *) 0; 189 restart_point = (off_t) 0; 190 } 191 | cmd_list rcmd 192 ; 193 194cmd: USER SP username CRLF 195 = { 196 user((char *) $3); 197 free((char *) $3); 198 } 199 | PASS SP password CRLF 200 = { 201 pass((char *) $3); 202 free((char *) $3); 203 } 204 | PORT check_login SP host_port CRLF 205 = { 206 usedefault = 0; 207 if (pdata >= 0) { 208 (void) close(pdata); 209 pdata = -1; 210 } 211/* H* port fix, part B: admonish the twit. 212 Also require login before PORT works */ 213 if ($2) { 214 if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) { 215 reply(200, "PORT command successful."); 216 } else { 217 syslog (LOG_WARNING, "refused %s from %s", 218 cbuf, remotehost); 219 reply(500, "You've GOT to be joking."); 220 } 221 } 222 } 223/* | PASV CRLF 224 = { 225 passive(); 226 } */ 227 | PASV check_login CRLF 228 = { 229/* Require login for PASV, too. This actually fixes a bug -- telnet to an 230 unfixed wu-ftpd and type PASV first off, and it crashes! */ 231 if ($2) { 232 passive(); 233 } 234 } 235 | TYPE SP type_code CRLF 236 = { 237 switch (cmd_type) { 238 239 case TYPE_A: 240 if (cmd_form == FORM_N) { 241 reply(200, "Type set to A."); 242 type = cmd_type; 243 form = cmd_form; 244 } else 245 reply(504, "Form must be N."); 246 break; 247 248 case TYPE_E: 249 reply(504, "Type E not implemented."); 250 break; 251 252 case TYPE_I: 253 reply(200, "Type set to I."); 254 type = cmd_type; 255 break; 256 257 case TYPE_L: 258#if NBBY == 8 259 if (cmd_bytesz == 8) { 260 reply(200, 261 "Type set to L (byte size 8)."); 262 type = cmd_type; 263 } else 264 reply(504, "Byte size must be 8."); 265#else /* NBBY == 8 */ 266 UNIMPLEMENTED for NBBY != 8 267#endif /* NBBY == 8 */ 268 } 269 } 270 | STRU SP struct_code CRLF 271 = { 272 switch ($3) { 273 274 case STRU_F: 275 reply(200, "STRU F ok."); 276 break; 277 278 default: 279 reply(504, "Unimplemented STRU type."); 280 } 281 } 282 | MODE SP mode_code CRLF 283 = { 284 switch ($3) { 285 286 case MODE_S: 287 reply(200, "MODE S ok."); 288 break; 289 290 default: 291 reply(502, "Unimplemented MODE type."); 292 } 293 } 294 | ALLO SP NUMBER CRLF 295 = { 296 reply(202, "ALLO command ignored."); 297 } 298 | ALLO SP NUMBER SP R SP NUMBER CRLF 299 = { 300 reply(202, "ALLO command ignored."); 301 } 302 | RETR check_login SP pathname CRLF 303 = { 304 if ($2 && $4) 305 retrieve((char *) 0, (char *) $4); 306 if ($4) 307 free((char *) $4); 308 } 309 | STOR check_login SP pathname CRLF 310 = { 311 if ($2 && $4) 312 store((char *) $4, "w", 0); 313 if ($4) 314 free((char *) $4); 315 } 316 | APPE check_login SP pathname CRLF 317 = { 318 if ($2 && $4) 319 store((char *) $4, "a", 0); 320 if ($4) 321 free((char *) $4); 322 } 323 | NLST check_login CRLF 324 = { 325 if ($2) 326 send_file_list("."); 327 } 328 | NLST check_login SP STRING CRLF 329 = { 330 if ($2 && $4) 331 send_file_list((char *) $4); 332 if ($4) 333 free((char *) $4); 334 } 335 | LIST check_login CRLF 336 = { 337 if ($2) 338 retrieve(LS_COMMAND, ""); 339 } 340 | LIST check_login SP pathname CRLF 341 = { 342 if ($2 && $4) 343 { 344 char buffer[sizeof(LS_COMMAND)+3]; 345 strcpy(buffer, LS_COMMAND); 346 strcat(buffer, " %s"); 347 retrieve(buffer, (char *) $4); 348 } 349 if ($4) 350 free((char *) $4); 351 } 352 | STAT check_login SP pathname CRLF 353 = { 354 if ($2 && $4) 355 statfilecmd((char *) $4); 356 if ($4) 357 free((char *) $4); 358 } 359 | STAT CRLF 360 = { 361 statcmd(); 362 } 363 | DELE check_login SP pathname CRLF 364 = { 365 if ($2 && $4) 366 delete((char *) $4); 367 if ($4) 368 free((char *) $4); 369 } 370 | RNTO SP pathname CRLF 371 = { 372 if (fromname) { 373 renamecmd(fromname, (char *) $3); 374 free(fromname); 375 fromname = (char *) 0; 376 } else { 377 reply(503, "Bad sequence of commands."); 378 } 379 free((char *) $3); 380 } 381 | ABOR CRLF 382 = { 383 reply(225, "ABOR command successful."); 384 } 385 | CWD check_login CRLF 386 = { 387 if ($2) 388 cwd(pw->pw_dir); 389 } 390 | CWD check_login SP pathname CRLF 391 = { 392 if ($2 && $4) 393 cwd((char *) $4); 394 if ($4) 395 free((char *) $4); 396 } 397 | HELP CRLF 398 = { 399 help(cmdtab, (char *) 0); 400 } 401 | HELP SP STRING CRLF 402 = { 403 register char *cp = (char *)$3; 404 405 if (strncasecmp(cp, "SITE", 4) == 0) { 406 cp = (char *)$3 + 4; 407 if (*cp == ' ') 408 cp++; 409 if (*cp) 410 help(sitetab, cp); 411 else 412 help(sitetab, (char *) 0); 413 } else 414 help(cmdtab, (char *) $3); 415 } 416 | NOOP CRLF 417 = { 418 reply(200, "NOOP command successful."); 419 } 420 | MKD check_login SP pathname CRLF 421 = { 422 if ($2 && $4) 423 makedir((char *) $4); 424 if ($4) 425 free((char *) $4); 426 } 427 | RMD check_login SP pathname CRLF 428 = { 429 if ($2 && $4) 430 removedir((char *) $4); 431 if ($4) 432 free((char *) $4); 433 } 434 | PWD check_login CRLF 435 = { 436 if ($2) 437 pwd(); 438 } 439 | CDUP check_login CRLF 440 = { 441 if ($2) 442 cwd(".."); 443 } 444 | SITE SP HELP CRLF 445 = { 446 help(sitetab, (char *) 0); 447 } 448 | SITE SP HELP SP STRING CRLF 449 = { 450 help(sitetab, (char *) $5); 451 } 452 | SITE SP UMASK check_login CRLF 453 = { 454 int oldmask; 455 456 if ($4) { 457 oldmask = umask(0); 458 (void) umask(oldmask); 459 reply(200, "Current UMASK is %03o", oldmask); 460 } 461 } 462 | SITE SP UMASK check_login SP octal_number CRLF 463 = { 464 int oldmask; 465 466 if ($4) { 467 if (($6 == -1) || ($6 > 0777)) { 468 reply(501, "Bad UMASK value"); 469 } else { 470 oldmask = umask($6); 471 reply(200, 472 "UMASK set to %03o (was %03o)", 473 $6, oldmask); 474 } 475 } 476 } 477 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 478 = { 479 if ($4 && $8) { 480 if ($6 > 0777) 481 reply(501, 482 "CHMOD: Mode value must be between 0 and 0777"); 483 else if (chmod((char *) $8, $6) < 0) 484 perror_reply(550, (char *) $8); 485 else 486 reply(200, "CHMOD command successful."); 487 } 488 if ($8) 489 free((char *) $8); 490 } 491 | SITE SP IDLE CRLF 492 = { 493 reply(200, 494 "Current IDLE time limit is %d seconds; max %d", 495 timeout, maxtimeout); 496 } 497 | SITE SP IDLE SP NUMBER CRLF 498 = { 499 if ($5 < 30 || $5 > maxtimeout) { 500 reply(501, 501 "Maximum IDLE time must be between 30 and %d seconds", 502 maxtimeout); 503 } else { 504 timeout = $5; 505 (void) alarm((unsigned) timeout); 506 reply(200, 507 "Maximum IDLE time set to %d seconds", 508 timeout); 509 } 510 } 511 | STOU check_login SP pathname CRLF 512 = { 513 if ($2 && $4) 514 store((char *) $4, "w", 1); 515 if ($4) 516 free((char *) $4); 517 } 518 | SYST CRLF 519 = { 520#ifdef unix 521#ifdef BSD 522 reply(215, "UNIX Type: L%d Version: BSD-%d", 523 NBBY, BSD); 524#else /* BSD */ 525 reply(215, "UNIX Type: L%d", NBBY); 526#endif /* BSD */ 527#else /* unix */ 528 reply(215, "UNKNOWN Type: L%d", NBBY); 529#endif /* unix */ 530 } 531 532 /* 533 * SIZE is not in RFC959, but Postel has blessed it and 534 * it will be in the updated RFC. 535 * 536 * Return size of file in a format suitable for 537 * using with RESTART (we just count bytes). 538 */ 539 | SIZE check_login SP pathname CRLF 540 = { 541 if ($2 && $4) 542 sizecmd((char *) $4); 543 if ($4) 544 free((char *) $4); 545 } 546 547 /* 548 * MDTM is not in RFC959, but Postel has blessed it and 549 * it will be in the updated RFC. 550 * 551 * Return modification time of file as an ISO 3307 552 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 553 * where xxx is the fractional second (of any precision, 554 * not necessarily 3 digits) 555 */ 556 | MDTM check_login SP pathname CRLF 557 = { 558 if ($2 && $4) { 559 struct stat stbuf; 560 if (stat((char *) $4, &stbuf) < 0) 561 perror_reply(550, (char *) $4); 562 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 563 reply(550, "%s: not a plain file.", 564 (char *) $4); 565 } else { 566 register struct tm *t; 567 struct tm *gmtime(); 568 t = gmtime(&stbuf.st_mtime); 569 reply(213, 570 "%d%02d%02d%02d%02d%02d", 571 t->tm_year+1900, t->tm_mon+1, t->tm_mday, 572 t->tm_hour, t->tm_min, t->tm_sec); 573 } 574 } 575 if ($4) 576 free((char *) $4); 577 } 578 | QUIT CRLF 579 = { 580 reply(221, "Goodbye."); 581 dologout(0); 582 } 583 | error CRLF 584 = { 585 yyerrok; 586 } 587 ; 588rcmd: RNFR check_login SP pathname CRLF 589 = { 590 char *renamefrom(); 591 592 restart_point = (off_t) 0; 593 if ($2 && $4) { 594 fromname = renamefrom((char *) $4); 595 if (fromname == (char *) 0 && $4) { 596 free((char *) $4); 597 } 598 } 599 } 600 | REST SP byte_size CRLF 601 = { 602 long atol(); 603 604 fromname = (char *) 0; 605 restart_point = $3; 606 reply(350, "Restarting at %ld. %s", restart_point, 607 "Send STORE or RETRIEVE to initiate transfer."); 608 } 609 ; 610 611username: STRING 612 ; 613 614password: /* empty */ 615 = { 616 *(char **)&($$) = (char *)calloc(1, sizeof(char)); 617 } 618 | STRING 619 ; 620 621byte_size: NUMBER 622 ; 623 624host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 625 NUMBER COMMA NUMBER 626 = { 627 register char *a, *p; 628 629 a = (char *)&data_dest.sin_addr; 630 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 631 632/* H* port fix, part A-1: Check the args against the client addr */ 633 p = (char *)&his_addr.sin_addr; 634 if (memcmp (a, p, sizeof (data_dest.sin_addr))) 635 memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */ 636 637 p = (char *)&data_dest.sin_port; 638 639/* H* port fix, part A-2: only allow client ports in "user space" */ 640 p[0] = 0; p[1] = 0; 641 cliport = ($9 << 8) + $11; 642 if (cliport > 1023) { 643 p[0] = $9; p[1] = $11; 644 } 645 646 p[0] = $9; p[1] = $11; 647 data_dest.sin_family = AF_INET; 648 } 649 ; 650 651form_code: N 652 = { 653 $$ = FORM_N; 654 } 655 | T 656 = { 657 $$ = FORM_T; 658 } 659 | C 660 = { 661 $$ = FORM_C; 662 } 663 ; 664 665type_code: A 666 = { 667 cmd_type = TYPE_A; 668 cmd_form = FORM_N; 669 } 670 | A SP form_code 671 = { 672 cmd_type = TYPE_A; 673 cmd_form = $3; 674 } 675 | E 676 = { 677 cmd_type = TYPE_E; 678 cmd_form = FORM_N; 679 } 680 | E SP form_code 681 = { 682 cmd_type = TYPE_E; 683 cmd_form = $3; 684 } 685 | I 686 = { 687 cmd_type = TYPE_I; 688 } 689 | L 690 = { 691 cmd_type = TYPE_L; 692 cmd_bytesz = NBBY; 693 } 694 | L SP byte_size 695 = { 696 cmd_type = TYPE_L; 697 cmd_bytesz = $3; 698 } 699 /* this is for a bug in the BBN ftp */ 700 | L byte_size 701 = { 702 cmd_type = TYPE_L; 703 cmd_bytesz = $2; 704 } 705 ; 706 707struct_code: F 708 = { 709 $$ = STRU_F; 710 } 711 | R 712 = { 713 $$ = STRU_R; 714 } 715 | P 716 = { 717 $$ = STRU_P; 718 } 719 ; 720 721mode_code: S 722 = { 723 $$ = MODE_S; 724 } 725 | B 726 = { 727 $$ = MODE_B; 728 } 729 | C 730 = { 731 $$ = MODE_C; 732 } 733 ; 734 735pathname: pathstring 736 = { 737 /* 738 * Problem: this production is used for all pathname 739 * processing, but only gives a 550 error reply. 740 * This is a valid reply in some cases but not in others. 741 */ 742 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 743 *(char **)&($$) = *ftpglob((char *) $1); 744 if (globerr != NULL) { 745 reply(550, globerr); 746/* $$ = NULL; */ 747 $$ = 0; 748 } 749 free((char *) $1); 750 } else 751 $$ = $1; 752 } 753 ; 754 755pathstring: STRING 756 ; 757 758octal_number: NUMBER 759 = { 760 register int ret, dec, multby, digit; 761 762 /* 763 * Convert a number that was read as decimal number 764 * to what it would be if it had been read as octal. 765 */ 766 dec = $1; 767 multby = 1; 768 ret = 0; 769 while (dec) { 770 digit = dec%10; 771 if (digit > 7) { 772 ret = -1; 773 break; 774 } 775 ret += digit * multby; 776 multby *= 8; 777 dec /= 10; 778 } 779 $$ = ret; 780 } 781 ; 782 783check_login: /* empty */ 784 = { 785 if (logged_in) 786 $$ = 1; 787 else { 788 reply(530, "Please login with USER and PASS."); 789 $$ = 0; 790 } 791 } 792 ; 793 794%% 795 796extern jmp_buf errcatch; 797 798#define CMD 0 /* beginning of command */ 799#define ARGS 1 /* expect miscellaneous arguments */ 800#define STR1 2 /* expect SP followed by STRING */ 801#define STR2 3 /* expect STRING */ 802#define OSTR 4 /* optional SP then STRING */ 803#define ZSTR1 5 /* SP then optional STRING */ 804#define ZSTR2 6 /* optional STRING after SP */ 805#define SITECMD 7 /* SITE command */ 806#define NSTR 8 /* Number followed by a string */ 807 808struct tab cmdtab[] = { /* In order defined in RFC 765 */ 809 { "USER", USER, STR1, 1, "<sp> username" }, 810 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 811 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 812 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 813 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 814 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 815 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 816 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 817 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 818 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 819 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 820 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 821 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 822 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 823 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 824 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 825 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 826 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 827 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 828 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 829 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 830 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 831 { "REST", REST, ARGS, 1, "(restart command)" }, 832 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 833 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 834 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 835 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 836 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 837 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 838 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 839 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 840 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 841 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 842 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 843 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 844 { "NOOP", NOOP, ARGS, 1, "" }, 845 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 846 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 847 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 848 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 849 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 850 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 851 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 852 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 853 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 854 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 855 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 856 { NULL, 0, 0, 0, 0 } 857}; 858 859struct tab sitetab[] = { 860 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 861 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 862 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 863 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 864 { NULL, 0, 0, 0, 0 } 865}; 866 867struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd) 868{ 869 870 for (; p->name != NULL; p++) 871 if (strcmp(cmd, p->name) == 0) 872 return (p); 873 return (0); 874} 875 876#include <arpa/telnet.h> 877 878/* 879 * getline - a hacked up version of fgets to ignore TELNET escape codes. 880 */ 881char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop) 882{ 883 register c; 884 register char *cs; 885 886 cs = s; 887/* tmpline may contain saved command from urgent mode interruption */ 888 for (c = 0; *(tmpline + c) && --n > 0; ++c) { 889 *cs++ = *(tmpline + c); 890 if (*(tmpline + c) == '\n') { 891 *cs++ = '\0'; 892 if (debug) 893 syslog(LOG_DEBUG, "command: %s", s); 894 *tmpline = '\0'; 895 return(s); 896 } 897 if (c == 0) 898 *tmpline = '\0'; 899 } 900 while ((c = getc(iop)) != EOF) { 901 c &= 0377; 902 if (c == IAC) { 903 if ((c = getc(iop)) != EOF) { 904 c &= 0377; 905 switch (c) { 906 case WILL: 907 case WONT: 908 c = getc(iop); 909 printf("%c%c%c", IAC, DONT, 0377&c); 910 (void) fflush(stdout); 911 continue; 912 case DO: 913 case DONT: 914 c = getc(iop); 915 printf("%c%c%c", IAC, WONT, 0377&c); 916 (void) fflush(stdout); 917 continue; 918 case IAC: 919 break; 920 default: 921 continue; /* ignore command */ 922 } 923 } 924 } 925 *cs++ = c; 926 if (--n <= 0 || c == '\n') 927 break; 928 } 929 if (c == EOF && cs == s) 930 return (NULL); 931 *cs++ = '\0'; 932 if (debug) 933 syslog(LOG_DEBUG, "command: %s", s); 934 return (s); 935} 936 937static VOIDRET toolong FUNCTION((input), int input) 938{ 939 time_t now; 940 941 reply(421, "Timeout (%d seconds): closing control connection.", timeout); 942 (void) time(&now); 943 syslog(LOG_INFO, "User %s timed out after %d seconds at %s", 944 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 945 dologout(1); 946} 947 948int yylex FUNCTION_NOARGS 949{ 950 static int cpos, state; 951 register char *cp, *cp2; 952 register struct tab *p; 953 int n; 954 char c, *copy(); 955 956 for (;;) { 957 switch (state) { 958 959 case CMD: 960 (void) signal(SIGALRM, toolong); 961 (void) alarm((unsigned) timeout); 962 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 963 reply(221, "You could at least say goodbye."); 964 dologout(0); 965 } 966 (void) alarm(0);
| 27*/ 28/* 29 * Copyright (c) 1985, 1988 Regents of the University of California. 30 * All rights reserved. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by the University of 43 * California, Berkeley and its contributors. 44 * 4. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 * 60 * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91 61 */ 62 63/* 64 * Grammar for FTP commands. 65 * See RFC 959. 66 */ 67 68%{ 69#include "opie_cfg.h" 70 71#include <sys/param.h> 72#include <sys/types.h> 73#include <sys/socket.h> 74#include <sys/stat.h> 75#include <netinet/in.h> 76#include <arpa/ftp.h> 77#include <signal.h> 78#include <setjmp.h> 79#include <syslog.h> 80#if TM_IN_SYS_TIME 81#include <sys/time.h> 82#else /* TM_IN_SYS_TIME */ 83#include <time.h> 84#endif /* TM_IN_SYS_TIME */ 85#include <pwd.h> 86#include <unistd.h> 87#include <stdio.h> 88#include <ctype.h> 89#include <stdlib.h> 90#include <string.h> 91 92#include "opie.h" 93 94#if HAVE_LS_G_FLAG 95#define LS_COMMAND "/bin/ls -lgA" 96#else /* HAVE_LS_G_FLAG */ 97#define LS_COMMAND "/bin/ls -lA" 98#endif /* HAVE_LS_G_FLAG */ 99 100extern struct sockaddr_in data_dest; 101extern struct sockaddr_in his_addr; 102extern int logged_in; 103extern struct passwd *pw; 104extern int guest; 105extern int type; 106extern int form; 107extern int debug; 108extern int timeout; 109extern int maxtimeout; 110extern int pdata; 111extern char *remotehost; 112extern char *proctitle; 113extern char *globerr; 114extern int usedefault; 115extern int transflag; 116extern char tmpline[]; 117char **ftpglob(); 118 119VOIDRET dologout __P((int)); 120VOIDRET upper __P((char *)); 121VOIDRET nack __P((char *)); 122VOIDRET opiefatal __P((char *)); 123 124VOIDRET pass __P((char *)); 125int user __P((char *)); 126VOIDRET passive __P((void)); 127VOIDRET retrieve __P((char *, char *)); 128VOIDRET store __P((char *, char *, int)); 129VOIDRET send_file_list __P((char *)); 130VOIDRET statfilecmd __P((char *)); 131VOIDRET statcmd __P((void)); 132VOIDRET delete __P((char *)); 133VOIDRET renamecmd __P((char *, char *)); 134VOIDRET cwd __P((char *)); 135VOIDRET makedir __P((char *)); 136VOIDRET removedir __P((char *)); 137VOIDRET pwd __P((void)); 138 139VOIDRET sizecmd __P((char *)); 140 141off_t restart_point; 142 143static int cmd_type; 144static int cmd_form; 145static int cmd_bytesz; 146static unsigned short cliport = 0; 147char cbuf[512]; 148char *fromname; 149 150struct tab { 151 char *name; 152 short token; 153 short state; 154 short implemented; /* 1 if command is implemented */ 155 char *help; 156}; 157 158VOIDRET help __P((struct tab *, char *)); 159 160struct tab cmdtab[], sitetab[]; 161 162%} 163 164%token 165 A B C E F I 166 L N P R S T 167 168 SP CRLF COMMA STRING NUMBER 169 170 USER PASS ACCT REIN QUIT PORT 171 PASV TYPE STRU MODE RETR STOR 172 APPE MLFL MAIL MSND MSOM MSAM 173 MRSQ MRCP ALLO REST RNFR RNTO 174 ABOR DELE CWD LIST NLST SITE 175 STAT HELP NOOP MKD RMD PWD 176 CDUP STOU SMNT SYST SIZE MDTM 177 178 UMASK IDLE CHMOD 179 180 LEXERR 181 182%start cmd_list 183 184%% 185 186cmd_list: /* empty */ 187 | cmd_list cmd 188 = { 189 fromname = (char *) 0; 190 restart_point = (off_t) 0; 191 } 192 | cmd_list rcmd 193 ; 194 195cmd: USER SP username CRLF 196 = { 197 user((char *) $3); 198 free((char *) $3); 199 } 200 | PASS SP password CRLF 201 = { 202 pass((char *) $3); 203 free((char *) $3); 204 } 205 | PORT check_login SP host_port CRLF 206 = { 207 usedefault = 0; 208 if (pdata >= 0) { 209 (void) close(pdata); 210 pdata = -1; 211 } 212/* H* port fix, part B: admonish the twit. 213 Also require login before PORT works */ 214 if ($2) { 215 if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) { 216 reply(200, "PORT command successful."); 217 } else { 218 syslog (LOG_WARNING, "refused %s from %s", 219 cbuf, remotehost); 220 reply(500, "You've GOT to be joking."); 221 } 222 } 223 } 224/* | PASV CRLF 225 = { 226 passive(); 227 } */ 228 | PASV check_login CRLF 229 = { 230/* Require login for PASV, too. This actually fixes a bug -- telnet to an 231 unfixed wu-ftpd and type PASV first off, and it crashes! */ 232 if ($2) { 233 passive(); 234 } 235 } 236 | TYPE SP type_code CRLF 237 = { 238 switch (cmd_type) { 239 240 case TYPE_A: 241 if (cmd_form == FORM_N) { 242 reply(200, "Type set to A."); 243 type = cmd_type; 244 form = cmd_form; 245 } else 246 reply(504, "Form must be N."); 247 break; 248 249 case TYPE_E: 250 reply(504, "Type E not implemented."); 251 break; 252 253 case TYPE_I: 254 reply(200, "Type set to I."); 255 type = cmd_type; 256 break; 257 258 case TYPE_L: 259#if NBBY == 8 260 if (cmd_bytesz == 8) { 261 reply(200, 262 "Type set to L (byte size 8)."); 263 type = cmd_type; 264 } else 265 reply(504, "Byte size must be 8."); 266#else /* NBBY == 8 */ 267 UNIMPLEMENTED for NBBY != 8 268#endif /* NBBY == 8 */ 269 } 270 } 271 | STRU SP struct_code CRLF 272 = { 273 switch ($3) { 274 275 case STRU_F: 276 reply(200, "STRU F ok."); 277 break; 278 279 default: 280 reply(504, "Unimplemented STRU type."); 281 } 282 } 283 | MODE SP mode_code CRLF 284 = { 285 switch ($3) { 286 287 case MODE_S: 288 reply(200, "MODE S ok."); 289 break; 290 291 default: 292 reply(502, "Unimplemented MODE type."); 293 } 294 } 295 | ALLO SP NUMBER CRLF 296 = { 297 reply(202, "ALLO command ignored."); 298 } 299 | ALLO SP NUMBER SP R SP NUMBER CRLF 300 = { 301 reply(202, "ALLO command ignored."); 302 } 303 | RETR check_login SP pathname CRLF 304 = { 305 if ($2 && $4) 306 retrieve((char *) 0, (char *) $4); 307 if ($4) 308 free((char *) $4); 309 } 310 | STOR check_login SP pathname CRLF 311 = { 312 if ($2 && $4) 313 store((char *) $4, "w", 0); 314 if ($4) 315 free((char *) $4); 316 } 317 | APPE check_login SP pathname CRLF 318 = { 319 if ($2 && $4) 320 store((char *) $4, "a", 0); 321 if ($4) 322 free((char *) $4); 323 } 324 | NLST check_login CRLF 325 = { 326 if ($2) 327 send_file_list("."); 328 } 329 | NLST check_login SP STRING CRLF 330 = { 331 if ($2 && $4) 332 send_file_list((char *) $4); 333 if ($4) 334 free((char *) $4); 335 } 336 | LIST check_login CRLF 337 = { 338 if ($2) 339 retrieve(LS_COMMAND, ""); 340 } 341 | LIST check_login SP pathname CRLF 342 = { 343 if ($2 && $4) 344 { 345 char buffer[sizeof(LS_COMMAND)+3]; 346 strcpy(buffer, LS_COMMAND); 347 strcat(buffer, " %s"); 348 retrieve(buffer, (char *) $4); 349 } 350 if ($4) 351 free((char *) $4); 352 } 353 | STAT check_login SP pathname CRLF 354 = { 355 if ($2 && $4) 356 statfilecmd((char *) $4); 357 if ($4) 358 free((char *) $4); 359 } 360 | STAT CRLF 361 = { 362 statcmd(); 363 } 364 | DELE check_login SP pathname CRLF 365 = { 366 if ($2 && $4) 367 delete((char *) $4); 368 if ($4) 369 free((char *) $4); 370 } 371 | RNTO SP pathname CRLF 372 = { 373 if (fromname) { 374 renamecmd(fromname, (char *) $3); 375 free(fromname); 376 fromname = (char *) 0; 377 } else { 378 reply(503, "Bad sequence of commands."); 379 } 380 free((char *) $3); 381 } 382 | ABOR CRLF 383 = { 384 reply(225, "ABOR command successful."); 385 } 386 | CWD check_login CRLF 387 = { 388 if ($2) 389 cwd(pw->pw_dir); 390 } 391 | CWD check_login SP pathname CRLF 392 = { 393 if ($2 && $4) 394 cwd((char *) $4); 395 if ($4) 396 free((char *) $4); 397 } 398 | HELP CRLF 399 = { 400 help(cmdtab, (char *) 0); 401 } 402 | HELP SP STRING CRLF 403 = { 404 register char *cp = (char *)$3; 405 406 if (strncasecmp(cp, "SITE", 4) == 0) { 407 cp = (char *)$3 + 4; 408 if (*cp == ' ') 409 cp++; 410 if (*cp) 411 help(sitetab, cp); 412 else 413 help(sitetab, (char *) 0); 414 } else 415 help(cmdtab, (char *) $3); 416 } 417 | NOOP CRLF 418 = { 419 reply(200, "NOOP command successful."); 420 } 421 | MKD check_login SP pathname CRLF 422 = { 423 if ($2 && $4) 424 makedir((char *) $4); 425 if ($4) 426 free((char *) $4); 427 } 428 | RMD check_login SP pathname CRLF 429 = { 430 if ($2 && $4) 431 removedir((char *) $4); 432 if ($4) 433 free((char *) $4); 434 } 435 | PWD check_login CRLF 436 = { 437 if ($2) 438 pwd(); 439 } 440 | CDUP check_login CRLF 441 = { 442 if ($2) 443 cwd(".."); 444 } 445 | SITE SP HELP CRLF 446 = { 447 help(sitetab, (char *) 0); 448 } 449 | SITE SP HELP SP STRING CRLF 450 = { 451 help(sitetab, (char *) $5); 452 } 453 | SITE SP UMASK check_login CRLF 454 = { 455 int oldmask; 456 457 if ($4) { 458 oldmask = umask(0); 459 (void) umask(oldmask); 460 reply(200, "Current UMASK is %03o", oldmask); 461 } 462 } 463 | SITE SP UMASK check_login SP octal_number CRLF 464 = { 465 int oldmask; 466 467 if ($4) { 468 if (($6 == -1) || ($6 > 0777)) { 469 reply(501, "Bad UMASK value"); 470 } else { 471 oldmask = umask($6); 472 reply(200, 473 "UMASK set to %03o (was %03o)", 474 $6, oldmask); 475 } 476 } 477 } 478 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 479 = { 480 if ($4 && $8) { 481 if ($6 > 0777) 482 reply(501, 483 "CHMOD: Mode value must be between 0 and 0777"); 484 else if (chmod((char *) $8, $6) < 0) 485 perror_reply(550, (char *) $8); 486 else 487 reply(200, "CHMOD command successful."); 488 } 489 if ($8) 490 free((char *) $8); 491 } 492 | SITE SP IDLE CRLF 493 = { 494 reply(200, 495 "Current IDLE time limit is %d seconds; max %d", 496 timeout, maxtimeout); 497 } 498 | SITE SP IDLE SP NUMBER CRLF 499 = { 500 if ($5 < 30 || $5 > maxtimeout) { 501 reply(501, 502 "Maximum IDLE time must be between 30 and %d seconds", 503 maxtimeout); 504 } else { 505 timeout = $5; 506 (void) alarm((unsigned) timeout); 507 reply(200, 508 "Maximum IDLE time set to %d seconds", 509 timeout); 510 } 511 } 512 | STOU check_login SP pathname CRLF 513 = { 514 if ($2 && $4) 515 store((char *) $4, "w", 1); 516 if ($4) 517 free((char *) $4); 518 } 519 | SYST CRLF 520 = { 521#ifdef unix 522#ifdef BSD 523 reply(215, "UNIX Type: L%d Version: BSD-%d", 524 NBBY, BSD); 525#else /* BSD */ 526 reply(215, "UNIX Type: L%d", NBBY); 527#endif /* BSD */ 528#else /* unix */ 529 reply(215, "UNKNOWN Type: L%d", NBBY); 530#endif /* unix */ 531 } 532 533 /* 534 * SIZE is not in RFC959, but Postel has blessed it and 535 * it will be in the updated RFC. 536 * 537 * Return size of file in a format suitable for 538 * using with RESTART (we just count bytes). 539 */ 540 | SIZE check_login SP pathname CRLF 541 = { 542 if ($2 && $4) 543 sizecmd((char *) $4); 544 if ($4) 545 free((char *) $4); 546 } 547 548 /* 549 * MDTM is not in RFC959, but Postel has blessed it and 550 * it will be in the updated RFC. 551 * 552 * Return modification time of file as an ISO 3307 553 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 554 * where xxx is the fractional second (of any precision, 555 * not necessarily 3 digits) 556 */ 557 | MDTM check_login SP pathname CRLF 558 = { 559 if ($2 && $4) { 560 struct stat stbuf; 561 if (stat((char *) $4, &stbuf) < 0) 562 perror_reply(550, (char *) $4); 563 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 564 reply(550, "%s: not a plain file.", 565 (char *) $4); 566 } else { 567 register struct tm *t; 568 struct tm *gmtime(); 569 t = gmtime(&stbuf.st_mtime); 570 reply(213, 571 "%d%02d%02d%02d%02d%02d", 572 t->tm_year+1900, t->tm_mon+1, t->tm_mday, 573 t->tm_hour, t->tm_min, t->tm_sec); 574 } 575 } 576 if ($4) 577 free((char *) $4); 578 } 579 | QUIT CRLF 580 = { 581 reply(221, "Goodbye."); 582 dologout(0); 583 } 584 | error CRLF 585 = { 586 yyerrok; 587 } 588 ; 589rcmd: RNFR check_login SP pathname CRLF 590 = { 591 char *renamefrom(); 592 593 restart_point = (off_t) 0; 594 if ($2 && $4) { 595 fromname = renamefrom((char *) $4); 596 if (fromname == (char *) 0 && $4) { 597 free((char *) $4); 598 } 599 } 600 } 601 | REST SP byte_size CRLF 602 = { 603 long atol(); 604 605 fromname = (char *) 0; 606 restart_point = $3; 607 reply(350, "Restarting at %ld. %s", restart_point, 608 "Send STORE or RETRIEVE to initiate transfer."); 609 } 610 ; 611 612username: STRING 613 ; 614 615password: /* empty */ 616 = { 617 *(char **)&($$) = (char *)calloc(1, sizeof(char)); 618 } 619 | STRING 620 ; 621 622byte_size: NUMBER 623 ; 624 625host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 626 NUMBER COMMA NUMBER 627 = { 628 register char *a, *p; 629 630 a = (char *)&data_dest.sin_addr; 631 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 632 633/* H* port fix, part A-1: Check the args against the client addr */ 634 p = (char *)&his_addr.sin_addr; 635 if (memcmp (a, p, sizeof (data_dest.sin_addr))) 636 memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */ 637 638 p = (char *)&data_dest.sin_port; 639 640/* H* port fix, part A-2: only allow client ports in "user space" */ 641 p[0] = 0; p[1] = 0; 642 cliport = ($9 << 8) + $11; 643 if (cliport > 1023) { 644 p[0] = $9; p[1] = $11; 645 } 646 647 p[0] = $9; p[1] = $11; 648 data_dest.sin_family = AF_INET; 649 } 650 ; 651 652form_code: N 653 = { 654 $$ = FORM_N; 655 } 656 | T 657 = { 658 $$ = FORM_T; 659 } 660 | C 661 = { 662 $$ = FORM_C; 663 } 664 ; 665 666type_code: A 667 = { 668 cmd_type = TYPE_A; 669 cmd_form = FORM_N; 670 } 671 | A SP form_code 672 = { 673 cmd_type = TYPE_A; 674 cmd_form = $3; 675 } 676 | E 677 = { 678 cmd_type = TYPE_E; 679 cmd_form = FORM_N; 680 } 681 | E SP form_code 682 = { 683 cmd_type = TYPE_E; 684 cmd_form = $3; 685 } 686 | I 687 = { 688 cmd_type = TYPE_I; 689 } 690 | L 691 = { 692 cmd_type = TYPE_L; 693 cmd_bytesz = NBBY; 694 } 695 | L SP byte_size 696 = { 697 cmd_type = TYPE_L; 698 cmd_bytesz = $3; 699 } 700 /* this is for a bug in the BBN ftp */ 701 | L byte_size 702 = { 703 cmd_type = TYPE_L; 704 cmd_bytesz = $2; 705 } 706 ; 707 708struct_code: F 709 = { 710 $$ = STRU_F; 711 } 712 | R 713 = { 714 $$ = STRU_R; 715 } 716 | P 717 = { 718 $$ = STRU_P; 719 } 720 ; 721 722mode_code: S 723 = { 724 $$ = MODE_S; 725 } 726 | B 727 = { 728 $$ = MODE_B; 729 } 730 | C 731 = { 732 $$ = MODE_C; 733 } 734 ; 735 736pathname: pathstring 737 = { 738 /* 739 * Problem: this production is used for all pathname 740 * processing, but only gives a 550 error reply. 741 * This is a valid reply in some cases but not in others. 742 */ 743 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 744 *(char **)&($$) = *ftpglob((char *) $1); 745 if (globerr != NULL) { 746 reply(550, globerr); 747/* $$ = NULL; */ 748 $$ = 0; 749 } 750 free((char *) $1); 751 } else 752 $$ = $1; 753 } 754 ; 755 756pathstring: STRING 757 ; 758 759octal_number: NUMBER 760 = { 761 register int ret, dec, multby, digit; 762 763 /* 764 * Convert a number that was read as decimal number 765 * to what it would be if it had been read as octal. 766 */ 767 dec = $1; 768 multby = 1; 769 ret = 0; 770 while (dec) { 771 digit = dec%10; 772 if (digit > 7) { 773 ret = -1; 774 break; 775 } 776 ret += digit * multby; 777 multby *= 8; 778 dec /= 10; 779 } 780 $$ = ret; 781 } 782 ; 783 784check_login: /* empty */ 785 = { 786 if (logged_in) 787 $$ = 1; 788 else { 789 reply(530, "Please login with USER and PASS."); 790 $$ = 0; 791 } 792 } 793 ; 794 795%% 796 797extern jmp_buf errcatch; 798 799#define CMD 0 /* beginning of command */ 800#define ARGS 1 /* expect miscellaneous arguments */ 801#define STR1 2 /* expect SP followed by STRING */ 802#define STR2 3 /* expect STRING */ 803#define OSTR 4 /* optional SP then STRING */ 804#define ZSTR1 5 /* SP then optional STRING */ 805#define ZSTR2 6 /* optional STRING after SP */ 806#define SITECMD 7 /* SITE command */ 807#define NSTR 8 /* Number followed by a string */ 808 809struct tab cmdtab[] = { /* In order defined in RFC 765 */ 810 { "USER", USER, STR1, 1, "<sp> username" }, 811 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 812 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 813 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 814 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 815 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 816 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 817 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 818 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 819 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 820 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 821 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 822 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 823 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 824 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 825 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 826 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 827 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 828 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 829 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 830 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 831 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 832 { "REST", REST, ARGS, 1, "(restart command)" }, 833 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 834 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 835 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 836 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 837 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 838 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 839 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 840 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 841 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 842 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 843 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 844 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 845 { "NOOP", NOOP, ARGS, 1, "" }, 846 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 847 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 848 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 849 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 850 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 851 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 852 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 853 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 854 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 855 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 856 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 857 { NULL, 0, 0, 0, 0 } 858}; 859 860struct tab sitetab[] = { 861 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 862 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 863 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 864 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 865 { NULL, 0, 0, 0, 0 } 866}; 867 868struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd) 869{ 870 871 for (; p->name != NULL; p++) 872 if (strcmp(cmd, p->name) == 0) 873 return (p); 874 return (0); 875} 876 877#include <arpa/telnet.h> 878 879/* 880 * getline - a hacked up version of fgets to ignore TELNET escape codes. 881 */ 882char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop) 883{ 884 register c; 885 register char *cs; 886 887 cs = s; 888/* tmpline may contain saved command from urgent mode interruption */ 889 for (c = 0; *(tmpline + c) && --n > 0; ++c) { 890 *cs++ = *(tmpline + c); 891 if (*(tmpline + c) == '\n') { 892 *cs++ = '\0'; 893 if (debug) 894 syslog(LOG_DEBUG, "command: %s", s); 895 *tmpline = '\0'; 896 return(s); 897 } 898 if (c == 0) 899 *tmpline = '\0'; 900 } 901 while ((c = getc(iop)) != EOF) { 902 c &= 0377; 903 if (c == IAC) { 904 if ((c = getc(iop)) != EOF) { 905 c &= 0377; 906 switch (c) { 907 case WILL: 908 case WONT: 909 c = getc(iop); 910 printf("%c%c%c", IAC, DONT, 0377&c); 911 (void) fflush(stdout); 912 continue; 913 case DO: 914 case DONT: 915 c = getc(iop); 916 printf("%c%c%c", IAC, WONT, 0377&c); 917 (void) fflush(stdout); 918 continue; 919 case IAC: 920 break; 921 default: 922 continue; /* ignore command */ 923 } 924 } 925 } 926 *cs++ = c; 927 if (--n <= 0 || c == '\n') 928 break; 929 } 930 if (c == EOF && cs == s) 931 return (NULL); 932 *cs++ = '\0'; 933 if (debug) 934 syslog(LOG_DEBUG, "command: %s", s); 935 return (s); 936} 937 938static VOIDRET toolong FUNCTION((input), int input) 939{ 940 time_t now; 941 942 reply(421, "Timeout (%d seconds): closing control connection.", timeout); 943 (void) time(&now); 944 syslog(LOG_INFO, "User %s timed out after %d seconds at %s", 945 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 946 dologout(1); 947} 948 949int yylex FUNCTION_NOARGS 950{ 951 static int cpos, state; 952 register char *cp, *cp2; 953 register struct tab *p; 954 int n; 955 char c, *copy(); 956 957 for (;;) { 958 switch (state) { 959 960 case CMD: 961 (void) signal(SIGALRM, toolong); 962 (void) alarm((unsigned) timeout); 963 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 964 reply(221, "You could at least say goodbye."); 965 dologout(0); 966 } 967 (void) alarm(0);
|