1220422Sgabor/* opieftpd.c: Main program for an FTP daemon. 2220422Sgabor 3210389Sgabor%%% portions-copyright-cmetz-96 4210389SgaborPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights 5210389SgaborReserved. The Inner Net License Version 2 applies to these portions of 6211496Sdesthe software. 7210389SgaborYou should have received a copy of the license with this software. If 8210389Sgaboryou didn't get a copy, you may request one from <license@inner.net>. 9210389Sgabor 10210389SgaborPortions of this software are Copyright 1995 by Randall Atkinson and Dan 11210389SgaborMcDonald, All Rights Reserved. All Rights under this copyright are assigned 12210389Sgaborto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 13210389SgaborLicense Agreement applies to this software. 14210389Sgabor 15210389Sgabor History: 16210389Sgabor 17210389Sgabor Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use 18210389Sgabor opiestrncpy(). Fix incorrect use of setproctitle(). 19210389Sgabor Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's 20210389Sgabor done already (and conditionally) in opie_cfg.h. 21210389Sgabor Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes. 22210389Sgabor Merged in a security fix to BSD-derived ftpds. 23210389Sgabor Modified by cmetz for OPIE 2.3. Fixed the filename at the top. 24210389Sgabor Moved LS_COMMAND here. 25210389Sgabor Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al. 26210389Sgabor Removed useless strings (I don't think that removing the 27210389Sgabor ucb copyright one is a problem -- please let me know if 28210389Sgabor I'm wrong). Changed default CMASK to 077. Removed random 29210389Sgabor comments. Use ANSI stdargs for reply/lreply if we can, 30210389Sgabor added stdargs version of reply/lreply. Don't declare the 31210389Sgabor tos variable unless IP_TOS defined. Include stdargs headers 32210389Sgabor early. More headers ifdefed. Made everything static. 33210389Sgabor Got rid of gethostname() call and use of hostname. Pared 34210389Sgabor down status response for places where header files frequently 35210389Sgabor cause trouble. Made logging of user logins (ala -l) 36210389Sgabor non-optional. Moved reply()/lrepy(). Fixed some prototypes. 37210389Sgabor Modified at NRL for OPIE 2.1. Added declaration of envp. Discard 38210389Sgabor result of opiechallenge (allows access control to work). 39210389Sgabor Added patches for AIX. Symbol changes for autoconf. 40210389Sgabor Modified at NRL for OPIE 2.01. Changed password lookup handling 41210389Sgabor to avoid problems with drain-bamaged shadow password packages. 42210389Sgabor Properly handle internal state for anonymous FTP. Unlock 43210389Sgabor user accounts properly if login fails because of /etc/shells. 44210578Sgabor Make sure to close syslog by function to avoid problems with 45210389Sgabor drain bamaged syslog implementations. 46210389Sgabor Modified at NRL for OPIE 2.0. 47210389Sgabor Originally from BSD Net/2. 48210389Sgabor 49210389Sgabor There is some really, really ugly code in here. 50210389Sgabor 51210389Sgabor$FreeBSD$ 52210389Sgabor*/ 53210389Sgabor/* 54210389Sgabor * Copyright (c) 1985, 1988, 1990 Regents of the University of California. 55210389Sgabor * All rights reserved. 56210389Sgabor * 57210578Sgabor * Redistribution and use in source and binary forms, with or without 58210578Sgabor * modification, are permitted provided that the following conditions 59210578Sgabor * are met: 60220421Sgabor * 1. Redistributions of source code must retain the above copyright 61210578Sgabor * notice, this list of conditions and the following disclaimer. 62210578Sgabor * 2. Redistributions in binary form must reproduce the above copyright 63210578Sgabor * notice, this list of conditions and the following disclaimer in the 64220421Sgabor * documentation and/or other materials provided with the distribution. 65210578Sgabor * 3. All advertising materials mentioning features or use of this software 66210578Sgabor * must display the following acknowledgement: 67220421Sgabor * This product includes software developed by the University of 68220421Sgabor * California, Berkeley and its contributors. 69210578Sgabor * 4. Neither the name of the University nor the names of its contributors 70210578Sgabor * may be used to endorse or promote products derived from this software 71210578Sgabor * without specific prior written permission. 72210578Sgabor * 73210578Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 74210578Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 75210578Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 76210578Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 77210578Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 78211364Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 79210578Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 80210578Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 81210578Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 82210578Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 83210578Sgabor * SUCH DAMAGE. 84210578Sgabor */ 85210578Sgabor 86210578Sgabor#include "opie_cfg.h" 87210578Sgabor 88210578Sgabor#if HAVE_ANSISTDARG 89210578Sgabor#include <stdarg.h> 90210578Sgabor#endif /* HAVE_ANSISTDARG */ 91210578Sgabor 92210578Sgabor/* 93210578Sgabor * FTP server. 94210578Sgabor */ 95210578Sgabor 96210578Sgabor#if HAVE_SYS_PARAM_H 97210389Sgabor#include <sys/param.h> 98210389Sgabor#endif /* HAVE_SYS_PARAM_H */ 99210389Sgabor#include <sys/stat.h> 100210389Sgabor/* #include <sys/ioctl.h> */ 101210389Sgabor#include <sys/socket.h> 102210389Sgabor#include <sys/wait.h> 103210389Sgabor#ifdef SYS_FCNTL_H 104210389Sgabor#include <sys/fcntl.h> 105210389Sgabor#else 106210430Sdelphij#include <fcntl.h> 107210389Sgabor#endif /* SYS_FCNTL_H */ 108210389Sgabor#include <sys/types.h> 109210389Sgabor 110210389Sgabor#include <netinet/in.h> 111210389Sgabor#include <netinet/in_systm.h> 112210389Sgabor#include <netinet/ip.h> 113210389Sgabor 114210389Sgabor#define FTP_NAMES 115210389Sgabor#include <arpa/ftp.h> 116210389Sgabor#include <arpa/inet.h> 117210389Sgabor#include <arpa/telnet.h> 118210389Sgabor 119210389Sgabor#include <signal.h> 120210389Sgabor#include <fcntl.h> 121210389Sgabor#if HAVE_TIME_H 122210389Sgabor#include <time.h> 123210389Sgabor#endif /* HAVE_TIME_H */ 124210389Sgabor#if HAVE_PWD_H 125210389Sgabor#include <pwd.h> 126210389Sgabor#endif /* HAVE_PWD_H */ 127210430Sdelphij#include <setjmp.h> 128210389Sgabor#include <netdb.h> 129210389Sgabor#include <errno.h> 130210389Sgabor#include <syslog.h> 131210389Sgabor#if HAVE_UNISTD_H 132210389Sgabor#include <unistd.h> 133210389Sgabor#endif /* HAVE_UNISTD_H */ 134210389Sgabor#include <stdio.h> 135210389Sgabor#include <ctype.h> 136210389Sgabor#include <stdlib.h> 137210389Sgabor#include <string.h> 138210389Sgabor#include <grp.h> 139210389Sgabor 140210389Sgabor#include "opie.h" 141210389Sgabor 142210389Sgabor#if HAVE_SHADOW_H 143210389Sgabor#include <shadow.h> 144210389Sgabor#endif /* HAVE_SHADOW_H */ 145210389Sgabor 146210389Sgabor#if HAVE_CRYPT_H 147210578Sgabor#include <crypt.h> 148210430Sdelphij#endif /* HAVE_CRYPT_H */ 149210430Sdelphij 150210430Sdelphij#if HAVE_SYS_UTSNAME_H 151211364Sgabor#include <sys/utsname.h> 152211364Sgabor#endif /* HAVE_SYS_UTSNAME_H */ 153211364Sgabor 154210430Sdelphij#ifdef _AIX 155210578Sgabor#include <sys/id.h> 156210430Sdelphij#include <sys/priv.h> 157210430Sdelphij#endif /* _AIX */ 158210389Sgabor 159210578Sgabor#ifdef IP_TOS 160210578Sgabor#ifndef IPTOS_THROUGHPUT 161210389Sgabor#undef IP_TOS 162210389Sgabor#endif /* !IPTOS_THROUGHPUT */ 163210389Sgabor#ifndef IPTOS_LOWDELAY 164210389Sgabor#undef IP_TOS 165210389Sgabor#endif /* !IPTOS_LOWDELAY */ 166210389Sgabor#endif /* IP_TOS */ 167210389Sgabor 168210430Sdelphijextern int errno; 169210389Sgaborextern char *home; /* pointer to home directory for glob */ 170210389Sgaborextern FILE *ftpd_popen __P((char *, char *)); 171210389Sgaborextern int ftpd_pclose __P((FILE *)); 172210389Sgaborextern char cbuf[]; 173210389Sgaborextern off_t restart_point; 174210389Sgabor 175210389Sgaborstatic struct sockaddr_in ctrl_addr; 176210389Sgaborstatic struct sockaddr_in data_source; 177210389Sgaborstruct sockaddr_in data_dest; 178210389Sgaborstruct sockaddr_in his_addr; 179210389Sgaborstatic struct sockaddr_in pasv_addr; 180210389Sgabor 181210389Sgaborstatic int data; 182210389Sgaborjmp_buf errcatch; 183210389Sgaborstatic jmp_buf urgcatch; 184210389Sgaborint logged_in; 185210389Sgaborstruct passwd *pw; 186210389Sgaborint debug; 187210389Sgaborint timeout = 900; /* timeout after 15 minutes of inactivity */ 188210389Sgaborint maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */ 189210389Sgabor 190211463Sgabor#if DOANONYMOUS 191210389Sgaborstatic int guest; 192210389Sgabor#endif /* DOANONYMOUS */ 193210389Sgaborint type; 194210389Sgaborint form; 195210389Sgaborstatic int stru; /* avoid C keyword */ 196210389Sgaborstatic int mode; 197210389Sgaborint usedefault = 1; /* for data transfers */ 198210389Sgaborint pdata = -1; /* for passive mode */ 199210389Sgaborstatic int transflag; 200210389Sgaborstatic off_t file_size; 201210389Sgaborstatic off_t byte_count; 202210389Sgabor 203210389Sgabor#if (!defined(CMASK) || CMASK == 0) 204210389Sgabor#undef CMASK 205210389Sgabor#define CMASK 077 206210389Sgabor#endif 207210389Sgabor 208210389Sgaborstatic int defumask = CMASK; /* default umask value */ 209210389Sgaborchar tmpline[7]; 210210389Sgaborchar remotehost[MAXHOSTNAMELEN]; 211210389Sgabor 212210389Sgabor/* 213210389Sgabor * Timeout intervals for retrying connections 214210389Sgabor * to hosts that don't accept PORT cmds. This 215210389Sgabor * is a kludge, but given the problems with TCP... 216210389Sgabor */ 217210389Sgabor#define SWAITMAX 90 /* wait at most 90 seconds */ 218210389Sgabor#define SWAITINT 5 /* interval between retries */ 219210389Sgabor 220210389Sgaborstatic int swaitmax = SWAITMAX; 221211463Sgaborstatic int swaitint = SWAITINT; 222210389Sgabor 223210389Sgabor#if DOTITLE 224210389Sgaborstatic char **Argv = NULL; /* pointer to argument vector */ 225210389Sgaborstatic char *LastArgv = NULL; /* end of argv */ 226210389Sgaborstatic char proctitle[BUFSIZ]; /* initial part of title */ 227210389Sgabor#endif /* DOTITLE */ 228210389Sgabor 229210389Sgaborstatic int af_pwok = 0, pwok = 0; 230210389Sgaborstatic struct opie opiestate; 231210389Sgabor 232210389SgaborVOIDRET perror_reply __P((int, char *)); 233210389SgaborVOIDRET dologout __P((int)); 234210430Sdelphijchar *getline __P((char *, int, FILE *)); 235210389SgaborVOIDRET upper __P((char *)); 236210389Sgabor 237210389Sgaborstatic VOIDRET lostconn __P((int)); 238210389Sgaborstatic VOIDRET myoob __P((int)); 239210389Sgaborstatic FILE *getdatasock __P((char *)); 240210389Sgaborstatic FILE *dataconn __P((char *, off_t, char *)); 241210389Sgaborstatic int checkuser __P((char *)); 242210389Sgaborstatic VOIDRET end_login __P((void)); 243210389Sgaborstatic VOIDRET send_data __P((FILE *, FILE *, off_t)); 244210389Sgaborstatic int receive_data __P((FILE *, FILE *)); 245210389Sgaborstatic char *gunique __P((char *)); 246210389Sgaborstatic char *sgetsave __P((char *)); 247210389Sgabor 248210389Sgaborint opielogwtmp __P((char *, char *, char *, char *)); 249210389Sgabor 250210389Sgaborint fclose __P((FILE *)); 251210389Sgabor 252210389Sgabor#ifdef HAVE_ANSISTDARG 253210389SgaborVOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 254210389Sgabor{ 255210389Sgabor va_list ap; 256210389Sgabor char buffer[1024]; 257210389Sgabor 258210389Sgabor va_start(ap, fmt); 259210389Sgabor vsprintf(buffer, fmt, ap); 260210389Sgabor va_end(ap); 261210461Sgabor 262210389Sgabor printf("%d %s\r\n", n, buffer); 263210461Sgabor fflush(stdout); 264210389Sgabor if (debug) 265210389Sgabor syslog(LOG_DEBUG, "<--- %d %s", n, buffer); 266210389Sgabor} 267210622Sgabor#else /* HAVE_ANSISTDARG */ 268210389SgaborVOIDRET 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) 269210430Sdelphij{ 270210389Sgabor printf("%d ", n); 271210389Sgabor printf(fmt, p0, p1, p2, p3, p4, p5); 272210389Sgabor printf("\r\n"); 273210389Sgabor fflush(stdout); 274210389Sgabor if (debug) { 275210389Sgabor syslog(LOG_DEBUG, "<--- %d ", n); 276210389Sgabor syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 277210389Sgabor } 278210389Sgabor} 279210389Sgabor#endif /* HAVE_ANSISTDARG */ 280210389Sgabor 281210389Sgabor#ifdef HAVE_ANSISTDARG 282210389SgaborVOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 283220421Sgabor{ 284210389Sgabor va_list ap; 285210389Sgabor char buffer[1024]; 286210389Sgabor 287210389Sgabor va_start(ap, fmt); 288210389Sgabor vsprintf(buffer, fmt, ap); 289210389Sgabor va_end(ap); 290210389Sgabor 291210389Sgabor printf("%d- %s\r\n", n, buffer); 292210389Sgabor fflush(stdout); 293210389Sgabor if (debug) 294210389Sgabor syslog(LOG_DEBUG, "<--- %d- %s", n, buffer); 295210389Sgabor} 296210389Sgabor#else /* HAVE_ANSISTDARG */ 297210389SgaborVOIDRET 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) 298210389Sgabor{ 299210389Sgabor printf("%d- ", n); 300210389Sgabor printf(fmt, p0, p1, p2, p3, p4, p5); 301210389Sgabor printf("\r\n"); 302210389Sgabor fflush(stdout); 303210389Sgabor if (debug) { 304210389Sgabor syslog(LOG_DEBUG, "<--- %d- ", n); 305210389Sgabor syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 306210389Sgabor } 307210389Sgabor} 308210389Sgabor#endif /* HAVE_ANSISTDARG */ 309210389Sgabor 310210389SgaborVOIDRET enable_signalling FUNCTION_NOARGS 311210389Sgabor{ 312210389Sgabor signal(SIGPIPE, lostconn); 313210389Sgabor if ((int)signal(SIGURG, myoob) < 0) 314210389Sgabor syslog(LOG_ERR, "signal: %m"); 315210389Sgabor} 316210389Sgabor 317210389SgaborVOIDRET disable_signalling FUNCTION_NOARGS 318210389Sgabor{ 319210389Sgabor signal(SIGPIPE, SIG_IGN); 320210389Sgabor if ((int)signal(SIGURG, SIG_IGN) < 0) 321210389Sgabor syslog(LOG_ERR, "signal: %m"); 322210389Sgabor} 323210389Sgabor 324220421Sgaborstatic VOIDRET lostconn FUNCTION((input), int input) 325220421Sgabor{ 326211364Sgabor if (debug) 327210389Sgabor syslog(LOG_DEBUG, "lost connection"); 328211364Sgabor dologout(-1); 329211364Sgabor} 330211364Sgabor 331211364Sgaborstatic char ttyline[20]; 332210389Sgabor 333211364Sgabor/* 334211364Sgabor * Helper function for sgetpwnam(). 335211364Sgabor */ 336211364Sgaborstatic char *sgetsave FUNCTION((s), char *s) 337211364Sgabor{ 338211364Sgabor char *new = malloc((unsigned) strlen(s) + 1); 339210389Sgabor 340210389Sgabor if (new == NULL) { 341210389Sgabor perror_reply(421, "Local resource failure: malloc"); 342210389Sgabor dologout(1); 343210389Sgabor /* NOTREACHED */ 344210389Sgabor } 345210389Sgabor strcpy(new, s); 346210461Sgabor return (new); 347210461Sgabor} 348210389Sgabor 349210389Sgabor/* 350210389Sgabor * Save the result of a getpwnam. Used for USER command, since 351210389Sgabor * the data returned must not be clobbered by any other command 352210389Sgabor * (e.g., globbing). 353210389Sgabor */ 354210389Sgaborstatic struct passwd *sgetpwnam FUNCTION((name), char *name) 355210389Sgabor{ 356210461Sgabor static struct passwd save; 357210389Sgabor register struct passwd *p; 358210389Sgabor 359210389Sgabor#if HAVE_SHADOW 360210389Sgabor struct spwd *spwd; 361210389Sgabor#endif /* HAVE_SHADOW */ 362210389Sgabor 363210389Sgabor if ((p = getpwnam(name)) == NULL) 364210389Sgabor return (p); 365210389Sgabor 366210389Sgabor#if HAVE_SHADOW 367210389Sgabor if ((spwd = getspnam(name)) == NULL) 368210389Sgabor return NULL; 369210479Sgabor 370210389Sgabor endspent(); 371210389Sgabor 372210389Sgabor p->pw_passwd = spwd->sp_pwdp; 373210389Sgabor#endif /* HAVE_SHADOW */ 374210389Sgabor 375210389Sgabor endpwent(); 376210389Sgabor 377210389Sgabor if (save.pw_name) { 378210389Sgabor free(save.pw_name); 379210389Sgabor free(save.pw_passwd); 380210389Sgabor free(save.pw_gecos); 381210389Sgabor free(save.pw_dir); 382210389Sgabor free(save.pw_shell); 383210389Sgabor } 384210389Sgabor save = *p; 385210389Sgabor save.pw_name = sgetsave(p->pw_name); 386210389Sgabor save.pw_passwd = sgetsave(p->pw_passwd); 387210389Sgabor save.pw_gecos = sgetsave(p->pw_gecos); 388210389Sgabor save.pw_dir = sgetsave(p->pw_dir); 389210389Sgabor save.pw_shell = sgetsave(p->pw_shell); 390210389Sgabor return (&save); 391210389Sgabor} 392210389Sgabor 393210389Sgaborint login_attempts; /* number of failed login attempts */ 394210389Sgaborint askpasswd; /* had user command, ask for passwd */ 395210389Sgabor 396210389Sgabor/* 397210389Sgabor * USER command. 398210389Sgabor * Sets global passwd pointer pw if named account exists and is acceptable; 399210389Sgabor * sets askpasswd if a PASS command is expected. If logged in previously, 400210389Sgabor * need to reset state. If name is "ftp" or "anonymous", the name is not in 401210389Sgabor * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 402210389Sgabor * If account doesn't exist, ask for passwd anyway. Otherwise, check user 403210389Sgabor * requesting login privileges. Disallow anyone who does not have a standard 404210389Sgabor * shell as returned by getusershell(). Disallow anyone mentioned in the file 405210389Sgabor * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 406210389Sgabor */ 407210389Sgaborint user FUNCTION((name), char *name) 408210389Sgabor{ 409210389Sgabor register char *cp; 410210389Sgabor char *shell; 411210389Sgabor 412210389Sgabor if (logged_in) { 413210389Sgabor#if DOANONYMOUS 414210389Sgabor if (guest) { 415210389Sgabor reply(530, "Can't change user from guest login."); 416210389Sgabor return -1; 417210389Sgabor } 418210389Sgabor#endif /* DOANONMOUS */ 419210389Sgabor end_login(); 420210389Sgabor } 421210389Sgabor askpasswd = 1; 422210389Sgabor#if DOANONYMOUS 423210389Sgabor guest = 0; 424210389Sgabor if (!strcmp(name, "ftp") || !strcmp(name, "anonymous")) 425210389Sgabor if (!checkuser("ftp") && !checkuser("anonymous")) 426210389Sgabor if ((pw = sgetpwnam("ftp")) != NULL) { 427210389Sgabor guest = 1; 428210389Sgabor askpasswd = 1; 429210389Sgabor reply(331, "Guest login ok, send your e-mail address as your password."); 430210389Sgabor syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost); 431210389Sgabor return 0; 432210389Sgabor } 433210389Sgabor#endif /* DOANONYMOUS */ 434210389Sgabor if (pw = sgetpwnam(name)) { 435210578Sgabor if ((shell = pw->pw_shell) == NULL || *shell == 0) 436210578Sgabor shell = _PATH_BSHELL; 437210578Sgabor while ((cp = getusershell()) != NULL) 438210578Sgabor if (!strcmp(cp, shell)) 439210578Sgabor break; 440210578Sgabor endusershell(); 441210578Sgabor if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) { 442210578Sgabor#if DEBUG 443210578Sgabor if (!cp) 444210578Sgabor syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell); 445210578Sgabor if (checkuser(name)) 446210578Sgabor syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?"); 447210578Sgabor if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) 448210389Sgabor syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd); 449210389Sgabor#endif /* DEBUG */ 450210389Sgabor pw = (struct passwd *) NULL; 451210389Sgabor askpasswd = -1; 452210389Sgabor } 453210389Sgabor } 454210389Sgabor { 455210389Sgabor char prompt[OPIE_CHALLENGE_MAX + 1]; 456210389Sgabor 457210389Sgabor opiechallenge(&opiestate, name, prompt); 458210389Sgabor 459210389Sgabor if (askpasswd == -1) { 460210389Sgabor syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost); 461210389Sgabor pwok = 0; 462210389Sgabor } else 463210389Sgabor pwok = af_pwok && opiealways(pw->pw_dir); 464210389Sgabor 465210389Sgabor#if NEW_PROMPTS 466210389Sgabor reply(331, "Response to %s %s for %s.", prompt, 467210389Sgabor#else /* NEW_PROMPTS */ 468210389Sgabor reply(331, "OTP response %s %s for %s.", prompt, 469210389Sgabor#endif /* NEW_PROMPTS */ 470210389Sgabor pwok ? "requested" : "required", name); 471210389Sgabor } 472210389Sgabor /* Delay before reading passwd after first failed attempt to slow down 473210389Sgabor passwd-guessing programs. */ 474210389Sgabor if (login_attempts) 475210389Sgabor sleep((unsigned) login_attempts); 476210389Sgabor 477210389Sgabor return 0; 478210389Sgabor} 479210389Sgabor 480210389Sgabor/* 481210389Sgabor * Check if a user is in the file _PATH_FTPUSERS 482210389Sgabor */ 483210389Sgaborstatic int checkuser FUNCTION((name), char *name) 484210389Sgabor{ 485210389Sgabor register FILE *fd; 486210389Sgabor register char *p; 487210389Sgabor char line[BUFSIZ]; 488210389Sgabor 489210389Sgabor if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 490210389Sgabor while (fgets(line, sizeof(line), fd) != NULL) 491210389Sgabor if ((p = strchr(line, '\n')) != NULL) { 492210389Sgabor *p = '\0'; 493210389Sgabor if (line[0] == '#') 494210389Sgabor continue; 495210389Sgabor if (!strcmp(line, name)) { 496210389Sgabor fclose(fd); 497210389Sgabor return (1); 498210389Sgabor } 499210389Sgabor } 500210389Sgabor fclose(fd); 501210389Sgabor } 502210389Sgabor return (0); 503210389Sgabor} 504210389Sgabor 505210389Sgabor/* 506210389Sgabor * Terminate login as previous user, if any, resetting state; 507 * used when USER command is given or login fails. 508 */ 509static VOIDRET end_login FUNCTION_NOARGS 510{ 511 disable_signalling(); 512 if (seteuid((uid_t) 0)) 513 syslog(LOG_ERR, "Can't set euid"); 514 if (logged_in) 515 opielogwtmp(ttyline, "", "", "ftp"); 516 pw = NULL; 517 logged_in = 0; 518#if DOANONYMOUS 519 guest = 0; 520#endif /* DOANONYMOUS */ 521 enable_signalling(); 522} 523 524VOIDRET pass FUNCTION((passwd), char *passwd) 525{ 526 int legit = askpasswd + 1, i; 527 528 if (logged_in || askpasswd == 0) { 529 reply(503, "Login with USER first."); 530 return; 531 } 532 askpasswd = 0; 533 534#if DOANONYMOUS 535 if (!guest) { /* "ftp" is only account allowed no password */ 536#endif /* DOANONYMOUS */ 537 i = opieverify(&opiestate, passwd); 538 if (legit && i && pwok) 539 i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd); 540 if (!legit || i) { 541 reply(530, "Login incorrect."); 542 pw = NULL; 543 if (login_attempts++ >= 5) { 544 syslog(LOG_WARNING, 545 "Repeated login failures for user %s from %s", 546 pw->pw_name, remotehost); 547 exit(0); 548 } 549 return; 550 } 551#if DOANONYMOUS 552 } else 553 if ((passwd[0] <= ' ') || checkuser(passwd)) { 554 reply(530, "No identity, no service."); 555 syslog(LOG_DEBUG, "Bogus address: %s", passwd); 556 exit(0); 557 } 558#endif /* DOANONYMOUS */ 559 login_attempts = 0; /* this time successful */ 560 if (setegid((gid_t) pw->pw_gid) < 0) { 561 reply(550, "Can't set gid."); 562 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno); 563 return; 564 } 565 initgroups(pw->pw_name, pw->pw_gid); 566 567 /* open wtmp before chroot */ 568 sprintf(ttyline, "ftp%d", getpid()); 569 opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp"); 570 logged_in = 1; 571 572#if DOANONYMOUS 573 if (guest) { 574 /* We MUST do a chdir() after the chroot. Otherwise the old current 575 directory will be accessible as "." outside the new root! */ 576 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 577 reply(550, "Can't set guest privileges."); 578 goto bad; 579 } 580 } else 581#endif /* DOANONYMOUS */ 582 if (chdir(pw->pw_dir) < 0) { 583 if (chdir("/") < 0) { 584 reply(530, "User %s: can't change directory to %s.", 585 pw->pw_name, pw->pw_dir); 586 goto bad; 587 } else 588 lreply(230, "No directory! Logging in with home=/"); 589 } 590/* This patch was contributed by an OPIE user. We don't know what it 591 does, exactly. It may or may not work. */ 592#ifdef _AIX 593 { 594 priv_t priv; 595 priv.pv_priv[0] = 0; 596 priv.pv_priv[1] = 0; 597 setgroups(NULL, NULL); 598 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH, 599 &priv, sizeof(priv_t)) < 0 || 600 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 || 601 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 || 602 seteuid((uid_t)pw->pw_uid) < 0) { 603 reply(550, "Can't set uid (_AIX3)."); 604 goto bad; 605 } 606 } 607#else /* _AIX */ 608 if (seteuid((uid_t) pw->pw_uid) < 0) { 609 reply(550, "Can't set uid."); 610 goto bad; 611 } 612#endif /* _AIX */ 613 /* 614 * Display a login message, if it exists. 615 * N.B. reply(230,) must follow the message. 616 */ 617 { 618 FILE *fd; 619 620 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 621 char *cp, line[128]; 622 623 while (fgets(line, sizeof(line), fd) != NULL) { 624 if ((cp = strchr(line, '\n')) != NULL) 625 *cp = '\0'; 626 lreply(230, "%s", line); 627 } 628 (void) fflush(stdout); 629 (void) fclose(fd); 630 } 631 } 632#if DOANONYMOUS 633 if (guest) { 634 reply(230, "Guest login ok, access restrictions apply."); 635#if DOTITLE 636 setproctitle("%s: anonymous/%.*s", remotehost, 637 sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"), 638 passwd); 639#endif /* DOTITLE */ 640 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s", 641 remotehost, passwd); 642 } else 643#endif /* DOANONYMOUS */ 644 { 645 reply(230, "User %s logged in.", pw->pw_name); 646 647#if DOTITLE 648 setproctitle("%s: %s", remotehost, pw->pw_name); 649#endif /* DOTITLE */ 650 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name); 651 } 652 home = pw->pw_dir; /* home dir for globbing */ 653 umask(defumask); 654 return; 655 656bad: 657 /* Forget all about it... */ 658 end_login(); 659} 660 661VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name) 662{ 663 FILE *fin, *dout; 664 struct stat st; 665 int (*closefunc) (); 666 667 if (cmd == 0) { 668 fin = fopen(name, "r"), closefunc = fclose; 669 st.st_size = 0; 670 } else { 671 char line[BUFSIZ]; 672 673 snprintf(line, sizeof(line), cmd, name); 674 name = line; 675 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 676 st.st_size = -1; 677#if HAVE_ST_BLKSIZE 678 st.st_blksize = BUFSIZ; 679#endif /* HAVE_ST_BLKSIZE */ 680 } 681 if (fin == NULL) { 682 if (errno != 0) 683 perror_reply(550, name); 684 return; 685 } 686 if (cmd == 0 && 687 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) { 688 reply(550, "%s: not a plain file.", name); 689 goto done; 690 } 691 if (restart_point) { 692 if (type == TYPE_A) { 693 register int i, n, c; 694 695 n = restart_point; 696 i = 0; 697 while (i++ < n) { 698 if ((c = getc(fin)) == EOF) { 699 perror_reply(550, name); 700 goto done; 701 } 702 if (c == '\n') 703 i++; 704 } 705 } else 706 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) { 707 perror_reply(550, name); 708 goto done; 709 } 710 } 711 dout = dataconn(name, st.st_size, "w"); 712 if (dout == NULL) 713 goto done; 714#if HAVE_ST_BLKSIZE 715 send_data(fin, dout, st.st_blksize); 716#else /* HAVE_ST_BLKSIZE */ 717 send_data(fin, dout, BUFSIZ); 718#endif /* HAVE_ST_BLKSIZE */ 719 fclose(dout); 720 data = -1; 721 pdata = -1; 722done: 723 (*closefunc) (fin); 724} 725 726VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique) 727{ 728 FILE *fout, *din; 729 struct stat st; 730 int (*closefunc) (); 731 732 if (unique && stat(name, &st) == 0 && 733 (name = gunique(name)) == NULL) 734 return; 735 736 if (restart_point) 737 mode = "r+w"; 738 fout = fopen(name, mode); 739 closefunc = fclose; 740 if (fout == NULL) { 741 perror_reply(553, name); 742 return; 743 } 744 if (restart_point) { 745 if (type == TYPE_A) { 746 register int i, n, c; 747 748 n = restart_point; 749 i = 0; 750 while (i++ < n) { 751 if ((c = getc(fout)) == EOF) { 752 perror_reply(550, name); 753 goto done; 754 } 755 if (c == '\n') 756 i++; 757 } 758 /* We must do this seek to "current" position because we are changing 759 from reading to writing. */ 760 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) { 761 perror_reply(550, name); 762 goto done; 763 } 764 } else 765 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) { 766 perror_reply(550, name); 767 goto done; 768 } 769 } 770 din = dataconn(name, (off_t) - 1, "r"); 771 if (din == NULL) 772 goto done; 773 if (receive_data(din, fout) == 0) { 774 if (unique) 775 reply(226, "Transfer complete (unique file name:%s).", 776 name); 777 else 778 reply(226, "Transfer complete."); 779 } 780 fclose(din); 781 data = -1; 782 pdata = -1; 783done: 784 (*closefunc) (fout); 785} 786 787static FILE *getdatasock FUNCTION((mode), char *mode) 788{ 789 int s, on = 1, tries; 790 791 if (data >= 0) 792 return (fdopen(data, mode)); 793 disable_signalling(); 794 if (seteuid((uid_t) 0)) 795 syslog(LOG_ERR, "Can't set euid"); 796 s = socket(AF_INET, SOCK_STREAM, 0); 797 if (s < 0) 798 goto bad; 799 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 800 (char *) &on, sizeof(on)) < 0) 801 goto bad; 802 /* anchor socket to avoid multi-homing problems */ 803 data_source.sin_family = AF_INET; 804 data_source.sin_addr = ctrl_addr.sin_addr; 805 for (tries = 1;; tries++) { 806 if (bind(s, (struct sockaddr *) & data_source, 807 sizeof(data_source)) >= 0) 808 break; 809 if (errno != EADDRINUSE || tries > 10) 810 goto bad; 811 sleep(tries); 812 } 813 if (seteuid((uid_t) pw->pw_uid)) 814 syslog(LOG_ERR, "Can't set euid"); 815 enable_signalling(); 816#ifdef IP_TOS 817 on = IPTOS_THROUGHPUT; 818 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0) 819 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 820#endif 821 return (fdopen(s, mode)); 822bad: 823 { 824 int t = errno; 825 826 if (seteuid((uid_t) pw->pw_uid)) 827 syslog(LOG_ERR, "Can't set euid"); 828 enable_signalling(); 829 close(s); 830 831 errno = t; 832 } 833 return (NULL); 834} 835 836static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode) 837{ 838 char sizebuf[32]; 839 FILE *file; 840 int retry = 0; 841#ifdef IP_TOS 842 int tos; 843#endif /* IP_TOS */ 844 845 file_size = size; 846 byte_count = 0; 847 if (size != (off_t) - 1) 848 snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size); 849 else 850 strcpy(sizebuf, ""); 851 if (pdata >= 0) { 852 struct sockaddr_in from; 853 int s, fromlen = sizeof(from); 854 855 s = accept(pdata, (struct sockaddr *) & from, &fromlen); 856 if (s < 0) { 857 reply(425, "Can't open data connection."); 858 close(pdata); 859 pdata = -1; 860 return (NULL); 861 } 862 close(pdata); 863 pdata = s; 864#ifdef IP_TOS 865 tos = IPTOS_LOWDELAY; 866 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos, 867 sizeof(int)); 868 869#endif 870 reply(150, "Opening %s mode data connection for %s%s.", 871 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 872 return (fdopen(pdata, mode)); 873 } 874 if (data >= 0) { 875 reply(125, "Using existing data connection for %s%s.", 876 name, sizebuf); 877 usedefault = 1; 878 return (fdopen(data, mode)); 879 } 880 if (usedefault) 881 data_dest = his_addr; 882 usedefault = 1; 883 file = getdatasock(mode); 884 if (file == NULL) { 885 reply(425, "Can't create data socket (%s,%d): %s.", 886 inet_ntoa(data_source.sin_addr), 887 ntohs(data_source.sin_port), strerror(errno)); 888 return (NULL); 889 } 890 data = fileno(file); 891 while (connect(data, (struct sockaddr *) & data_dest, 892 sizeof(data_dest)) < 0) { 893 if (errno == EADDRINUSE && retry < swaitmax) { 894 sleep((unsigned) swaitint); 895 retry += swaitint; 896 continue; 897 } 898 perror_reply(425, "Can't build data connection"); 899 fclose(file); 900 data = -1; 901 return (NULL); 902 } 903 reply(150, "Opening %s mode data connection for %s%s.", 904 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 905 return (file); 906} 907 908/* 909 * Tranfer the contents of "instr" to 910 * "outstr" peer using the appropriate 911 * encapsulation of the data subject 912 * to Mode, Structure, and Type. 913 * 914 * NB: Form isn't handled. 915 */ 916static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize) 917{ 918 register int c, cnt; 919 register char *buf; 920 int netfd, filefd; 921 922 transflag++; 923 if (setjmp(urgcatch)) { 924 transflag = 0; 925 return; 926 } 927 switch (type) { 928 929 case TYPE_A: 930 while ((c = getc(instr)) != EOF) { 931 byte_count++; 932 if (c == '\n') { 933 if (ferror(outstr)) 934 goto data_err; 935 putc('\r', outstr); 936 } 937 putc(c, outstr); 938 } 939 fflush(outstr); 940 transflag = 0; 941 if (ferror(instr)) 942 goto file_err; 943 if (ferror(outstr)) 944 goto data_err; 945 reply(226, "Transfer complete."); 946 return; 947 948 case TYPE_I: 949 case TYPE_L: 950 if ((buf = malloc((u_int) blksize)) == NULL) { 951 transflag = 0; 952 perror_reply(451, "Local resource failure: malloc"); 953 return; 954 } 955 netfd = fileno(outstr); 956 filefd = fileno(instr); 957 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 && 958 write(netfd, buf, cnt) == cnt) 959 byte_count += cnt; 960 transflag = 0; 961 free(buf); 962 if (cnt != 0) { 963 if (cnt < 0) 964 goto file_err; 965 goto data_err; 966 } 967 reply(226, "Transfer complete."); 968 return; 969 default: 970 transflag = 0; 971 reply(550, "Unimplemented TYPE %d in send_data", type); 972 return; 973 } 974 975data_err: 976 transflag = 0; 977 perror_reply(426, "Data connection"); 978 return; 979 980file_err: 981 transflag = 0; 982 perror_reply(551, "Error on input file"); 983} 984 985/* 986 * Transfer data from peer to 987 * "outstr" using the appropriate 988 * encapulation of the data subject 989 * to Mode, Structure, and Type. 990 * 991 * N.B.: Form isn't handled. 992 */ 993static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr) 994{ 995 register int c; 996 int cnt, bare_lfs = 0; 997 char buf[BUFSIZ]; 998 999 transflag++; 1000 if (setjmp(urgcatch)) { 1001 transflag = 0; 1002 return (-1); 1003 } 1004 switch (type) { 1005 1006 case TYPE_I: 1007 case TYPE_L: 1008 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 1009 if (write(fileno(outstr), buf, cnt) != cnt) 1010 goto file_err; 1011 byte_count += cnt; 1012 } 1013 if (cnt < 0) 1014 goto data_err; 1015 transflag = 0; 1016 return (0); 1017 1018 case TYPE_E: 1019 reply(553, "TYPE E not implemented."); 1020 transflag = 0; 1021 return (-1); 1022 1023 case TYPE_A: 1024 while ((c = getc(instr)) != EOF) { 1025 byte_count++; 1026 if (c == '\n') 1027 bare_lfs++; 1028 while (c == '\r') { 1029 if (ferror(outstr)) 1030 goto data_err; 1031 if ((c = getc(instr)) != '\n') { 1032 putc('\r', outstr); 1033 if (c == '\0' || c == EOF) 1034 goto contin2; 1035 } 1036 } 1037 putc(c, outstr); 1038 contin2:; 1039 } 1040 fflush(outstr); 1041 if (ferror(instr)) 1042 goto data_err; 1043 if (ferror(outstr)) 1044 goto file_err; 1045 transflag = 0; 1046 if (bare_lfs) { 1047 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 1048 printf(" File may not have transferred correctly.\r\n"); 1049 } 1050 return (0); 1051 default: 1052 reply(550, "Unimplemented TYPE %d in receive_data", type); 1053 transflag = 0; 1054 return (-1); 1055 } 1056 1057data_err: 1058 transflag = 0; 1059 perror_reply(426, "Data Connection"); 1060 return (-1); 1061 1062file_err: 1063 transflag = 0; 1064 perror_reply(452, "Error writing file"); 1065 return (-1); 1066} 1067 1068VOIDRET statfilecmd FUNCTION((filename), char *filename) 1069{ 1070 char line[BUFSIZ]; 1071 FILE *fin; 1072 int c; 1073 1074#if HAVE_LS_G_FLAG 1075 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename); 1076#else /* HAVE_LS_G_FLAG */ 1077 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename); 1078#endif /* HAVE_LS_G_FLAG */ 1079 fin = ftpd_popen(line, "r"); 1080 lreply(211, "status of %s:", filename); 1081 while ((c = getc(fin)) != EOF) { 1082 if (c == '\n') { 1083 if (ferror(stdout)) { 1084 perror_reply(421, "control connection"); 1085 ftpd_pclose(fin); 1086 dologout(1); 1087 /* NOTREACHED */ 1088 } 1089 if (ferror(fin)) { 1090 perror_reply(551, filename); 1091 ftpd_pclose(fin); 1092 return; 1093 } 1094 putc('\r', stdout); 1095 } 1096 putc(c, stdout); 1097 } 1098 ftpd_pclose(fin); 1099 reply(211, "End of Status"); 1100} 1101 1102VOIDRET statcmd FUNCTION_NOARGS 1103{ 1104/* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */ 1105 struct sockaddr_in *sin; 1106 u_char *a, *p; 1107 1108 lreply(211, "FTP server status:"); 1109 printf(" \r\n"); 1110 printf(" Connected to %s", remotehost); 1111 if (!isdigit(remotehost[0])) 1112 printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 1113 printf("\r\n"); 1114 if (logged_in) { 1115#if DOANONYMOUS 1116 if (guest) 1117 printf(" Logged in anonymously\r\n"); 1118 else 1119#endif /* DOANONYMOUS */ 1120 printf(" Logged in as %s\r\n", pw->pw_name); 1121 } else 1122 if (askpasswd) 1123 printf(" Waiting for password\r\n"); 1124 else 1125 printf(" Waiting for user name\r\n"); 1126 if (data != -1) 1127 printf(" Data connection open\r\n"); 1128 else 1129 if (pdata != -1) { 1130 printf(" in Passive mode"); 1131 sin = &pasv_addr; 1132 goto printaddr; 1133 } else 1134 if (usedefault == 0) { 1135 printf(" PORT"); 1136 sin = &data_dest; 1137 printaddr: 1138 a = (u_char *) & sin->sin_addr; 1139 p = (u_char *) & sin->sin_port; 1140#define UC(b) (((int) b) & 0xff) 1141 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 1142 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1143#undef UC 1144 } else 1145 printf(" No data connection\r\n"); 1146 reply(211, "End of status"); 1147} 1148 1149VOIDRET opiefatal FUNCTION((s), char *s) 1150{ 1151 reply(451, "Error in server: %s\n", s); 1152 reply(221, "Closing connection due to server error."); 1153 dologout(0); 1154 /* NOTREACHED */ 1155} 1156 1157static VOIDRET ack FUNCTION((s), char *s) 1158{ 1159 reply(250, "%s command successful.", s); 1160} 1161 1162VOIDRET nack FUNCTION((s), char *s) 1163{ 1164 reply(502, "%s command not implemented.", s); 1165} 1166 1167VOIDRET yyerror FUNCTION((s), char *s) 1168{ 1169 char *cp; 1170 1171 if (cp = strchr(cbuf, '\n')) 1172 *cp = '\0'; 1173 reply(500, "'%s': command not understood.", cbuf); 1174} 1175 1176VOIDRET delete FUNCTION((name), char *name) 1177{ 1178 struct stat st; 1179 1180 if (stat(name, &st) < 0) { 1181 perror_reply(550, name); 1182 return; 1183 } 1184 if ((st.st_mode & S_IFMT) == S_IFDIR) { 1185 if (rmdir(name) < 0) { 1186 perror_reply(550, name); 1187 return; 1188 } 1189 goto done; 1190 } 1191 if (unlink(name) < 0) { 1192 perror_reply(550, name); 1193 return; 1194 } 1195done: 1196 ack("DELE"); 1197} 1198 1199VOIDRET cwd FUNCTION((path), char *path) 1200{ 1201 if (chdir(path) < 0) 1202 perror_reply(550, path); 1203 else 1204 ack("CWD"); 1205} 1206 1207VOIDRET makedir FUNCTION((name), char *name) 1208{ 1209 if (mkdir(name, 0777) < 0) 1210 perror_reply(550, name); 1211 else 1212 reply(257, "MKD command successful."); 1213} 1214 1215VOIDRET removedir FUNCTION((name), char *name) 1216{ 1217 if (rmdir(name) < 0) 1218 perror_reply(550, name); 1219 else 1220 ack("RMD"); 1221} 1222 1223VOIDRET pwd FUNCTION_NOARGS 1224{ 1225 char path[MAXPATHLEN + 1]; 1226 1227 if (getcwd(path, MAXPATHLEN) == (char *) NULL) 1228 reply(550, "%s.", path); 1229 else 1230 reply(257, "\"%s\" is current directory.", path); 1231} 1232 1233char *renamefrom FUNCTION((name), char *name) 1234{ 1235 struct stat st; 1236 1237 if (stat(name, &st) < 0) { 1238 perror_reply(550, name); 1239 return ((char *) 0); 1240 } 1241 reply(350, "File exists, ready for destination name"); 1242 return (name); 1243} 1244 1245VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to) 1246{ 1247 if (rename(from, to) < 0) 1248 perror_reply(550, "rename"); 1249 else 1250 ack("RNTO"); 1251} 1252 1253static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin) 1254{ 1255 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr, 1256 sizeof(struct in_addr), AF_INET); 1257 time_t t, time(); 1258 1259 if (hp) 1260 opiestrncpy(remotehost, hp->h_name, sizeof(remotehost)); 1261 else 1262 opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost)); 1263#if DOTITLE 1264 setproctitle("%s: connected", remotehost); 1265#endif /* DOTITLE */ 1266 1267 t = time((time_t *) 0); 1268 syslog(LOG_INFO, "connection from %s at %s", 1269 remotehost, ctime(&t)); 1270} 1271 1272/* 1273 * Record logout in wtmp file 1274 * and exit with supplied status. 1275 */ 1276VOIDRET dologout FUNCTION((status), int status) 1277{ 1278 disable_signalling(); 1279 if (logged_in) { 1280 if (seteuid((uid_t) 0)) 1281 syslog(LOG_ERR, "Can't set euid"); 1282 opielogwtmp(ttyline, "", "", "ftp"); 1283 } 1284 /* beware of flushing buffers after a SIGPIPE */ 1285 _exit(status); 1286} 1287 1288static VOIDRET myoob FUNCTION((input), int input) 1289{ 1290 char *cp; 1291 1292 /* only process if transfer occurring */ 1293 if (!transflag) 1294 return; 1295 cp = tmpline; 1296 if (getline(cp, 7, stdin) == NULL) { 1297 reply(221, "You could at least say goodbye."); 1298 dologout(0); 1299 } 1300 upper(cp); 1301 if (strcmp(cp, "ABOR\r\n") == 0) { 1302 tmpline[0] = '\0'; 1303 reply(426, "Transfer aborted. Data connection closed."); 1304 reply(226, "Abort successful"); 1305 longjmp(urgcatch, 1); 1306 } 1307 if (strcmp(cp, "STAT\r\n") == 0) { 1308 if (file_size != (off_t) - 1) 1309 reply(213, "Status: %lu of %lu bytes transferred", 1310 byte_count, file_size); 1311 else 1312 reply(213, "Status: %lu bytes transferred", byte_count); 1313 } 1314} 1315 1316/* 1317 * Note: a response of 425 is not mentioned as a possible response to 1318 * the PASV command in RFC959. However, it has been blessed as 1319 * a legitimate response by Jon Postel in a telephone conversation 1320 * with Rick Adams on 25 Jan 89. 1321 */ 1322VOIDRET passive FUNCTION_NOARGS 1323{ 1324 int len; 1325 register char *p, *a; 1326 1327 pdata = socket(AF_INET, SOCK_STREAM, 0); 1328 if (pdata < 0) { 1329 perror_reply(425, "Can't open passive connection"); 1330 return; 1331 } 1332 pasv_addr = ctrl_addr; 1333 pasv_addr.sin_port = 0; 1334 if (seteuid((uid_t) 0)) 1335 syslog(LOG_ERR, "Can't set euid"); 1336 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) { 1337 seteuid((uid_t) pw->pw_uid); 1338 goto pasv_error; 1339 } 1340 if (seteuid((uid_t) pw->pw_uid)) 1341 syslog(LOG_ERR, "Can't set euid"); 1342 len = sizeof(pasv_addr); 1343 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0) 1344 goto pasv_error; 1345 if (listen(pdata, 1) < 0) 1346 goto pasv_error; 1347 a = (char *) &pasv_addr.sin_addr; 1348 p = (char *) &pasv_addr.sin_port; 1349 1350#define UC(b) (((int) b) & 0xff) 1351 1352 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 1353 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1354 return; 1355 1356pasv_error: 1357 close(pdata); 1358 pdata = -1; 1359 perror_reply(425, "Can't open passive connection"); 1360 return; 1361} 1362 1363/* 1364 * Generate unique name for file with basename "local". 1365 * The file named "local" is already known to exist. 1366 * Generates failure reply on error. 1367 */ 1368static char *gunique FUNCTION((local), char *local) 1369{ 1370 static char new[MAXPATHLEN+1]; 1371 struct stat st; 1372 char *cp = strrchr(local, '/'); 1373 int count = 0; 1374 1375 if (cp) 1376 *cp = '\0'; 1377 if (stat(cp ? local : ".", &st) < 0) { 1378 perror_reply(553, cp ? local : "."); 1379 return ((char *) 0); 1380 } 1381 if (cp) 1382 *cp = '/'; 1383 strcpy(new, local); 1384 cp = new + strlen(new); 1385 *cp++ = '.'; 1386 for (count = 1; count < 100; count++) { 1387 snprintf(cp, sizeof(new) - (cp - new), "%d", count); 1388 if (stat(new, &st) < 0) 1389 return (new); 1390 } 1391 reply(452, "Unique file name cannot be created."); 1392 return ((char *) 0); 1393} 1394 1395/* 1396 * Format and send reply containing system error number. 1397 */ 1398VOIDRET perror_reply FUNCTION((code, string), int code AND char *string) 1399{ 1400 reply(code, "%s: %s.", string, strerror(errno)); 1401} 1402 1403static char *onefile[] = 1404{ 1405 "", 1406 0 1407}; 1408 1409VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles) 1410{ 1411 struct stat st; 1412 DIR *dirp = NULL; 1413 struct dirent *dir; 1414 FILE *dout = NULL; 1415 register char **dirlist, *dirname; 1416 int simple = 0; 1417 1418 if (strpbrk(whichfiles, "~{[*?") != NULL) { 1419 extern char **ftpglob(), *globerr; 1420 1421 globerr = NULL; 1422 dirlist = ftpglob(whichfiles); 1423 if (globerr != NULL) { 1424 reply(550, globerr); 1425 return; 1426 } else 1427 if (dirlist == NULL) { 1428 errno = ENOENT; 1429 perror_reply(550, whichfiles); 1430 return; 1431 } 1432 } else { 1433 onefile[0] = whichfiles; 1434 dirlist = onefile; 1435 simple = 1; 1436 } 1437 1438 if (setjmp(urgcatch)) { 1439 transflag = 0; 1440 return; 1441 } 1442 while (dirname = *dirlist++) { 1443 if (stat(dirname, &st) < 0) { 1444 /* If user typed "ls -l", etc, and the client used NLST, do what the 1445 user meant. */ 1446 if (dirname[0] == '-' && *dirlist == NULL && 1447 transflag == 0) { 1448 retrieve("/bin/ls %s", dirname); 1449 return; 1450 } 1451 perror_reply(550, whichfiles); 1452 if (dout != NULL) { 1453 fclose(dout); 1454 transflag = 0; 1455 data = -1; 1456 pdata = -1; 1457 } 1458 return; 1459 } 1460 if ((st.st_mode & S_IFMT) == S_IFREG) { 1461 if (dout == NULL) { 1462 dout = dataconn("file list", (off_t) - 1, "w"); 1463 if (dout == NULL) 1464 return; 1465 transflag++; 1466 } 1467 fprintf(dout, "%s%s\n", dirname, 1468 type == TYPE_A ? "\r" : ""); 1469 byte_count += strlen(dirname) + 1; 1470 continue; 1471 } else 1472 if ((st.st_mode & S_IFMT) != S_IFDIR) 1473 continue; 1474 1475 if ((dirp = opendir(dirname)) == NULL) 1476 continue; 1477 1478 while ((dir = readdir(dirp)) != NULL) { 1479 char nbuf[MAXPATHLEN+1]; 1480 1481 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1)) 1482 continue; 1483 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 1484 (strlen(dir->d_name) == 2)) 1485 continue; 1486 1487 snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); 1488 1489 /* We have to do a stat to insure it's not a directory or special file. */ 1490 if (simple || (stat(nbuf, &st) == 0 && 1491 (st.st_mode & S_IFMT) == S_IFREG)) { 1492 if (dout == NULL) { 1493 dout = dataconn("file list", (off_t) - 1, "w"); 1494 if (dout == NULL) 1495 return; 1496 transflag++; 1497 } 1498 if (nbuf[0] == '.' && nbuf[1] == '/') 1499 fprintf(dout, "%s%s\n", &nbuf[2], 1500 type == TYPE_A ? "\r" : ""); 1501 else 1502 fprintf(dout, "%s%s\n", nbuf, 1503 type == TYPE_A ? "\r" : ""); 1504 byte_count += strlen(nbuf) + 1; 1505 } 1506 } 1507 closedir(dirp); 1508 } 1509 1510 if (dout == NULL) 1511 reply(550, "No files found."); 1512 else 1513 if (ferror(dout) != 0) 1514 perror_reply(550, "Data connection"); 1515 else 1516 reply(226, "Transfer complete."); 1517 1518 transflag = 0; 1519 if (dout != NULL) 1520 fclose(dout); 1521 data = -1; 1522 pdata = -1; 1523} 1524 1525#if DOTITLE 1526/* 1527 * clobber argv so ps will show what we're doing. 1528 * (stolen from sendmail) 1529 * warning, since this is usually started from inetd.conf, it 1530 * often doesn't have much of an environment or arglist to overwrite. 1531 */ 1532VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c) 1533{ 1534 register char *p, *bp, ch; 1535 register int i; 1536 char buf[BUFSIZ]; 1537 1538 snprintf(buf, sizeof(buf), fmt, a, b, c); 1539 1540 /* make ps print our process name */ 1541 p = Argv[0]; 1542 *p++ = '-'; 1543 1544 i = strlen(buf); 1545 if (i > LastArgv - p - 2) { 1546 i = LastArgv - p - 2; 1547 buf[i] = '\0'; 1548 } 1549 bp = buf; 1550 while (ch = *bp++) 1551 if (ch != '\n' && ch != '\r') 1552 *p++ = ch; 1553 while (p < LastArgv) 1554 *p++ = ' '; 1555} 1556#endif /* DOTITLE */ 1557 1558VOIDRET catchexit FUNCTION_NOARGS 1559{ 1560 closelog(); 1561} 1562 1563int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[]) 1564{ 1565 int addrlen, on = 1; 1566 char *cp; 1567#ifdef IP_TOS 1568 int tos; 1569#endif /* IP_TOS */ 1570 1571 { 1572 int i; 1573 1574 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) 1575 close(i); 1576 } 1577 1578 /* LOG_NDELAY sets up the logging connection immediately, necessary for 1579 anonymous ftp's that chroot and can't do it later. */ 1580 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 1581 atexit(catchexit); 1582 addrlen = sizeof(his_addr); 1583 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) { 1584 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); 1585 exit(1); 1586 } 1587 addrlen = sizeof(ctrl_addr); 1588 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) { 1589 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]); 1590 exit(1); 1591 } 1592#ifdef IP_TOS 1593 tos = IPTOS_LOWDELAY; 1594 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0) 1595 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 1596#endif 1597 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 1598 debug = 0; 1599#if DOTITLE 1600 /* Save start and extent of argv for setproctitle. */ 1601 Argv = argv; 1602 while (*envp) 1603 envp++; 1604 LastArgv = envp[-1] + strlen(envp[-1]); 1605#endif /* DOTITLE */ 1606 1607 argc--, argv++; 1608 while (argc > 0 && *argv[0] == '-') { 1609 for (cp = &argv[0][1]; *cp; cp++) 1610 switch (*cp) { 1611 1612 case 'v': 1613 debug = 1; 1614 break; 1615 1616 case 'd': 1617 debug = 1; 1618 break; 1619 1620 case 'l': 1621 break; 1622 1623 case 't': 1624 timeout = atoi(++cp); 1625 if (maxtimeout < timeout) 1626 maxtimeout = timeout; 1627 goto nextopt; 1628 1629 case 'T': 1630 maxtimeout = atoi(++cp); 1631 if (timeout > maxtimeout) 1632 timeout = maxtimeout; 1633 goto nextopt; 1634 1635 case 'u': 1636 { 1637 int val = 0; 1638 1639 while (*++cp && *cp >= '0' && *cp <= '9') 1640 val = val * 8 + *cp - '0'; 1641 if (*cp) 1642 fprintf(stderr, "ftpd: Bad value for -u\n"); 1643 else 1644 defumask = val; 1645 goto nextopt; 1646 } 1647 1648 default: 1649 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 1650 *cp); 1651 break; 1652 } 1653nextopt: 1654 argc--, argv++; 1655 } 1656 freopen(_PATH_DEVNULL, "w", stderr); 1657 signal(SIGCHLD, SIG_IGN); 1658 enable_signalling(); 1659 1660 /* Try to handle urgent data inline */ 1661#ifdef SO_OOBINLINE 1662 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0) 1663 syslog(LOG_ERR, "setsockopt: %m"); 1664#endif 1665 1666#ifdef F_SETOWN 1667 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 1668 syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 1669#endif 1670 dolog(&his_addr); 1671 /* Set up default state */ 1672 data = -1; 1673 type = TYPE_A; 1674 form = FORM_N; 1675 stru = STRU_F; 1676 mode = MODE_S; 1677 tmpline[0] = '\0'; 1678 af_pwok = opieaccessfile(remotehost); 1679 1680 { 1681 FILE *fd; 1682 char line[128]; 1683 1684 /* If logins are disabled, print out the message. */ 1685 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 1686 while (fgets(line, sizeof(line), fd) != NULL) { 1687 if ((cp = strchr(line, '\n')) != NULL) 1688 *cp = '\0'; 1689 lreply(530, "%s", line); 1690 } 1691 (void) fflush(stdout); 1692 (void) fclose(fd); 1693 reply(530, "System not available."); 1694 exit(0); 1695 } 1696 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 1697 while (fgets(line, sizeof(line), fd) != NULL) { 1698 if ((cp = strchr(line, '\n')) != NULL) 1699 *cp = '\0'; 1700 lreply(220, "%s", line); 1701 } 1702 (void) fflush(stdout); 1703 (void) fclose(fd); 1704 /* reply(220,) must follow */ 1705 } 1706 }; 1707 1708 reply(220, "FTP server ready."); 1709 1710 setjmp(errcatch); 1711 for (;;) 1712 yyparse(); 1713 /* NOTREACHED */ 1714 return 0; 1715} 1716