ftpd.c revision 6740
11592Srgrimes/* 21592Srgrimes * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 31592Srgrimes * The Regents of the University of California. All rights reserved. 41592Srgrimes * 51592Srgrimes * Redistribution and use in source and binary forms, with or without 61592Srgrimes * modification, are permitted provided that the following conditions 71592Srgrimes * are met: 81592Srgrimes * 1. Redistributions of source code must retain the above copyright 91592Srgrimes * notice, this list of conditions and the following disclaimer. 101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111592Srgrimes * notice, this list of conditions and the following disclaimer in the 121592Srgrimes * documentation and/or other materials provided with the distribution. 131592Srgrimes * 3. All advertising materials mentioning features or use of this software 141592Srgrimes * must display the following acknowledgement: 151592Srgrimes * This product includes software developed by the University of 161592Srgrimes * California, Berkeley and its contributors. 171592Srgrimes * 4. Neither the name of the University nor the names of its contributors 181592Srgrimes * may be used to endorse or promote products derived from this software 191592Srgrimes * without specific prior written permission. 201592Srgrimes * 211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311592Srgrimes * SUCH DAMAGE. 321592Srgrimes */ 331592Srgrimes 341592Srgrimes#ifndef lint 351592Srgrimesstatic char copyright[] = 361592Srgrimes"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 371592Srgrimes The Regents of the University of California. All rights reserved.\n"; 381592Srgrimes#endif /* not lint */ 391592Srgrimes 401592Srgrimes#ifndef lint 411592Srgrimesstatic char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; 421592Srgrimes#endif /* not lint */ 431592Srgrimes 441592Srgrimes/* 451592Srgrimes * FTP server. 461592Srgrimes */ 471592Srgrimes#include <sys/param.h> 481592Srgrimes#include <sys/stat.h> 491592Srgrimes#include <sys/ioctl.h> 501592Srgrimes#include <sys/socket.h> 511592Srgrimes#include <sys/wait.h> 521592Srgrimes 531592Srgrimes#include <netinet/in.h> 541592Srgrimes#include <netinet/in_systm.h> 551592Srgrimes#include <netinet/ip.h> 561592Srgrimes 571592Srgrimes#define FTP_NAMES 581592Srgrimes#include <arpa/ftp.h> 591592Srgrimes#include <arpa/inet.h> 601592Srgrimes#include <arpa/telnet.h> 611592Srgrimes 621592Srgrimes#include <ctype.h> 631592Srgrimes#include <dirent.h> 641592Srgrimes#include <err.h> 651592Srgrimes#include <errno.h> 661592Srgrimes#include <fcntl.h> 671592Srgrimes#include <glob.h> 681592Srgrimes#include <limits.h> 691592Srgrimes#include <netdb.h> 701592Srgrimes#include <pwd.h> 711592Srgrimes#include <setjmp.h> 721592Srgrimes#include <signal.h> 731592Srgrimes#include <stdio.h> 741592Srgrimes#include <stdlib.h> 751592Srgrimes#include <string.h> 761592Srgrimes#include <syslog.h> 771592Srgrimes#include <time.h> 781592Srgrimes#include <unistd.h> 791592Srgrimes 803938Spst#ifdef SKEY 813938Spst#include <skey.h> 823938Spst#endif 833938Spst 841592Srgrimes#include "pathnames.h" 851592Srgrimes#include "extern.h" 861592Srgrimes 871592Srgrimes#if __STDC__ 881592Srgrimes#include <stdarg.h> 891592Srgrimes#else 901592Srgrimes#include <varargs.h> 911592Srgrimes#endif 921592Srgrimes 931592Srgrimesstatic char version[] = "Version 6.00"; 941592Srgrimes 951592Srgrimesextern off_t restart_point; 961592Srgrimesextern char cbuf[]; 971592Srgrimes 981592Srgrimesstruct sockaddr_in ctrl_addr; 991592Srgrimesstruct sockaddr_in data_source; 1001592Srgrimesstruct sockaddr_in data_dest; 1011592Srgrimesstruct sockaddr_in his_addr; 1021592Srgrimesstruct sockaddr_in pasv_addr; 1031592Srgrimes 1041592Srgrimesint data; 1051592Srgrimesjmp_buf errcatch, urgcatch; 1061592Srgrimesint logged_in; 1071592Srgrimesstruct passwd *pw; 1081592Srgrimesint debug; 1091592Srgrimesint timeout = 900; /* timeout after 15 minutes of inactivity */ 1101592Srgrimesint maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 1111592Srgrimesint logging; 1121592Srgrimesint guest; 1136740Sguido#ifdef STATS 1146740Sguidoint stats; 1156740Sguidoint statfd = -1; 1166740Sguido#endif 1171592Srgrimesint type; 1181592Srgrimesint form; 1191592Srgrimesint stru; /* avoid C keyword */ 1201592Srgrimesint mode; 1211592Srgrimesint usedefault = 1; /* for data transfers */ 1221592Srgrimesint pdata = -1; /* for passive mode */ 1231592Srgrimessig_atomic_t transflag; 1241592Srgrimesoff_t file_size; 1251592Srgrimesoff_t byte_count; 1261592Srgrimes#if !defined(CMASK) || CMASK == 0 1271592Srgrimes#undef CMASK 1281592Srgrimes#define CMASK 027 1291592Srgrimes#endif 1301592Srgrimesint defumask = CMASK; /* default umask value */ 1311592Srgrimeschar tmpline[7]; 1321592Srgrimeschar hostname[MAXHOSTNAMELEN]; 1331592Srgrimeschar remotehost[MAXHOSTNAMELEN]; 1346740Sguido#ifdef STATS 1356740Sguidochar *ident = NULL; 1366740Sguido#endif 1371592Srgrimes 1381592Srgrimes/* 1391592Srgrimes * Timeout intervals for retrying connections 1401592Srgrimes * to hosts that don't accept PORT cmds. This 1411592Srgrimes * is a kludge, but given the problems with TCP... 1421592Srgrimes */ 1431592Srgrimes#define SWAITMAX 90 /* wait at most 90 seconds */ 1441592Srgrimes#define SWAITINT 5 /* interval between retries */ 1451592Srgrimes 1461592Srgrimesint swaitmax = SWAITMAX; 1471592Srgrimesint swaitint = SWAITINT; 1481592Srgrimes 1491592Srgrimes#ifdef SETPROCTITLE 1501592Srgrimeschar **Argv = NULL; /* pointer to argument vector */ 1511592Srgrimeschar *LastArgv = NULL; /* end of argv */ 1521592Srgrimeschar proctitle[LINE_MAX]; /* initial part of title */ 1531592Srgrimes#endif /* SETPROCTITLE */ 1541592Srgrimes 1552193Sguido#ifdef SKEY 1562193Sguidoint pwok = 0; 1573938Spstchar addr_string[20]; /* XXX */ 1582193Sguido#endif 1592193Sguido 1601592Srgrimes#define LOGCMD(cmd, file) \ 1611592Srgrimes if (logging > 1) \ 1621592Srgrimes syslog(LOG_INFO,"%s %s%s", cmd, \ 1631592Srgrimes *(file) == '/' ? "" : curdir(), file); 1641592Srgrimes#define LOGCMD2(cmd, file1, file2) \ 1651592Srgrimes if (logging > 1) \ 1661592Srgrimes syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 1671592Srgrimes *(file1) == '/' ? "" : curdir(), file1, \ 1681592Srgrimes *(file2) == '/' ? "" : curdir(), file2); 1691592Srgrimes#define LOGBYTES(cmd, file, cnt) \ 1701592Srgrimes if (logging > 1) { \ 1711592Srgrimes if (cnt == (off_t)-1) \ 1721592Srgrimes syslog(LOG_INFO,"%s %s%s", cmd, \ 1731592Srgrimes *(file) == '/' ? "" : curdir(), file); \ 1741592Srgrimes else \ 1751592Srgrimes syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 1761592Srgrimes cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 1771592Srgrimes } 1781592Srgrimes 1791592Srgrimesstatic void ack __P((char *)); 1801592Srgrimesstatic void myoob __P((int)); 1811592Srgrimesstatic int checkuser __P((char *)); 1821592Srgrimesstatic FILE *dataconn __P((char *, off_t, char *)); 1831592Srgrimesstatic void dolog __P((struct sockaddr_in *)); 1841592Srgrimesstatic char *curdir __P((void)); 1851592Srgrimesstatic void end_login __P((void)); 1861592Srgrimesstatic FILE *getdatasock __P((char *)); 1871592Srgrimesstatic char *gunique __P((char *)); 1881592Srgrimesstatic void lostconn __P((int)); 1891592Srgrimesstatic int receive_data __P((FILE *, FILE *)); 1901592Srgrimesstatic void send_data __P((FILE *, FILE *, off_t)); 1911592Srgrimesstatic struct passwd * 1921592Srgrimes sgetpwnam __P((char *)); 1931592Srgrimesstatic char *sgetsave __P((char *)); 1941592Srgrimes 1951592Srgrimesstatic char * 1961592Srgrimescurdir() 1971592Srgrimes{ 1981592Srgrimes static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 1991592Srgrimes 2001592Srgrimes if (getcwd(path, sizeof(path)-2) == NULL) 2011592Srgrimes return (""); 2021592Srgrimes if (path[1] != '\0') /* special case for root dir. */ 2031592Srgrimes strcat(path, "/"); 2041592Srgrimes /* For guest account, skip / since it's chrooted */ 2051592Srgrimes return (guest ? path+1 : path); 2061592Srgrimes} 2071592Srgrimes 2081592Srgrimesint 2091592Srgrimesmain(argc, argv, envp) 2101592Srgrimes int argc; 2111592Srgrimes char *argv[]; 2121592Srgrimes char **envp; 2131592Srgrimes{ 2141592Srgrimes int addrlen, ch, on = 1, tos; 2151592Srgrimes char *cp, line[LINE_MAX]; 2161592Srgrimes FILE *fd; 2171592Srgrimes 2183938Spst tzset(); /* in case no timezone database in ~ftp */ 2193938Spst 2201592Srgrimes /* 2211592Srgrimes * LOG_NDELAY sets up the logging connection immediately, 2221592Srgrimes * necessary for anonymous ftp's that chroot and can't do it later. 2231592Srgrimes */ 2241592Srgrimes openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 2251592Srgrimes addrlen = sizeof(his_addr); 2261592Srgrimes if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 2271592Srgrimes syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 2281592Srgrimes exit(1); 2291592Srgrimes } 2303938Spst#ifdef SKEY 2313938Spst strcpy(addr_string, inet_ntoa(his_addr.sin_addr)); 2323938Spst#endif 2331592Srgrimes addrlen = sizeof(ctrl_addr); 2341592Srgrimes if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 2351592Srgrimes syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 2361592Srgrimes exit(1); 2371592Srgrimes } 2381592Srgrimes#ifdef IP_TOS 2391592Srgrimes tos = IPTOS_LOWDELAY; 2401592Srgrimes if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 2411592Srgrimes syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 2421592Srgrimes#endif 2431592Srgrimes data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 2441592Srgrimes debug = 0; 2451592Srgrimes#ifdef SETPROCTITLE 2461592Srgrimes /* 2471592Srgrimes * Save start and extent of argv for setproctitle. 2481592Srgrimes */ 2491592Srgrimes Argv = argv; 2501592Srgrimes while (*envp) 2511592Srgrimes envp++; 2521592Srgrimes LastArgv = envp[-1] + strlen(envp[-1]); 2531592Srgrimes#endif /* SETPROCTITLE */ 2541592Srgrimes 2556740Sguido 2566740Sguido#ifdef STATS 2576740Sguido while ((ch = getopt(argc, argv, "dlSt:T:u:v")) != EOF) { 2586740Sguido#else 2591592Srgrimes while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) { 2606740Sguido#endif 2611592Srgrimes switch (ch) { 2621592Srgrimes case 'd': 2631592Srgrimes debug = 1; 2641592Srgrimes break; 2651592Srgrimes 2661592Srgrimes case 'l': 2671592Srgrimes logging++; /* > 1 == extra logging */ 2681592Srgrimes break; 2691592Srgrimes 2701592Srgrimes case 't': 2711592Srgrimes timeout = atoi(optarg); 2721592Srgrimes if (maxtimeout < timeout) 2731592Srgrimes maxtimeout = timeout; 2741592Srgrimes break; 2756740Sguido#ifdef STATS 2766740Sguido case 'S': 2776740Sguido stats = 1; 2786740Sguido break; 2796740Sguido#endif 2801592Srgrimes case 'T': 2811592Srgrimes maxtimeout = atoi(optarg); 2821592Srgrimes if (timeout > maxtimeout) 2831592Srgrimes timeout = maxtimeout; 2841592Srgrimes break; 2851592Srgrimes 2861592Srgrimes case 'u': 2871592Srgrimes { 2881592Srgrimes long val = 0; 2891592Srgrimes 2901592Srgrimes val = strtol(optarg, &optarg, 8); 2911592Srgrimes if (*optarg != '\0' || val < 0) 2921592Srgrimes warnx("bad value for -u"); 2931592Srgrimes else 2941592Srgrimes defumask = val; 2951592Srgrimes break; 2961592Srgrimes } 2971592Srgrimes 2981592Srgrimes case 'v': 2991592Srgrimes debug = 1; 3001592Srgrimes break; 3011592Srgrimes 3021592Srgrimes default: 3031592Srgrimes warnx("unknown flag -%c ignored", optopt); 3041592Srgrimes break; 3051592Srgrimes } 3061592Srgrimes } 3071592Srgrimes (void) freopen(_PATH_DEVNULL, "w", stderr); 3081592Srgrimes (void) signal(SIGPIPE, lostconn); 3091592Srgrimes (void) signal(SIGCHLD, SIG_IGN); 3101592Srgrimes if ((int)signal(SIGURG, myoob) < 0) 3111592Srgrimes syslog(LOG_ERR, "signal: %m"); 3121592Srgrimes 3131592Srgrimes /* Try to handle urgent data inline */ 3141592Srgrimes#ifdef SO_OOBINLINE 3151592Srgrimes if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 3161592Srgrimes syslog(LOG_ERR, "setsockopt: %m"); 3171592Srgrimes#endif 3181592Srgrimes 3191592Srgrimes#ifdef F_SETOWN 3201592Srgrimes if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 3211592Srgrimes syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 3221592Srgrimes#endif 3231592Srgrimes dolog(&his_addr); 3241592Srgrimes /* 3251592Srgrimes * Set up default state 3261592Srgrimes */ 3271592Srgrimes data = -1; 3281592Srgrimes type = TYPE_A; 3291592Srgrimes form = FORM_N; 3301592Srgrimes stru = STRU_F; 3311592Srgrimes mode = MODE_S; 3321592Srgrimes tmpline[0] = '\0'; 3331592Srgrimes 3341592Srgrimes /* If logins are disabled, print out the message. */ 3351592Srgrimes if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 3361592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 3371592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 3381592Srgrimes *cp = '\0'; 3391592Srgrimes lreply(530, "%s", line); 3401592Srgrimes } 3411592Srgrimes (void) fflush(stdout); 3421592Srgrimes (void) fclose(fd); 3431592Srgrimes reply(530, "System not available."); 3441592Srgrimes exit(0); 3451592Srgrimes } 3461592Srgrimes if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 3471592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 3481592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 3491592Srgrimes *cp = '\0'; 3501592Srgrimes lreply(220, "%s", line); 3511592Srgrimes } 3521592Srgrimes (void) fflush(stdout); 3531592Srgrimes (void) fclose(fd); 3541592Srgrimes /* reply(220,) must follow */ 3551592Srgrimes } 3561592Srgrimes (void) gethostname(hostname, sizeof(hostname)); 3571592Srgrimes reply(220, "%s FTP server (%s) ready.", hostname, version); 3581592Srgrimes (void) setjmp(errcatch); 3591592Srgrimes for (;;) 3601592Srgrimes (void) yyparse(); 3611592Srgrimes /* NOTREACHED */ 3621592Srgrimes} 3631592Srgrimes 3641592Srgrimesstatic void 3651592Srgrimeslostconn(signo) 3661592Srgrimes int signo; 3671592Srgrimes{ 3681592Srgrimes 3691592Srgrimes if (debug) 3701592Srgrimes syslog(LOG_DEBUG, "lost connection"); 3711592Srgrimes dologout(-1); 3721592Srgrimes} 3731592Srgrimes 3741592Srgrimesstatic char ttyline[20]; 3751592Srgrimes 3761592Srgrimes/* 3771592Srgrimes * Helper function for sgetpwnam(). 3781592Srgrimes */ 3791592Srgrimesstatic char * 3801592Srgrimessgetsave(s) 3811592Srgrimes char *s; 3821592Srgrimes{ 3831592Srgrimes char *new = malloc((unsigned) strlen(s) + 1); 3841592Srgrimes 3851592Srgrimes if (new == NULL) { 3861592Srgrimes perror_reply(421, "Local resource failure: malloc"); 3871592Srgrimes dologout(1); 3881592Srgrimes /* NOTREACHED */ 3891592Srgrimes } 3901592Srgrimes (void) strcpy(new, s); 3911592Srgrimes return (new); 3921592Srgrimes} 3931592Srgrimes 3941592Srgrimes/* 3951592Srgrimes * Save the result of a getpwnam. Used for USER command, since 3961592Srgrimes * the data returned must not be clobbered by any other command 3971592Srgrimes * (e.g., globbing). 3981592Srgrimes */ 3991592Srgrimesstatic struct passwd * 4001592Srgrimessgetpwnam(name) 4011592Srgrimes char *name; 4021592Srgrimes{ 4031592Srgrimes static struct passwd save; 4041592Srgrimes struct passwd *p; 4051592Srgrimes 4061592Srgrimes if ((p = getpwnam(name)) == NULL) 4071592Srgrimes return (p); 4081592Srgrimes if (save.pw_name) { 4091592Srgrimes free(save.pw_name); 4101592Srgrimes free(save.pw_passwd); 4111592Srgrimes free(save.pw_gecos); 4121592Srgrimes free(save.pw_dir); 4131592Srgrimes free(save.pw_shell); 4141592Srgrimes } 4151592Srgrimes save = *p; 4161592Srgrimes save.pw_name = sgetsave(p->pw_name); 4171592Srgrimes save.pw_passwd = sgetsave(p->pw_passwd); 4181592Srgrimes save.pw_gecos = sgetsave(p->pw_gecos); 4191592Srgrimes save.pw_dir = sgetsave(p->pw_dir); 4201592Srgrimes save.pw_shell = sgetsave(p->pw_shell); 4211592Srgrimes return (&save); 4221592Srgrimes} 4231592Srgrimes 4241592Srgrimesstatic int login_attempts; /* number of failed login attempts */ 4251592Srgrimesstatic int askpasswd; /* had user command, ask for passwd */ 4261592Srgrimesstatic char curname[10]; /* current USER name */ 4271592Srgrimes 4281592Srgrimes/* 4291592Srgrimes * USER command. 4301592Srgrimes * Sets global passwd pointer pw if named account exists and is acceptable; 4311592Srgrimes * sets askpasswd if a PASS command is expected. If logged in previously, 4321592Srgrimes * need to reset state. If name is "ftp" or "anonymous", the name is not in 4331592Srgrimes * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 4341592Srgrimes * If account doesn't exist, ask for passwd anyway. Otherwise, check user 4351592Srgrimes * requesting login privileges. Disallow anyone who does not have a standard 4361592Srgrimes * shell as returned by getusershell(). Disallow anyone mentioned in the file 4371592Srgrimes * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 4381592Srgrimes */ 4391592Srgrimesvoid 4401592Srgrimesuser(name) 4411592Srgrimes char *name; 4421592Srgrimes{ 4431592Srgrimes char *cp, *shell; 4441592Srgrimes 4451592Srgrimes if (logged_in) { 4461592Srgrimes if (guest) { 4471592Srgrimes reply(530, "Can't change user from guest login."); 4481592Srgrimes return; 4491592Srgrimes } 4501592Srgrimes end_login(); 4511592Srgrimes } 4521592Srgrimes 4531592Srgrimes guest = 0; 4541592Srgrimes if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 4551592Srgrimes if (checkuser("ftp") || checkuser("anonymous")) 4561592Srgrimes reply(530, "User %s access denied.", name); 4571592Srgrimes else if ((pw = sgetpwnam("ftp")) != NULL) { 4581592Srgrimes guest = 1; 4591592Srgrimes askpasswd = 1; 4601592Srgrimes reply(331, 4613938Spst "Guest login ok, send your email address as password."); 4621592Srgrimes } else 4631592Srgrimes reply(530, "User %s unknown.", name); 4641592Srgrimes if (!askpasswd && logging) 4651592Srgrimes syslog(LOG_NOTICE, 4661592Srgrimes "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 4671592Srgrimes return; 4681592Srgrimes } 4691592Srgrimes if (pw = sgetpwnam(name)) { 4701592Srgrimes if ((shell = pw->pw_shell) == NULL || *shell == 0) 4711592Srgrimes shell = _PATH_BSHELL; 4721592Srgrimes while ((cp = getusershell()) != NULL) 4731592Srgrimes if (strcmp(cp, shell) == 0) 4741592Srgrimes break; 4751592Srgrimes endusershell(); 4761592Srgrimes 4771592Srgrimes if (cp == NULL || checkuser(name)) { 4781592Srgrimes reply(530, "User %s access denied.", name); 4791592Srgrimes if (logging) 4801592Srgrimes syslog(LOG_NOTICE, 4811592Srgrimes "FTP LOGIN REFUSED FROM %s, %s", 4821592Srgrimes remotehost, name); 4831592Srgrimes pw = (struct passwd *) NULL; 4841592Srgrimes return; 4851592Srgrimes } 4861592Srgrimes } 4871592Srgrimes if (logging) 4881592Srgrimes strncpy(curname, name, sizeof(curname)-1); 4892193Sguido#ifdef SKEY 4903938Spst pwok = skeyaccess(name, NULL, remotehost, addr_string); 4912193Sguido reply(331, "%s", skey_challenge(name, pw, pwok)); 4922193Sguido#else 4931592Srgrimes reply(331, "Password required for %s.", name); 4942193Sguido#endif 4951592Srgrimes askpasswd = 1; 4961592Srgrimes /* 4971592Srgrimes * Delay before reading passwd after first failed 4981592Srgrimes * attempt to slow down passwd-guessing programs. 4991592Srgrimes */ 5001592Srgrimes if (login_attempts) 5011592Srgrimes sleep((unsigned) login_attempts); 5021592Srgrimes} 5031592Srgrimes 5041592Srgrimes/* 5051592Srgrimes * Check if a user is in the file _PATH_FTPUSERS 5061592Srgrimes */ 5071592Srgrimesstatic int 5081592Srgrimescheckuser(name) 5091592Srgrimes char *name; 5101592Srgrimes{ 5111592Srgrimes FILE *fd; 5121592Srgrimes int found = 0; 5131592Srgrimes char *p, line[BUFSIZ]; 5141592Srgrimes 5151592Srgrimes if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 5161592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) 5171592Srgrimes if ((p = strchr(line, '\n')) != NULL) { 5181592Srgrimes *p = '\0'; 5191592Srgrimes if (line[0] == '#') 5201592Srgrimes continue; 5212930Sdg if (strcmp(line, name) == 0) { 5221592Srgrimes found = 1; 5231592Srgrimes break; 5241592Srgrimes } 5251592Srgrimes } 5261592Srgrimes (void) fclose(fd); 5271592Srgrimes } 5281592Srgrimes return (found); 5291592Srgrimes} 5301592Srgrimes 5311592Srgrimes/* 5321592Srgrimes * Terminate login as previous user, if any, resetting state; 5331592Srgrimes * used when USER command is given or login fails. 5341592Srgrimes */ 5351592Srgrimesstatic void 5361592Srgrimesend_login() 5371592Srgrimes{ 5381592Srgrimes 5391592Srgrimes (void) seteuid((uid_t)0); 5401592Srgrimes if (logged_in) 5411592Srgrimes logwtmp(ttyline, "", ""); 5421592Srgrimes pw = NULL; 5431592Srgrimes logged_in = 0; 5441592Srgrimes guest = 0; 5451592Srgrimes} 5461592Srgrimes 5471592Srgrimesvoid 5481592Srgrimespass(passwd) 5491592Srgrimes char *passwd; 5501592Srgrimes{ 5511592Srgrimes char *salt, *xpasswd; 5521592Srgrimes FILE *fd; 5531592Srgrimes 5541592Srgrimes if (logged_in || askpasswd == 0) { 5551592Srgrimes reply(503, "Login with USER first."); 5561592Srgrimes return; 5571592Srgrimes } 5581592Srgrimes askpasswd = 0; 5591592Srgrimes if (!guest) { /* "ftp" is only account allowed no password */ 5601592Srgrimes if (pw == NULL) 5611592Srgrimes salt = "xx"; 5621592Srgrimes else 5631592Srgrimes salt = pw->pw_passwd; 5642193Sguido#ifdef SKEY 5652193Sguido xpasswd = skey_crypt(passwd, salt, pw, pwok); 5663206Spst pwok = 0; 5672193Sguido#else 5681592Srgrimes xpasswd = crypt(passwd, salt); 5692193Sguido#endif 5701592Srgrimes /* The strcmp does not catch null passwords! */ 5711592Srgrimes if (pw == NULL || *pw->pw_passwd == '\0' || 5721592Srgrimes strcmp(xpasswd, pw->pw_passwd)) { 5731592Srgrimes reply(530, "Login incorrect."); 5741592Srgrimes if (logging) 5751592Srgrimes syslog(LOG_NOTICE, 5761592Srgrimes "FTP LOGIN FAILED FROM %s, %s", 5771592Srgrimes remotehost, curname); 5781592Srgrimes pw = NULL; 5791592Srgrimes if (login_attempts++ >= 5) { 5801592Srgrimes syslog(LOG_NOTICE, 5811592Srgrimes "repeated login failures from %s", 5821592Srgrimes remotehost); 5831592Srgrimes exit(0); 5841592Srgrimes } 5851592Srgrimes return; 5861592Srgrimes } 5871592Srgrimes } 5881592Srgrimes login_attempts = 0; /* this time successful */ 5891592Srgrimes if (setegid((gid_t)pw->pw_gid) < 0) { 5901592Srgrimes reply(550, "Can't set gid."); 5911592Srgrimes return; 5921592Srgrimes } 5931592Srgrimes (void) initgroups(pw->pw_name, pw->pw_gid); 5941592Srgrimes 5951592Srgrimes /* open wtmp before chroot */ 5961592Srgrimes (void)sprintf(ttyline, "ftp%d", getpid()); 5971592Srgrimes logwtmp(ttyline, pw->pw_name, remotehost); 5981592Srgrimes logged_in = 1; 5991592Srgrimes 6006740Sguido#ifdef STATS 6016740Sguido if (guest && stats == 1 && statfd < 0) 6026740Sguido if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) 6036740Sguido stats = 0; 6046740Sguido#endif 6056740Sguido 6061592Srgrimes if (guest) { 6071592Srgrimes /* 6081592Srgrimes * We MUST do a chdir() after the chroot. Otherwise 6091592Srgrimes * the old current directory will be accessible as "." 6101592Srgrimes * outside the new root! 6111592Srgrimes */ 6121592Srgrimes if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 6131592Srgrimes reply(550, "Can't set guest privileges."); 6141592Srgrimes goto bad; 6151592Srgrimes } 6161592Srgrimes } else if (chdir(pw->pw_dir) < 0) { 6171592Srgrimes if (chdir("/") < 0) { 6181592Srgrimes reply(530, "User %s: can't change directory to %s.", 6191592Srgrimes pw->pw_name, pw->pw_dir); 6201592Srgrimes goto bad; 6211592Srgrimes } else 6221592Srgrimes lreply(230, "No directory! Logging in with home=/"); 6231592Srgrimes } 6241592Srgrimes if (seteuid((uid_t)pw->pw_uid) < 0) { 6251592Srgrimes reply(550, "Can't set uid."); 6261592Srgrimes goto bad; 6271592Srgrimes } 6281592Srgrimes /* 6291592Srgrimes * Display a login message, if it exists. 6301592Srgrimes * N.B. reply(230,) must follow the message. 6311592Srgrimes */ 6321592Srgrimes if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 6331592Srgrimes char *cp, line[LINE_MAX]; 6341592Srgrimes 6351592Srgrimes while (fgets(line, sizeof(line), fd) != NULL) { 6361592Srgrimes if ((cp = strchr(line, '\n')) != NULL) 6371592Srgrimes *cp = '\0'; 6381592Srgrimes lreply(230, "%s", line); 6391592Srgrimes } 6401592Srgrimes (void) fflush(stdout); 6411592Srgrimes (void) fclose(fd); 6421592Srgrimes } 6431592Srgrimes if (guest) { 6446740Sguido#ifdef STATS 6456740Sguido char * copy(); 6466740Sguido 6476740Sguido if (ident != NULL) 6486740Sguido free(ident); 6496740Sguido ident = (char *) copy(passwd); 6506740Sguido#endif 6511592Srgrimes reply(230, "Guest login ok, access restrictions apply."); 6521592Srgrimes#ifdef SETPROCTITLE 6531592Srgrimes snprintf(proctitle, sizeof(proctitle), 6541592Srgrimes "%s: anonymous/%.*s", remotehost, 6551592Srgrimes sizeof(proctitle) - sizeof(remotehost) - 6561592Srgrimes sizeof(": anonymous/"), passwd); 6571592Srgrimes setproctitle(proctitle); 6581592Srgrimes#endif /* SETPROCTITLE */ 6591592Srgrimes if (logging) 6601592Srgrimes syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 6611592Srgrimes remotehost, passwd); 6621592Srgrimes } else { 6631592Srgrimes reply(230, "User %s logged in.", pw->pw_name); 6641592Srgrimes#ifdef SETPROCTITLE 6651592Srgrimes snprintf(proctitle, sizeof(proctitle), 6661592Srgrimes "%s: %s", remotehost, pw->pw_name); 6671592Srgrimes setproctitle(proctitle); 6681592Srgrimes#endif /* SETPROCTITLE */ 6691592Srgrimes if (logging) 6701592Srgrimes syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 6711592Srgrimes remotehost, pw->pw_name); 6721592Srgrimes } 6731592Srgrimes (void) umask(defumask); 6741592Srgrimes return; 6751592Srgrimesbad: 6761592Srgrimes /* Forget all about it... */ 6771592Srgrimes end_login(); 6781592Srgrimes} 6791592Srgrimes 6801592Srgrimesvoid 6811592Srgrimesretrieve(cmd, name) 6821592Srgrimes char *cmd, *name; 6831592Srgrimes{ 6841592Srgrimes FILE *fin, *dout; 6851592Srgrimes struct stat st; 6861592Srgrimes int (*closefunc) __P((FILE *)); 6876740Sguido#ifdef STATS 6886740Sguido long start; 6896740Sguido#endif 6901592Srgrimes 6911592Srgrimes if (cmd == 0) { 6921592Srgrimes fin = fopen(name, "r"), closefunc = fclose; 6931592Srgrimes st.st_size = 0; 6941592Srgrimes } else { 6951592Srgrimes char line[BUFSIZ]; 6961592Srgrimes 6971592Srgrimes (void) sprintf(line, cmd, name), name = line; 6981592Srgrimes fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 6991592Srgrimes st.st_size = -1; 7001592Srgrimes st.st_blksize = BUFSIZ; 7011592Srgrimes } 7021592Srgrimes if (fin == NULL) { 7031592Srgrimes if (errno != 0) { 7041592Srgrimes perror_reply(550, name); 7051592Srgrimes if (cmd == 0) { 7061592Srgrimes LOGCMD("get", name); 7071592Srgrimes } 7081592Srgrimes } 7091592Srgrimes return; 7101592Srgrimes } 7111592Srgrimes byte_count = -1; 7121592Srgrimes if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { 7131592Srgrimes reply(550, "%s: not a plain file.", name); 7141592Srgrimes goto done; 7151592Srgrimes } 7161592Srgrimes if (restart_point) { 7171592Srgrimes if (type == TYPE_A) { 7181592Srgrimes off_t i, n; 7191592Srgrimes int c; 7201592Srgrimes 7211592Srgrimes n = restart_point; 7221592Srgrimes i = 0; 7231592Srgrimes while (i++ < n) { 7241592Srgrimes if ((c=getc(fin)) == EOF) { 7251592Srgrimes perror_reply(550, name); 7261592Srgrimes goto done; 7271592Srgrimes } 7281592Srgrimes if (c == '\n') 7291592Srgrimes i++; 7301592Srgrimes } 7311592Srgrimes } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 7321592Srgrimes perror_reply(550, name); 7331592Srgrimes goto done; 7341592Srgrimes } 7351592Srgrimes } 7361592Srgrimes dout = dataconn(name, st.st_size, "w"); 7371592Srgrimes if (dout == NULL) 7381592Srgrimes goto done; 7396740Sguido#ifdef STATS 7406740Sguido time(&start); 7416740Sguido#endif 7421592Srgrimes send_data(fin, dout, st.st_blksize); 7436740Sguido#ifdef STATS 7446740Sguido if (cmd == 0 && guest && stats) 7456740Sguido logxfer( name, st.st_size, start); 7466740Sguido#endif 7471592Srgrimes (void) fclose(dout); 7481592Srgrimes data = -1; 7491592Srgrimes pdata = -1; 7501592Srgrimesdone: 7511592Srgrimes if (cmd == 0) 7521592Srgrimes LOGBYTES("get", name, byte_count); 7531592Srgrimes (*closefunc)(fin); 7541592Srgrimes} 7551592Srgrimes 7561592Srgrimesvoid 7571592Srgrimesstore(name, mode, unique) 7581592Srgrimes char *name, *mode; 7591592Srgrimes int unique; 7601592Srgrimes{ 7611592Srgrimes FILE *fout, *din; 7621592Srgrimes struct stat st; 7631592Srgrimes int (*closefunc) __P((FILE *)); 7641592Srgrimes 7651592Srgrimes if (unique && stat(name, &st) == 0 && 7661592Srgrimes (name = gunique(name)) == NULL) { 7671592Srgrimes LOGCMD(*mode == 'w' ? "put" : "append", name); 7681592Srgrimes return; 7691592Srgrimes } 7701592Srgrimes 7711592Srgrimes if (restart_point) 7721592Srgrimes mode = "r+"; 7731592Srgrimes fout = fopen(name, mode); 7741592Srgrimes closefunc = fclose; 7751592Srgrimes if (fout == NULL) { 7761592Srgrimes perror_reply(553, name); 7771592Srgrimes LOGCMD(*mode == 'w' ? "put" : "append", name); 7781592Srgrimes return; 7791592Srgrimes } 7801592Srgrimes byte_count = -1; 7811592Srgrimes if (restart_point) { 7821592Srgrimes if (type == TYPE_A) { 7831592Srgrimes off_t i, n; 7841592Srgrimes int c; 7851592Srgrimes 7861592Srgrimes n = restart_point; 7871592Srgrimes i = 0; 7881592Srgrimes while (i++ < n) { 7891592Srgrimes if ((c=getc(fout)) == EOF) { 7901592Srgrimes perror_reply(550, name); 7911592Srgrimes goto done; 7921592Srgrimes } 7931592Srgrimes if (c == '\n') 7941592Srgrimes i++; 7951592Srgrimes } 7961592Srgrimes /* 7971592Srgrimes * We must do this seek to "current" position 7981592Srgrimes * because we are changing from reading to 7991592Srgrimes * writing. 8001592Srgrimes */ 8011592Srgrimes if (fseek(fout, 0L, L_INCR) < 0) { 8021592Srgrimes perror_reply(550, name); 8031592Srgrimes goto done; 8041592Srgrimes } 8051592Srgrimes } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 8061592Srgrimes perror_reply(550, name); 8071592Srgrimes goto done; 8081592Srgrimes } 8091592Srgrimes } 8101592Srgrimes din = dataconn(name, (off_t)-1, "r"); 8111592Srgrimes if (din == NULL) 8121592Srgrimes goto done; 8131592Srgrimes if (receive_data(din, fout) == 0) { 8141592Srgrimes if (unique) 8151592Srgrimes reply(226, "Transfer complete (unique file name:%s).", 8161592Srgrimes name); 8171592Srgrimes else 8181592Srgrimes reply(226, "Transfer complete."); 8191592Srgrimes } 8201592Srgrimes (void) fclose(din); 8211592Srgrimes data = -1; 8221592Srgrimes pdata = -1; 8231592Srgrimesdone: 8241592Srgrimes LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 8251592Srgrimes (*closefunc)(fout); 8261592Srgrimes} 8271592Srgrimes 8281592Srgrimesstatic FILE * 8291592Srgrimesgetdatasock(mode) 8301592Srgrimes char *mode; 8311592Srgrimes{ 8321592Srgrimes int on = 1, s, t, tries; 8331592Srgrimes 8341592Srgrimes if (data >= 0) 8351592Srgrimes return (fdopen(data, mode)); 8361592Srgrimes (void) seteuid((uid_t)0); 8371592Srgrimes s = socket(AF_INET, SOCK_STREAM, 0); 8381592Srgrimes if (s < 0) 8391592Srgrimes goto bad; 8401592Srgrimes if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 8411592Srgrimes (char *) &on, sizeof(on)) < 0) 8421592Srgrimes goto bad; 8431592Srgrimes /* anchor socket to avoid multi-homing problems */ 8441592Srgrimes data_source.sin_family = AF_INET; 8451592Srgrimes data_source.sin_addr = ctrl_addr.sin_addr; 8461592Srgrimes for (tries = 1; ; tries++) { 8471592Srgrimes if (bind(s, (struct sockaddr *)&data_source, 8481592Srgrimes sizeof(data_source)) >= 0) 8491592Srgrimes break; 8501592Srgrimes if (errno != EADDRINUSE || tries > 10) 8511592Srgrimes goto bad; 8521592Srgrimes sleep(tries); 8531592Srgrimes } 8541592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 8551592Srgrimes#ifdef IP_TOS 8561592Srgrimes on = IPTOS_THROUGHPUT; 8571592Srgrimes if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 8581592Srgrimes syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 8591592Srgrimes#endif 8601592Srgrimes return (fdopen(s, mode)); 8611592Srgrimesbad: 8621592Srgrimes /* Return the real value of errno (close may change it) */ 8631592Srgrimes t = errno; 8641592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 8651592Srgrimes (void) close(s); 8661592Srgrimes errno = t; 8671592Srgrimes return (NULL); 8681592Srgrimes} 8691592Srgrimes 8701592Srgrimesstatic FILE * 8711592Srgrimesdataconn(name, size, mode) 8721592Srgrimes char *name; 8731592Srgrimes off_t size; 8741592Srgrimes char *mode; 8751592Srgrimes{ 8761592Srgrimes char sizebuf[32]; 8771592Srgrimes FILE *file; 8781592Srgrimes int retry = 0, tos; 8791592Srgrimes 8801592Srgrimes file_size = size; 8811592Srgrimes byte_count = 0; 8821592Srgrimes if (size != (off_t) -1) 8831592Srgrimes (void) sprintf(sizebuf, " (%qd bytes)", size); 8841592Srgrimes else 8851592Srgrimes (void) strcpy(sizebuf, ""); 8861592Srgrimes if (pdata >= 0) { 8871592Srgrimes struct sockaddr_in from; 8881592Srgrimes int s, fromlen = sizeof(from); 8891592Srgrimes 8901592Srgrimes s = accept(pdata, (struct sockaddr *)&from, &fromlen); 8911592Srgrimes if (s < 0) { 8921592Srgrimes reply(425, "Can't open data connection."); 8931592Srgrimes (void) close(pdata); 8941592Srgrimes pdata = -1; 8951592Srgrimes return (NULL); 8961592Srgrimes } 8971592Srgrimes (void) close(pdata); 8981592Srgrimes pdata = s; 8991592Srgrimes#ifdef IP_TOS 9001592Srgrimes tos = IPTOS_LOWDELAY; 9011592Srgrimes (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 9021592Srgrimes sizeof(int)); 9031592Srgrimes#endif 9041592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 9051592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 9061592Srgrimes return (fdopen(pdata, mode)); 9071592Srgrimes } 9081592Srgrimes if (data >= 0) { 9091592Srgrimes reply(125, "Using existing data connection for '%s'%s.", 9101592Srgrimes name, sizebuf); 9111592Srgrimes usedefault = 1; 9121592Srgrimes return (fdopen(data, mode)); 9131592Srgrimes } 9141592Srgrimes if (usedefault) 9151592Srgrimes data_dest = his_addr; 9161592Srgrimes usedefault = 1; 9171592Srgrimes file = getdatasock(mode); 9181592Srgrimes if (file == NULL) { 9191592Srgrimes reply(425, "Can't create data socket (%s,%d): %s.", 9201592Srgrimes inet_ntoa(data_source.sin_addr), 9211592Srgrimes ntohs(data_source.sin_port), strerror(errno)); 9221592Srgrimes return (NULL); 9231592Srgrimes } 9241592Srgrimes data = fileno(file); 9251592Srgrimes while (connect(data, (struct sockaddr *)&data_dest, 9261592Srgrimes sizeof(data_dest)) < 0) { 9271592Srgrimes if (errno == EADDRINUSE && retry < swaitmax) { 9281592Srgrimes sleep((unsigned) swaitint); 9291592Srgrimes retry += swaitint; 9301592Srgrimes continue; 9311592Srgrimes } 9321592Srgrimes perror_reply(425, "Can't build data connection"); 9331592Srgrimes (void) fclose(file); 9341592Srgrimes data = -1; 9351592Srgrimes return (NULL); 9361592Srgrimes } 9371592Srgrimes reply(150, "Opening %s mode data connection for '%s'%s.", 9381592Srgrimes type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 9391592Srgrimes return (file); 9401592Srgrimes} 9411592Srgrimes 9421592Srgrimes/* 9431592Srgrimes * Tranfer the contents of "instr" to "outstr" peer using the appropriate 9441592Srgrimes * encapsulation of the data subject * to Mode, Structure, and Type. 9451592Srgrimes * 9461592Srgrimes * NB: Form isn't handled. 9471592Srgrimes */ 9481592Srgrimesstatic void 9491592Srgrimessend_data(instr, outstr, blksize) 9501592Srgrimes FILE *instr, *outstr; 9511592Srgrimes off_t blksize; 9521592Srgrimes{ 9531592Srgrimes int c, cnt, filefd, netfd; 9541592Srgrimes char *buf; 9551592Srgrimes 9561592Srgrimes transflag++; 9571592Srgrimes if (setjmp(urgcatch)) { 9581592Srgrimes transflag = 0; 9591592Srgrimes return; 9601592Srgrimes } 9611592Srgrimes switch (type) { 9621592Srgrimes 9631592Srgrimes case TYPE_A: 9641592Srgrimes while ((c = getc(instr)) != EOF) { 9651592Srgrimes byte_count++; 9661592Srgrimes if (c == '\n') { 9671592Srgrimes if (ferror(outstr)) 9681592Srgrimes goto data_err; 9691592Srgrimes (void) putc('\r', outstr); 9701592Srgrimes } 9711592Srgrimes (void) putc(c, outstr); 9721592Srgrimes } 9731592Srgrimes fflush(outstr); 9741592Srgrimes transflag = 0; 9751592Srgrimes if (ferror(instr)) 9761592Srgrimes goto file_err; 9771592Srgrimes if (ferror(outstr)) 9781592Srgrimes goto data_err; 9791592Srgrimes reply(226, "Transfer complete."); 9801592Srgrimes return; 9811592Srgrimes 9821592Srgrimes case TYPE_I: 9831592Srgrimes case TYPE_L: 9841592Srgrimes if ((buf = malloc((u_int)blksize)) == NULL) { 9851592Srgrimes transflag = 0; 9861592Srgrimes perror_reply(451, "Local resource failure: malloc"); 9871592Srgrimes return; 9881592Srgrimes } 9891592Srgrimes netfd = fileno(outstr); 9901592Srgrimes filefd = fileno(instr); 9911592Srgrimes while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 9921592Srgrimes write(netfd, buf, cnt) == cnt) 9931592Srgrimes byte_count += cnt; 9941592Srgrimes transflag = 0; 9951592Srgrimes (void)free(buf); 9961592Srgrimes if (cnt != 0) { 9971592Srgrimes if (cnt < 0) 9981592Srgrimes goto file_err; 9991592Srgrimes goto data_err; 10001592Srgrimes } 10011592Srgrimes reply(226, "Transfer complete."); 10021592Srgrimes return; 10031592Srgrimes default: 10041592Srgrimes transflag = 0; 10051592Srgrimes reply(550, "Unimplemented TYPE %d in send_data", type); 10061592Srgrimes return; 10071592Srgrimes } 10081592Srgrimes 10091592Srgrimesdata_err: 10101592Srgrimes transflag = 0; 10111592Srgrimes perror_reply(426, "Data connection"); 10121592Srgrimes return; 10131592Srgrimes 10141592Srgrimesfile_err: 10151592Srgrimes transflag = 0; 10161592Srgrimes perror_reply(551, "Error on input file"); 10171592Srgrimes} 10181592Srgrimes 10191592Srgrimes/* 10201592Srgrimes * Transfer data from peer to "outstr" using the appropriate encapulation of 10211592Srgrimes * the data subject to Mode, Structure, and Type. 10221592Srgrimes * 10231592Srgrimes * N.B.: Form isn't handled. 10241592Srgrimes */ 10251592Srgrimesstatic int 10261592Srgrimesreceive_data(instr, outstr) 10271592Srgrimes FILE *instr, *outstr; 10281592Srgrimes{ 10291592Srgrimes int c; 10301592Srgrimes int cnt, bare_lfs = 0; 10311592Srgrimes char buf[BUFSIZ]; 10321592Srgrimes 10331592Srgrimes transflag++; 10341592Srgrimes if (setjmp(urgcatch)) { 10351592Srgrimes transflag = 0; 10361592Srgrimes return (-1); 10371592Srgrimes } 10381592Srgrimes switch (type) { 10391592Srgrimes 10401592Srgrimes case TYPE_I: 10411592Srgrimes case TYPE_L: 10421592Srgrimes while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 10431592Srgrimes if (write(fileno(outstr), buf, cnt) != cnt) 10441592Srgrimes goto file_err; 10451592Srgrimes byte_count += cnt; 10461592Srgrimes } 10471592Srgrimes if (cnt < 0) 10481592Srgrimes goto data_err; 10491592Srgrimes transflag = 0; 10501592Srgrimes return (0); 10511592Srgrimes 10521592Srgrimes case TYPE_E: 10531592Srgrimes reply(553, "TYPE E not implemented."); 10541592Srgrimes transflag = 0; 10551592Srgrimes return (-1); 10561592Srgrimes 10571592Srgrimes case TYPE_A: 10581592Srgrimes while ((c = getc(instr)) != EOF) { 10591592Srgrimes byte_count++; 10601592Srgrimes if (c == '\n') 10611592Srgrimes bare_lfs++; 10621592Srgrimes while (c == '\r') { 10631592Srgrimes if (ferror(outstr)) 10641592Srgrimes goto data_err; 10651592Srgrimes if ((c = getc(instr)) != '\n') { 10661592Srgrimes (void) putc ('\r', outstr); 10671592Srgrimes if (c == '\0' || c == EOF) 10681592Srgrimes goto contin2; 10691592Srgrimes } 10701592Srgrimes } 10711592Srgrimes (void) putc(c, outstr); 10721592Srgrimes contin2: ; 10731592Srgrimes } 10741592Srgrimes fflush(outstr); 10751592Srgrimes if (ferror(instr)) 10761592Srgrimes goto data_err; 10771592Srgrimes if (ferror(outstr)) 10781592Srgrimes goto file_err; 10791592Srgrimes transflag = 0; 10801592Srgrimes if (bare_lfs) { 10811592Srgrimes lreply(226, 10821592Srgrimes "WARNING! %d bare linefeeds received in ASCII mode", 10831592Srgrimes bare_lfs); 10841592Srgrimes (void)printf(" File may not have transferred correctly.\r\n"); 10851592Srgrimes } 10861592Srgrimes return (0); 10871592Srgrimes default: 10881592Srgrimes reply(550, "Unimplemented TYPE %d in receive_data", type); 10891592Srgrimes transflag = 0; 10901592Srgrimes return (-1); 10911592Srgrimes } 10921592Srgrimes 10931592Srgrimesdata_err: 10941592Srgrimes transflag = 0; 10951592Srgrimes perror_reply(426, "Data Connection"); 10961592Srgrimes return (-1); 10971592Srgrimes 10981592Srgrimesfile_err: 10991592Srgrimes transflag = 0; 11001592Srgrimes perror_reply(452, "Error writing file"); 11011592Srgrimes return (-1); 11021592Srgrimes} 11031592Srgrimes 11041592Srgrimesvoid 11051592Srgrimesstatfilecmd(filename) 11061592Srgrimes char *filename; 11071592Srgrimes{ 11081592Srgrimes FILE *fin; 11091592Srgrimes int c; 11101592Srgrimes char line[LINE_MAX]; 11111592Srgrimes 11121592Srgrimes (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); 11131592Srgrimes fin = ftpd_popen(line, "r"); 11141592Srgrimes lreply(211, "status of %s:", filename); 11151592Srgrimes while ((c = getc(fin)) != EOF) { 11161592Srgrimes if (c == '\n') { 11171592Srgrimes if (ferror(stdout)){ 11181592Srgrimes perror_reply(421, "control connection"); 11191592Srgrimes (void) ftpd_pclose(fin); 11201592Srgrimes dologout(1); 11211592Srgrimes /* NOTREACHED */ 11221592Srgrimes } 11231592Srgrimes if (ferror(fin)) { 11241592Srgrimes perror_reply(551, filename); 11251592Srgrimes (void) ftpd_pclose(fin); 11261592Srgrimes return; 11271592Srgrimes } 11281592Srgrimes (void) putc('\r', stdout); 11291592Srgrimes } 11301592Srgrimes (void) putc(c, stdout); 11311592Srgrimes } 11321592Srgrimes (void) ftpd_pclose(fin); 11331592Srgrimes reply(211, "End of Status"); 11341592Srgrimes} 11351592Srgrimes 11361592Srgrimesvoid 11371592Srgrimesstatcmd() 11381592Srgrimes{ 11391592Srgrimes struct sockaddr_in *sin; 11401592Srgrimes u_char *a, *p; 11411592Srgrimes 11421592Srgrimes lreply(211, "%s FTP server status:", hostname, version); 11431592Srgrimes printf(" %s\r\n", version); 11441592Srgrimes printf(" Connected to %s", remotehost); 11451592Srgrimes if (!isdigit(remotehost[0])) 11461592Srgrimes printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 11471592Srgrimes printf("\r\n"); 11481592Srgrimes if (logged_in) { 11491592Srgrimes if (guest) 11501592Srgrimes printf(" Logged in anonymously\r\n"); 11511592Srgrimes else 11521592Srgrimes printf(" Logged in as %s\r\n", pw->pw_name); 11531592Srgrimes } else if (askpasswd) 11541592Srgrimes printf(" Waiting for password\r\n"); 11551592Srgrimes else 11561592Srgrimes printf(" Waiting for user name\r\n"); 11571592Srgrimes printf(" TYPE: %s", typenames[type]); 11581592Srgrimes if (type == TYPE_A || type == TYPE_E) 11591592Srgrimes printf(", FORM: %s", formnames[form]); 11601592Srgrimes if (type == TYPE_L) 11611592Srgrimes#if NBBY == 8 11621592Srgrimes printf(" %d", NBBY); 11631592Srgrimes#else 11641592Srgrimes printf(" %d", bytesize); /* need definition! */ 11651592Srgrimes#endif 11661592Srgrimes printf("; STRUcture: %s; transfer MODE: %s\r\n", 11671592Srgrimes strunames[stru], modenames[mode]); 11681592Srgrimes if (data != -1) 11691592Srgrimes printf(" Data connection open\r\n"); 11701592Srgrimes else if (pdata != -1) { 11711592Srgrimes printf(" in Passive mode"); 11721592Srgrimes sin = &pasv_addr; 11731592Srgrimes goto printaddr; 11741592Srgrimes } else if (usedefault == 0) { 11751592Srgrimes printf(" PORT"); 11761592Srgrimes sin = &data_dest; 11771592Srgrimesprintaddr: 11781592Srgrimes a = (u_char *) &sin->sin_addr; 11791592Srgrimes p = (u_char *) &sin->sin_port; 11801592Srgrimes#define UC(b) (((int) b) & 0xff) 11811592Srgrimes printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 11821592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 11831592Srgrimes#undef UC 11841592Srgrimes } else 11851592Srgrimes printf(" No data connection\r\n"); 11861592Srgrimes reply(211, "End of status"); 11871592Srgrimes} 11881592Srgrimes 11891592Srgrimesvoid 11901592Srgrimesfatal(s) 11911592Srgrimes char *s; 11921592Srgrimes{ 11931592Srgrimes 11941592Srgrimes reply(451, "Error in server: %s\n", s); 11951592Srgrimes reply(221, "Closing connection due to server error."); 11961592Srgrimes dologout(0); 11971592Srgrimes /* NOTREACHED */ 11981592Srgrimes} 11991592Srgrimes 12001592Srgrimesvoid 12011592Srgrimes#if __STDC__ 12021592Srgrimesreply(int n, const char *fmt, ...) 12031592Srgrimes#else 12041592Srgrimesreply(n, fmt, va_alist) 12051592Srgrimes int n; 12061592Srgrimes char *fmt; 12071592Srgrimes va_dcl 12081592Srgrimes#endif 12091592Srgrimes{ 12101592Srgrimes va_list ap; 12111592Srgrimes#if __STDC__ 12121592Srgrimes va_start(ap, fmt); 12131592Srgrimes#else 12141592Srgrimes va_start(ap); 12151592Srgrimes#endif 12161592Srgrimes (void)printf("%d ", n); 12171592Srgrimes (void)vprintf(fmt, ap); 12181592Srgrimes (void)printf("\r\n"); 12191592Srgrimes (void)fflush(stdout); 12201592Srgrimes if (debug) { 12211592Srgrimes syslog(LOG_DEBUG, "<--- %d ", n); 12221592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 12231592Srgrimes } 12241592Srgrimes} 12251592Srgrimes 12261592Srgrimesvoid 12271592Srgrimes#if __STDC__ 12281592Srgrimeslreply(int n, const char *fmt, ...) 12291592Srgrimes#else 12301592Srgrimeslreply(n, fmt, va_alist) 12311592Srgrimes int n; 12321592Srgrimes char *fmt; 12331592Srgrimes va_dcl 12341592Srgrimes#endif 12351592Srgrimes{ 12361592Srgrimes va_list ap; 12371592Srgrimes#if __STDC__ 12381592Srgrimes va_start(ap, fmt); 12391592Srgrimes#else 12401592Srgrimes va_start(ap); 12411592Srgrimes#endif 12421592Srgrimes (void)printf("%d- ", n); 12431592Srgrimes (void)vprintf(fmt, ap); 12441592Srgrimes (void)printf("\r\n"); 12451592Srgrimes (void)fflush(stdout); 12461592Srgrimes if (debug) { 12471592Srgrimes syslog(LOG_DEBUG, "<--- %d- ", n); 12481592Srgrimes vsyslog(LOG_DEBUG, fmt, ap); 12491592Srgrimes } 12501592Srgrimes} 12511592Srgrimes 12521592Srgrimesstatic void 12531592Srgrimesack(s) 12541592Srgrimes char *s; 12551592Srgrimes{ 12561592Srgrimes 12571592Srgrimes reply(250, "%s command successful.", s); 12581592Srgrimes} 12591592Srgrimes 12601592Srgrimesvoid 12611592Srgrimesnack(s) 12621592Srgrimes char *s; 12631592Srgrimes{ 12641592Srgrimes 12651592Srgrimes reply(502, "%s command not implemented.", s); 12661592Srgrimes} 12671592Srgrimes 12681592Srgrimes/* ARGSUSED */ 12691592Srgrimesvoid 12701592Srgrimesyyerror(s) 12711592Srgrimes char *s; 12721592Srgrimes{ 12731592Srgrimes char *cp; 12741592Srgrimes 12751592Srgrimes if (cp = strchr(cbuf,'\n')) 12761592Srgrimes *cp = '\0'; 12771592Srgrimes reply(500, "'%s': command not understood.", cbuf); 12781592Srgrimes} 12791592Srgrimes 12801592Srgrimesvoid 12811592Srgrimesdelete(name) 12821592Srgrimes char *name; 12831592Srgrimes{ 12841592Srgrimes struct stat st; 12851592Srgrimes 12861592Srgrimes LOGCMD("delete", name); 12871592Srgrimes if (stat(name, &st) < 0) { 12881592Srgrimes perror_reply(550, name); 12891592Srgrimes return; 12901592Srgrimes } 12911592Srgrimes if ((st.st_mode&S_IFMT) == S_IFDIR) { 12921592Srgrimes if (rmdir(name) < 0) { 12931592Srgrimes perror_reply(550, name); 12941592Srgrimes return; 12951592Srgrimes } 12961592Srgrimes goto done; 12971592Srgrimes } 12981592Srgrimes if (unlink(name) < 0) { 12991592Srgrimes perror_reply(550, name); 13001592Srgrimes return; 13011592Srgrimes } 13021592Srgrimesdone: 13031592Srgrimes ack("DELE"); 13041592Srgrimes} 13051592Srgrimes 13061592Srgrimesvoid 13071592Srgrimescwd(path) 13081592Srgrimes char *path; 13091592Srgrimes{ 13101592Srgrimes 13111592Srgrimes if (chdir(path) < 0) 13121592Srgrimes perror_reply(550, path); 13131592Srgrimes else 13141592Srgrimes ack("CWD"); 13151592Srgrimes} 13161592Srgrimes 13171592Srgrimesvoid 13181592Srgrimesmakedir(name) 13191592Srgrimes char *name; 13201592Srgrimes{ 13211592Srgrimes 13221592Srgrimes LOGCMD("mkdir", name); 13231592Srgrimes if (mkdir(name, 0777) < 0) 13241592Srgrimes perror_reply(550, name); 13251592Srgrimes else 13261592Srgrimes reply(257, "MKD command successful."); 13271592Srgrimes} 13281592Srgrimes 13291592Srgrimesvoid 13301592Srgrimesremovedir(name) 13311592Srgrimes char *name; 13321592Srgrimes{ 13331592Srgrimes 13341592Srgrimes LOGCMD("rmdir", name); 13351592Srgrimes if (rmdir(name) < 0) 13361592Srgrimes perror_reply(550, name); 13371592Srgrimes else 13381592Srgrimes ack("RMD"); 13391592Srgrimes} 13401592Srgrimes 13411592Srgrimesvoid 13421592Srgrimespwd() 13431592Srgrimes{ 13441592Srgrimes char path[MAXPATHLEN + 1]; 13451592Srgrimes 13461592Srgrimes if (getwd(path) == (char *)NULL) 13471592Srgrimes reply(550, "%s.", path); 13481592Srgrimes else 13491592Srgrimes reply(257, "\"%s\" is current directory.", path); 13501592Srgrimes} 13511592Srgrimes 13521592Srgrimeschar * 13531592Srgrimesrenamefrom(name) 13541592Srgrimes char *name; 13551592Srgrimes{ 13561592Srgrimes struct stat st; 13571592Srgrimes 13581592Srgrimes if (stat(name, &st) < 0) { 13591592Srgrimes perror_reply(550, name); 13601592Srgrimes return ((char *)0); 13611592Srgrimes } 13621592Srgrimes reply(350, "File exists, ready for destination name"); 13631592Srgrimes return (name); 13641592Srgrimes} 13651592Srgrimes 13661592Srgrimesvoid 13671592Srgrimesrenamecmd(from, to) 13681592Srgrimes char *from, *to; 13691592Srgrimes{ 13701592Srgrimes 13711592Srgrimes LOGCMD2("rename", from, to); 13721592Srgrimes if (rename(from, to) < 0) 13731592Srgrimes perror_reply(550, "rename"); 13741592Srgrimes else 13751592Srgrimes ack("RNTO"); 13761592Srgrimes} 13771592Srgrimes 13781592Srgrimesstatic void 13791592Srgrimesdolog(sin) 13801592Srgrimes struct sockaddr_in *sin; 13811592Srgrimes{ 13821592Srgrimes struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 13831592Srgrimes sizeof(struct in_addr), AF_INET); 13841592Srgrimes 13851592Srgrimes if (hp) 13861592Srgrimes (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); 13871592Srgrimes else 13881592Srgrimes (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 13891592Srgrimes sizeof(remotehost)); 13901592Srgrimes#ifdef SETPROCTITLE 13911592Srgrimes snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 13921592Srgrimes setproctitle(proctitle); 13931592Srgrimes#endif /* SETPROCTITLE */ 13941592Srgrimes 13951592Srgrimes if (logging) 13961592Srgrimes syslog(LOG_INFO, "connection from %s", remotehost); 13971592Srgrimes} 13981592Srgrimes 13991592Srgrimes/* 14001592Srgrimes * Record logout in wtmp file 14011592Srgrimes * and exit with supplied status. 14021592Srgrimes */ 14031592Srgrimesvoid 14041592Srgrimesdologout(status) 14051592Srgrimes int status; 14061592Srgrimes{ 14071592Srgrimes 14081592Srgrimes if (logged_in) { 14091592Srgrimes (void) seteuid((uid_t)0); 14101592Srgrimes logwtmp(ttyline, "", ""); 14111592Srgrimes } 14121592Srgrimes /* beware of flushing buffers after a SIGPIPE */ 14131592Srgrimes _exit(status); 14141592Srgrimes} 14151592Srgrimes 14161592Srgrimesstatic void 14171592Srgrimesmyoob(signo) 14181592Srgrimes int signo; 14191592Srgrimes{ 14201592Srgrimes char *cp; 14211592Srgrimes 14221592Srgrimes /* only process if transfer occurring */ 14231592Srgrimes if (!transflag) 14241592Srgrimes return; 14251592Srgrimes cp = tmpline; 14261592Srgrimes if (getline(cp, 7, stdin) == NULL) { 14271592Srgrimes reply(221, "You could at least say goodbye."); 14281592Srgrimes dologout(0); 14291592Srgrimes } 14301592Srgrimes upper(cp); 14311592Srgrimes if (strcmp(cp, "ABOR\r\n") == 0) { 14321592Srgrimes tmpline[0] = '\0'; 14331592Srgrimes reply(426, "Transfer aborted. Data connection closed."); 14341592Srgrimes reply(226, "Abort successful"); 14351592Srgrimes longjmp(urgcatch, 1); 14361592Srgrimes } 14371592Srgrimes if (strcmp(cp, "STAT\r\n") == 0) { 14381592Srgrimes if (file_size != (off_t) -1) 14391592Srgrimes reply(213, "Status: %qd of %qd bytes transferred", 14401592Srgrimes byte_count, file_size); 14411592Srgrimes else 14421592Srgrimes reply(213, "Status: %qd bytes transferred", byte_count); 14431592Srgrimes } 14441592Srgrimes} 14451592Srgrimes 14461592Srgrimes/* 14471592Srgrimes * Note: a response of 425 is not mentioned as a possible response to 14481592Srgrimes * the PASV command in RFC959. However, it has been blessed as 14491592Srgrimes * a legitimate response by Jon Postel in a telephone conversation 14501592Srgrimes * with Rick Adams on 25 Jan 89. 14511592Srgrimes */ 14521592Srgrimesvoid 14531592Srgrimespassive() 14541592Srgrimes{ 14551592Srgrimes int len; 14561592Srgrimes char *p, *a; 14571592Srgrimes 14581592Srgrimes pdata = socket(AF_INET, SOCK_STREAM, 0); 14591592Srgrimes if (pdata < 0) { 14601592Srgrimes perror_reply(425, "Can't open passive connection"); 14611592Srgrimes return; 14621592Srgrimes } 14631592Srgrimes pasv_addr = ctrl_addr; 14641592Srgrimes pasv_addr.sin_port = 0; 14651592Srgrimes (void) seteuid((uid_t)0); 14661592Srgrimes if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 14671592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 14681592Srgrimes goto pasv_error; 14691592Srgrimes } 14701592Srgrimes (void) seteuid((uid_t)pw->pw_uid); 14711592Srgrimes len = sizeof(pasv_addr); 14721592Srgrimes if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 14731592Srgrimes goto pasv_error; 14741592Srgrimes if (listen(pdata, 1) < 0) 14751592Srgrimes goto pasv_error; 14761592Srgrimes a = (char *) &pasv_addr.sin_addr; 14771592Srgrimes p = (char *) &pasv_addr.sin_port; 14781592Srgrimes 14791592Srgrimes#define UC(b) (((int) b) & 0xff) 14801592Srgrimes 14811592Srgrimes reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 14821592Srgrimes UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 14831592Srgrimes return; 14841592Srgrimes 14851592Srgrimespasv_error: 14861592Srgrimes (void) close(pdata); 14871592Srgrimes pdata = -1; 14881592Srgrimes perror_reply(425, "Can't open passive connection"); 14891592Srgrimes return; 14901592Srgrimes} 14911592Srgrimes 14921592Srgrimes/* 14931592Srgrimes * Generate unique name for file with basename "local". 14941592Srgrimes * The file named "local" is already known to exist. 14951592Srgrimes * Generates failure reply on error. 14961592Srgrimes */ 14971592Srgrimesstatic char * 14981592Srgrimesgunique(local) 14991592Srgrimes char *local; 15001592Srgrimes{ 15011592Srgrimes static char new[MAXPATHLEN]; 15021592Srgrimes struct stat st; 15031592Srgrimes int count; 15041592Srgrimes char *cp; 15051592Srgrimes 15061592Srgrimes cp = strrchr(local, '/'); 15071592Srgrimes if (cp) 15081592Srgrimes *cp = '\0'; 15091592Srgrimes if (stat(cp ? local : ".", &st) < 0) { 15101592Srgrimes perror_reply(553, cp ? local : "."); 15111592Srgrimes return ((char *) 0); 15121592Srgrimes } 15131592Srgrimes if (cp) 15141592Srgrimes *cp = '/'; 15151592Srgrimes (void) strcpy(new, local); 15161592Srgrimes cp = new + strlen(new); 15171592Srgrimes *cp++ = '.'; 15181592Srgrimes for (count = 1; count < 100; count++) { 15191592Srgrimes (void)sprintf(cp, "%d", count); 15201592Srgrimes if (stat(new, &st) < 0) 15211592Srgrimes return (new); 15221592Srgrimes } 15231592Srgrimes reply(452, "Unique file name cannot be created."); 15241592Srgrimes return (NULL); 15251592Srgrimes} 15261592Srgrimes 15271592Srgrimes/* 15281592Srgrimes * Format and send reply containing system error number. 15291592Srgrimes */ 15301592Srgrimesvoid 15311592Srgrimesperror_reply(code, string) 15321592Srgrimes int code; 15331592Srgrimes char *string; 15341592Srgrimes{ 15351592Srgrimes 15361592Srgrimes reply(code, "%s: %s.", string, strerror(errno)); 15371592Srgrimes} 15381592Srgrimes 15391592Srgrimesstatic char *onefile[] = { 15401592Srgrimes "", 15411592Srgrimes 0 15421592Srgrimes}; 15431592Srgrimes 15441592Srgrimesvoid 15451592Srgrimessend_file_list(whichf) 15461592Srgrimes char *whichf; 15471592Srgrimes{ 15481592Srgrimes struct stat st; 15491592Srgrimes DIR *dirp = NULL; 15501592Srgrimes struct dirent *dir; 15511592Srgrimes FILE *dout = NULL; 15521592Srgrimes char **dirlist, *dirname; 15531592Srgrimes int simple = 0; 15541592Srgrimes int freeglob = 0; 15551592Srgrimes glob_t gl; 15561592Srgrimes 15571592Srgrimes if (strpbrk(whichf, "~{[*?") != NULL) { 15581592Srgrimes int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 15591592Srgrimes 15601592Srgrimes memset(&gl, 0, sizeof(gl)); 15611592Srgrimes freeglob = 1; 15621592Srgrimes if (glob(whichf, flags, 0, &gl)) { 15631592Srgrimes reply(550, "not found"); 15641592Srgrimes goto out; 15651592Srgrimes } else if (gl.gl_pathc == 0) { 15661592Srgrimes errno = ENOENT; 15671592Srgrimes perror_reply(550, whichf); 15681592Srgrimes goto out; 15691592Srgrimes } 15701592Srgrimes dirlist = gl.gl_pathv; 15711592Srgrimes } else { 15721592Srgrimes onefile[0] = whichf; 15731592Srgrimes dirlist = onefile; 15741592Srgrimes simple = 1; 15751592Srgrimes } 15761592Srgrimes 15771592Srgrimes if (setjmp(urgcatch)) { 15781592Srgrimes transflag = 0; 15791592Srgrimes goto out; 15801592Srgrimes } 15811592Srgrimes while (dirname = *dirlist++) { 15821592Srgrimes if (stat(dirname, &st) < 0) { 15831592Srgrimes /* 15841592Srgrimes * If user typed "ls -l", etc, and the client 15851592Srgrimes * used NLST, do what the user meant. 15861592Srgrimes */ 15871592Srgrimes if (dirname[0] == '-' && *dirlist == NULL && 15881592Srgrimes transflag == 0) { 15891592Srgrimes retrieve("/bin/ls %s", dirname); 15901592Srgrimes goto out; 15911592Srgrimes } 15921592Srgrimes perror_reply(550, whichf); 15931592Srgrimes if (dout != NULL) { 15941592Srgrimes (void) fclose(dout); 15951592Srgrimes transflag = 0; 15961592Srgrimes data = -1; 15971592Srgrimes pdata = -1; 15981592Srgrimes } 15991592Srgrimes goto out; 16001592Srgrimes } 16011592Srgrimes 16021592Srgrimes if (S_ISREG(st.st_mode)) { 16031592Srgrimes if (dout == NULL) { 16041592Srgrimes dout = dataconn("file list", (off_t)-1, "w"); 16051592Srgrimes if (dout == NULL) 16061592Srgrimes goto out; 16071592Srgrimes transflag++; 16081592Srgrimes } 16091592Srgrimes fprintf(dout, "%s%s\n", dirname, 16101592Srgrimes type == TYPE_A ? "\r" : ""); 16111592Srgrimes byte_count += strlen(dirname) + 1; 16121592Srgrimes continue; 16131592Srgrimes } else if (!S_ISDIR(st.st_mode)) 16141592Srgrimes continue; 16151592Srgrimes 16161592Srgrimes if ((dirp = opendir(dirname)) == NULL) 16171592Srgrimes continue; 16181592Srgrimes 16191592Srgrimes while ((dir = readdir(dirp)) != NULL) { 16201592Srgrimes char nbuf[MAXPATHLEN]; 16211592Srgrimes 16221592Srgrimes if (dir->d_name[0] == '.' && dir->d_namlen == 1) 16231592Srgrimes continue; 16241592Srgrimes if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 16251592Srgrimes dir->d_namlen == 2) 16261592Srgrimes continue; 16271592Srgrimes 16281592Srgrimes sprintf(nbuf, "%s/%s", dirname, dir->d_name); 16291592Srgrimes 16301592Srgrimes /* 16311592Srgrimes * We have to do a stat to insure it's 16321592Srgrimes * not a directory or special file. 16331592Srgrimes */ 16341592Srgrimes if (simple || (stat(nbuf, &st) == 0 && 16351592Srgrimes S_ISREG(st.st_mode))) { 16361592Srgrimes if (dout == NULL) { 16371592Srgrimes dout = dataconn("file list", (off_t)-1, 16381592Srgrimes "w"); 16391592Srgrimes if (dout == NULL) 16401592Srgrimes goto out; 16411592Srgrimes transflag++; 16421592Srgrimes } 16431592Srgrimes if (nbuf[0] == '.' && nbuf[1] == '/') 16441592Srgrimes fprintf(dout, "%s%s\n", &nbuf[2], 16451592Srgrimes type == TYPE_A ? "\r" : ""); 16461592Srgrimes else 16471592Srgrimes fprintf(dout, "%s%s\n", nbuf, 16481592Srgrimes type == TYPE_A ? "\r" : ""); 16491592Srgrimes byte_count += strlen(nbuf) + 1; 16501592Srgrimes } 16511592Srgrimes } 16521592Srgrimes (void) closedir(dirp); 16531592Srgrimes } 16541592Srgrimes 16551592Srgrimes if (dout == NULL) 16561592Srgrimes reply(550, "No files found."); 16571592Srgrimes else if (ferror(dout) != 0) 16581592Srgrimes perror_reply(550, "Data connection"); 16591592Srgrimes else 16601592Srgrimes reply(226, "Transfer complete."); 16611592Srgrimes 16621592Srgrimes transflag = 0; 16631592Srgrimes if (dout != NULL) 16641592Srgrimes (void) fclose(dout); 16651592Srgrimes data = -1; 16661592Srgrimes pdata = -1; 16671592Srgrimesout: 16681592Srgrimes if (freeglob) { 16691592Srgrimes freeglob = 0; 16701592Srgrimes globfree(&gl); 16711592Srgrimes } 16721592Srgrimes} 16731592Srgrimes 16741592Srgrimes#ifdef SETPROCTITLE 16751592Srgrimes/* 16761592Srgrimes * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 16771592Srgrimes * Warning, since this is usually started from inetd.conf, it often doesn't 16781592Srgrimes * have much of an environment or arglist to overwrite. 16791592Srgrimes */ 16801592Srgrimesvoid 16811592Srgrimes#if __STDC__ 16821592Srgrimessetproctitle(const char *fmt, ...) 16831592Srgrimes#else 16841592Srgrimessetproctitle(fmt, va_alist) 16851592Srgrimes char *fmt; 16861592Srgrimes va_dcl 16871592Srgrimes#endif 16881592Srgrimes{ 16891592Srgrimes int i; 16901592Srgrimes va_list ap; 16911592Srgrimes char *p, *bp, ch; 16921592Srgrimes char buf[LINE_MAX]; 16931592Srgrimes 16941592Srgrimes#if __STDC__ 16951592Srgrimes va_start(ap, fmt); 16961592Srgrimes#else 16971592Srgrimes va_start(ap); 16981592Srgrimes#endif 16991592Srgrimes (void)vsnprintf(buf, sizeof(buf), fmt, ap); 17001592Srgrimes 17011592Srgrimes /* make ps print our process name */ 17021592Srgrimes p = Argv[0]; 17031592Srgrimes *p++ = '-'; 17041592Srgrimes 17051592Srgrimes i = strlen(buf); 17061592Srgrimes if (i > LastArgv - p - 2) { 17071592Srgrimes i = LastArgv - p - 2; 17081592Srgrimes buf[i] = '\0'; 17091592Srgrimes } 17101592Srgrimes bp = buf; 17111592Srgrimes while (ch = *bp++) 17121592Srgrimes if (ch != '\n' && ch != '\r') 17131592Srgrimes *p++ = ch; 17141592Srgrimes while (p < LastArgv) 17151592Srgrimes *p++ = ' '; 17161592Srgrimes} 17171592Srgrimes#endif /* SETPROCTITLE */ 17186740Sguido 17196740Sguido#ifdef STATS 17206740Sguidologxfer(name, size, start) 17216740Sguido char *name; 17226740Sguido long size; 17236740Sguido long start; 17246740Sguido{ 17256740Sguido char buf[1024]; 17266740Sguido char path[MAXPATHLEN + 1]; 17276740Sguido long now; 17286740Sguido 17296740Sguido if (statfd >= 0 && getwd(path) != NULL) { 17306740Sguido time(&now); 17316740Sguido sprintf(buf, "%.20s!%s!%s!%s/%s!%ld!%ld\n", 17326740Sguido ctime(&now)+4, ident, remotehost, 17336740Sguido path, name, size, now - start + (now == start)); 17346740Sguido write(statfd, buf, strlen(buf)); 17356740Sguido } 17366740Sguido} 17376740Sguido 17386740Sguidochar * 17396740Sguidocopy(s) 17406740Sguido char *s; 17416740Sguido{ 17426740Sguido char *p; 17436740Sguido 17446740Sguido p = malloc((unsigned) strlen(s) + 1); 17456740Sguido if (p == NULL) 17466740Sguido fatal("Ran out of memory."); 17476740Sguido (void) strcpy(p, s); 17486740Sguido return (p); 17496740Sguido} 17506740Sguido#endif 1751