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