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