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