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 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1198 */ 1199 char * 1200 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(s); 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 || c == '\n') 1253 break; 1254 } 1255 got_eof: 1256 sigprocmask(SIG_SETMASK, &osset, NULL); 1257 if (c == EOF && cs == s) 1258 return (NULL); 1259 *cs++ = '\0'; 1260 if (ftpdebug) { 1261 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1262 /* Don't syslog passwords */ 1263 syslog(LOG_DEBUG, "command: %.5s ???", s); 1264 } else { 1265 register char *cp; 1266 register int len; 1267 1268 /* Don't syslog trailing CR-LF */ 1269 len = strlen(s); 1270 cp = s + len - 1; 1271 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1272 --cp; 1273 --len; 1274 } 1275 syslog(LOG_DEBUG, "command: %.*s", len, s); 1276 } 1277 } 1278 return (s); 1279 } 1280 1281 static void 1282 toolong(int signo) 1283 { 1284 1285 reply(421, 1286 "Timeout (%d seconds): closing control connection.", timeout); 1287 if (logging) 1288 syslog(LOG_INFO, "User %s timed out after %d seconds", 1289 (pw ? pw -> pw_name : "unknown"), timeout); 1290 dologout(1); 1291 } 1292 1293 static int 1294 yylex(void) 1295 { 1296 static int cpos; 1297 char *cp, *cp2; 1298 struct tab *p; 1299 int n; 1300 char c; 1301 1302 for (;;) { 1303 switch (state) { 1304 1305 case CMD: 1306 (void) signal(SIGALRM, toolong); 1307 (void) alarm(timeout); 1308 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1309 reply(221, "You could at least say goodbye."); 1310 dologout(0); 1311 } 1312 (void) alarm(0); 1313 #ifdef SETPROCTITLE 1314 if (strncasecmp(cbuf, "PASS", 4) != 0) 1315 setproctitle("%s: %s", proctitle, cbuf); 1316 #endif /* SETPROCTITLE */ 1317 if ((cp = strchr(cbuf, '\r'))) { 1318 *cp++ = '\n'; 1319 *cp = '\0'; 1320 } 1321 if ((cp = strpbrk(cbuf, " \n"))) 1322 cpos = cp - cbuf; 1323 if (cpos == 0) 1324 cpos = 4; 1325 c = cbuf[cpos]; 1326 cbuf[cpos] = '\0'; 1327 upper(cbuf); 1328 p = lookup(cmdtab, cbuf); 1329 cbuf[cpos] = c; 1330 if (p != 0) { 1331 yylval.s = p->name; 1332 if (!p->implemented) 1333 return (NOTIMPL); /* state remains CMD */ 1334 state = p->state; 1335 return (p->token); 1336 } 1337 break; 1338 1339 case SITECMD: 1340 if (cbuf[cpos] == ' ') { 1341 cpos++; 1342 return (SP); 1343 } 1344 cp = &cbuf[cpos]; 1345 if ((cp2 = strpbrk(cp, " \n"))) 1346 cpos = cp2 - cbuf; 1347 c = cbuf[cpos]; 1348 cbuf[cpos] = '\0'; 1349 upper(cp); 1350 p = lookup(sitetab, cp); 1351 cbuf[cpos] = c; 1352 if (guest == 0 && p != 0) { 1353 yylval.s = p->name; 1354 if (!p->implemented) { 1355 state = CMD; 1356 return (NOTIMPL); 1357 } 1358 state = p->state; 1359 return (p->token); 1360 } 1361 state = CMD; 1362 break; 1363 1364 case ZSTR1: 1365 case OSTR: 1366 if (cbuf[cpos] == '\n') { 1367 state = CMD; 1368 return (CRLF); 1369 } 1370 /* FALLTHROUGH */ 1371 1372 case STR1: 1373 dostr1: 1374 if (cbuf[cpos] == ' ') { 1375 cpos++; 1376 state = state == OSTR ? STR2 : state+1; 1377 return (SP); 1378 } 1379 break; 1380 1381 case ZSTR2: 1382 if (cbuf[cpos] == '\n') { 1383 state = CMD; 1384 return (CRLF); 1385 } 1386 /* FALLTHROUGH */ 1387 1388 case STR2: 1389 cp = &cbuf[cpos]; 1390 n = strlen(cp); 1391 cpos += n - 1; 1392 /* 1393 * Make sure the string is nonempty and \n terminated. 1394 */ 1395 if (n > 1 && cbuf[cpos] == '\n') { 1396 cbuf[cpos] = '\0'; 1397 yylval.s = copy(cp); 1398 cbuf[cpos] = '\n'; 1399 state = ARGS; 1400 return (STRING); 1401 } 1402 break; 1403 1404 case NSTR: 1405 if (cbuf[cpos] == ' ') { 1406 cpos++; 1407 return (SP); 1408 } 1409 if (isdigit(cbuf[cpos])) { 1410 cp = &cbuf[cpos]; 1411 while (isdigit(cbuf[++cpos])) 1412 ; 1413 c = cbuf[cpos]; 1414 cbuf[cpos] = '\0'; 1415 yylval.u.i = atoi(cp); 1416 cbuf[cpos] = c; 1417 state = STR1; 1418 return (NUMBER); 1419 } 1420 state = STR1; 1421 goto dostr1; 1422 1423 case ARGS: 1424 if (isdigit(cbuf[cpos])) { 1425 cp = &cbuf[cpos]; 1426 while (isdigit(cbuf[++cpos])) 1427 ; 1428 c = cbuf[cpos]; 1429 cbuf[cpos] = '\0'; 1430 yylval.u.i = atoi(cp); 1431 yylval.u.o = strtoull(cp, NULL, 10); 1432 cbuf[cpos] = c; 1433 return (NUMBER); 1434 } 1435 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1436 && !isalnum(cbuf[cpos + 3])) { 1437 cpos += 3; 1438 return ALL; 1439 } 1440 switch (cbuf[cpos++]) { 1441 1442 case '\n': 1443 state = CMD; 1444 return (CRLF); 1445 1446 case ' ': 1447 return (SP); 1448 1449 case ',': 1450 return (COMMA); 1451 1452 case 'A': 1453 case 'a': 1454 return (A); 1455 1456 case 'B': 1457 case 'b': 1458 return (B); 1459 1460 case 'C': 1461 case 'c': 1462 return (C); 1463 1464 case 'E': 1465 case 'e': 1466 return (E); 1467 1468 case 'F': 1469 case 'f': 1470 return (F); 1471 1472 case 'I': 1473 case 'i': 1474 return (I); 1475 1476 case 'L': 1477 case 'l': 1478 return (L); 1479 1480 case 'N': 1481 case 'n': 1482 return (N); 1483 1484 case 'P': 1485 case 'p': 1486 return (P); 1487 1488 case 'R': 1489 case 'r': 1490 return (R); 1491 1492 case 'S': 1493 case 's': 1494 return (S); 1495 1496 case 'T': 1497 case 't': 1498 return (T); 1499 1500 } 1501 break; 1502 1503 default: 1504 fatalerror("Unknown state in scanner."); 1505 } 1506 state = CMD; 1507 return (LEXERR); 1508 } 1509 } 1510 1511 void 1512 upper(char *s) 1513 { 1514 while (*s != '\0') { 1515 if (islower(*s)) 1516 *s = toupper(*s); 1517 s++; 1518 } 1519 } 1520 1521 static char * 1522 copy(char *s) 1523 { 1524 char *p; 1525 1526 p = malloc(strlen(s) + 1); 1527 if (p == NULL) 1528 fatalerror("Ran out of memory."); 1529 (void) strcpy(p, s); 1530 return (p); 1531 } 1532 1533 static void 1534 help(struct tab *ctab, char *s) 1535 { 1536 struct tab *c; 1537 int width, NCMDS; 1538 char *type; 1539 1540 if (ctab == sitetab) 1541 type = "SITE "; 1542 else 1543 type = ""; 1544 width = 0, NCMDS = 0; 1545 for (c = ctab; c->name != NULL; c++) { 1546 int len = strlen(c->name); 1547 1548 if (len > width) 1549 width = len; 1550 NCMDS++; 1551 } 1552 width = (width + 8) &~ 7; 1553 if (s == 0) { 1554 int i, j, w; 1555 int columns, lines; 1556 1557 lreply(214, "The following %scommands are recognized %s.", 1558 type, "(* =>'s unimplemented)"); 1559 columns = 76 / width; 1560 if (columns == 0) 1561 columns = 1; 1562 lines = (NCMDS + columns - 1) / columns; 1563 for (i = 0; i < lines; i++) { 1564 printf(" "); 1565 for (j = 0; j < columns; j++) { 1566 c = ctab + j * lines + i; 1567 printf("%s%c", c->name, 1568 c->implemented ? ' ' : '*'); 1569 if (c + lines >= &ctab[NCMDS]) 1570 break; 1571 w = strlen(c->name) + 1; 1572 while (w < width) { 1573 putchar(' '); 1574 w++; 1575 } 1576 } 1577 printf("\r\n"); 1578 } 1579 (void) fflush(stdout); 1580 if (hostinfo) 1581 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1582 else 1583 reply(214, "End."); 1584 return; 1585 } 1586 upper(s); 1587 c = lookup(ctab, s); 1588 if (c == NULL) { 1589 reply(502, "Unknown command %s.", s); 1590 return; 1591 } 1592 if (c->implemented) 1593 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1594 else 1595 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1596 c->name, c->help); 1597 } 1598 1599 static void 1600 sizecmd(char *filename) 1601 { 1602 switch (type) { 1603 case TYPE_L: 1604 case TYPE_I: { 1605 struct stat stbuf; 1606 if (stat(filename, &stbuf) < 0) 1607 perror_reply(550, filename); 1608 else if (!S_ISREG(stbuf.st_mode)) 1609 reply(550, "%s: not a plain file.", filename); 1610 else 1611 reply(213, "%lld", (intmax_t)stbuf.st_size); 1612 break; } 1613 case TYPE_A: { 1614 FILE *fin; 1615 int c; 1616 off_t count; 1617 struct stat stbuf; 1618 fin = fopen(filename, "r"); 1619 if (fin == NULL) { 1620 perror_reply(550, filename); 1621 return; 1622 } 1623 if (fstat(fileno(fin), &stbuf) < 0) { 1624 perror_reply(550, filename); 1625 (void) fclose(fin); 1626 return; 1627 } else if (!S_ISREG(stbuf.st_mode)) { 1628 reply(550, "%s: not a plain file.", filename); 1629 (void) fclose(fin); 1630 return; 1631 } else if (stbuf.st_size > MAXASIZE) { 1632 reply(550, "%s: too large for type A SIZE.", filename); 1633 (void) fclose(fin); 1634 return; 1635 } 1636 1637 count = 0; 1638 while((c=getc(fin)) != EOF) { 1639 if (c == '\n') /* will get expanded to \r\n */ 1640 count++; 1641 count++; 1642 } 1643 (void) fclose(fin); 1644 1645 reply(213, "%lld", (intmax_t)count); 1646 break; } 1647 default: 1648 reply(504, "SIZE not implemented for type %s.", 1649 typenames[type]); 1650 } 1651 } 1652 1653 /* Return 1, if port check is done. Return 0, if not yet. */ 1654 static int 1655 port_check(const char *pcmd) 1656 { 1657 if (his_addr.su_family == AF_INET) { 1658 if (data_dest.su_family != AF_INET) { 1659 usedefault = 1; 1660 reply(500, "Invalid address rejected."); 1661 return 1; 1662 } 1663 if (paranoid && 1664 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1665 memcmp(&data_dest.su_sin.sin_addr, 1666 &his_addr.su_sin.sin_addr, 1667 sizeof(data_dest.su_sin.sin_addr)))) { 1668 usedefault = 1; 1669 reply(500, "Illegal PORT range rejected."); 1670 } else { 1671 usedefault = 0; 1672 if (pdata >= 0) { 1673 (void) close(pdata); 1674 pdata = -1; 1675 } 1676 reply(200, "%s command successful.", pcmd); 1677 } 1678 return 1; 1679 } 1680 return 0; 1681 } 1682 1683 static int 1684 check_login1(void) 1685 { 1686 if (logged_in) 1687 return 1; 1688 else { 1689 reply(530, "Please login with USER and PASS."); 1690 return 0; 1691 } 1692 } 1693 1694 /* 1695 * Replace leading "~user" in a pathname by the user's login directory. 1696 * Returned string will be in a freshly malloced buffer unless it's NULL. 1697 */ 1698 static char * 1699 exptilde(char *s) 1700 { 1701 char *p, *q; 1702 char *path, *user; 1703 struct passwd *ppw; 1704 1705 if ((p = strdup(s)) == NULL) 1706 return (NULL); 1707 if (*p != '~') 1708 return (p); 1709 1710 user = p + 1; /* skip tilde */ 1711 if ((path = strchr(p, '/')) != NULL) 1712 *(path++) = '\0'; /* separate ~user from the rest of path */ 1713 if (*user == '\0') /* no user specified, use the current user */ 1714 user = pw->pw_name; 1715 /* read passwd even for the current user since we may be chrooted */ 1716 if ((ppw = getpwnam(user)) != NULL) { 1717 /* user found, substitute login directory for ~user */ 1718 if (path) 1719 asprintf(&q, "%s/%s", ppw->pw_dir, path); 1720 else 1721 q = strdup(ppw->pw_dir); 1722 free(p); 1723 p = q; 1724 } else { 1725 /* user not found, undo the damage */ 1726 if (path) 1727 path[-1] = '/'; 1728 } 1729 return (p); 1730 } 1731 1732 /* 1733 * Expand glob(3) patterns possibly present in a pathname. 1734 * Avoid expanding to a pathname including '\r' or '\n' in order to 1735 * not disrupt the FTP protocol. 1736 * The expansion found must be unique. 1737 * Return the result as a malloced string, or NULL if an error occured. 1738 * 1739 * Problem: this production is used for all pathname 1740 * processing, but only gives a 550 error reply. 1741 * This is a valid reply in some cases but not in others. 1742 */ 1743 static char * 1744 expglob(char *s) 1745 { 1746 char *p, **pp, *rval; 1747 int flags = GLOB_BRACE | GLOB_NOCHECK; 1748 int n; 1749 glob_t gl; 1750 1751 memset(&gl, 0, sizeof(gl)); 1752 flags |= GLOB_LIMIT; 1753 gl.gl_matchc = MAXGLOBARGS; 1754 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1755 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1756 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1757 p = *pp; 1758 n++; 1759 } 1760 if (n == 0) 1761 rval = strdup(s); 1762 else if (n == 1) 1763 rval = strdup(p); 1764 else { 1765 reply(550, "Wildcard is ambiguous."); 1766 rval = NULL; 1767 } 1768 } else { 1769 reply(550, "Wildcard expansion error."); 1770 rval = NULL; 1771 } 1772 globfree(&gl); 1773 return (rval); 1774 } 1775 1776 #ifdef INET6 1777 /* Return 1, if port check is done. Return 0, if not yet. */ 1778 static int 1779 port_check_v6(const char *pcmd) 1780 { 1781 if (his_addr.su_family == AF_INET6) { 1782 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 1783 /* Convert data_dest into v4 mapped sockaddr.*/ 1784 v4map_data_dest(); 1785 if (data_dest.su_family != AF_INET6) { 1786 usedefault = 1; 1787 reply(500, "Invalid address rejected."); 1788 return 1; 1789 } 1790 if (paranoid && 1791 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1792 memcmp(&data_dest.su_sin6.sin6_addr, 1793 &his_addr.su_sin6.sin6_addr, 1794 sizeof(data_dest.su_sin6.sin6_addr)))) { 1795 usedefault = 1; 1796 reply(500, "Illegal PORT range rejected."); 1797 } else { 1798 usedefault = 0; 1799 if (pdata >= 0) { 1800 (void) close(pdata); 1801 pdata = -1; 1802 } 1803 reply(200, "%s command successful.", pcmd); 1804 } 1805 return 1; 1806 } 1807 return 0; 1808 } 1809 1810 static void 1811 v4map_data_dest(void) 1812 { 1813 struct in_addr savedaddr; 1814 int savedport; 1815 1816 if (data_dest.su_family != AF_INET) { 1817 usedefault = 1; 1818 reply(500, "Invalid address rejected."); 1819 return; 1820 } 1821 1822 savedaddr = data_dest.su_sin.sin_addr; 1823 savedport = data_dest.su_port; 1824 1825 memset(&data_dest, 0, sizeof(data_dest)); 1826 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 1827 data_dest.su_sin6.sin6_family = AF_INET6; 1828 data_dest.su_sin6.sin6_port = savedport; 1829 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 1830 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 1831 (caddr_t)&savedaddr, sizeof(savedaddr)); 1832 } 1833 #endif 1834