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