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 static int port_check_v6(const char *); 1149 static void sizecmd(char *); 1150 static void toolong(int); 1151 static void v4map_data_dest(void); 1152 static int yylex(void); 1153 1154 static struct tab * 1155 lookup(struct tab *p, char *cmd) 1156 { 1157 1158 for (; p->name != NULL; p++) 1159 if (strcmp(cmd, p->name) == 0) 1160 return (p); 1161 return (0); 1162 } 1163 1164 #include <arpa/telnet.h> 1165 1166 /* 1167 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1168 */ 1169 char * 1170 getline(char *s, int n, FILE *iop) 1171 { 1172 int c; 1173 register char *cs; 1174 sigset_t sset, osset; 1175 1176 cs = s; 1177 /* tmpline may contain saved command from urgent mode interruption */ 1178 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1179 *cs++ = tmpline[c]; 1180 if (tmpline[c] == '\n') { 1181 *cs++ = '\0'; 1182 if (ftpdebug) 1183 syslog(LOG_DEBUG, "command: %s", s); 1184 tmpline[0] = '\0'; 1185 return(s); 1186 } 1187 if (c == 0) 1188 tmpline[0] = '\0'; 1189 } 1190 /* SIGURG would interrupt stdio if not blocked during the read loop */ 1191 sigemptyset(&sset); 1192 sigaddset(&sset, SIGURG); 1193 sigprocmask(SIG_BLOCK, &sset, &osset); 1194 while ((c = getc(iop)) != EOF) { 1195 c &= 0377; 1196 if (c == IAC) { 1197 if ((c = getc(iop)) == EOF) 1198 goto got_eof; 1199 c &= 0377; 1200 switch (c) { 1201 case WILL: 1202 case WONT: 1203 if ((c = getc(iop)) == EOF) 1204 goto got_eof; 1205 printf("%c%c%c", IAC, DONT, 0377&c); 1206 (void) fflush(stdout); 1207 continue; 1208 case DO: 1209 case DONT: 1210 if ((c = getc(iop)) == EOF) 1211 goto got_eof; 1212 printf("%c%c%c", IAC, WONT, 0377&c); 1213 (void) fflush(stdout); 1214 continue; 1215 case IAC: 1216 break; 1217 default: 1218 continue; /* ignore command */ 1219 } 1220 } 1221 *cs++ = c; 1222 if (--n <= 0 || c == '\n') 1223 break; 1224 } 1225 got_eof: 1226 sigprocmask(SIG_SETMASK, &osset, NULL); 1227 if (c == EOF && cs == s) 1228 return (NULL); 1229 *cs++ = '\0'; 1230 if (ftpdebug) { 1231 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1232 /* Don't syslog passwords */ 1233 syslog(LOG_DEBUG, "command: %.5s ???", s); 1234 } else { 1235 register char *cp; 1236 register int len; 1237 1238 /* Don't syslog trailing CR-LF */ 1239 len = strlen(s); 1240 cp = s + len - 1; 1241 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1242 --cp; 1243 --len; 1244 } 1245 syslog(LOG_DEBUG, "command: %.*s", len, s); 1246 } 1247 } 1248 return (s); 1249 } 1250 1251 static void 1252 toolong(int signo) 1253 { 1254 1255 reply(421, 1256 "Timeout (%d seconds): closing control connection.", timeout); 1257 if (logging) 1258 syslog(LOG_INFO, "User %s timed out after %d seconds", 1259 (pw ? pw -> pw_name : "unknown"), timeout); 1260 dologout(1); 1261 } 1262 1263 static int 1264 yylex(void) 1265 { 1266 static int cpos; 1267 char *cp, *cp2; 1268 struct tab *p; 1269 int n; 1270 char c; 1271 1272 for (;;) { 1273 switch (state) { 1274 1275 case CMD: 1276 (void) signal(SIGALRM, toolong); 1277 (void) alarm(timeout); 1278 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1279 reply(221, "You could at least say goodbye."); 1280 dologout(0); 1281 } 1282 (void) alarm(0); 1283 #ifdef SETPROCTITLE 1284 if (strncasecmp(cbuf, "PASS", 4) != 0) 1285 setproctitle("%s: %s", proctitle, cbuf); 1286 #endif /* SETPROCTITLE */ 1287 if ((cp = strchr(cbuf, '\r'))) { 1288 *cp++ = '\n'; 1289 *cp = '\0'; 1290 } 1291 if ((cp = strpbrk(cbuf, " \n"))) 1292 cpos = cp - cbuf; 1293 if (cpos == 0) 1294 cpos = 4; 1295 c = cbuf[cpos]; 1296 cbuf[cpos] = '\0'; 1297 upper(cbuf); 1298 p = lookup(cmdtab, cbuf); 1299 cbuf[cpos] = c; 1300 if (p != 0) { 1301 yylval.s = p->name; 1302 if (!p->implemented) 1303 return (NOTIMPL); /* state remains CMD */ 1304 state = p->state; 1305 return (p->token); 1306 } 1307 break; 1308 1309 case SITECMD: 1310 if (cbuf[cpos] == ' ') { 1311 cpos++; 1312 return (SP); 1313 } 1314 cp = &cbuf[cpos]; 1315 if ((cp2 = strpbrk(cp, " \n"))) 1316 cpos = cp2 - cbuf; 1317 c = cbuf[cpos]; 1318 cbuf[cpos] = '\0'; 1319 upper(cp); 1320 p = lookup(sitetab, cp); 1321 cbuf[cpos] = c; 1322 if (guest == 0 && p != 0) { 1323 yylval.s = p->name; 1324 if (!p->implemented) { 1325 state = CMD; 1326 return (NOTIMPL); 1327 } 1328 state = p->state; 1329 return (p->token); 1330 } 1331 state = CMD; 1332 break; 1333 1334 case ZSTR1: 1335 case OSTR: 1336 if (cbuf[cpos] == '\n') { 1337 state = CMD; 1338 return (CRLF); 1339 } 1340 /* FALLTHROUGH */ 1341 1342 case STR1: 1343 dostr1: 1344 if (cbuf[cpos] == ' ') { 1345 cpos++; 1346 state = state == OSTR ? STR2 : state+1; 1347 return (SP); 1348 } 1349 break; 1350 1351 case ZSTR2: 1352 if (cbuf[cpos] == '\n') { 1353 state = CMD; 1354 return (CRLF); 1355 } 1356 /* FALLTHROUGH */ 1357 1358 case STR2: 1359 cp = &cbuf[cpos]; 1360 n = strlen(cp); 1361 cpos += n - 1; 1362 /* 1363 * Make sure the string is nonempty and \n terminated. 1364 */ 1365 if (n > 1 && cbuf[cpos] == '\n') { 1366 cbuf[cpos] = '\0'; 1367 yylval.s = copy(cp); 1368 cbuf[cpos] = '\n'; 1369 state = ARGS; 1370 return (STRING); 1371 } 1372 break; 1373 1374 case NSTR: 1375 if (cbuf[cpos] == ' ') { 1376 cpos++; 1377 return (SP); 1378 } 1379 if (isdigit(cbuf[cpos])) { 1380 cp = &cbuf[cpos]; 1381 while (isdigit(cbuf[++cpos])) 1382 ; 1383 c = cbuf[cpos]; 1384 cbuf[cpos] = '\0'; 1385 yylval.u.i = atoi(cp); 1386 cbuf[cpos] = c; 1387 state = STR1; 1388 return (NUMBER); 1389 } 1390 state = STR1; 1391 goto dostr1; 1392 1393 case ARGS: 1394 if (isdigit(cbuf[cpos])) { 1395 cp = &cbuf[cpos]; 1396 while (isdigit(cbuf[++cpos])) 1397 ; 1398 c = cbuf[cpos]; 1399 cbuf[cpos] = '\0'; 1400 yylval.u.i = atoi(cp); 1401 yylval.u.o = strtoull(cp, NULL, 10); 1402 cbuf[cpos] = c; 1403 return (NUMBER); 1404 } 1405 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1406 && !isalnum(cbuf[cpos + 3])) { 1407 cpos += 3; 1408 return ALL; 1409 } 1410 switch (cbuf[cpos++]) { 1411 1412 case '\n': 1413 state = CMD; 1414 return (CRLF); 1415 1416 case ' ': 1417 return (SP); 1418 1419 case ',': 1420 return (COMMA); 1421 1422 case 'A': 1423 case 'a': 1424 return (A); 1425 1426 case 'B': 1427 case 'b': 1428 return (B); 1429 1430 case 'C': 1431 case 'c': 1432 return (C); 1433 1434 case 'E': 1435 case 'e': 1436 return (E); 1437 1438 case 'F': 1439 case 'f': 1440 return (F); 1441 1442 case 'I': 1443 case 'i': 1444 return (I); 1445 1446 case 'L': 1447 case 'l': 1448 return (L); 1449 1450 case 'N': 1451 case 'n': 1452 return (N); 1453 1454 case 'P': 1455 case 'p': 1456 return (P); 1457 1458 case 'R': 1459 case 'r': 1460 return (R); 1461 1462 case 'S': 1463 case 's': 1464 return (S); 1465 1466 case 'T': 1467 case 't': 1468 return (T); 1469 1470 } 1471 break; 1472 1473 default: 1474 fatalerror("Unknown state in scanner."); 1475 } 1476 state = CMD; 1477 return (LEXERR); 1478 } 1479 } 1480 1481 void 1482 upper(char *s) 1483 { 1484 while (*s != '\0') { 1485 if (islower(*s)) 1486 *s = toupper(*s); 1487 s++; 1488 } 1489 } 1490 1491 static char * 1492 copy(char *s) 1493 { 1494 char *p; 1495 1496 p = malloc(strlen(s) + 1); 1497 if (p == NULL) 1498 fatalerror("Ran out of memory."); 1499 (void) strcpy(p, s); 1500 return (p); 1501 } 1502 1503 static void 1504 help(struct tab *ctab, char *s) 1505 { 1506 struct tab *c; 1507 int width, NCMDS; 1508 char *type; 1509 1510 if (ctab == sitetab) 1511 type = "SITE "; 1512 else 1513 type = ""; 1514 width = 0, NCMDS = 0; 1515 for (c = ctab; c->name != NULL; c++) { 1516 int len = strlen(c->name); 1517 1518 if (len > width) 1519 width = len; 1520 NCMDS++; 1521 } 1522 width = (width + 8) &~ 7; 1523 if (s == 0) { 1524 int i, j, w; 1525 int columns, lines; 1526 1527 lreply(214, "The following %scommands are recognized %s.", 1528 type, "(* =>'s unimplemented)"); 1529 columns = 76 / width; 1530 if (columns == 0) 1531 columns = 1; 1532 lines = (NCMDS + columns - 1) / columns; 1533 for (i = 0; i < lines; i++) { 1534 printf(" "); 1535 for (j = 0; j < columns; j++) { 1536 c = ctab + j * lines + i; 1537 printf("%s%c", c->name, 1538 c->implemented ? ' ' : '*'); 1539 if (c + lines >= &ctab[NCMDS]) 1540 break; 1541 w = strlen(c->name) + 1; 1542 while (w < width) { 1543 putchar(' '); 1544 w++; 1545 } 1546 } 1547 printf("\r\n"); 1548 } 1549 (void) fflush(stdout); 1550 if (hostinfo) 1551 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1552 else 1553 reply(214, "End."); 1554 return; 1555 } 1556 upper(s); 1557 c = lookup(ctab, s); 1558 if (c == NULL) { 1559 reply(502, "Unknown command %s.", s); 1560 return; 1561 } 1562 if (c->implemented) 1563 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1564 else 1565 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1566 c->name, c->help); 1567 } 1568 1569 static void 1570 sizecmd(char *filename) 1571 { 1572 switch (type) { 1573 case TYPE_L: 1574 case TYPE_I: { 1575 struct stat stbuf; 1576 if (stat(filename, &stbuf) < 0) 1577 perror_reply(550, filename); 1578 else if (!S_ISREG(stbuf.st_mode)) 1579 reply(550, "%s: not a plain file.", filename); 1580 else 1581 reply(213, "%jd", (intmax_t)stbuf.st_size); 1582 break; } 1583 case TYPE_A: { 1584 FILE *fin; 1585 int c; 1586 off_t count; 1587 struct stat stbuf; 1588 fin = fopen(filename, "r"); 1589 if (fin == NULL) { 1590 perror_reply(550, filename); 1591 return; 1592 } 1593 if (fstat(fileno(fin), &stbuf) < 0) { 1594 perror_reply(550, filename); 1595 (void) fclose(fin); 1596 return; 1597 } else if (!S_ISREG(stbuf.st_mode)) { 1598 reply(550, "%s: not a plain file.", filename); 1599 (void) fclose(fin); 1600 return; 1601 } else if (stbuf.st_size > MAXASIZE) { 1602 reply(550, "%s: too large for type A SIZE.", filename); 1603 (void) fclose(fin); 1604 return; 1605 } 1606 1607 count = 0; 1608 while((c=getc(fin)) != EOF) { 1609 if (c == '\n') /* will get expanded to \r\n */ 1610 count++; 1611 count++; 1612 } 1613 (void) fclose(fin); 1614 1615 reply(213, "%jd", (intmax_t)count); 1616 break; } 1617 default: 1618 reply(504, "SIZE not implemented for type %s.", 1619 typenames[type]); 1620 } 1621 } 1622 1623 /* Return 1, if port check is done. Return 0, if not yet. */ 1624 static int 1625 port_check(const char *pcmd) 1626 { 1627 if (his_addr.su_family == AF_INET) { 1628 if (data_dest.su_family != AF_INET) { 1629 usedefault = 1; 1630 reply(500, "Invalid address rejected."); 1631 return 1; 1632 } 1633 if (paranoid && 1634 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1635 memcmp(&data_dest.su_sin.sin_addr, 1636 &his_addr.su_sin.sin_addr, 1637 sizeof(data_dest.su_sin.sin_addr)))) { 1638 usedefault = 1; 1639 reply(500, "Illegal PORT range rejected."); 1640 } else { 1641 usedefault = 0; 1642 if (pdata >= 0) { 1643 (void) close(pdata); 1644 pdata = -1; 1645 } 1646 reply(200, "%s command successful.", pcmd); 1647 } 1648 return 1; 1649 } 1650 return 0; 1651 } 1652 1653 static int 1654 check_login1(void) 1655 { 1656 if (logged_in) 1657 return 1; 1658 else { 1659 reply(530, "Please login with USER and PASS."); 1660 return 0; 1661 } 1662 } 1663 1664 /* 1665 * Replace leading "~user" in a pathname by the user's login directory. 1666 * Returned string will be in a freshly malloced buffer unless it's NULL. 1667 */ 1668 static char * 1669 exptilde(char *s) 1670 { 1671 char *p, *q; 1672 char *path, *user; 1673 struct passwd *ppw; 1674 1675 if ((p = strdup(s)) == NULL) 1676 return (NULL); 1677 if (*p != '~') 1678 return (p); 1679 1680 user = p + 1; /* skip tilde */ 1681 if ((path = strchr(p, '/')) != NULL) 1682 *(path++) = '\0'; /* separate ~user from the rest of path */ 1683 if (*user == '\0') /* no user specified, use the current user */ 1684 user = pw->pw_name; 1685 /* read passwd even for the current user since we may be chrooted */ 1686 if ((ppw = getpwnam(user)) != NULL) { 1687 /* user found, substitute login directory for ~user */ 1688 if (path) 1689 asprintf(&q, "%s/%s", ppw->pw_dir, path); 1690 else 1691 q = strdup(ppw->pw_dir); 1692 free(p); 1693 p = q; 1694 } else { 1695 /* user not found, undo the damage */ 1696 if (path) 1697 path[-1] = '/'; 1698 } 1699 return (p); 1700 } 1701 1702 /* 1703 * Expand glob(3) patterns possibly present in a pathname. 1704 * Avoid expanding to a pathname including '\r' or '\n' in order to 1705 * not disrupt the FTP protocol. 1706 * The expansion found must be unique. 1707 * Return the result as a malloced string, or NULL if an error occured. 1708 * 1709 * Problem: this production is used for all pathname 1710 * processing, but only gives a 550 error reply. 1711 * This is a valid reply in some cases but not in others. 1712 */ 1713 static char * 1714 expglob(char *s) 1715 { 1716 char *p, **pp, *rval; 1717 int flags = GLOB_BRACE | GLOB_NOCHECK; 1718 int n; 1719 glob_t gl; 1720 1721 memset(&gl, 0, sizeof(gl)); 1722 flags |= GLOB_LIMIT; 1723 gl.gl_matchc = MAXGLOBARGS; 1724 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1725 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1726 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1727 p = *pp; 1728 n++; 1729 } 1730 if (n == 0) 1731 rval = strdup(s); 1732 else if (n == 1) 1733 rval = strdup(p); 1734 else { 1735 reply(550, "Wildcard is ambiguous."); 1736 rval = NULL; 1737 } 1738 } else { 1739 reply(550, "Wildcard expansion error."); 1740 rval = NULL; 1741 } 1742 globfree(&gl); 1743 return (rval); 1744 } 1745 1746 #ifdef INET6 1747 /* Return 1, if port check is done. Return 0, if not yet. */ 1748 static int 1749 port_check_v6(const char *pcmd) 1750 { 1751 if (his_addr.su_family == AF_INET6) { 1752 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 1753 /* Convert data_dest into v4 mapped sockaddr.*/ 1754 v4map_data_dest(); 1755 if (data_dest.su_family != AF_INET6) { 1756 usedefault = 1; 1757 reply(500, "Invalid address rejected."); 1758 return 1; 1759 } 1760 if (paranoid && 1761 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1762 memcmp(&data_dest.su_sin6.sin6_addr, 1763 &his_addr.su_sin6.sin6_addr, 1764 sizeof(data_dest.su_sin6.sin6_addr)))) { 1765 usedefault = 1; 1766 reply(500, "Illegal PORT range rejected."); 1767 } else { 1768 usedefault = 0; 1769 if (pdata >= 0) { 1770 (void) close(pdata); 1771 pdata = -1; 1772 } 1773 reply(200, "%s command successful.", pcmd); 1774 } 1775 return 1; 1776 } 1777 return 0; 1778 } 1779 1780 static void 1781 v4map_data_dest(void) 1782 { 1783 struct in_addr savedaddr; 1784 int savedport; 1785 1786 if (data_dest.su_family != AF_INET) { 1787 usedefault = 1; 1788 reply(500, "Invalid address rejected."); 1789 return; 1790 } 1791 1792 savedaddr = data_dest.su_sin.sin_addr; 1793 savedport = data_dest.su_port; 1794 1795 memset(&data_dest, 0, sizeof(data_dest)); 1796 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 1797 data_dest.su_sin6.sin6_family = AF_INET6; 1798 data_dest.su_sin6.sin6_port = savedport; 1799 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 1800 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 1801 (caddr_t)&savedaddr, sizeof(savedaddr)); 1802 } 1803 #endif 1804