ftpcmd.y revision 137850
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 45static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 46#endif 47static const char rcsid[] = 48 "$FreeBSD: head/libexec/ftpd/ftpcmd.y 137850 2004-11-18 11:27:31Z yar $"; 49#endif /* not lint */ 50 51#include <sys/param.h> 52#include <sys/socket.h> 53#include <sys/stat.h> 54 55#include <netinet/in.h> 56#include <arpa/ftp.h> 57 58#include <ctype.h> 59#include <errno.h> 60#include <glob.h> 61#include <libutil.h> 62#include <limits.h> 63#include <md5.h> 64#include <netdb.h> 65#include <pwd.h> 66#include <signal.h> 67#include <stdint.h> 68#include <stdio.h> 69#include <stdlib.h> 70#include <string.h> 71#include <syslog.h> 72#include <time.h> 73#include <unistd.h> 74 75#include "extern.h" 76#include "pathnames.h" 77 78extern union sockunion data_dest, his_addr; 79extern int hostinfo; 80extern int logged_in; 81extern struct passwd *pw; 82extern int guest; 83extern char *homedir; 84extern int paranoid; 85extern int logging; 86extern int type; 87extern int form; 88extern int ftpdebug; 89extern int timeout; 90extern int maxtimeout; 91extern int pdata; 92extern char *hostname; 93extern char proctitle[]; 94extern int usedefault; 95extern char tmpline[]; 96extern int readonly; 97extern int noepsv; 98extern int noretr; 99extern int noguestretr; 100extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 101 102off_t restart_point; 103 104static int cmd_type; 105static int cmd_form; 106static int cmd_bytesz; 107static int state; 108char cbuf[512]; 109char *fromname = NULL; 110 111extern int epsvall; 112 113%} 114 115%union { 116 struct { 117 off_t o; 118 int i; 119 } u; 120 char *s; 121} 122 123%token 124 A B C E F I 125 L N P R S T 126 ALL 127 128 SP CRLF COMMA 129 130 USER PASS ACCT REIN QUIT PORT 131 PASV TYPE STRU MODE RETR STOR 132 APPE MLFL MAIL MSND MSOM MSAM 133 MRSQ MRCP ALLO REST RNFR RNTO 134 ABOR DELE CWD LIST NLST SITE 135 STAT HELP NOOP MKD RMD PWD 136 CDUP STOU SMNT SYST SIZE MDTM 137 LPRT LPSV EPRT EPSV 138 139 UMASK IDLE CHMOD MDFIVE 140 141 LEXERR NOTIMPL 142 143%token <s> STRING 144%token <u> NUMBER 145 146%type <u.i> check_login octal_number byte_size 147%type <u.i> check_login_ro check_login_epsv 148%type <u.i> struct_code mode_code type_code form_code 149%type <s> pathstring pathname password username 150%type <s> ALL NOTIMPL 151 152%start cmd_list 153 154%% 155 156cmd_list 157 : /* empty */ 158 | cmd_list cmd 159 { 160 if (fromname) 161 free(fromname); 162 fromname = NULL; 163 restart_point = 0; 164 } 165 | cmd_list rcmd 166 ; 167 168cmd 169 : USER SP username CRLF 170 { 171 user($3); 172 free($3); 173 } 174 | PASS SP password CRLF 175 { 176 pass($3); 177 free($3); 178 } 179 | PASS CRLF 180 { 181 pass(""); 182 } 183 | PORT check_login SP host_port CRLF 184 { 185 if (epsvall) { 186 reply(501, "no PORT allowed after EPSV ALL"); 187 goto port_done; 188 } 189 if (!$2) 190 goto port_done; 191 if (port_check("PORT") == 1) 192 goto port_done; 193#ifdef INET6 194 if ((his_addr.su_family != AF_INET6 || 195 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 196 /* shoud never happen */ 197 usedefault = 1; 198 reply(500, "Invalid address rejected."); 199 goto port_done; 200 } 201 port_check_v6("pcmd"); 202#endif 203 port_done: 204 ; 205 } 206 | LPRT check_login SP host_long_port CRLF 207 { 208 if (epsvall) { 209 reply(501, "no LPRT allowed after EPSV ALL"); 210 goto lprt_done; 211 } 212 if (!$2) 213 goto lprt_done; 214 if (port_check("LPRT") == 1) 215 goto lprt_done; 216#ifdef INET6 217 if (his_addr.su_family != AF_INET6) { 218 usedefault = 1; 219 reply(500, "Invalid address rejected."); 220 goto lprt_done; 221 } 222 if (port_check_v6("LPRT") == 1) 223 goto lprt_done; 224#endif 225 lprt_done: 226 ; 227 } 228 | EPRT check_login SP STRING CRLF 229 { 230 char delim; 231 char *tmp = NULL; 232 char *p, *q; 233 char *result[3]; 234 struct addrinfo hints; 235 struct addrinfo *res; 236 int i; 237 238 if (epsvall) { 239 reply(501, "no EPRT allowed after EPSV ALL"); 240 goto eprt_done; 241 } 242 if (!$2) 243 goto eprt_done; 244 245 memset(&data_dest, 0, sizeof(data_dest)); 246 tmp = strdup($4); 247 if (ftpdebug) 248 syslog(LOG_DEBUG, "%s", tmp); 249 if (!tmp) { 250 fatalerror("not enough core"); 251 /*NOTREACHED*/ 252 } 253 p = tmp; 254 delim = p[0]; 255 p++; 256 memset(result, 0, sizeof(result)); 257 for (i = 0; i < 3; i++) { 258 q = strchr(p, delim); 259 if (!q || *q != delim) { 260 parsefail: 261 reply(500, 262 "Invalid argument, rejected."); 263 if (tmp) 264 free(tmp); 265 usedefault = 1; 266 goto eprt_done; 267 } 268 *q++ = '\0'; 269 result[i] = p; 270 if (ftpdebug) 271 syslog(LOG_DEBUG, "%d: %s", i, p); 272 p = q; 273 } 274 275 /* some more sanity check */ 276 p = result[0]; 277 while (*p) { 278 if (!isdigit(*p)) 279 goto parsefail; 280 p++; 281 } 282 p = result[2]; 283 while (*p) { 284 if (!isdigit(*p)) 285 goto parsefail; 286 p++; 287 } 288 289 /* grab address */ 290 memset(&hints, 0, sizeof(hints)); 291 if (atoi(result[0]) == 1) 292 hints.ai_family = PF_INET; 293#ifdef INET6 294 else if (atoi(result[0]) == 2) 295 hints.ai_family = PF_INET6; 296#endif 297 else 298 hints.ai_family = PF_UNSPEC; /*XXX*/ 299 hints.ai_socktype = SOCK_STREAM; 300 i = getaddrinfo(result[1], result[2], &hints, &res); 301 if (i) 302 goto parsefail; 303 memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 304#ifdef INET6 305 if (his_addr.su_family == AF_INET6 306 && data_dest.su_family == AF_INET6) { 307 /* XXX more sanity checks! */ 308 data_dest.su_sin6.sin6_scope_id = 309 his_addr.su_sin6.sin6_scope_id; 310 } 311#endif 312 free(tmp); 313 tmp = NULL; 314 315 if (port_check("EPRT") == 1) 316 goto eprt_done; 317#ifdef INET6 318 if (his_addr.su_family != AF_INET6) { 319 usedefault = 1; 320 reply(500, "Invalid address rejected."); 321 goto eprt_done; 322 } 323 if (port_check_v6("EPRT") == 1) 324 goto eprt_done; 325#endif 326 eprt_done: 327 free($4); 328 } 329 | PASV check_login CRLF 330 { 331 if (epsvall) 332 reply(501, "no PASV allowed after EPSV ALL"); 333 else if ($2) 334 passive(); 335 } 336 | LPSV check_login CRLF 337 { 338 if (epsvall) 339 reply(501, "no LPSV allowed after EPSV ALL"); 340 else if ($2) 341 long_passive("LPSV", PF_UNSPEC); 342 } 343 | EPSV check_login_epsv SP NUMBER CRLF 344 { 345 if ($2) { 346 int pf; 347 switch ($4.i) { 348 case 1: 349 pf = PF_INET; 350 break; 351#ifdef INET6 352 case 2: 353 pf = PF_INET6; 354 break; 355#endif 356 default: 357 pf = -1; /*junk value*/ 358 break; 359 } 360 long_passive("EPSV", pf); 361 } 362 } 363 | EPSV check_login_epsv SP ALL CRLF 364 { 365 if ($2) { 366 reply(200, 367 "EPSV ALL command successful."); 368 epsvall++; 369 } 370 } 371 | EPSV check_login_epsv CRLF 372 { 373 if ($2) 374 long_passive("EPSV", PF_UNSPEC); 375 } 376 | TYPE check_login SP type_code CRLF 377 { 378 if ($2) { 379 switch (cmd_type) { 380 381 case TYPE_A: 382 if (cmd_form == FORM_N) { 383 reply(200, "Type set to A."); 384 type = cmd_type; 385 form = cmd_form; 386 } else 387 reply(504, "Form must be N."); 388 break; 389 390 case TYPE_E: 391 reply(504, "Type E not implemented."); 392 break; 393 394 case TYPE_I: 395 reply(200, "Type set to I."); 396 type = cmd_type; 397 break; 398 399 case TYPE_L: 400#if CHAR_BIT == 8 401 if (cmd_bytesz == 8) { 402 reply(200, 403 "Type set to L (byte size 8)."); 404 type = cmd_type; 405 } else 406 reply(504, "Byte size must be 8."); 407#else /* CHAR_BIT == 8 */ 408 UNIMPLEMENTED for CHAR_BIT != 8 409#endif /* CHAR_BIT == 8 */ 410 } 411 } 412 } 413 | STRU check_login SP struct_code CRLF 414 { 415 if ($2) { 416 switch ($4) { 417 418 case STRU_F: 419 reply(200, "STRU F ok."); 420 break; 421 422 default: 423 reply(504, "Unimplemented STRU type."); 424 } 425 } 426 } 427 | MODE check_login SP mode_code CRLF 428 { 429 if ($2) { 430 switch ($4) { 431 432 case MODE_S: 433 reply(200, "MODE S ok."); 434 break; 435 436 default: 437 reply(502, "Unimplemented MODE type."); 438 } 439 } 440 } 441 | ALLO check_login SP NUMBER CRLF 442 { 443 if ($2) { 444 reply(202, "ALLO command ignored."); 445 } 446 } 447 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 448 { 449 if ($2) { 450 reply(202, "ALLO command ignored."); 451 } 452 } 453 | RETR check_login SP pathname CRLF 454 { 455 if (noretr || (guest && noguestretr)) 456 reply(500, "RETR command is disabled"); 457 else if ($2 && $4 != NULL) 458 retrieve(NULL, $4); 459 460 if ($4 != NULL) 461 free($4); 462 } 463 | STOR check_login_ro SP pathname CRLF 464 { 465 if ($2 && $4 != NULL) 466 store($4, "w", 0); 467 if ($4 != NULL) 468 free($4); 469 } 470 | APPE check_login_ro SP pathname CRLF 471 { 472 if ($2 && $4 != NULL) 473 store($4, "a", 0); 474 if ($4 != NULL) 475 free($4); 476 } 477 | NLST check_login CRLF 478 { 479 if ($2) 480 send_file_list("."); 481 } 482 | NLST check_login SP pathstring CRLF 483 { 484 if ($2) 485 send_file_list($4); 486 free($4); 487 } 488 | LIST check_login CRLF 489 { 490 if ($2) 491 retrieve(_PATH_LS " -lgA", ""); 492 } 493 | LIST check_login SP pathstring CRLF 494 { 495 if ($2) 496 retrieve(_PATH_LS " -lgA %s", $4); 497 free($4); 498 } 499 | STAT check_login SP pathname CRLF 500 { 501 if ($2 && $4 != NULL) 502 statfilecmd($4); 503 if ($4 != NULL) 504 free($4); 505 } 506 | STAT check_login CRLF 507 { 508 if ($2) { 509 statcmd(); 510 } 511 } 512 | DELE check_login_ro SP pathname CRLF 513 { 514 if ($2 && $4 != NULL) 515 delete($4); 516 if ($4 != NULL) 517 free($4); 518 } 519 | RNTO check_login_ro SP pathname CRLF 520 { 521 if ($2 && $4 != NULL) { 522 if (fromname) { 523 renamecmd(fromname, $4); 524 free(fromname); 525 fromname = NULL; 526 } else { 527 reply(503, "Bad sequence of commands."); 528 } 529 } 530 if ($4 != NULL) 531 free($4); 532 } 533 | ABOR check_login CRLF 534 { 535 if ($2) 536 reply(225, "ABOR command successful."); 537 } 538 | CWD check_login CRLF 539 { 540 if ($2) { 541 cwd(homedir); 542 } 543 } 544 | CWD check_login SP pathname CRLF 545 { 546 if ($2 && $4 != NULL) 547 cwd($4); 548 if ($4 != NULL) 549 free($4); 550 } 551 | HELP CRLF 552 { 553 help(cmdtab, NULL); 554 } 555 | HELP SP STRING CRLF 556 { 557 char *cp = $3; 558 559 if (strncasecmp(cp, "SITE", 4) == 0) { 560 cp = $3 + 4; 561 if (*cp == ' ') 562 cp++; 563 if (*cp) 564 help(sitetab, cp); 565 else 566 help(sitetab, NULL); 567 } else 568 help(cmdtab, $3); 569 free($3); 570 } 571 | NOOP CRLF 572 { 573 reply(200, "NOOP command successful."); 574 } 575 | MKD check_login_ro SP pathname CRLF 576 { 577 if ($2 && $4 != NULL) 578 makedir($4); 579 if ($4 != NULL) 580 free($4); 581 } 582 | RMD check_login_ro SP pathname CRLF 583 { 584 if ($2 && $4 != NULL) 585 removedir($4); 586 if ($4 != NULL) 587 free($4); 588 } 589 | PWD check_login CRLF 590 { 591 if ($2) 592 pwd(); 593 } 594 | CDUP check_login CRLF 595 { 596 if ($2) 597 cwd(".."); 598 } 599 | SITE SP HELP CRLF 600 { 601 help(sitetab, NULL); 602 } 603 | SITE SP HELP SP STRING CRLF 604 { 605 help(sitetab, $5); 606 free($5); 607 } 608 | SITE SP MDFIVE check_login SP pathname CRLF 609 { 610 char p[64], *q; 611 612 if ($4 && $6) { 613 q = MD5File($6, p); 614 if (q != NULL) 615 reply(200, "MD5(%s) = %s", $6, p); 616 else 617 perror_reply(550, $6); 618 } 619 if ($6) 620 free($6); 621 } 622 | SITE SP UMASK check_login CRLF 623 { 624 int oldmask; 625 626 if ($4) { 627 oldmask = umask(0); 628 (void) umask(oldmask); 629 reply(200, "Current UMASK is %03o", oldmask); 630 } 631 } 632 | SITE SP UMASK check_login SP octal_number CRLF 633 { 634 int oldmask; 635 636 if ($4) { 637 if (($6 == -1) || ($6 > 0777)) { 638 reply(501, "Bad UMASK value"); 639 } else { 640 oldmask = umask($6); 641 reply(200, 642 "UMASK set to %03o (was %03o)", 643 $6, oldmask); 644 } 645 } 646 } 647 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 648 { 649 if ($4 && ($8 != NULL)) { 650 if (($6 == -1 ) || ($6 > 0777)) 651 reply(501, "Bad mode value"); 652 else if (chmod($8, $6) < 0) 653 perror_reply(550, $8); 654 else 655 reply(200, "CHMOD command successful."); 656 } 657 if ($8 != NULL) 658 free($8); 659 } 660 | SITE SP check_login IDLE CRLF 661 { 662 if ($3) 663 reply(200, 664 "Current IDLE time limit is %d seconds; max %d", 665 timeout, maxtimeout); 666 } 667 | SITE SP check_login IDLE SP NUMBER CRLF 668 { 669 if ($3) { 670 if ($6.i < 30 || $6.i > maxtimeout) { 671 reply(501, 672 "Maximum IDLE time must be between 30 and %d seconds", 673 maxtimeout); 674 } else { 675 timeout = $6.i; 676 (void) alarm(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 ; 766rcmd 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 796username 797 : STRING 798 ; 799 800password 801 : /* empty */ 802 { 803 $$ = (char *)calloc(1, sizeof(char)); 804 } 805 | STRING 806 ; 807 808byte_size 809 : NUMBER 810 { 811 $$ = $1.i; 812 } 813 ; 814 815host_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 830host_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 876form_code 877 : N 878 { 879 $$ = FORM_N; 880 } 881 | T 882 { 883 $$ = FORM_T; 884 } 885 | C 886 { 887 $$ = FORM_C; 888 } 889 ; 890 891type_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 934struct_code 935 : F 936 { 937 $$ = STRU_F; 938 } 939 | R 940 { 941 $$ = STRU_R; 942 } 943 | P 944 { 945 $$ = STRU_P; 946 } 947 ; 948 949mode_code 950 : S 951 { 952 $$ = MODE_S; 953 } 954 | B 955 { 956 $$ = MODE_B; 957 } 958 | C 959 { 960 $$ = MODE_C; 961 } 962 ; 963 964pathname 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 991pathstring 992 : STRING 993 ; 994 995octal_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 1022check_login 1023 : /* empty */ 1024 { 1025 $$ = check_login1(); 1026 } 1027 ; 1028 1029check_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 1041check_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 1069struct tab { 1070 char *name; 1071 short token; 1072 short state; 1073 short implemented; /* 1 if command is implemented */ 1074 char *help; 1075}; 1076 1077struct 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 1132struct 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 1141static char *copy(char *); 1142static char *expglob(char *); 1143static char *exptilde(char *); 1144static void help(struct tab *, char *); 1145static struct tab * 1146 lookup(struct tab *, char *); 1147static int port_check(const char *); 1148static int port_check_v6(const char *); 1149static void sizecmd(char *); 1150static void toolong(int); 1151static void v4map_data_dest(void); 1152static int yylex(void); 1153 1154static struct tab * 1155lookup(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 */ 1169char * 1170getline(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 } 1225got_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 1251static void 1252toolong(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 1263static int 1264yylex(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 1481void 1482upper(char *s) 1483{ 1484 while (*s != '\0') { 1485 if (islower(*s)) 1486 *s = toupper(*s); 1487 s++; 1488 } 1489} 1490 1491static char * 1492copy(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 1503static void 1504help(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 1569static void 1570sizecmd(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. */ 1624static int 1625port_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 1653static int 1654check_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 */ 1668static char * 1669exptilde(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 */ 1713static char * 1714expglob(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, "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. */ 1748static int 1749port_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 1780static void 1781v4map_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