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