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