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