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