opieftpd.c revision 59121
1/* opieftpd.c: Main program for an FTP daemon. 2 3%%% portions-copyright-cmetz-96 4Portions of this software are Copyright 1996-1998 by Craig Metz, All Rights 5Reserved. The Inner Net License Version 2 applies to these portions of 6the software. 7You should have received a copy of the license with this software. If 8you didn't get a copy, you may request one from <license@inner.net>. 9 10Portions of this software are Copyright 1995 by Randall Atkinson and Dan 11McDonald, All Rights Reserved. All Rights under this copyright are assigned 12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13License Agreement applies to this software. 14 15 History: 16 17 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's 18 done already (and conditionally) in opie_cfg.h. 19 Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes. 20 Merged in a security fix to BSD-derived ftpds. 21 Modified by cmetz for OPIE 2.3. Fixed the filename at the top. 22 Moved LS_COMMAND here. 23 Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al. 24 Removed useless strings (I don't think that removing the 25 ucb copyright one is a problem -- please let me know if 26 I'm wrong). Changed default CMASK to 077. Removed random 27 comments. Use ANSI stdargs for reply/lreply if we can, 28 added stdargs version of reply/lreply. Don't declare the 29 tos variable unless IP_TOS defined. Include stdargs headers 30 early. More headers ifdefed. Made everything static. 31 Got rid of gethostname() call and use of hostname. Pared 32 down status response for places where header files frequently 33 cause trouble. Made logging of user logins (ala -l) 34 non-optional. Moved reply()/lrepy(). Fixed some prototypes. 35 Modified at NRL for OPIE 2.1. Added declaration of envp. Discard 36 result of opiechallenge (allows access control to work). 37 Added patches for AIX. Symbol changes for autoconf. 38 Modified at NRL for OPIE 2.01. Changed password lookup handling 39 to avoid problems with drain-bamaged shadow password packages. 40 Properly handle internal state for anonymous FTP. Unlock 41 user accounts properly if login fails because of /etc/shells. 42 Make sure to close syslog by function to avoid problems with 43 drain bamaged syslog implementations. 44 Modified at NRL for OPIE 2.0. 45 Originally from BSD Net/2. 46 47 There is some really, really ugly code in here. 48 49$FreeBSD: head/contrib/opie/opieftpd.c 59121 2000-04-10 11:18:54Z kris $ 50*/ 51/* 52 * Copyright (c) 1985, 1988, 1990 Regents of the University of California. 53 * All rights reserved. 54 * 55 * Redistribution and use in source and binary forms, with or without 56 * modification, are permitted provided that the following conditions 57 * are met: 58 * 1. Redistributions of source code must retain the above copyright 59 * notice, this list of conditions and the following disclaimer. 60 * 2. Redistributions in binary form must reproduce the above copyright 61 * notice, this list of conditions and the following disclaimer in the 62 * documentation and/or other materials provided with the distribution. 63 * 3. All advertising materials mentioning features or use of this software 64 * must display the following acknowledgement: 65 * This product includes software developed by the University of 66 * California, Berkeley and its contributors. 67 * 4. Neither the name of the University nor the names of its contributors 68 * may be used to endorse or promote products derived from this software 69 * without specific prior written permission. 70 * 71 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 72 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 73 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 74 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 75 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 76 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 77 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 78 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 79 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 80 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 81 * SUCH DAMAGE. 82 */ 83 84#include "opie_cfg.h" 85 86#if HAVE_ANSISTDARG 87#include <stdarg.h> 88#endif /* HAVE_ANSISTDARG */ 89 90/* 91 * FTP server. 92 */ 93 94#if HAVE_SYS_PARAM_H 95#include <sys/param.h> 96#endif /* HAVE_SYS_PARAM_H */ 97#include <sys/stat.h> 98/* #include <sys/ioctl.h> */ 99#include <sys/socket.h> 100#include <sys/wait.h> 101#ifdef SYS_FCNTL_H 102#include <sys/fcntl.h> 103#else 104#include <fcntl.h> 105#endif /* SYS_FCNTL_H */ 106#include <sys/types.h> 107 108#include <netinet/in.h> 109#include <netinet/in_systm.h> 110#include <netinet/ip.h> 111 112#define FTP_NAMES 113#include <arpa/ftp.h> 114#include <arpa/inet.h> 115#include <arpa/telnet.h> 116 117#include <signal.h> 118#include <fcntl.h> 119#if HAVE_TIME_H 120#include <time.h> 121#endif /* HAVE_TIME_H */ 122#if HAVE_PWD_H 123#include <pwd.h> 124#endif /* HAVE_PWD_H */ 125#include <setjmp.h> 126#include <netdb.h> 127#include <errno.h> 128#include <syslog.h> 129#if HAVE_UNISTD_H 130#include <unistd.h> 131#endif /* HAVE_UNISTD_H */ 132#include <stdio.h> 133#include <ctype.h> 134#include <stdlib.h> 135#include <string.h> 136#include <grp.h> 137 138#include "opie.h" 139 140#if HAVE_SHADOW_H 141#include <shadow.h> 142#endif /* HAVE_SHADOW_H */ 143 144#if HAVE_CRYPT_H 145#include <crypt.h> 146#endif /* HAVE_CRYPT_H */ 147 148#if HAVE_SYS_UTSNAME_H 149#include <sys/utsname.h> 150#endif /* HAVE_SYS_UTSNAME_H */ 151 152#ifdef _AIX 153#include <sys/id.h> 154#include <sys/priv.h> 155#endif /* _AIX */ 156 157#ifdef IP_TOS 158#ifndef IPTOS_THROUGHPUT 159#undef IP_TOS 160#endif /* !IPTOS_THROUGHPUT */ 161#ifndef IPTOS_LOWDELAY 162#undef IP_TOS 163#endif /* !IPTOS_LOWDELAY */ 164#endif /* IP_TOS */ 165 166extern int errno; 167extern char *home; /* pointer to home directory for glob */ 168extern FILE *ftpd_popen __P((char *, char *)); 169extern int ftpd_pclose __P((FILE *)); 170extern char cbuf[]; 171extern off_t restart_point; 172 173static struct sockaddr_in ctrl_addr; 174static struct sockaddr_in data_source; 175struct sockaddr_in data_dest; 176struct sockaddr_in his_addr; 177static struct sockaddr_in pasv_addr; 178 179static int data; 180jmp_buf errcatch; 181static jmp_buf urgcatch; 182int logged_in; 183struct passwd *pw; 184int debug; 185int timeout = 900; /* timeout after 15 minutes of inactivity */ 186int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */ 187 188#if DOANONYMOUS 189static int guest; 190#endif /* DOANONYMOUS */ 191int type; 192int form; 193static int stru; /* avoid C keyword */ 194static int mode; 195int usedefault = 1; /* for data transfers */ 196int pdata = -1; /* for passive mode */ 197static int transflag; 198static off_t file_size; 199static off_t byte_count; 200 201#if (!defined(CMASK) || CMASK == 0) 202#undef CMASK 203#define CMASK 077 204#endif 205 206static int defumask = CMASK; /* default umask value */ 207char tmpline[7]; 208char remotehost[MAXHOSTNAMELEN]; 209 210/* 211 * Timeout intervals for retrying connections 212 * to hosts that don't accept PORT cmds. This 213 * is a kludge, but given the problems with TCP... 214 */ 215#define SWAITMAX 90 /* wait at most 90 seconds */ 216#define SWAITINT 5 /* interval between retries */ 217 218static int swaitmax = SWAITMAX; 219static int swaitint = SWAITINT; 220 221#if DOTITLE 222static char **Argv = NULL; /* pointer to argument vector */ 223static char *LastArgv = NULL; /* end of argv */ 224static char proctitle[BUFSIZ]; /* initial part of title */ 225#endif /* DOTITLE */ 226 227static int af_pwok = 0, pwok = 0; 228static struct opie opiestate; 229 230VOIDRET perror_reply __P((int, char *)); 231VOIDRET dologout __P((int)); 232char *getline __P((char *, int, FILE *)); 233VOIDRET upper __P((char *)); 234 235static VOIDRET lostconn __P((int)); 236static VOIDRET myoob __P((int)); 237static FILE *getdatasock __P((char *)); 238static FILE *dataconn __P((char *, off_t, char *)); 239static int checkuser __P((char *)); 240static VOIDRET end_login __P((void)); 241static VOIDRET send_data __P((FILE *, FILE *, off_t)); 242static int receive_data __P((FILE *, FILE *)); 243static char *gunique __P((char *)); 244static char *sgetsave __P((char *)); 245 246int opielogwtmp __P((char *, char *, char *)); 247 248int fclose __P((FILE *)); 249 250#ifdef HAVE_ANSISTDARG 251VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 252{ 253 va_list ap; 254 char buffer[1024]; 255 256 va_start(ap, fmt); 257 vsprintf(buffer, fmt, ap); 258 va_end(ap); 259 260 printf("%d %s\r\n", n, buffer); 261 fflush(stdout); 262 if (debug) 263 syslog(LOG_DEBUG, "<--- %d %s", n, buffer); 264} 265#else /* HAVE_ANSISTDARG */ 266VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5) 267{ 268 printf("%d ", n); 269 printf(fmt, p0, p1, p2, p3, p4, p5); 270 printf("\r\n"); 271 fflush(stdout); 272 if (debug) { 273 syslog(LOG_DEBUG, "<--- %d ", n); 274 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 275 } 276} 277#endif /* HAVE_ANSISTDARG */ 278 279#ifdef HAVE_ANSISTDARG 280VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 281{ 282 va_list ap; 283 char buffer[1024]; 284 285 va_start(ap, fmt); 286 vsprintf(buffer, fmt, ap); 287 va_end(ap); 288 289 printf("%d- %s\r\n", n, buffer); 290 fflush(stdout); 291 if (debug) 292 syslog(LOG_DEBUG, "<--- %d- %s", n, buffer); 293} 294#else /* HAVE_ANSISTDARG */ 295VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5) 296{ 297 printf("%d- ", n); 298 printf(fmt, p0, p1, p2, p3, p4, p5); 299 printf("\r\n"); 300 fflush(stdout); 301 if (debug) { 302 syslog(LOG_DEBUG, "<--- %d- ", n); 303 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 304 } 305} 306#endif /* HAVE_ANSISTDARG */ 307 308VOIDRET enable_signalling FUNCTION_NOARGS 309{ 310 signal(SIGPIPE, lostconn); 311 if ((int)signal(SIGURG, myoob) < 0) 312 syslog(LOG_ERR, "signal: %m"); 313} 314 315VOIDRET disable_signalling FUNCTION_NOARGS 316{ 317 signal(SIGPIPE, SIG_IGN); 318 if ((int)signal(SIGURG, SIG_IGN) < 0) 319 syslog(LOG_ERR, "signal: %m"); 320} 321 322static VOIDRET lostconn FUNCTION((input), int input) 323{ 324 if (debug) 325 syslog(LOG_DEBUG, "lost connection"); 326 dologout(-1); 327} 328 329static char ttyline[20]; 330 331/* 332 * Helper function for sgetpwnam(). 333 */ 334static char *sgetsave FUNCTION((s), char *s) 335{ 336 char *new = malloc((unsigned) strlen(s) + 1); 337 338 if (new == NULL) { 339 perror_reply(421, "Local resource failure: malloc"); 340 dologout(1); 341 /* NOTREACHED */ 342 } 343 strcpy(new, s); 344 return (new); 345} 346 347/* 348 * Save the result of a getpwnam. Used for USER command, since 349 * the data returned must not be clobbered by any other command 350 * (e.g., globbing). 351 */ 352static struct passwd *sgetpwnam FUNCTION((name), char *name) 353{ 354 static struct passwd save; 355 register struct passwd *p; 356 357#if HAVE_SHADOW 358 struct spwd *spwd; 359#endif /* HAVE_SHADOW */ 360 361 if ((p = getpwnam(name)) == NULL) 362 return (p); 363 364#if HAVE_SHADOW 365 if ((spwd = getspnam(name)) == NULL) 366 return NULL; 367 368 endspent(); 369 370 p->pw_passwd = spwd->sp_pwdp; 371#endif /* HAVE_SHADOW */ 372 373 endpwent(); 374 375 if (save.pw_name) { 376 free(save.pw_name); 377 free(save.pw_passwd); 378 free(save.pw_gecos); 379 free(save.pw_dir); 380 free(save.pw_shell); 381 } 382 save = *p; 383 save.pw_name = sgetsave(p->pw_name); 384 save.pw_passwd = sgetsave(p->pw_passwd); 385 save.pw_gecos = sgetsave(p->pw_gecos); 386 save.pw_dir = sgetsave(p->pw_dir); 387 save.pw_shell = sgetsave(p->pw_shell); 388 return (&save); 389} 390 391int login_attempts; /* number of failed login attempts */ 392int askpasswd; /* had user command, ask for passwd */ 393 394/* 395 * USER command. 396 * Sets global passwd pointer pw if named account exists and is acceptable; 397 * sets askpasswd if a PASS command is expected. If logged in previously, 398 * need to reset state. If name is "ftp" or "anonymous", the name is not in 399 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 400 * If account doesn't exist, ask for passwd anyway. Otherwise, check user 401 * requesting login privileges. Disallow anyone who does not have a standard 402 * shell as returned by getusershell(). Disallow anyone mentioned in the file 403 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 404 */ 405int user FUNCTION((name), char *name) 406{ 407 register char *cp; 408 char *shell; 409 410 if (logged_in) { 411#if DOANONYMOUS 412 if (guest) { 413 reply(530, "Can't change user from guest login."); 414 return -1; 415 } 416#endif /* DOANONMOUS */ 417 end_login(); 418 } 419 askpasswd = 1; 420#if DOANONYMOUS 421 guest = 0; 422 if (!strcmp(name, "ftp") || !strcmp(name, "anonymous")) 423 if (!checkuser("ftp") && !checkuser("anonymous")) 424 if ((pw = sgetpwnam("ftp")) != NULL) { 425 guest = 1; 426 askpasswd = 1; 427 reply(331, "Guest login ok, send your e-mail address as your password."); 428 syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost); 429 return 0; 430 } 431#endif /* DOANONYMOUS */ 432 if (pw = sgetpwnam(name)) { 433 if ((shell = pw->pw_shell) == NULL || *shell == 0) 434 shell = _PATH_BSHELL; 435 while ((cp = getusershell()) != NULL) 436 if (!strcmp(cp, shell)) 437 break; 438 endusershell(); 439 if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) { 440#if DEBUG 441 if (!cp) 442 syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell); 443 if (checkuser(name)) 444 syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?"); 445 if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) 446 syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd); 447#endif /* DEBUG */ 448 pw = (struct passwd *) NULL; 449 askpasswd = -1; 450 } 451 } 452 { 453 char prompt[OPIE_CHALLENGE_MAX + 1]; 454 455 opiechallenge(&opiestate, name, prompt); 456 457 if (askpasswd == -1) { 458 syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost); 459 pwok = 0; 460 } else 461 pwok = af_pwok && opiealways(pw->pw_dir); 462 463#if NEW_PROMPTS 464 reply(331, "Response to %s %s for %s.", prompt, 465#else /* NEW_PROMPTS */ 466 reply(331, "OTP response %s %s for %s.", prompt, 467#endif /* NEW_PROMPTS */ 468 pwok ? "requested" : "required", name); 469 } 470 /* Delay before reading passwd after first failed attempt to slow down 471 passwd-guessing programs. */ 472 if (login_attempts) 473 sleep((unsigned) login_attempts); 474 475 return 0; 476} 477 478/* 479 * Check if a user is in the file _PATH_FTPUSERS 480 */ 481static int checkuser FUNCTION((name), char *name) 482{ 483 register FILE *fd; 484 register char *p; 485 char line[BUFSIZ]; 486 487 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 488 while (fgets(line, sizeof(line), fd) != NULL) 489 if ((p = strchr(line, '\n')) != NULL) { 490 *p = '\0'; 491 if (line[0] == '#') 492 continue; 493 if (!strcmp(line, name)) { 494 fclose(fd); 495 return (1); 496 } 497 } 498 fclose(fd); 499 } 500 return (0); 501} 502 503/* 504 * Terminate login as previous user, if any, resetting state; 505 * used when USER command is given or login fails. 506 */ 507static VOIDRET end_login FUNCTION_NOARGS 508{ 509 disable_signalling(); 510 if (seteuid((uid_t) 0)) 511 syslog(LOG_ERR, "Can't set euid"); 512 if (logged_in) 513 opielogwtmp(ttyline, "", ""); 514 pw = NULL; 515 logged_in = 0; 516#if DOANONYMOUS 517 guest = 0; 518#endif /* DOANONYMOUS */ 519 enable_signalling(); 520} 521 522VOIDRET pass FUNCTION((passwd), char *passwd) 523{ 524 int legit = askpasswd + 1, i; 525 526 if (logged_in || askpasswd == 0) { 527 reply(503, "Login with USER first."); 528 return; 529 } 530 askpasswd = 0; 531 532#if DOANONYMOUS 533 if (!guest) { /* "ftp" is only account allowed no password */ 534#endif /* DOANONYMOUS */ 535 i = opieverify(&opiestate, passwd); 536 if (legit && i && pwok) 537 i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd); 538 if (!legit || i) { 539 reply(530, "Login incorrect."); 540 pw = NULL; 541 if (login_attempts++ >= 5) { 542 syslog(LOG_WARNING, 543 "Repeated login failures for user %s from %s", 544 pw->pw_name, remotehost); 545 exit(0); 546 } 547 return; 548 } 549#if DOANONYMOUS 550 } else 551 if ((passwd[0] <= ' ') || checkuser(passwd)) { 552 reply(530, "No identity, no service."); 553 syslog(LOG_DEBUG, "Bogus address: %s", passwd); 554 exit(0); 555 } 556#endif /* DOANONYMOUS */ 557 login_attempts = 0; /* this time successful */ 558 if (setegid((gid_t) pw->pw_gid) < 0) { 559 reply(550, "Can't set gid."); 560 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno); 561 return; 562 } 563 initgroups(pw->pw_name, pw->pw_gid); 564 565 /* open wtmp before chroot */ 566 sprintf(ttyline, "ftp%d", getpid()); 567 opielogwtmp(ttyline, pw->pw_name, remotehost); 568 logged_in = 1; 569 570#if DOANONYMOUS 571 if (guest) { 572 /* We MUST do a chdir() after the chroot. Otherwise the old current 573 directory will be accessible as "." outside the new root! */ 574 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 575 reply(550, "Can't set guest privileges."); 576 goto bad; 577 } 578 } else 579#endif /* DOANONYMOUS */ 580 if (chdir(pw->pw_dir) < 0) { 581 if (chdir("/") < 0) { 582 reply(530, "User %s: can't change directory to %s.", 583 pw->pw_name, pw->pw_dir); 584 goto bad; 585 } else 586 lreply(230, "No directory! Logging in with home=/"); 587 } 588/* This patch was contributed by an OPIE user. We don't know what it 589 does, exactly. It may or may not work. */ 590#ifdef _AIX 591 { 592 priv_t priv; 593 priv.pv_priv[0] = 0; 594 priv.pv_priv[1] = 0; 595 setgroups(NULL, NULL); 596 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH, 597 &priv, sizeof(priv_t)) < 0 || 598 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 || 599 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 || 600 seteuid((uid_t)pw->pw_uid) < 0) { 601 reply(550, "Can't set uid (_AIX3)."); 602 goto bad; 603 } 604 } 605#else /* _AIX */ 606 if (seteuid((uid_t) pw->pw_uid) < 0) { 607 reply(550, "Can't set uid."); 608 goto bad; 609 } 610#endif /* _AIX */ 611 /* 612 * Display a login message, if it exists. 613 * N.B. reply(230,) must follow the message. 614 */ 615 { 616 FILE *fd; 617 618 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 619 char *cp, line[128]; 620 621 while (fgets(line, sizeof(line), fd) != NULL) { 622 if ((cp = strchr(line, '\n')) != NULL) 623 *cp = '\0'; 624 lreply(230, "%s", line); 625 } 626 (void) fflush(stdout); 627 (void) fclose(fd); 628 } 629 } 630#if DOANONYMOUS 631 if (guest) { 632 reply(230, "Guest login ok, access restrictions apply."); 633#if DOTITLE 634 snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%s", remotehost, 635 passwd); 636 setproctitle(proctitle); 637#endif /* DOTITLE */ 638 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s", 639 remotehost, passwd); 640 } else 641#endif /* DOANONYMOUS */ 642 { 643 reply(230, "User %s logged in.", pw->pw_name); 644 645#if DOTITLE 646 snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); 647 setproctitle(proctitle); 648#endif /* DOTITLE */ 649 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name); 650 } 651 home = pw->pw_dir; /* home dir for globbing */ 652 umask(defumask); 653 return; 654 655bad: 656 /* Forget all about it... */ 657 end_login(); 658} 659 660VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name) 661{ 662 FILE *fin, *dout; 663 struct stat st; 664 int (*closefunc) (); 665 666 if (cmd == 0) { 667 fin = fopen(name, "r"), closefunc = fclose; 668 st.st_size = 0; 669 } else { 670 char line[BUFSIZ]; 671 672 snprintf(line, sizeof(line), cmd, name); 673 name = line; 674 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 675 st.st_size = -1; 676#if HAVE_ST_BLKSIZE 677 st.st_blksize = BUFSIZ; 678#endif /* HAVE_ST_BLKSIZE */ 679 } 680 if (fin == NULL) { 681 if (errno != 0) 682 perror_reply(550, name); 683 return; 684 } 685 if (cmd == 0 && 686 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) { 687 reply(550, "%s: not a plain file.", name); 688 goto done; 689 } 690 if (restart_point) { 691 if (type == TYPE_A) { 692 register int i, n, c; 693 694 n = restart_point; 695 i = 0; 696 while (i++ < n) { 697 if ((c = getc(fin)) == EOF) { 698 perror_reply(550, name); 699 goto done; 700 } 701 if (c == '\n') 702 i++; 703 } 704 } else 705 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) { 706 perror_reply(550, name); 707 goto done; 708 } 709 } 710 dout = dataconn(name, st.st_size, "w"); 711 if (dout == NULL) 712 goto done; 713#if HAVE_ST_BLKSIZE 714 send_data(fin, dout, st.st_blksize); 715#else /* HAVE_ST_BLKSIZE */ 716 send_data(fin, dout, BUFSIZ); 717#endif /* HAVE_ST_BLKSIZE */ 718 fclose(dout); 719 data = -1; 720 pdata = -1; 721done: 722 (*closefunc) (fin); 723} 724 725VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique) 726{ 727 FILE *fout, *din; 728 struct stat st; 729 int (*closefunc) (); 730 731 if (unique && stat(name, &st) == 0 && 732 (name = gunique(name)) == NULL) 733 return; 734 735 if (restart_point) 736 mode = "r+w"; 737 fout = fopen(name, mode); 738 closefunc = fclose; 739 if (fout == NULL) { 740 perror_reply(553, name); 741 return; 742 } 743 if (restart_point) { 744 if (type == TYPE_A) { 745 register int i, n, c; 746 747 n = restart_point; 748 i = 0; 749 while (i++ < n) { 750 if ((c = getc(fout)) == EOF) { 751 perror_reply(550, name); 752 goto done; 753 } 754 if (c == '\n') 755 i++; 756 } 757 /* We must do this seek to "current" position because we are changing 758 from reading to writing. */ 759 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) { 760 perror_reply(550, name); 761 goto done; 762 } 763 } else 764 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) { 765 perror_reply(550, name); 766 goto done; 767 } 768 } 769 din = dataconn(name, (off_t) - 1, "r"); 770 if (din == NULL) 771 goto done; 772 if (receive_data(din, fout) == 0) { 773 if (unique) 774 reply(226, "Transfer complete (unique file name:%s).", 775 name); 776 else 777 reply(226, "Transfer complete."); 778 } 779 fclose(din); 780 data = -1; 781 pdata = -1; 782done: 783 (*closefunc) (fout); 784} 785 786static FILE *getdatasock FUNCTION((mode), char *mode) 787{ 788 int s, on = 1, tries; 789 790 if (data >= 0) 791 return (fdopen(data, mode)); 792 disable_signalling(); 793 if (seteuid((uid_t) 0)) 794 syslog(LOG_ERR, "Can't set euid"); 795 s = socket(AF_INET, SOCK_STREAM, 0); 796 if (s < 0) 797 goto bad; 798 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 799 (char *) &on, sizeof(on)) < 0) 800 goto bad; 801 /* anchor socket to avoid multi-homing problems */ 802 data_source.sin_family = AF_INET; 803 data_source.sin_addr = ctrl_addr.sin_addr; 804 for (tries = 1;; tries++) { 805 if (bind(s, (struct sockaddr *) & data_source, 806 sizeof(data_source)) >= 0) 807 break; 808 if (errno != EADDRINUSE || tries > 10) 809 goto bad; 810 sleep(tries); 811 } 812 if (seteuid((uid_t) pw->pw_uid)) 813 syslog(LOG_ERR, "Can't set euid"); 814 enable_signalling(); 815#ifdef IP_TOS 816 on = IPTOS_THROUGHPUT; 817 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0) 818 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 819#endif 820 return (fdopen(s, mode)); 821bad: 822 { 823 int t = errno; 824 825 if (seteuid((uid_t) pw->pw_uid)) 826 syslog(LOG_ERR, "Can't set euid"); 827 enable_signalling(); 828 close(s); 829 830 errno = t; 831 } 832 return (NULL); 833} 834 835static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode) 836{ 837 char sizebuf[32]; 838 FILE *file; 839 int retry = 0; 840#ifdef IP_TOS 841 int tos; 842#endif /* IP_TOS */ 843 844 file_size = size; 845 byte_count = 0; 846 if (size != (off_t) - 1) 847 snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size); 848 else 849 strcpy(sizebuf, ""); 850 if (pdata >= 0) { 851 struct sockaddr_in from; 852 int s, fromlen = sizeof(from); 853 854 s = accept(pdata, (struct sockaddr *) & from, &fromlen); 855 if (s < 0) { 856 reply(425, "Can't open data connection."); 857 close(pdata); 858 pdata = -1; 859 return (NULL); 860 } 861 close(pdata); 862 pdata = s; 863#ifdef IP_TOS 864 tos = IPTOS_LOWDELAY; 865 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos, 866 sizeof(int)); 867 868#endif 869 reply(150, "Opening %s mode data connection for %s%s.", 870 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 871 return (fdopen(pdata, mode)); 872 } 873 if (data >= 0) { 874 reply(125, "Using existing data connection for %s%s.", 875 name, sizebuf); 876 usedefault = 1; 877 return (fdopen(data, mode)); 878 } 879 if (usedefault) 880 data_dest = his_addr; 881 usedefault = 1; 882 file = getdatasock(mode); 883 if (file == NULL) { 884 reply(425, "Can't create data socket (%s,%d): %s.", 885 inet_ntoa(data_source.sin_addr), 886 ntohs(data_source.sin_port), strerror(errno)); 887 return (NULL); 888 } 889 data = fileno(file); 890 while (connect(data, (struct sockaddr *) & data_dest, 891 sizeof(data_dest)) < 0) { 892 if (errno == EADDRINUSE && retry < swaitmax) { 893 sleep((unsigned) swaitint); 894 retry += swaitint; 895 continue; 896 } 897 perror_reply(425, "Can't build data connection"); 898 fclose(file); 899 data = -1; 900 return (NULL); 901 } 902 reply(150, "Opening %s mode data connection for %s%s.", 903 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 904 return (file); 905} 906 907/* 908 * Tranfer the contents of "instr" to 909 * "outstr" peer using the appropriate 910 * encapsulation of the data subject 911 * to Mode, Structure, and Type. 912 * 913 * NB: Form isn't handled. 914 */ 915static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize) 916{ 917 register int c, cnt; 918 register char *buf; 919 int netfd, filefd; 920 921 transflag++; 922 if (setjmp(urgcatch)) { 923 transflag = 0; 924 return; 925 } 926 switch (type) { 927 928 case TYPE_A: 929 while ((c = getc(instr)) != EOF) { 930 byte_count++; 931 if (c == '\n') { 932 if (ferror(outstr)) 933 goto data_err; 934 putc('\r', outstr); 935 } 936 putc(c, outstr); 937 } 938 fflush(outstr); 939 transflag = 0; 940 if (ferror(instr)) 941 goto file_err; 942 if (ferror(outstr)) 943 goto data_err; 944 reply(226, "Transfer complete."); 945 return; 946 947 case TYPE_I: 948 case TYPE_L: 949 if ((buf = malloc((u_int) blksize)) == NULL) { 950 transflag = 0; 951 perror_reply(451, "Local resource failure: malloc"); 952 return; 953 } 954 netfd = fileno(outstr); 955 filefd = fileno(instr); 956 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 && 957 write(netfd, buf, cnt) == cnt) 958 byte_count += cnt; 959 transflag = 0; 960 free(buf); 961 if (cnt != 0) { 962 if (cnt < 0) 963 goto file_err; 964 goto data_err; 965 } 966 reply(226, "Transfer complete."); 967 return; 968 default: 969 transflag = 0; 970 reply(550, "Unimplemented TYPE %d in send_data", type); 971 return; 972 } 973 974data_err: 975 transflag = 0; 976 perror_reply(426, "Data connection"); 977 return; 978 979file_err: 980 transflag = 0; 981 perror_reply(551, "Error on input file"); 982} 983 984/* 985 * Transfer data from peer to 986 * "outstr" using the appropriate 987 * encapulation of the data subject 988 * to Mode, Structure, and Type. 989 * 990 * N.B.: Form isn't handled. 991 */ 992static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr) 993{ 994 register int c; 995 int cnt, bare_lfs = 0; 996 char buf[BUFSIZ]; 997 998 transflag++; 999 if (setjmp(urgcatch)) { 1000 transflag = 0; 1001 return (-1); 1002 } 1003 switch (type) { 1004 1005 case TYPE_I: 1006 case TYPE_L: 1007 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 1008 if (write(fileno(outstr), buf, cnt) != cnt) 1009 goto file_err; 1010 byte_count += cnt; 1011 } 1012 if (cnt < 0) 1013 goto data_err; 1014 transflag = 0; 1015 return (0); 1016 1017 case TYPE_E: 1018 reply(553, "TYPE E not implemented."); 1019 transflag = 0; 1020 return (-1); 1021 1022 case TYPE_A: 1023 while ((c = getc(instr)) != EOF) { 1024 byte_count++; 1025 if (c == '\n') 1026 bare_lfs++; 1027 while (c == '\r') { 1028 if (ferror(outstr)) 1029 goto data_err; 1030 if ((c = getc(instr)) != '\n') { 1031 putc('\r', outstr); 1032 if (c == '\0' || c == EOF) 1033 goto contin2; 1034 } 1035 } 1036 putc(c, outstr); 1037 contin2:; 1038 } 1039 fflush(outstr); 1040 if (ferror(instr)) 1041 goto data_err; 1042 if (ferror(outstr)) 1043 goto file_err; 1044 transflag = 0; 1045 if (bare_lfs) { 1046 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 1047 printf(" File may not have transferred correctly.\r\n"); 1048 } 1049 return (0); 1050 default: 1051 reply(550, "Unimplemented TYPE %d in receive_data", type); 1052 transflag = 0; 1053 return (-1); 1054 } 1055 1056data_err: 1057 transflag = 0; 1058 perror_reply(426, "Data Connection"); 1059 return (-1); 1060 1061file_err: 1062 transflag = 0; 1063 perror_reply(452, "Error writing file"); 1064 return (-1); 1065} 1066 1067VOIDRET statfilecmd FUNCTION((filename), char *filename) 1068{ 1069 char line[BUFSIZ]; 1070 FILE *fin; 1071 int c; 1072 1073#if HAVE_LS_G_FLAG 1074 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename); 1075#else /* HAVE_LS_G_FLAG */ 1076 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename); 1077#endif /* HAVE_LS_G_FLAG */ 1078 fin = ftpd_popen(line, "r"); 1079 lreply(211, "status of %s:", filename); 1080 while ((c = getc(fin)) != EOF) { 1081 if (c == '\n') { 1082 if (ferror(stdout)) { 1083 perror_reply(421, "control connection"); 1084 ftpd_pclose(fin); 1085 dologout(1); 1086 /* NOTREACHED */ 1087 } 1088 if (ferror(fin)) { 1089 perror_reply(551, filename); 1090 ftpd_pclose(fin); 1091 return; 1092 } 1093 putc('\r', stdout); 1094 } 1095 putc(c, stdout); 1096 } 1097 ftpd_pclose(fin); 1098 reply(211, "End of Status"); 1099} 1100 1101VOIDRET statcmd FUNCTION_NOARGS 1102{ 1103/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */ 1104 struct sockaddr_in *sin; 1105 u_char *a, *p; 1106 1107 lreply(211, "FTP server status:"); 1108 printf(" \r\n"); 1109 printf(" Connected to %s", remotehost); 1110 if (!isdigit(remotehost[0])) 1111 printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 1112 printf("\r\n"); 1113 if (logged_in) { 1114#if DOANONYMOUS 1115 if (guest) 1116 printf(" Logged in anonymously\r\n"); 1117 else 1118#endif /* DOANONYMOUS */ 1119 printf(" Logged in as %s\r\n", pw->pw_name); 1120 } else 1121 if (askpasswd) 1122 printf(" Waiting for password\r\n"); 1123 else 1124 printf(" Waiting for user name\r\n"); 1125 if (data != -1) 1126 printf(" Data connection open\r\n"); 1127 else 1128 if (pdata != -1) { 1129 printf(" in Passive mode"); 1130 sin = &pasv_addr; 1131 goto printaddr; 1132 } else 1133 if (usedefault == 0) { 1134 printf(" PORT"); 1135 sin = &data_dest; 1136 printaddr: 1137 a = (u_char *) & sin->sin_addr; 1138 p = (u_char *) & sin->sin_port; 1139#define UC(b) (((int) b) & 0xff) 1140 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 1141 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1142#undef UC 1143 } else 1144 printf(" No data connection\r\n"); 1145 reply(211, "End of status"); 1146} 1147 1148VOIDRET opiefatal FUNCTION((s), char *s) 1149{ 1150 reply(451, "Error in server: %s\n", s); 1151 reply(221, "Closing connection due to server error."); 1152 dologout(0); 1153 /* NOTREACHED */ 1154} 1155 1156static VOIDRET ack FUNCTION((s), char *s) 1157{ 1158 reply(250, "%s command successful.", s); 1159} 1160 1161VOIDRET nack FUNCTION((s), char *s) 1162{ 1163 reply(502, "%s command not implemented.", s); 1164} 1165 1166VOIDRET yyerror FUNCTION((s), char *s) 1167{ 1168 char *cp; 1169 1170 if (cp = strchr(cbuf, '\n')) 1171 *cp = '\0'; 1172 reply(500, "'%s': command not understood.", cbuf); 1173} 1174 1175VOIDRET delete FUNCTION((name), char *name) 1176{ 1177 struct stat st; 1178 1179 if (stat(name, &st) < 0) { 1180 perror_reply(550, name); 1181 return; 1182 } 1183 if ((st.st_mode & S_IFMT) == S_IFDIR) { 1184 if (rmdir(name) < 0) { 1185 perror_reply(550, name); 1186 return; 1187 } 1188 goto done; 1189 } 1190 if (unlink(name) < 0) { 1191 perror_reply(550, name); 1192 return; 1193 } 1194done: 1195 ack("DELE"); 1196} 1197 1198VOIDRET cwd FUNCTION((path), char *path) 1199{ 1200 if (chdir(path) < 0) 1201 perror_reply(550, path); 1202 else 1203 ack("CWD"); 1204} 1205 1206VOIDRET makedir FUNCTION((name), char *name) 1207{ 1208 if (mkdir(name, 0777) < 0) 1209 perror_reply(550, name); 1210 else 1211 reply(257, "MKD command successful."); 1212} 1213 1214VOIDRET removedir FUNCTION((name), char *name) 1215{ 1216 if (rmdir(name) < 0) 1217 perror_reply(550, name); 1218 else 1219 ack("RMD"); 1220} 1221 1222VOIDRET pwd FUNCTION_NOARGS 1223{ 1224 char path[MAXPATHLEN + 1]; 1225 1226 if (getcwd(path, MAXPATHLEN) == (char *) NULL) 1227 reply(550, "%s.", path); 1228 else 1229 reply(257, "\"%s\" is current directory.", path); 1230} 1231 1232char *renamefrom FUNCTION((name), char *name) 1233{ 1234 struct stat st; 1235 1236 if (stat(name, &st) < 0) { 1237 perror_reply(550, name); 1238 return ((char *) 0); 1239 } 1240 reply(350, "File exists, ready for destination name"); 1241 return (name); 1242} 1243 1244VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to) 1245{ 1246 if (rename(from, to) < 0) 1247 perror_reply(550, "rename"); 1248 else 1249 ack("RNTO"); 1250} 1251 1252static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin) 1253{ 1254 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr, 1255 sizeof(struct in_addr), AF_INET); 1256 time_t t, time(); 1257 1258 if (hp) 1259 strncpy(remotehost, hp->h_name, sizeof(remotehost)); 1260 else 1261 strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost)); 1262 remotehost[sizeof(remotehost) - 1] = '\0'; 1263#if DOTITLE 1264 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 1265 setproctitle(proctitle); 1266#endif /* DOTITLE */ 1267 1268 t = time((time_t *) 0); 1269 syslog(LOG_INFO, "connection from %s at %s", 1270 remotehost, ctime(&t)); 1271} 1272 1273/* 1274 * Record logout in wtmp file 1275 * and exit with supplied status. 1276 */ 1277VOIDRET dologout FUNCTION((status), int status) 1278{ 1279 disable_signalling(); 1280 if (logged_in) { 1281 if (seteuid((uid_t) 0)) 1282 syslog(LOG_ERR, "Can't set euid"); 1283 opielogwtmp(ttyline, "", ""); 1284 } 1285 /* beware of flushing buffers after a SIGPIPE */ 1286 _exit(status); 1287} 1288 1289static VOIDRET myoob FUNCTION((input), int input) 1290{ 1291 char *cp; 1292 1293 /* only process if transfer occurring */ 1294 if (!transflag) 1295 return; 1296 cp = tmpline; 1297 if (getline(cp, 7, stdin) == NULL) { 1298 reply(221, "You could at least say goodbye."); 1299 dologout(0); 1300 } 1301 upper(cp); 1302 if (strcmp(cp, "ABOR\r\n") == 0) { 1303 tmpline[0] = '\0'; 1304 reply(426, "Transfer aborted. Data connection closed."); 1305 reply(226, "Abort successful"); 1306 longjmp(urgcatch, 1); 1307 } 1308 if (strcmp(cp, "STAT\r\n") == 0) { 1309 if (file_size != (off_t) - 1) 1310 reply(213, "Status: %lu of %lu bytes transferred", 1311 byte_count, file_size); 1312 else 1313 reply(213, "Status: %lu bytes transferred", byte_count); 1314 } 1315} 1316 1317/* 1318 * Note: a response of 425 is not mentioned as a possible response to 1319 * the PASV command in RFC959. However, it has been blessed as 1320 * a legitimate response by Jon Postel in a telephone conversation 1321 * with Rick Adams on 25 Jan 89. 1322 */ 1323VOIDRET passive FUNCTION_NOARGS 1324{ 1325 int len; 1326 register char *p, *a; 1327 1328 pdata = socket(AF_INET, SOCK_STREAM, 0); 1329 if (pdata < 0) { 1330 perror_reply(425, "Can't open passive connection"); 1331 return; 1332 } 1333 pasv_addr = ctrl_addr; 1334 pasv_addr.sin_port = 0; 1335 if (seteuid((uid_t) 0)) 1336 syslog(LOG_ERR, "Can't set euid"); 1337 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) { 1338 seteuid((uid_t) pw->pw_uid); 1339 goto pasv_error; 1340 } 1341 if (seteuid((uid_t) pw->pw_uid)) 1342 syslog(LOG_ERR, "Can't set euid"); 1343 len = sizeof(pasv_addr); 1344 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0) 1345 goto pasv_error; 1346 if (listen(pdata, 1) < 0) 1347 goto pasv_error; 1348 a = (char *) &pasv_addr.sin_addr; 1349 p = (char *) &pasv_addr.sin_port; 1350 1351#define UC(b) (((int) b) & 0xff) 1352 1353 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 1354 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1355 return; 1356 1357pasv_error: 1358 close(pdata); 1359 pdata = -1; 1360 perror_reply(425, "Can't open passive connection"); 1361 return; 1362} 1363 1364/* 1365 * Generate unique name for file with basename "local". 1366 * The file named "local" is already known to exist. 1367 * Generates failure reply on error. 1368 */ 1369static char *gunique FUNCTION((local), char *local) 1370{ 1371 static char new[MAXPATHLEN+1]; 1372 struct stat st; 1373 char *cp = strrchr(local, '/'); 1374 int count = 0; 1375 1376 if (cp) 1377 *cp = '\0'; 1378 if (stat(cp ? local : ".", &st) < 0) { 1379 perror_reply(553, cp ? local : "."); 1380 return ((char *) 0); 1381 } 1382 if (cp) 1383 *cp = '/'; 1384 strcpy(new, local); 1385 cp = new + strlen(new); 1386 *cp++ = '.'; 1387 for (count = 1; count < 100; count++) { 1388 snprintf(cp, sizeof(new) - (cp - new), "%d", count); 1389 if (stat(new, &st) < 0) 1390 return (new); 1391 } 1392 reply(452, "Unique file name cannot be created."); 1393 return ((char *) 0); 1394} 1395 1396/* 1397 * Format and send reply containing system error number. 1398 */ 1399VOIDRET perror_reply FUNCTION((code, string), int code AND char *string) 1400{ 1401 reply(code, "%s: %s.", string, strerror(errno)); 1402} 1403 1404static char *onefile[] = 1405{ 1406 "", 1407 0 1408}; 1409 1410VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles) 1411{ 1412 struct stat st; 1413 DIR *dirp = NULL; 1414 struct dirent *dir; 1415 FILE *dout = NULL; 1416 register char **dirlist, *dirname; 1417 int simple = 0; 1418 1419 if (strpbrk(whichfiles, "~{[*?") != NULL) { 1420 extern char **ftpglob(), *globerr; 1421 1422 globerr = NULL; 1423 dirlist = ftpglob(whichfiles); 1424 if (globerr != NULL) { 1425 reply(550, globerr); 1426 return; 1427 } else 1428 if (dirlist == NULL) { 1429 errno = ENOENT; 1430 perror_reply(550, whichfiles); 1431 return; 1432 } 1433 } else { 1434 onefile[0] = whichfiles; 1435 dirlist = onefile; 1436 simple = 1; 1437 } 1438 1439 if (setjmp(urgcatch)) { 1440 transflag = 0; 1441 return; 1442 } 1443 while (dirname = *dirlist++) { 1444 if (stat(dirname, &st) < 0) { 1445 /* If user typed "ls -l", etc, and the client used NLST, do what the 1446 user meant. */ 1447 if (dirname[0] == '-' && *dirlist == NULL && 1448 transflag == 0) { 1449 retrieve("/bin/ls %s", dirname); 1450 return; 1451 } 1452 perror_reply(550, whichfiles); 1453 if (dout != NULL) { 1454 fclose(dout); 1455 transflag = 0; 1456 data = -1; 1457 pdata = -1; 1458 } 1459 return; 1460 } 1461 if ((st.st_mode & S_IFMT) == S_IFREG) { 1462 if (dout == NULL) { 1463 dout = dataconn("file list", (off_t) - 1, "w"); 1464 if (dout == NULL) 1465 return; 1466 transflag++; 1467 } 1468 fprintf(dout, "%s%s\n", dirname, 1469 type == TYPE_A ? "\r" : ""); 1470 byte_count += strlen(dirname) + 1; 1471 continue; 1472 } else 1473 if ((st.st_mode & S_IFMT) != S_IFDIR) 1474 continue; 1475 1476 if ((dirp = opendir(dirname)) == NULL) 1477 continue; 1478 1479 while ((dir = readdir(dirp)) != NULL) { 1480 char nbuf[MAXPATHLEN+1]; 1481 1482 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1)) 1483 continue; 1484 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 1485 (strlen(dir->d_name) == 2)) 1486 continue; 1487 1488 snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); 1489 1490 /* We have to do a stat to insure it's not a directory or special file. */ 1491 if (simple || (stat(nbuf, &st) == 0 && 1492 (st.st_mode & S_IFMT) == S_IFREG)) { 1493 if (dout == NULL) { 1494 dout = dataconn("file list", (off_t) - 1, "w"); 1495 if (dout == NULL) 1496 return; 1497 transflag++; 1498 } 1499 if (nbuf[0] == '.' && nbuf[1] == '/') 1500 fprintf(dout, "%s%s\n", &nbuf[2], 1501 type == TYPE_A ? "\r" : ""); 1502 else 1503 fprintf(dout, "%s%s\n", nbuf, 1504 type == TYPE_A ? "\r" : ""); 1505 byte_count += strlen(nbuf) + 1; 1506 } 1507 } 1508 closedir(dirp); 1509 } 1510 1511 if (dout == NULL) 1512 reply(550, "No files found."); 1513 else 1514 if (ferror(dout) != 0) 1515 perror_reply(550, "Data connection"); 1516 else 1517 reply(226, "Transfer complete."); 1518 1519 transflag = 0; 1520 if (dout != NULL) 1521 fclose(dout); 1522 data = -1; 1523 pdata = -1; 1524} 1525 1526#if DOTITLE 1527/* 1528 * clobber argv so ps will show what we're doing. 1529 * (stolen from sendmail) 1530 * warning, since this is usually started from inetd.conf, it 1531 * often doesn't have much of an environment or arglist to overwrite. 1532 */ 1533VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c) 1534{ 1535 register char *p, *bp, ch; 1536 register int i; 1537 char buf[BUFSIZ]; 1538 1539 snprintf(buf, sizeof(buf), fmt, a, b, c); 1540 1541 /* make ps print our process name */ 1542 p = Argv[0]; 1543 *p++ = '-'; 1544 1545 i = strlen(buf); 1546 if (i > LastArgv - p - 2) { 1547 i = LastArgv - p - 2; 1548 buf[i] = '\0'; 1549 } 1550 bp = buf; 1551 while (ch = *bp++) 1552 if (ch != '\n' && ch != '\r') 1553 *p++ = ch; 1554 while (p < LastArgv) 1555 *p++ = ' '; 1556} 1557#endif /* DOTITLE */ 1558 1559VOIDRET catchexit FUNCTION_NOARGS 1560{ 1561 closelog(); 1562} 1563 1564int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[]) 1565{ 1566 int addrlen, on = 1; 1567 char *cp; 1568#ifdef IP_TOS 1569 int tos; 1570#endif /* IP_TOS */ 1571 1572 { 1573 int i; 1574 1575 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) 1576 close(i); 1577 } 1578 1579 /* LOG_NDELAY sets up the logging connection immediately, necessary for 1580 anonymous ftp's that chroot and can't do it later. */ 1581 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 1582 atexit(catchexit); 1583 addrlen = sizeof(his_addr); 1584 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) { 1585 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); 1586 exit(1); 1587 } 1588 addrlen = sizeof(ctrl_addr); 1589 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) { 1590 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]); 1591 exit(1); 1592 } 1593#ifdef IP_TOS 1594 tos = IPTOS_LOWDELAY; 1595 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0) 1596 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 1597#endif 1598 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 1599 debug = 0; 1600#if DOTITLE 1601 /* Save start and extent of argv for setproctitle. */ 1602 Argv = argv; 1603 while (*envp) 1604 envp++; 1605 LastArgv = envp[-1] + strlen(envp[-1]); 1606#endif /* DOTITLE */ 1607 1608 argc--, argv++; 1609 while (argc > 0 && *argv[0] == '-') { 1610 for (cp = &argv[0][1]; *cp; cp++) 1611 switch (*cp) { 1612 1613 case 'v': 1614 debug = 1; 1615 break; 1616 1617 case 'd': 1618 debug = 1; 1619 break; 1620 1621 case 'l': 1622 break; 1623 1624 case 't': 1625 timeout = atoi(++cp); 1626 if (maxtimeout < timeout) 1627 maxtimeout = timeout; 1628 goto nextopt; 1629 1630 case 'T': 1631 maxtimeout = atoi(++cp); 1632 if (timeout > maxtimeout) 1633 timeout = maxtimeout; 1634 goto nextopt; 1635 1636 case 'u': 1637 { 1638 int val = 0; 1639 1640 while (*++cp && *cp >= '0' && *cp <= '9') 1641 val = val * 8 + *cp - '0'; 1642 if (*cp) 1643 fprintf(stderr, "ftpd: Bad value for -u\n"); 1644 else 1645 defumask = val; 1646 goto nextopt; 1647 } 1648 1649 default: 1650 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 1651 *cp); 1652 break; 1653 } 1654nextopt: 1655 argc--, argv++; 1656 } 1657 freopen(_PATH_DEVNULL, "w", stderr); 1658 signal(SIGCHLD, SIG_IGN); 1659 enable_signalling(); 1660 1661 /* Try to handle urgent data inline */ 1662#ifdef SO_OOBINLINE 1663 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0) 1664 syslog(LOG_ERR, "setsockopt: %m"); 1665#endif 1666 1667#ifdef F_SETOWN 1668 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 1669 syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 1670#endif 1671 dolog(&his_addr); 1672 /* Set up default state */ 1673 data = -1; 1674 type = TYPE_A; 1675 form = FORM_N; 1676 stru = STRU_F; 1677 mode = MODE_S; 1678 tmpline[0] = '\0'; 1679 af_pwok = opieaccessfile(remotehost); 1680 1681 { 1682 FILE *fd; 1683 char line[128]; 1684 1685 /* If logins are disabled, print out the message. */ 1686 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 1687 while (fgets(line, sizeof(line), fd) != NULL) { 1688 if ((cp = strchr(line, '\n')) != NULL) 1689 *cp = '\0'; 1690 lreply(530, "%s", line); 1691 } 1692 (void) fflush(stdout); 1693 (void) fclose(fd); 1694 reply(530, "System not available."); 1695 exit(0); 1696 } 1697 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 1698 while (fgets(line, sizeof(line), fd) != NULL) { 1699 if ((cp = strchr(line, '\n')) != NULL) 1700 *cp = '\0'; 1701 lreply(220, "%s", line); 1702 } 1703 (void) fflush(stdout); 1704 (void) fclose(fd); 1705 /* reply(220,) must follow */ 1706 } 1707 }; 1708 1709 reply(220, "FTP server ready."); 1710 1711 setjmp(errcatch); 1712 for (;;) 1713 yyparse(); 1714 /* NOTREACHED */ 1715 return 0; 1716} 1717