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