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