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