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