This is ftpd.c in view mode; [Download] [Up]
/* Copyright (c) 1985, 1988, 1990 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. 3. All advertising * materials mentioning features or use of this software must display the * following acknowledgement: This product includes software developed by the * University of California, Berkeley and its contributors. 4. Neither the * name of the University nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)$Id: ftpd.c,v 1.29 1997/03/03 09:39:42 sob Exp sob $ based on ftpd.c 5.40 (Berkeley) 7/2/91"; #endif /* not lint */ #define SPT_NONE 0 /* don't use it at all */ #define SPT_REUSEARGV 1 /* cover argv with title information */ #define SPT_BUILTIN 2 /* use libc builtin */ #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */ #define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */ #define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */ #define SPT_SCO 6 /* write kernel u. area */ /* FTP server. */ #include "config.h" #include <sys/types.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/file.h> #include <sys/wait.h> #ifdef AIX #include <sys/id.h> #include <sys/priv.h> #endif #ifdef AUX #include <compat.h> #endif #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #define FTP_NAMES #include <arpa/ftp.h> #include <arpa/inet.h> #include <arpa/telnet.h> #include <ctype.h> #include <stdio.h> #include <signal.h> #include <pwd.h> #include <setjmp.h> #include <netdb.h> #include <errno.h> #include <string.h> /* * Arrange to use either varargs or stdargs * */ #ifdef __STDC__ #include <stdarg.h> #define VA_LOCAL_DECL va_list ap; #define VA_START(f) va_start(ap, f) #define VA_END va_end(ap) #else #include <varargs.h> #define VA_LOCAL_DECL va_list ap; #define VA_START(f) va_start(ap) #define VA_END va_end(ap) #endif #ifdef SYSSYSLOG #include <sys/syslog.h> #else #include <syslog.h> #endif #include <time.h> #include "conversions.h" #include "extensions.h" #include "pathnames.h" #ifdef M_UNIX #include <arpa/nameser.h> #include <resolv.h> #endif #if defined(SVR4) || defined(ISC) #include <fcntl.h> #endif #ifdef HAVE_SYSINFO #include <sys/systeminfo.h> #endif #ifdef SHADOW_PASSWORD #include <shadow.h> #endif #ifdef KERBEROS #include <sys/types.h> #include <auth.h> #include <krb.h> #endif #ifdef ULTRIX_AUTH #include <auth.h> #include <sys/svcinfo.h> #endif #ifndef HAVE_SYMLINK #define lstat stat #endif #ifdef HAVE_DIRENT #include <dirent.h> #else #include <sys/dir.h> #endif #if (defined(_BSDI_VERSION) && (_BSDI_VERSION < 199501)) /* before version 2 */ #define LONGOFF_T /* sizeof(off_t) == sizeof(long) */ #endif #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 /* may be too big */ #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE !TRUE #endif #if defined(_SCO_DS) && !defined(SIGURG) #define SIGURG SIGUSR1 #endif /* File containing login names NOT to be used on this machine. Commonly used * to disallow uucp. */ extern int errno; extern int pidfd; #ifdef __STDC__ extern char *ctime(const time_t *); #ifndef NO_CRYPT_PROTO extern char *crypt(const char *, const char *); #endif extern FILE *ftpd_popen(char *program, char *type, int closestderr), *fopen(const char *, const char *), *freopen(const char *, const char *, FILE *); extern int ftpd_pclose(FILE *iop), fclose(FILE *); extern char *getline(), *realpath(const char *pathname, char *result); #else extern char *ctime(); #ifndef NO_CRYPT_PROTO #ifdef _M_UNIX extern char *crypt(const char *, const char *); #else extern char *crypt(); #endif #endif extern FILE *ftpd_popen(), *fopen(),*freopen(); extern int ftpd_pclose(), fclose(); extern char *getline(), *realpath(); #endif extern char version[]; extern char *home; /* pointer to home directory for glob */ extern char cbuf[]; extern off_t restart_point; extern yyerrorcalled; struct sockaddr_in ctrl_addr; struct sockaddr_in data_source; struct sockaddr_in data_dest; struct sockaddr_in his_addr; struct sockaddr_in pasv_addr; #ifdef VIRTUAL int virtual_mode=0; char virtual_root[MAXPATHLEN]; char virtual_banner[MAXPATHLEN]; #endif int data; jmp_buf errcatch, urgcatch; int logged_in = 0; struct passwd *pw; int debug; int timeout = 900; /* timeout after 15 minutes of inactivity */ int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 * hours */ /* previously defaulted to 1, and -l or -L set them to 1, so that there was no way to turn them *off*! Changed so that the manpage reflects common sense. -L is way noisy; -l we'll change to be "just right". _H*/ int logging = 0; int log_commands = 0; #ifdef SECUREOSF #define SecureWare #include <prot.h> #endif int anonymous = 1; int guest; int type; int form; int stru; /* avoid C keyword */ int mode; int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ int transflag; off_t file_size; off_t byte_count; #if !defined(CMASK) || CMASK == 0 #undef CMASK #define CMASK 002 #endif mode_t defumask = CMASK; /* default umask value */ char tmpline[7]; char hostname[MAXHOSTNAMELEN]; char remotehost[MAXHOSTNAMELEN]; char remoteaddr[MAXHOSTNAMELEN]; /* log failures 27-apr-93 ehk/bm */ #ifdef LOG_FAILED #define MAXUSERNAMELEN 32 char the_user[MAXUSERNAMELEN]; #endif /* Access control and logging passwords */ /* OFF by default. _H*/ int use_accessfile = 0; char guestpw[MAXHOSTNAMELEN]; char privatepw[MAXHOSTNAMELEN]; int nameserved = 0; extern char authuser[]; extern int authenticated; /* File transfer logging */ int xferlog = 0; int log_outbound_xfers = 0; int log_incoming_xfers = 0; char logfile[MAXPATHLEN]; /* Allow use of lreply(); this is here since some older FTP clients don't * support continuation messages. In violation of the RFCs... */ int dolreplies = 1; /* Spontaneous reply text. To be sent along with next reply to user */ char *autospout = NULL; int autospout_free = 0; /* allowed on-the-fly file manipulations (compress, tar) */ int mangleopts = 0; /* number of login failures before attempts are logged and FTP *EXITS* */ int lgi_failure_threshold = 5; /* Timeout intervals for retrying connections to hosts that don't accept PORT * cmds. This is a kludge, but given the problems with TCP... */ #define SWAITMAX 90 /* wait at most 90 seconds */ #define SWAITINT 5 /* interval between retries */ int swaitmax = SWAITMAX; int swaitint = SWAITINT; #ifdef __STDC__ SIGNAL_TYPE lostconn(int sig); SIGNAL_TYPE randomsig(int sig); SIGNAL_TYPE myoob(int sig); FILE *getdatasock(char *mode), *dataconn(char *name, off_t size, char *mode); void setproctitle(const char *fmt, ...); void reply(int, char *fmt, ...); void lreply(int, char *fmt, ...); #else SIGNAL_TYPE lostconn(); SIGNAL_TYPE randomsig(); SIGNAL_TYPE myoob(); FILE *getdatasock(), *dataconn(); void setproctitle(); void reply(); void lreply(); #endif #ifdef NEED_SIGFIX extern sigset_t block_sigmask; /* defined in sigfix.c */ #endif char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ char proctitle[BUFSIZ]; /* initial part of title */ #ifdef SKEY #include <skey.h> int pwok = 0; #endif #ifdef KERBEROS void init_krb(); void end_krb(); char krb_ticket_name[100]; #endif /* KERBEROS */ #ifdef ULTRIX_AUTH int ultrix_check_pass(char *passwd, char *xpasswd); #endif /* ls program commands and options for lreplies on and off */ char ls_long[50]; char ls_short[50]; struct aclmember *entry = NULL; #ifdef __STDC__ void end_login(void); void send_data(FILE *, FILE *, off_t); void dolog(struct sockaddr_in *); void dologout(int); void perror_reply(int, char *); #else void end_login(); void send_data(); void dolog(); void dologout(); void perror_reply(); #endif void #ifdef __STDC__ main(int argc, char **argv, char **envp) #else main(argc,argv,envp) int argc; char **argv; char **envp; #endif { int addrlen, on = 1; #ifdef IPTOS_LOWDELAY int tos; #endif int c; extern int optopt; extern char *optarg; struct hostent *shp; #ifdef VIRTUAL int virtual_len; struct sockaddr_in virtual_addr; struct sockaddr_in *virtual_ptr; #endif #ifdef AUX setcompat(COMPAT_POSIX | COMPAT_BSDSETUGID); #endif #ifdef FACILITY openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY); #else openlog("ftpd", LOG_PID); #endif #ifdef SecureWare setluid(1); /* make sure there is a valid luid */ set_auth_parameters(argc,argv); setreuid(0, 0); #endif #if defined(M_UNIX) && !defined(_M_UNIX) res_init(); /* bug in old (1.1.1) resolver */ _res.retrans = 20; /* because of fake syslog in 3.2.2 */ setlogmask(LOG_UPTO(LOG_INFO)); #endif addrlen = sizeof(his_addr); if (getpeername(0, (struct sockaddr *) &his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); #ifndef DEBUG exit(1); #endif } addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *) &ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m", argv[0]); #ifndef DEBUG exit(1); #endif } #ifdef IPTOS_LOWDELAY tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); #endif data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); debug = 0; /* Save start and extent of argv for setproctitle. */ Argv = argv; while (*envp) envp++; LastArgv = envp[-1] + strlen(envp[-1]); while ((c = getopt(argc, argv, ":aAvdlLiot:T:u:")) != -1) { switch (c) { case 'a': use_accessfile = 1; break; case 'A': use_accessfile = 0; break; case 'v': debug = 1; break; case 'd': debug = 1; break; case 'l': logging = 1; break; case 'L': log_commands = 1; break; case 'i': log_incoming_xfers = 1; break; case 'o': log_outbound_xfers = 1; break; case 't': timeout = atoi(optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'T': maxtimeout = atoi(optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 'u': { unsigned int val = 0; while (*optarg && *optarg >= '0' && *optarg <= '9') val = val * 8 + *optarg++ - '0'; if (*optarg || val > 0777) syslog(LOG_ERR, "bad value for -u"); else defumask = val; break; } case ':': syslog(LOG_ERR, "option -%c requires an argument", optopt); break; default: syslog(LOG_ERR, "unknown option -%c ignored", optopt); break; } } (void) freopen(_PATH_DEVNULL, "w", stderr); /* Checking for random signals ... */ #ifdef NEED_SIGFIX sigemptyset(&block_sigmask); #endif #ifndef SIG_DEBUG #ifdef SIGHUP (void) signal(SIGHUP, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGHUP); #endif #endif #ifdef SIGINT (void) signal(SIGINT, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGINT); #endif #endif #ifdef SIGQUIT (void) signal(SIGQUIT, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGQUIT); #endif #endif #ifdef SIGILL (void) signal(SIGILL, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGILL); #endif #endif #ifdef SIGTRAP (void) signal(SIGTRAP, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGTRAP); #endif #endif #ifdef SIGIOT (void) signal(SIGIOT, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGIOT); #endif #endif #ifdef SIGEMT (void) signal(SIGEMT, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGEMT); #endif #endif #ifdef SIGFPE (void) signal(SIGFPE, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGFPE); #endif #endif #ifdef SIGKILL (void) signal(SIGKILL, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGKILL); #endif #endif #ifdef SIGBUS (void) signal(SIGBUS, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGBUS); #endif #endif #ifdef SIGSEGV (void) signal(SIGSEGV, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGSEGV); #endif #endif #ifdef SIGSYS (void) signal(SIGSYS, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGSYS); #endif #endif #ifdef SIGALRM (void) signal(SIGALRM, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGALRM); #endif #endif #ifdef SIGSTOP (void) signal(SIGSTOP, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGSTOP); #endif #endif #ifdef SIGTSTP (void) signal(SIGTSTP, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGTSTP); #endif #endif #ifdef SIGTTIN (void) signal(SIGTTIN, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGTTIN); #endif #endif #ifdef SIGTTOU (void) signal(SIGTTOU, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGTTOU); #endif #endif #ifdef SIGIO (void) signal(SIGIO, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGIO); #endif #endif #ifdef SIGXCPU (void) signal(SIGXCPU, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGXCPU); #endif #endif #ifdef SIGXFSZ (void) signal(SIGXFSZ, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGXFSZ); #endif #endif #ifdef SIGWINCH (void) signal(SIGWINCH, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGWINCH); #endif #endif #ifdef SIGVTALRM (void) signal(SIGVTALRM, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGVTALRM); #endif #endif #ifdef SIGPROF (void) signal(SIGPROF, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGPROF); #endif #endif #ifdef SIGUSR1 (void) signal(SIGUSR1, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGUSR1); #endif #endif #ifdef SIGUSR2 (void) signal(SIGUSR2, randomsig); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGUSR2); #endif #endif #ifdef SIGPIPE (void) signal(SIGPIPE, lostconn); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGPIPE); #endif #endif #ifdef SIGCHLD (void) signal(SIGCHLD, SIG_IGN); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGCHLD); #endif #endif #ifdef SIGURG if ((int) signal(SIGURG, myoob) < 0) syslog(LOG_ERR, "signal: %m"); #ifdef NEED_SIGFIX sigaddset(&block_sigmask, SIGURG); #endif #endif #endif /* SIG_DEBUG */ /* Try to handle urgent data inline */ #ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(int)) < 0) syslog(LOG_ERR, "setsockopt (SO_OOBINLINE): %m"); #endif #ifdef F_SETOWN if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #elif defined(SIOCSPGRP) { int pid; pid = getpid(); if (ioctl(fileno(stdin), SIOCSPGRP, &pid) == -1) syslog(LOG_ERR, "ioctl SIOCSPGRP: %m"); } #endif dolog(&his_addr); /* Set up default state */ data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; yyerrorcalled = 0; #ifdef HAVE_SYSINFO sysinfo(SI_HOSTNAME, hostname, sizeof (hostname)); #else (void) gethostname(hostname, sizeof (hostname)); #endif /* set the FQDN here */ shp = gethostbyname(hostname); if (shp != NULL) (void) strncpy(hostname, shp->h_name, sizeof(hostname)); access_init(); authenticate(); conv_init(); #ifdef VIRTUAL virtual_len = sizeof(virtual_addr); if (getsockname(0, (struct sockaddr *) &virtual_addr, &virtual_len) == 0) { virtual_ptr = (struct sockaddr_in *) &virtual_addr; entry = (struct aclmember *) NULL; while (getaclentry("virtual", &entry)) { if (!ARG0 || !ARG1 || !ARG2) continue; if (!strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr))) { if(!strcmp(ARG1, "root")) { syslog(LOG_NOTICE, "VirtualFTP Connect to: %s", inet_ntoa(virtual_ptr->sin_addr)); virtual_mode = 1; strncpy(virtual_root, ARG2, MAXPATHLEN); /* reset hostname to this virtual name */ shp = gethostbyaddr((char *) &virtual_ptr->sin_addr, sizeof (struct in_addr), AF_INET); if (shp != NULL) (void) strncpy(hostname, shp->h_name, sizeof(hostname)); } if(!strcmp(ARG1, "banner")) strncpy(virtual_banner, ARG2, MAXPATHLEN); if(!strcmp(ARG1, "logfile")) strncpy(logfile, ARG2, MAXPATHLEN); } } } if (!virtual_mode || logfile[0] == '\0') #endif strcpy(logfile, _PATH_XFERLOG); if (is_shutdown(1, 1) != 0) { syslog(LOG_INFO, "connection refused (server shut down) from %s [%s]", remotehost, remoteaddr); reply(500, "%s FTP server shut down -- please try again later.", hostname); exit(0); } show_banner(220); entry = (struct aclmember *) NULL; if (getaclentry("lslong", &entry) && ARG0 && (int)strlen(ARG0) > 0) { strcpy(ls_long,ARG0); if (ARG1 && strlen(ARG1)) { strcat(ls_long," "); strcat(ls_long,ARG1); } } else { #if defined(SVR4) || defined(ISC) #ifndef AIX strcpy(ls_long,"/bin/ls -la"); #else strcpy(ls_long,"/bin/ls -lA"); #endif #else strcpy(ls_long,"/bin/ls -lgA"); #endif } strcat(ls_long," %s"); entry = (struct aclmember *) NULL; if (getaclentry("lsshort", &entry) && ARG0 && (int)strlen(ARG0) > 0) { strcpy(ls_short,ARG0); if (ARG1 && strlen(ARG1)) { strcat(ls_short," "); strcat(ls_short,ARG1); } } else { #if defined(SVR4) || defined(ISC) #ifndef AIX strcpy(ls_short,"/bin/ls -la"); #else strcpy(ls_short,"/bin/ls -lA"); #endif #else strcpy(ls_short,"/bin/ls -lgA"); #endif } strcat(ls_short," %s"); reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); for (;;) (void) yyparse(); /* NOTREACHED */ } SIGNAL_TYPE #ifdef __STDC__ randomsig(int sig) #else randomsig(sig) int sig; #endif { #ifdef HAVE_SIGLIST syslog(LOG_ERR, "exiting on signal %d: %s", sig, sys_siglist[sig] ); #else syslog(LOG_ERR, "exiting on signal %d", sig); #endif chdir("/"); signal(SIGIOT, SIG_DFL); signal(SIGILL, SIG_DFL); exit (1); /* dologout(-1); *//* NOTREACHED */ } SIGNAL_TYPE #ifdef __STDC__ lostconn(int sig) #else lostconn(sig) int sig; #endif { if (debug) syslog(LOG_DEBUG, "lost connection to %s [%s]", remotehost, remoteaddr); dologout(-1); } static char ttyline[20]; /* Helper function for sgetpwnam(). */ char * #ifdef __STDC__ sgetsave(char *s) #else sgetsave(s) char *s; #endif { char *new; new = (char *) malloc(strlen(s) + 1); if (new == NULL) { perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s); return (new); } /* Save the result of a getpwnam. Used for USER command, since the data * returned must not be clobbered by any other command (e.g., globbing). */ struct passwd * #ifdef __STDC__ sgetpwnam(char *name) #else sgetpwnam(name) char *name; #endif { static struct passwd save; register struct passwd *p; #ifdef M_UNIX struct passwd *ret = (struct passwd *) NULL; #endif #ifdef __STDC__ char *sgetsave(char *s); #else char *sgetsave(); #endif #ifdef KERBEROS register struct authorization *q; #endif /* KERBEROS */ #ifdef SecureWare struct pr_passwd *pr; #endif #ifdef KERBEROS init_krb(); q = getauthuid(p->pw_uid); end_krb(); #endif /* KERBEROS */ #ifdef M_UNIX # ifdef SecureWare if ((pr = getprpwnam(name)) == NULL) goto DONE; # endif /* SecureWare */ if ((p = getpwnam(name)) == NULL) goto DONE; #else /* M_UNIX */ # ifdef SecureWare if ((pr = getprpwnam(name)) == NULL) return((struct passwd *) pr); # endif /* SecureWare */ if ((p = getpwnam(name)) == NULL) return (p); #endif /* M_UNIX */ if (save.pw_name) free(save.pw_name); if (save.pw_gecos) free(save.pw_gecos); if (save.pw_dir) free(save.pw_dir); if (save.pw_shell) free(save.pw_shell); save = *p; save.pw_name = sgetsave(p->pw_name); #ifdef KERBEROS save.pw_passwd = sgetsave(q->a_password); #elif defined(SecureWare) if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt) save.pw_passwd = sgetsave(pr->ufld.fd_encrypt); else save.pw_passwd = sgetsave(""); #else save.pw_passwd = sgetsave(p->pw_passwd); #endif #ifdef SHADOW_PASSWORD if (p) { struct spwd *spw; setspent(); if ((spw = getspnam(p->pw_name)) != NULL) { /* TODO: check if password has expired etc. */ free(save.pw_passwd); save.pw_passwd = sgetsave(spw->sp_pwdp); } /* Shadow passwords are optional on Linux. --marekm */ #ifndef LINUX else{ free(save.pw_passwd); save.pw_passwd = sgetsave(""); } #endif /* marekm's fix for linux proc file system shadow passwd exposure problem */ endspent(); } #endif save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); #ifdef M_UNIX ret = &save; DONE: endpwent(); #endif #ifdef SecureWare endprpwent(); #endif #ifdef M_UNIX return(ret); #else return(&save); #endif } #ifdef SKEY /* * From Wietse Venema, Eindhoven University of Technology. */ /* skey_challenge - additional password prompt stuff */ #ifdef __STDC__ char *skey_challenge(char *name, struct passwd *pwd, int pwok) #else char *skey_challenge(name, pwd, pwok) char *name; struct passwd *pwd; int pwok; #endif { static char buf[128]; char sbuf[40]; struct skey skey; /* Display s/key challenge where appropriate. */ if (pwd == NULL || skeychallenge(&skey, pwd->pw_name, sbuf)) sprintf(buf, "Password required for %s.", name); else sprintf(buf, "[%s] %s for %s.", sbuf, pwok ? "allowed" : "required", name); return (buf); } #endif int login_attempts; /* number of failed login attempts */ int askpasswd; /* had user command, ask for passwd */ /* USER command. Sets global passwd pointer pw if named account exists and is * acceptable; sets askpasswd if a PASS command is expected. If logged in * previously, need to reset state. If name is "ftp" or "anonymous", the * name is not in _PATH_FTPUSERS, and ftp account exists, set anonymous and * pw, then just return. If account doesn't exist, ask for passwd anyway. * Otherwise, check user requesting login privileges. Disallow anyone who * does not have a standard shell as returned by getusershell(). Disallow * anyone mentioned in the file _PATH_FTPUSERS to allow people such as root * and uucp to be avoided. */ void #ifdef __STDC__ user(char *name) #else user(name) char *name; #endif { register char *cp; char *shell; char *getusershell(); int why = 0; /* H* fix: if we're logged in at all, we can't log in again. */ if (logged_in) { reply(530, "Already logged in."); return; } #ifdef HOST_ACCESS /* 19-Mar-93 BM */ if (!rhost_ok(name, remotehost, remoteaddr)) { reply(530, "User %s access denied.", name); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (name in %s) FROM %s [%s], %s", _PATH_FTPHOSTS, remotehost, remoteaddr, name); return; } #endif #ifdef LOG_FAILED /* 06-Nov-92 EHK */ strncpy(the_user, name, MAXUSERNAMELEN - 1); #endif if (logged_in) { /* Now a no-op. _H*/ if (anonymous || guest) { reply(530, "Can't change user from guest login."); return; } end_login(); } anonymous = 0; acl_remove(); if (!strcasecmp(name, "ftp") || !strcasecmp(name, "anonymous")) { struct aclmember *entry = NULL; int machineok=1; char guestservername[MAXHOSTNAMELEN]; guestservername[0]='\0'; if (checkuser("ftp") || checkuser("anonymous")) { reply(530, "User %s access denied.", name); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp in /etc/ftpusers) FROM %s [%s], %s", remotehost, remoteaddr, name); return; /* ** Algorithm used: ** - if no "guestserver" directive is present, ** anonymous access is allowed, for backward compatibility. ** - if a "guestserver" directive is present, ** anonymous access is restricted to the machines listed, ** usually the machine whose CNAME on the current domain ** is "ftp"... ** ** the format of the "guestserver" line is ** guestserver [<machine1> [<machineN>]] ** that is, "guestserver" will forbid anonymous access on all machines ** while "guestserver ftp inf" will allow anonymous access on ** the two machines whose CNAMES are "ftp.enst.fr" and "inf.enst.fr". ** ** if anonymous access is denied on the current machine, ** the user will be asked to use the first machine listed (if any) ** on the "guestserver" line instead: ** 530- Guest login not allowed on this machine, ** connect to ftp.enst.fr instead. ** ** -- <Nicolas.Pioch@enst.fr> */ } else if (getaclentry("guestserver", &entry) && ARG0 && (int)strlen(ARG0) > 0) { struct hostent *tmphostent; /* ** if a "guestserver" line is present, ** default is not to allow guest logins */ machineok=0; if (hostname[0] && ((tmphostent=gethostbyname(hostname)))) { /* ** hostname is the only first part of the FQDN ** this may or may not correspond to the h_name value ** (machines with more than one IP#, CNAMEs...) ** -> need to fix that, calling gethostbyname on hostname ** ** WARNING! ** for SunOS 4.x, you need to have a working resolver in the libc ** for CNAMES to work properly. ** If you don't, add "-lresolv" to the libraries before compiling! */ char dns_localhost[MAXHOSTNAMELEN]; int machinecount; strncpy(dns_localhost, tmphostent->h_name, sizeof(dns_localhost)); dns_localhost[sizeof(dns_localhost)-1]='\0'; for (machinecount=0; entry->arg[machinecount] && (entry->arg[machinecount])[0]; machinecount++) { if ((tmphostent=gethostbyname(entry->arg[machinecount]))) { /* ** remember the name of the first machine for redirection */ if ((!machinecount) && tmphostent->h_name) { strncpy(guestservername, entry->arg[machinecount], sizeof(guestservername)); guestservername[sizeof(guestservername)-1]='\0'; } if (!strcasecmp(tmphostent->h_name, dns_localhost)) { machineok++; break; } } } } } if (!machineok) { if (guestservername[0]) reply(530, "Guest login not allowed on this machine, connect to %s instead.", guestservername); else reply(530, "Guest login not allowed on this machine."); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (localhost not in guestservers) FROM %s [%s], %s", remotehost, remoteaddr, name); /* End of the big patch -- Nap */ } else if ((pw = sgetpwnam("ftp")) != NULL) { anonymous = 1; /* for the access_ok call */ if ((why = access_ok(530)) == 1) { askpasswd = 1; /* H* fix: obey use_accessfile a little better. This way, things set on the command line [like xferlog stuff] don't get stupidly overridden. XXX: all these checks maybe should be in acl.c and access.c */ if (use_accessfile) acl_setfunctions(); reply(331, "Guest login ok, send your complete e-mail address as password."); } else if (why == 0) { reply(530, "User %s access denied..", name); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s [%s], %s", remotehost, remoteaddr, name); anonymous = 0; } else { reply(530, "User %s access denied.", name); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s [%s], %s", remotehost, remoteaddr, name); dologout(0); } } else { reply(530, "User %s unknown.", name); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp not in /etc/passwd) FROM %s [%s], %s", remotehost, remoteaddr, name); } return; } #ifdef ANON_ONLY /* H* fix: define the above to completely DISABLE logins by real users, despite ftpusers, shells, or any of that rot. You can always hang your "real" server off some other port, and access-control it. */ else { /* "ftp" or "anon" -- MARK your conditionals, okay?! */ reply(530, "User %s unknown.", name); syslog (LOG_NOTICE, "FTP LOGIN REFUSED (not anonymous) FROM %s [%s], %s", remotehost, remoteaddr, name); return; } /* fall here if username okay in any case */ #endif /* ANON_ONLY */ if ((pw = sgetpwnam(name)) != NULL) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = _PATH_BSHELL; while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL || checkuser(name)) { reply(530, "User %s access denied...(bad shell)", name); /* if (logging) -- inconsistent, removed. _H*/ syslog(LOG_NOTICE, "FTP LOGIN REFUSED (bad shell) FROM %s [%s], %s", remotehost, remoteaddr, name); pw = (struct passwd *) NULL; return; } /* if user is a member of any of the guestgroups, cause a chroot() */ /* after they log in successfully */ if (use_accessfile) /* see above. _H*/ guest = acl_guestgroup(pw); } if (access_ok(530) < 1) { reply(530, "User %s access denied....", name); syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s [%s], %s", remotehost, remoteaddr, name); return; } else if (use_accessfile) /* see above. _H*/ acl_setfunctions(); #ifdef SKEY pwok = skeyaccess(name, NULL, remotehost, remoteaddr); reply(331, "%s", skey_challenge(name, pw, pwok)); #else reply(331, "Password required for %s.", name); #endif askpasswd = 1; /* Delay before reading passwd after first failed attempt to slow down * passwd-guessing programs. */ if (login_attempts) sleep((unsigned) login_attempts); return; } /* Check if a user is in the file _PATH_FTPUSERS */ int #ifdef __STDC__ checkuser(char *name) #else checkuser(name) char *name; #endif { register FILE *fd; register char *p; char line[BUFSIZ]; if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) if ((p = strchr(line, '\n')) != NULL) { *p = '\0'; if (line[0] == '#') continue; if (strcmp(line, name) == 0) { (void) fclose(fd); return (1); } } (void) fclose(fd); } return (0); } /* Terminate login as previous user, if any, resetting state; used when USER * command is given or login fails. */ void #ifdef __STDC__ end_login(void) #else end_login() #endif { delay_signaling(); /* we can't allow any signals while euid==0: kinch */ (void) seteuid((uid_t) 0); if (logged_in) logwtmp(ttyline, "", ""); pw = NULL; logged_in = 0; anonymous = 0; guest = 0; } int #ifdef __STDC__ validate_eaddr(char *eaddr) #else validate_eaddr(eaddr) char *eaddr; #endif { int i, host, state; for (i = host = state = 0; eaddr[i] != '\0'; i++) { switch (eaddr[i]) { case '.': if (!host) return 0; if (state == 2) state = 3; host = 0; break; case '@': if (!host || state > 1 || !strncasecmp("ftp", eaddr + i - host, host)) return 0; state = 2; host = 0; break; case '!': case '%': if (!host || state > 1) return 0; state = 1; host = 0; break; case '-': break; default: host++; } } if (((state == 3) && host > 1) || ((state == 2) && !host) || ((state == 1) && host > 1)) return 1; else return 0; } void #ifdef __STDC__ pass(char *passwd) #else pass(passwd) char *passwd; #endif { char *xpasswd, *salt; #ifdef ULTRIX_AUTH int numfails; #endif /* ULTRIX_AUTH */ if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; /* Disable lreply() if the first character of the password is '-' since * some hosts don't understand continuation messages and hang... */ if (*passwd == '-') dolreplies = 0; else dolreplies = 1; /* ******** REGULAR/GUEST USER PASSWORD PROCESSING ********** */ if (!anonymous) { /* "ftp" is only account allowed no password */ if (*passwd == '-') passwd++; *guestpw = '\0'; if (pw == NULL) salt = "xx"; /* XXX */ else salt = pw->pw_passwd; #ifdef SECUREOSF xpasswd = bigcrypt(passwd, salt); #else #ifdef KERBEROS xpasswd = crypt16(passwd, salt); #else #ifdef SKEY xpasswd = skey_crypt(passwd, salt, pw, pwok); pwok = 0; #else xpasswd = crypt(passwd, salt); #endif #endif #endif #ifdef ULTRIX_AUTH if ((numfails = ultrix_check_pass(passwd, xpasswd)) < 0) { #else /* The strcmp does not catch null passwords! */ if (pw == NULL || *pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { #endif reply(530, "Login incorrect."); #ifdef LOG_FAILED /* 27-Apr-93 EHK/BM */ /* H* add-on: yell about attempts to use the trojan. This may alarm you if you're "stringsing" the binary and you see "NULL" pop out in just about the same place as it would have in 2.2c! */ if (! strcmp (passwd, "NULL")) syslog(LOG_NOTICE, "REFUSED \"NULL\" from %s [%s], %s", remotehost, remoteaddr, the_user); else syslog(LOG_INFO, "failed login from %s [%s], %s", remotehost, remoteaddr, the_user); #endif acl_remove(); pw = NULL; if (++login_attempts >= lgi_failure_threshold) { syslog(LOG_NOTICE, "repeated login failures from %s [%s]", remotehost, remoteaddr); exit(0); } return; } /* ANONYMOUS USER PROCESSING STARTS HERE */ } else { char *pwin, *pwout = guestpw; struct aclmember *entry = NULL; int valid; if (getaclentry("passwd-check", &entry) && ARG0 && strcasecmp(ARG0, "none")) { if (!strcasecmp(ARG0, "rfc822")) valid = validate_eaddr(passwd); else if (!strcasecmp(ARG0, "trivial")) valid = (strchr(passwd, '@') == NULL) ? 0 : 1; else valid = 1; if (!valid && ARG1 && !strcasecmp(ARG1, "enforce")) { lreply(530, "The response '%s' is not valid", passwd); lreply(530, "Please use your e-mail address as your password"); lreply(530, " for example: %s@%s or %s@", authenticated ? authuser : "joe", remotehost, authenticated ? authuser : "joe"); lreply(530, "[%s will be added if password ends with @]", remotehost); reply(530, "Login incorrect."); acl_remove(); if (++login_attempts >= lgi_failure_threshold) { syslog(LOG_NOTICE, "repeated login failures from %s [%s]", remotehost, remoteaddr); exit(0); } return; } else if (!valid) { lreply(230, "The response '%s' is not valid", passwd); lreply(230, "Next time please use your e-mail address as your password"); lreply(230, " for example: %s@%s", authenticated ? authuser : "joe", remotehost); } } if (!*passwd) { strcpy(guestpw, "[none_given]"); } else { int cnt = sizeof(guestpw) - 2; for (pwin = passwd; *pwin && cnt--; pwin++) if (!isgraph(*pwin)) *pwout++ = '_'; else *pwout++ = *pwin; } } /* if logging is enabled, open logfile before chroot or set group ID */ if (log_outbound_xfers || log_incoming_xfers) { xferlog = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0660); if (xferlog < 0) { syslog(LOG_ERR, "cannot open logfile %s: %s", logfile, strerror(errno)); xferlog = 0; } } #ifdef DEBUG /* I had a lot of trouble getting xferlog working, because of two factors: acl_setfunctions making stupid assumptions, and sprintf LOSING. _H*/ /* * Actually, sprintf was not losing, but the rules changed... next release * this will be fixed the correct way, but right now, it works well enough * -- sob */ syslog (LOG_INFO, "-i %d,-o %d,xferlog %s: %d", log_incoming_xfers, log_outbound_xfers, logfile, xferlog); #endif enable_signaling(); /* we can allow signals once again: kinch */ /* if autogroup command applies to user's class change pw->pw_gid */ if (anonymous && use_accessfile) /* see above. _H*/ (void) acl_autogroup(pw); /* END AUTHENTICATION */ login_attempts = 0; /* this time successful */ /* SET GROUP ID STARTS HERE */ #ifndef AIX (void) setegid((gid_t) pw->pw_gid); #else (void) setgid((gid_t)pw->pw_gid); #endif (void) initgroups(pw->pw_name, pw->pw_gid); #ifdef DEBUG syslog (LOG_DEBUG, "initgroups has been called"); #endif /* WTMP PROCESSING STARTS HERE */ /* open wtmp before chroot */ #if (defined(BSD) && (BSD >= 199103)) (void) sprintf(ttyline, "ftp%ld", getpid()); #else (void) sprintf(ttyline, "ftpd%d", getpid()); #endif #ifdef DEBUG syslog (LOG_DEBUG, "about to call wtmp"); #endif logwtmp(ttyline, pw->pw_name, remotehost); logged_in = 1; expand_id(); if (anonymous || guest) { /* We MUST do a chdir() after the chroot. Otherwise the old current * directory will be accessible as "." outside the new root! */ #ifdef VIRTUAL if (virtual_mode && !guest) { if (pw->pw_dir) free(pw->pw_dir); pw->pw_dir = sgetsave(virtual_root); } #endif if (anonymous) { if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { reply(550, "Can't set guest privileges."); goto bad; } } else if (guest) { char *sp; /* determine root and home directory */ if ((sp = strstr(pw->pw_dir, "/./")) == NULL) { if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { reply(550, "Can't set guest privileges."); goto bad; } } else { *sp++ = '\0'; if (chroot(pw->pw_dir) < 0 || chdir(++sp) < 0) { reply(550, "Can't set guest privileges."); goto bad; } } } } #ifdef AIX { /* AIX 3 lossage. Don't ask. It's undocumented. */ priv_t priv; priv.pv_priv[0] = 0; priv.pv_priv[1] = 0; /* setgroups(NULL, NULL);*/ if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH, &priv, sizeof(priv_t)) < 0 || setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 || seteuid((uid_t)pw->pw_uid) < 0) { reply(550, "Can't set uid (AIX3)."); goto bad; } } #ifdef UID_DEBUG lreply(230, "ruid=%d, euid=%d, suid=%d, luid=%d", getuidx(ID_REAL), getuidx(ID_EFFECTIVE), getuidx(ID_SAVED), getuidx(ID_LOGIN)); lreply(230, "rgid=%d, egid=%d, sgid=%d, lgid=%d", getgidx(ID_REAL), getgidx(ID_EFFECTIVE), getgidx(ID_SAVED), getgidx(ID_LOGIN)); #endif #else #ifdef HAVE_SETREUID if (setreuid(-1, (uid_t) pw->pw_uid) < 0) { #else if (seteuid((uid_t) pw->pw_uid) < 0) { #endif reply(550, "Can't set uid."); goto bad; } #endif if (!anonymous && !guest) { if (chdir(pw->pw_dir) < 0) { if (chdir("/") < 0) { reply(530, "User %s: can't change directory to %s.", pw->pw_name, pw->pw_dir); goto bad; } else lreply(230, "No directory! Logging in with home=/"); } } /* following two lines were inside the next scope... */ show_message(230, LOG_IN); show_readme(230, LOG_IN); #ifdef ULTRIX_AUTH if (!anonymous && numfails > 0) { lreply(230, "There have been %d unsuccessful login attempts on your account", numfails); } #endif /* ULTRIX_AUTH */ if (anonymous) { (void) is_shutdown(0, 0); /* display any shutdown messages now */ reply(230, "Guest login ok, access restrictions apply."); sprintf(proctitle, "%s: anonymous/%.*s", remotehost, (int) (sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/")), passwd); setproctitle("%s", proctitle); if (logging) syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s [%s], %s", remotehost, remoteaddr, passwd); } else { reply(230, "User %s logged in.%s", pw->pw_name, guest ? " Access restrictions apply." : ""); sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); setproctitle(proctitle); if (logging) syslog(LOG_INFO, "FTP LOGIN FROM %s [%s], %s", remotehost, remoteaddr, pw->pw_name); /* H* mod: if non-anonymous user, copy it to "authuser" so everyone can see it, since whoever he was @foreign-host is now largely irrelevant. */ strcpy (authuser, pw->pw_name); } /* anonymous */ home = pw->pw_dir; /* home dir for globbing */ (void) umask(defumask); return; bad: /* Forget all about it... */ if (xferlog) close(xferlog); xferlog = 0; end_login(); return; } char * #ifdef __STDC__ opt_string(int options) #else opt_string(options) int options; #endif { static char buf[100]; char *ptr = buf; if ((options & O_COMPRESS) != 0) /* debian fixes: NULL -> 0 */ *ptr++ = 'C'; if ((options & O_TAR) != 0) *ptr++ = 'T'; if ((options & O_UNCOMPRESS) != 0) *ptr++ = 'U'; if (options == 0) *ptr++ = '_'; *ptr++ = '\0'; return (buf); } void #ifdef __STDC__ retrieve(char *cmd, char *name) #else retrieve(cmd,name) char *cmd; char *name; #endif { FILE *fin, *dout; struct stat st, junk; int (*closefunc) () = NULL; int options = 0; time_t start_time = time(NULL); char *logname; char namebuf[MAXPATHLEN]; char fnbuf[MAXPATHLEN]; struct convert *cptr; #ifdef __STDC__ extern int checknoretrieve (char *); #else extern int checknoretrieve (); #endif int stat_ret; if (cmd == NULL && (stat_ret = stat(name, &st)) == 0) /* there isn't a command and the file exists */ if (use_accessfile && checknoretrieve(name)) /* see above. _H*/ return; logname = (char *)NULL; if (cmd == NULL && stat_ret != 0) { /* file does not exist */ char *ptr; cptr = cvtptr; /* get ready to try conversions */ if (cptr == NULL) { reply(550, "%s: No such file OR directory.", name); return; } do { if (!(mangleopts & O_COMPRESS) && (cptr->options & O_COMPRESS)) continue; if (!(mangleopts & O_UNCOMPRESS) && (cptr->options & O_UNCOMPRESS)) continue; if (!(mangleopts & O_TAR) && (cptr->options & O_TAR)) continue; if ( (cptr->stripfix) && (cptr->postfix) ) { int pfxlen = strlen(cptr->postfix); int sfxlen = strlen(cptr->stripfix); int namelen = strlen(name); (void) strcpy(fnbuf, name); if (namelen <= pfxlen) continue; if ((namelen - pfxlen + sfxlen) >= sizeof(fnbuf)) continue; if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix)) continue; *(fnbuf + namelen - pfxlen) = '\0'; (void) strcat(fnbuf, cptr->stripfix); if (stat(fnbuf, &st) != 0) continue; } else if (cptr->postfix) { int pfxlen = strlen(cptr->postfix); int namelen = strlen(name); if (namelen <= pfxlen) continue; (void) strcpy(fnbuf, name); if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix)) continue; *(fnbuf + namelen - pfxlen) = (char) NULL; if (stat(fnbuf, &st) != 0) continue; } else if (cptr->stripfix) { (void) strcpy(fnbuf, name); (void) strcat(fnbuf, cptr->stripfix); if (stat(fnbuf, &st) != 0) continue; } else { reply(550, "%s: No such file OR directory.", name); return; } if (S_ISDIR(st.st_mode)) { if (!(cptr->types & T_DIR)) { reply(550, "Cannot %s directories.", cptr->name); return; } if (cptr->options & O_TAR) { strcpy(namebuf, fnbuf); strcat(namebuf, "/.notar"); if (stat(namebuf, &junk) == 0) { reply(550, "Sorry, you may not TAR that directory."); return; } } } /* XXX: checknoretrieve() test is weak in that if I can't get /etc/passwd but I can tar /etc or /, I still win. Be careful out there... _H* but you could put .notar in / and /etc and stop that ! */ if (use_accessfile && checknoretrieve(fnbuf)) return; if (S_ISREG(st.st_mode) && (cptr->types & T_REG) == 0) { reply(550, "Cannot %s plain files.", cptr->name); return; } if (S_ISREG(st.st_mode) != 0 && S_ISDIR(st.st_mode) != 0) { reply(550, "Cannot %s special files.", cptr->name); return; } if (!(cptr->types & T_ASCII) && deny_badasciixfer(550, "")) return; logname = &fnbuf[0]; options |= cptr->options; strcpy(namebuf, cptr->external_cmd); if ((ptr = strchr(namebuf, ' ')) != NULL) *ptr = '\0'; if (stat(namebuf, &st) != 0) { syslog(LOG_ERR, "external command %s not found", namebuf); reply(550, "Local error: conversion program not found. Cannot %s file.", cptr->name); return; } (void) retrieve(cptr->external_cmd, logname); goto dolog; /* transfer of converted file completed */ } while ( (cptr = cptr->next) != NULL ); } if (cmd == NULL) { /* no command */ fin = fopen(name, "r"), closefunc = fclose; st.st_size = 0; } else { /* run command */ static char line[BUFSIZ]; (void) sprintf(line, cmd, name), name = line; fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose; st.st_size = -1; #ifdef HAVE_ST_BLKSIZE st.st_blksize = BUFSIZ; #endif } if (fin == NULL) { if (errno != 0) perror_reply(550, name); return; } if (cmd == NULL && (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) { reply(550, "%s: not a plain file.", name); goto done; } if (restart_point) { if (type == TYPE_A) { register int i, n, c; n = restart_point; i = 0; while (i++ < n) { if ((c = getc(fin)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; #ifdef HAVE_ST_BLKSIZE send_data(fin, dout, st.st_blksize*2); #else send_data(fin, dout, BUFSIZ); #endif (void) fclose(dout); dolog: if (log_outbound_xfers && xferlog && (cmd == 0)) { char msg[MAXPATHLEN]; char *msg2; /* for stupid_sprintf */ int xfertime = time(NULL) - start_time; time_t curtime = time(NULL); int loop; if (!xfertime) xfertime++; realpath((logname != NULL) ? logname : name, &namebuf[0]); for (loop = 0; namebuf[loop]; loop++) if (isspace(namebuf[loop]) || iscntrl(namebuf[loop])) namebuf[loop] = '_'; #ifdef STUPID_SPRINTF /* Some sprintfs can't deal with a lot of arguments, so we split this */ #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T) sprintf(msg, "%.24s %d %s %qd ", #else sprintf(msg, "%.24s %d %s %d ", #endif ctime(&curtime), xfertime, remotehost, byte_count ); msg2 = msg + strlen(msg); /* sigh */ sprintf(msg2, "%s %c %s %c %c %s ftp %d %s\n", namebuf, (type == TYPE_A) ? 'a' : 'b', opt_string(options), 'o', anonymous ? 'a' : (guest ? 'g' : 'r'), anonymous ? guestpw : pw->pw_name, authenticated, authenticated ? authuser : "*" ); #else #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T) sprintf(msg, "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s\n", #else sprintf(msg, "%.24s %d %s %d %s %c %s %c %c %s ftp %d %s\n", #endif ctime(&curtime), xfertime, remotehost, byte_count, namebuf, (type == TYPE_A) ? 'a' : 'b', opt_string(options), 'o', anonymous ? 'a' : (guest ? 'g' : 'r'), anonymous ? guestpw : pw->pw_name, authenticated, authenticated ? authuser : "*" ); #endif /* stupid_sprintf */ write(xferlog, msg, strlen(msg)); } data = -1; pdata = -1; done: if (closefunc) (*closefunc) (fin); } void #ifdef __STDC__ store(char *name, char *mode, int unique) #else store(name,mode,unique) char *name; char *mode; int unique; #endif { FILE *fout, *din; struct stat st; int (*closefunc) (); #ifdef __STDC__ char *gunique(char *local); #else char *gunique(); #endif time_t start_time = time(NULL); struct aclmember *entry = NULL; int fdout; #ifdef OVERWRITE int overwrite = 1; #endif /* OVERWRITE */ #ifdef UPLOAD int open_flags = (O_RDWR | O_CREAT | ((mode != NULL && *mode == 'a') ? O_APPEND : O_TRUNC)); mode_t oldmask; int f_mode = -1, dir_mode, match_value = -1; uid_t uid; gid_t gid; uid_t oldid; int valid = 0; #endif /* UPLOAD */ if (unique && stat(name, &st) == 0 && (name = gunique(name)) == NULL) return; /* * check the filename, is it legal? */ if ( (fn_check(name)) <= 0 ) return; #ifdef OVERWRITE /* if overwrite permission denied and file exists... then deny the user * permission to write the file. */ while (getaclentry("overwrite", &entry) && ARG0 && ARG1 != NULL) { if (type_match(ARG1)) if (strcmp(ARG0, "yes") != 0) { overwrite = 0; open_flags |= O_EXCL; } } #ifdef PARANOID overwrite = 0; #endif if (!overwrite && !stat(name, &st)) { reply(553, "%s: Permission denied. (Overwrite)", name); return; } #endif /* OVERWRITE */ #ifdef UPLOAD if ( (match_value = upl_check(name, &uid, &gid, &f_mode, &valid)) < 0 ) return; /* do not truncate the file if we are restarting */ if (restart_point) open_flags &= ~O_TRUNC; /* if the user has an explicit new file mode, than open the file using * that mode. We must take care to not let the umask affect the file * mode. * * else open the file and let the default umask determine the file mode. */ if (f_mode >= 0) { oldmask = umask(0000); fdout = open(name, open_flags, f_mode); umask(oldmask); } else fdout = open(name, open_flags, 0666); if (fdout < 0) { perror_reply(553, name); return; } /* if we have a uid and gid, then use them. */ if (valid > 0) { oldid = geteuid(); delay_signaling(); /* we can't allow any signals while euid==0: kinch */ (void) seteuid((uid_t) 0); if ((fchown(fdout, uid, gid)) < 0) { (void) seteuid(oldid); enable_signaling(); /* we can allow signals once again: kinch */ perror_reply(550, "fchown"); return; } (void) seteuid(oldid); enable_signaling(); /* we can allow signals once again: kinch */ } #endif /* UPLOAD */ if (restart_point && (open_flags & O_APPEND) == 0) mode = "r+"; #ifdef UPLOAD fout = fdopen(fdout, mode); #else fout = fopen(name, mode); #endif /* UPLOAD */ closefunc = fclose; if (fout == NULL) { perror_reply(553, name); return; } if (restart_point) { if (type == TYPE_A) { register int i, n, c; n = restart_point; i = 0; while (i++ < n) { if ((c = getc(fout)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } /* We must do this seek to "current" position because we are * changing from reading to writing. */ if (fseek(fout, 0L, L_INCR) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } din = dataconn(name, (off_t) - 1, "r"); if (din == NULL) goto done; if (receive_data(din, fout) == 0) { if (unique) reply(226, "Transfer complete (unique file name:%s).", name); else reply(226, "Transfer complete."); } (void) fclose(din); dolog: if (log_incoming_xfers && xferlog) { char namebuf[MAXPATHLEN], msg[MAXPATHLEN]; char *msg2; /* for stupid_sprintf */ int xfertime = time(NULL) - start_time; time_t curtime = time(NULL); int loop; if (!xfertime) xfertime++; realpath(name, namebuf); for (loop = 0; namebuf[loop]; loop++) if (isspace(namebuf[loop]) || iscntrl(namebuf[loop])) namebuf[loop] = '_'; #ifdef STUPID_SPRINTF /* see above */ #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T) sprintf(msg, "%.24s %d %s %qd ", #else sprintf(msg, "%.24s %d %s %d ", #endif ctime(&curtime), xfertime, remotehost, byte_count ); msg2 = msg + strlen(msg); /* sigh */ sprintf(msg2, "%s %c %s %c %c %s ftp %d %s\n", namebuf, (type == TYPE_A) ? 'a' : 'b', opt_string(0), 'i', anonymous ? 'a' : (guest ? 'g' : 'r'), anonymous ? guestpw : pw->pw_name, authenticated, authenticated ? authuser : "*" ); #else #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T) sprintf(msg, "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s\n", #else sprintf(msg, "%.24s %d %s %d %s %c %s %c %c %s ftp %d %s\n", #endif ctime(&curtime), xfertime, remotehost, byte_count, namebuf, (type == TYPE_A) ? 'a' : 'b', opt_string(0), 'i', anonymous ? 'a' : (guest ? 'g' : 'r'), anonymous ? guestpw : pw->pw_name, authenticated, authenticated ? authuser : "*" ); #endif /* STUPID_SPRINTF */ write(xferlog, msg, strlen(msg)); } data = -1; pdata = -1; done: (*closefunc) (fout); } FILE * #ifdef __STDC__ getdatasock(char *mode) #else getdatasock(mode) char *mode; #endif { int s, on = 1, tries; if (data >= 0) return (fdopen(data, mode)); delay_signaling(); /* we can't allow any signals while euid==0: kinch */ (void) seteuid((uid_t) 0); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) goto bad; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ data_source.sin_family = AF_INET; data_source.sin_addr = ctrl_addr.sin_addr; #if defined(VIRTUAL) && defined(CANT_BIND) /* can't bind to virtual address */ data_source.sin_addr.s_addr = htonl(INADDR_ANY); #endif for (tries = 1;; tries++) { if (bind(s, (struct sockaddr *) &data_source, sizeof(data_source)) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; sleep(tries); } #if defined(M_UNIX) && !defined(_M_UNIX) /* bug in old TCP/IP release */ { struct linger li; li.l_onoff = 1; li.l_linger = 900; if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(struct linger)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); goto bad; } } #endif (void) seteuid((uid_t) pw->pw_uid); enable_signaling(); /* we can allow signals once again: kinch */ #ifdef IPTOS_THROUGHPUT on = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); #endif #ifdef TCP_NOPUSH /* * Turn off push flag to keep sender TCP from sending short packets * at the boundaries of each write(). Should probably do a SO_SNDBUF * to set the send buffer size as well, but that may not be desirable * in heavy-load situations. */ on = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m"); #endif return (fdopen(s, mode)); bad: (void) seteuid((uid_t) pw->pw_uid); enable_signaling(); /* we can allow signals once again: kinch */ (void) close(s); return (NULL); } FILE * #ifdef __STDC__ dataconn(char *name, off_t size, char *mode) #else dataconn(name,size,mode) char *name; off_t size; char *mode; #endif { char sizebuf[32]; FILE *file; int retry = 0; #ifdef IPTOS_LOWDELAY int tos; #endif file_size = size; byte_count = 0; if (size != (off_t) - 1) #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T) (void) sprintf(sizebuf, " (%qd bytes)", size); #else (void) sprintf(sizebuf, " (%d bytes)", size); #endif else (void) strcpy(sizebuf, ""); if (pdata >= 0) { struct sockaddr_in from; int s, fromlen = sizeof(from); #ifdef FD_ZERO struct timeval timeout; fd_set set; FD_ZERO(&set); FD_SET(pdata, &set); timeout.tv_usec = 0; timeout.tv_sec = 120; if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 || (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) { #else alarm(120); s = accept(pdata, (struct sockaddr *) &from, &fromlen); alarm(0); if (s < 0) { #endif reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; return (NULL); } (void) close(pdata); pdata = s; #ifdef IPTOS_LOWDELAY tos = IPTOS_LOWDELAY; (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)); #endif reply(150, "Opening %s mode data connection for %s%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (fdopen(pdata, mode)); } if (data >= 0) { reply(125, "Using existing data connection for %s%s.", name, sizebuf); usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = his_addr; usedefault = 1; file = getdatasock(mode); if (file == NULL) { reply(425, "Can't create data socket (%s,%d): %s.", inet_ntoa(data_source.sin_addr), ntohs(data_source.sin_port), strerror(errno)); return (NULL); } data = fileno(file); while (connect(data, (struct sockaddr *) &data_dest, sizeof(data_dest)) < 0) { if ((errno == EADDRINUSE || errno == EINTR) && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; continue; } perror_reply(425, "Can't build data connection"); (void) fclose(file); data = -1; return (NULL); } reply(150, "Opening %s mode data connection for %s%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (file); } /* Tranfer the contents of "instr" to "outstr" peer using the appropriate * encapsulation of the data subject to Mode, Structure, and Type. * * NB: Form isn't handled. */ void #ifdef __STDC__ send_data(FILE *instr, FILE *outstr, off_t blksize) #else send_data(instr,outstr,blksize) FILE *instr; FILE *outstr; off_t blksize; #endif { register int c, cnt; register char *buf; int netfd, filefd; transflag++; if (setjmp(urgcatch)) { transflag = 0; return; } switch (type) { case TYPE_A: while ((c = getc(instr)) != EOF) { byte_count++; if (c == '\n') { if (ferror(outstr)) goto data_err; (void) putc('\r', outstr); } (void) putc(c, outstr); } fflush(outstr); transflag = 0; if (ferror(instr)) goto file_err; if (ferror(outstr)) goto data_err; reply(226, "Transfer complete."); return; case TYPE_I: case TYPE_L: if ((buf = (char *) malloc((u_int) blksize)) == NULL) { transflag = 0; perror_reply(451, "Local resource failure: malloc"); return; } netfd = fileno(outstr); filefd = fileno(instr); /* Debian fix: this seems gratuitous somehow, testing ... XXX: */ #ifdef bogus__linux__ while ((cnt = read(filefd, buf, (u_int)blksize)) > 0) { int outcnt=0, newcnt=0; while ((outcnt=write(netfd, buf+newcnt, cnt-newcnt))!= cnt-newcnt) newcnt+=outcnt; byte_count += cnt; } #else while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 && write(netfd, buf, cnt) == cnt) byte_count += cnt; #endif transflag = 0; (void) free(buf); if (cnt != 0) { if (cnt < 0) goto file_err; goto data_err; } reply(226, "Transfer complete."); return; default: transflag = 0; reply(550, "Unimplemented TYPE %d in send_data", type); return; } data_err: transflag = 0; perror_reply(426, "Data connection"); return; file_err: transflag = 0; perror_reply(551, "Error on input file"); } /* Transfer data from peer to "outstr" using the appropriate encapulation of * the data subject to Mode, Structure, and Type. * * N.B.: Form isn't handled. */ int #ifdef __STDC__ receive_data(FILE *instr, FILE *outstr) #else receive_data(instr,outstr) FILE *instr; FILE *outstr; #endif { register int c; int cnt, bare_lfs = 0; char buf[BUFSIZ]; transflag++; if (setjmp(urgcatch)) { transflag = 0; return (-1); } switch (type) { case TYPE_I: case TYPE_L: while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { if (write(fileno(outstr), buf, cnt) != cnt) goto file_err; byte_count += cnt; } if (cnt < 0) goto data_err; transflag = 0; return (0); case TYPE_E: reply(553, "TYPE E not implemented."); transflag = 0; return (-1); case TYPE_A: while ((c = getc(instr)) != EOF) { byte_count++; if (c == '\n') bare_lfs++; while (c == '\r') { if (ferror(outstr)) goto data_err; if ((c = getc(instr)) != '\n') { (void) putc('\r', outstr); if (c == EOF) /* null byte fix, noid@cyborg.larc.nasa.gov */ goto contin2; } } (void) putc(c, outstr); contin2:; } fflush(outstr); if (ferror(instr)) goto data_err; if (ferror(outstr)) goto file_err; transflag = 0; if (bare_lfs) { lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); printf(" File may not have transferred correctly.\r\n"); } return (0); default: reply(550, "Unimplemented TYPE %d in receive_data", type); transflag = 0; return (-1); } data_err: transflag = 0; perror_reply(426, "Data Connection"); return (-1); file_err: transflag = 0; perror_reply(452, "Error writing file"); return (-1); } void #ifdef __STDC__ statfilecmd(char *filename) #else statfilecmd(filename) char *filename; #endif { char line[BUFSIZ]; FILE *fin; int c; if (anonymous && dolreplies) (void) snprintf(line, sizeof(line), ls_long, filename); else (void) snprintf(line, sizeof(line), ls_short, filename); fin = ftpd_popen(line, "r", 0); lreply(213, "status of %s:", filename); while ((c = getc(fin)) != EOF) { if (c == '\n') { if (ferror(stdout)) { perror_reply(421, "control connection"); (void) ftpd_pclose(fin); dologout(1); /* NOTREACHED */ } if (ferror(fin)) { perror_reply(551, filename); (void) ftpd_pclose(fin); return; } (void) putc('\r', stdout); } (void) putc(c, stdout); } (void) ftpd_pclose(fin); reply(213, "End of Status"); } void #ifdef __STDC__ statcmd(void) #else statcmd() #endif { struct sockaddr_in *sin; u_char *a, *p; lreply(211, "%s FTP server status:", hostname); printf(" %s\r\n", version); printf(" Connected to %s", remotehost); if (!isdigit(remotehost[0])) printf(" (%s)", inet_ntoa(his_addr.sin_addr)); printf("\r\n"); if (logged_in) { if (anonymous) printf(" Logged in anonymously\r\n"); else printf(" Logged in as %s\r\n", pw->pw_name); } else if (askpasswd) printf(" Waiting for password\r\n"); else printf(" Waiting for user name\r\n"); printf(" TYPE: %s", typenames[type]); if (type == TYPE_A || type == TYPE_E) printf(", FORM: %s", formnames[form]); if (type == TYPE_L) #ifdef NBBY printf(" %d", NBBY); #else printf(" %d", bytesize);/* need definition! */ #endif printf("; STRUcture: %s; transfer MODE: %s\r\n", strunames[stru], modenames[mode]); if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { printf(" in Passive mode"); sin = &pasv_addr; goto printaddr; } else if (usedefault == 0) { printf(" PORT"); sin = &data_dest; printaddr: a = (u_char *) & sin->sin_addr; p = (u_char *) & sin->sin_port; #define UC(b) (((int) b) & 0xff) printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); #undef UC } else printf(" No data connection\r\n"); reply(211, "End of status"); } void #ifdef __STDC__ fatal(char *s) #else fatal(s) char *s; #endif { reply(451, "Error in server: %s\n", s); reply(221, "Closing connection due to server error."); dologout(0); /* NOTREACHED */ } void #if defined (HAVE_VPRINTF) /* VARARGS2 */ #ifdef __STDC__ reply(int n, char *fmt, ...) #else reply(n, fmt, va_alist) int n; char * fmt; va_dcl #endif { VA_LOCAL_DECL VA_START(fmt); if (autospout != NULL) { char *ptr = autospout; printf("%d-", n); while (*ptr) { if (*ptr == '\n') { fputs("\r\n", stdout); if (*(++ptr)) printf("%03d-", n); } else { putc(*ptr++,stdout); } } if (*(--ptr) != '\n') printf("\r\n"); if (autospout_free) { (void) free(autospout); autospout_free = 0; } autospout = 0; } printf("%d ", n); vprintf(fmt, ap); printf("\r\n"); (void) fflush(stdout); if (debug) { char buf[BUFSIZ]; (void) vsprintf(buf, fmt, ap); syslog(LOG_DEBUG, "<--- %d ", n); syslog(LOG_DEBUG, buf); } VA_END; } void /* VARARGS2 */ #ifdef __STDC__ lreply(int n, char *fmt,...) #else lreply(n, fmt, va_alist) int n; char * fmt; va_dcl #endif { VA_LOCAL_DECL VA_START(fmt); if (!dolreplies) return; printf("%d-", n); vprintf(fmt, ap); printf("\r\n"); (void) fflush(stdout); if (debug) { char buf[BUFSIZ]; (void) vsprintf(buf, fmt, ap); syslog(LOG_DEBUG, "<--- %d- ", n); syslog(LOG_DEBUG, buf); } VA_END; } #else /* VARARGS2 */ void reply(int n, char *fmt, int p0, int p1, int p2, int p3, int p4, int p5) { if (autospout != NULL) { char *ptr = autospout; printf("%d-", n); while (*ptr) { if (*ptr == '\n') { printf("\r\n"); if (*(++ptr)) printf("%d-", n); } else { putc(*ptr++,stdout); } } if (*(--ptr) != '\n') printf("\r\n"); if (autospout_free) { (void) free(autospout); autospout_free = 0; } autospout = 0; } printf("%d ", n); printf(fmt, p0, p1, p2, p3, p4, p5); printf("\r\n"); (void) fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d ", n); syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); } } void /* VARARGS2 */ void lreply(int n, char *fmt, int p0, int p1, int p2, int p3, int p4, int p5) { if (!dolreplies) return; printf("%d-", n); printf(fmt, p0, p1, p2, p3, p4, p5); printf("\r\n"); (void) fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d- ", n); syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); } } #endif void #ifdef __STDC__ ack(char *s) #else ack(s) char *s; #endif { reply(250, "%s command successful.", s); } void #ifdef __STDC__ nack(char *s) #else nack(s) char *s; #endif { reply(502, "%s command not implemented.", s); } void #ifdef __STDC__ yyerror(char *s) #else yyerror(s) char *s; #endif { char *cp; if (s == NULL || yyerrorcalled != 0) return; if ((cp = strchr(cbuf, '\n')) != NULL) *cp = '\0'; reply(500, "'%s': command not understood.", cbuf); yyerrorcalled = 1; return; } void #ifdef __STDC__ delete(char *name) #else delete(name) char *name; #endif { struct stat st; /* * delete permission? */ if ( (del_check(name)) == 0 ) return; if (lstat(name, &st) < 0) { perror_reply(550, name); return; } if ((st.st_mode & S_IFMT) == S_IFDIR) { uid_t uid; gid_t gid; int valid; /* * check the directory, can we rmdir here? */ if ( (dir_check(name, &uid, &gid, &valid)) <= 0 ) return; if (rmdir(name) < 0) { perror_reply(550, name); return; } goto done; } if (unlink(name) < 0) { perror_reply(550, name); return; } done: { char path[MAXPATHLEN]; realpath(name, path); if (anonymous) { syslog(LOG_NOTICE, "%s of %s [%s] deleted %s", guestpw, remotehost, remoteaddr, path); } else { syslog(LOG_NOTICE, "%s of %s [%s] deleted %s", pw->pw_name, remotehost, remoteaddr, path); } } ack("DELE"); } void #ifdef __STDC__ cwd(char *path) #else cwd(path) char *path; #endif { struct aclmember *entry = NULL; char cdpath[MAXPATHLEN + 1]; if (chdir(path) < 0) { /* alias checking */ while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL) { if (!strcasecmp(ARG0, path)) { if (chdir(ARG1) < 0) perror_reply(550, path); else { show_message(250, C_WD); show_readme(250, C_WD); ack("CWD"); } return; } } /* check for "cdpath" directories. */ entry = (struct aclmember *) NULL; while (getaclentry("cdpath", &entry) && ARG0 != NULL) { strcpy(cdpath,ARG0); strcat(cdpath,"/"); strcat(cdpath,path); if (chdir(cdpath) >= 0) { show_message(250, C_WD); show_readme(250, C_WD); ack("CWD"); return; } } perror_reply(550,path); } else { show_message(250, C_WD); show_readme(250, C_WD); ack("CWD"); } } void #ifdef __STDC__ makedir(char *name) #else makedir(name) char *name; #endif { uid_t uid; gid_t gid; int valid; /* * check the directory, can we mkdir here? */ if ( (dir_check(name, &uid, &gid, &valid)) <= 0 ) return; /* * check the filename, is it legal? */ if ( (fn_check(name)) <= 0 ) return; if (mkdir(name, 0777) < 0) { if (errno == EEXIST) perror_reply(553, name); else perror_reply(550, name); return; } reply(257, "MKD command successful."); } void #ifdef __STDC__ removedir(char *name) #else removedir(name) char *name; #endif { uid_t uid; gid_t gid; int valid; /* * check the directory, can we rmdir here? */ if ( (dir_check(name, &uid, &gid, &valid)) <= 0 ) return; /* * delete permission? */ if ( (del_check(name)) == 0 ) return; if (rmdir(name) < 0) { if (errno == EBUSY) perror_reply(450, name); else perror_reply(550, name); } else ack("RMD"); } void #ifdef __STDC__ pwd(void) #else pwd() #endif { char path[MAXPATHLEN + 1]; #ifdef HAVE_GETCWD extern char *getcwd(); #else #ifdef __STDC__ extern char *getwd(char *); #else extern char *getwd(); #endif #endif #ifdef HAVE_GETCWD if (getcwd(path,MAXPATHLEN) == (char *) NULL) #else if (getwd(path) == (char *) NULL) #endif /* Dink! If you couldn't get the path and the buffer is now likely to be undefined, why are you trying to PRINT it?! _H* reply(550, "%s.", path); */ { realpath (".", path); /* realpath_on_steroids can deal */ } reply(257, "\"%s\" is current directory.", path); } char * #ifdef __STDC__ renamefrom(char *name) #else renamefrom(name) char *name; #endif { struct stat st; struct aclmember *entry = NULL; /* Added: fixes a bug. _H*/ if (lstat(name, &st) < 0) { perror_reply(550, name); return ((char *) 0); } /* if rename permission denied and file exists... then deny the user * permission to rename the file. */ while (getaclentry("rename", &entry) && ARG0 && ARG1 != NULL) { if (type_match(ARG1)) if (strcmp(ARG0, "yes")) { reply(553, "%s: Permission denied. (rename)", name); return ((char *) 0); } } reply(350, "File exists, ready for destination name"); return (name); } void #ifdef __STDC__ renamecmd(char *from, char *to) #else renamecmd(from,to) char *from; char *to; #endif { struct stat st; /* * check the filename, is it legal? */ if ( (fn_check(to)) == 0 ) return; #ifdef PARANOID /* Almost forgot about this. Don't allow renaming TO existing files -- otherwise someone can rename "trivial" to "warez", and "warez" is gone! XXX: This part really should do the same "overwrite" check as store(). */ if (!stat(to, &st)) { reply (550, "%s: Permission denied. (rename)", to); return; } #endif if (rename(from, to) < 0) perror_reply(550, "rename"); else ack("RNTO"); } void #ifdef __STDC__ dolog(struct sockaddr_in *sin) #else dolog(sin) struct sockaddr_in *sin; #endif { struct hostent *hp; char *blah; #ifdef DNS_TRYAGAIN int num_dns_tries = 0; /* * 27-Apr-93 EHK/BM * far away connections might take some time to get their IP address * resolved. That's why we try again -- maybe our DNS cache has the * PTR-RR now. This code is sloppy. Far better is to check what the * resolver returned so that in case of error, there's no need to * try again. */ dns_again: hp = gethostbyaddr((char *) &sin->sin_addr, sizeof (struct in_addr), AF_INET); if ( !hp && ++num_dns_tries <= 1 ) { sleep(3); goto dns_again; /* try DNS lookup once more */ } #else hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET); #endif blah = inet_ntoa(sin->sin_addr); (void) strncpy(remoteaddr, blah, sizeof(remoteaddr)); if (!strcmp(remoteaddr, "0.0.0.0")) { nameserved = 1; strncpy(remotehost, "localhost", sizeof(remotehost)); } else { if (hp) { nameserved = 1; (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); } else { nameserved = 0; (void) strncpy(remotehost, remoteaddr, sizeof(remotehost)); } } sprintf(proctitle, "%s: connected", remotehost); setproctitle(proctitle); #if 0 /* this is redundant unless the caller doesn't do *anything*, and tcpd will pick it up and deal with it better anyways. _H*/ if (logging) syslog(LOG_INFO, "connection from %s [%s]", remotehost, remoteaddr); #endif } /* Record logout in wtmp file and exit with supplied status. */ void #ifdef __STDC__ dologout(int status) #else dologout(status) int status; #endif { /* * Prevent reception of SIGURG from resulting in a resumption * back to the main program loop. */ transflag = 0; if (logged_in) { delay_signaling(); /* we can't allow any signals while euid==0: kinch */ (void) seteuid((uid_t) 0); logwtmp(ttyline, "", ""); } if (logging) syslog(LOG_INFO, "FTP session closed"); if (xferlog) close(xferlog); acl_remove(); close (data); /* H* fix: clean up a little better */ close (pdata); /* beware of flushing buffers after a SIGPIPE */ _exit(status); } SIGNAL_TYPE #ifdef __STDC__ myoob(int sig) #else myoob(sig) int sig; #endif { char *cp; /* only process if transfer occurring */ if (!transflag) return; cp = tmpline; if (getline(cp, 7, stdin) == NULL) { reply(221, "You could at least say goodbye."); dologout(0); } upper(cp); if (strcmp(cp, "ABOR\r\n") == 0) { tmpline[0] = '\0'; reply(426, "Transfer aborted. Data connection closed."); reply(226, "Abort successful"); (void) signal(SIGURG, myoob); longjmp(urgcatch, 1); } if (strcmp(cp, "STAT\r\n") == 0) { if (file_size != (off_t) - 1) reply(213, "Status: %lu of %lu bytes transferred", byte_count, file_size); else reply(213, "Status: %lu bytes transferred", byte_count); } } /* Note: a response of 425 is not mentioned as a possible response to the * PASV command in RFC959. However, it has been blessed as a legitimate * response by Jon Postel in a telephone conversation with Rick Adams on 25 * Jan 89. */ void #ifdef __STDC__ passive(void) #else passive() #endif { int len; register char *p, *a; /* H* fix: if we already *have* a passive socket, close it first. Prevents a whole variety of entertaining clogging attacks. */ if (pdata > 0) close (pdata); pdata = socket(AF_INET, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } pasv_addr = ctrl_addr; pasv_addr.sin_port = 0; delay_signaling(); /* we can't allow any signals while euid==0: kinch */ (void) seteuid((uid_t) 0); /* XXX: not needed if > 1024 */ if (bind(pdata, (struct sockaddr *) &pasv_addr, sizeof(pasv_addr)) < 0) { (void) seteuid((uid_t) pw->pw_uid); enable_signaling(); /* we can allow signals once again: kinch */ goto pasv_error; } (void) seteuid((uid_t) pw->pw_uid); enable_signaling(); /* we can allow signals once again: kinch */ len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; a = (char *) &pasv_addr.sin_addr; p = (char *) &pasv_addr.sin_port; #define UC(b) (((int) b) & 0xff) reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); return; pasv_error: (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return; } /* Generate unique name for file with basename "local". The file named * "local" is already known to exist. Generates failure reply on error. */ char * #ifdef __STDC__ gunique(char *local) #else gunique(local) char *local; #endif { static char new[MAXPATHLEN]; struct stat st; char *cp = strrchr(local, '/'); int count = 0; if (cp) *cp = '\0'; if (stat(cp ? local : ".", &st) < 0) { perror_reply(553, cp ? local : "."); return ((char *) 0); } if (cp) *cp = '/'; (void) strcpy(new, local); cp = new + strlen(new); *cp++ = '.'; for (count = 1; count < 100; count++) { (void) sprintf(cp, "%d", count); if (stat(new, &st) < 0) return (new); } reply(452, "Unique file name cannot be created."); return ((char *) 0); } /* Format and send reply containing system error number. */ void #ifdef __STDC__ perror_reply(int code, char *string) #else perror_reply(code,string) int code; char *string; #endif { reply(code, "%s: %s.", string, strerror(errno)); } static char *onefile[] = {"", 0}; void #ifdef __STDC__ send_file_list(char *whichfiles) #else send_file_list(whichfiles) char *whichfiles; #endif { struct stat st; DIR *dirp = NULL; #ifdef HAVE_DIRENT struct dirent *dir; #else struct direct *dir; #endif FILE *dout = NULL; register char **dirlist, *dirname; int simple = 0; #ifdef __STDC__ char *strpbrk(const char *, const char *); #else char *strpbrk(); #endif if (strpbrk(whichfiles, "~{[*?") != NULL) { #ifdef __STDC__ extern char **ftpglob(register char *v), #else extern char **ftpglob(), #endif *globerr; globerr = NULL; dirlist = ftpglob(whichfiles); if (globerr != NULL) { reply(550, globerr); return; } else if (dirlist == NULL) { errno = ENOENT; perror_reply(550, whichfiles); return; } } else { onefile[0] = whichfiles; dirlist = onefile; simple = 1; } if (setjmp(urgcatch)) { transflag = 0; return; } while ((dirname = *dirlist++) != NULL) { if (stat(dirname, &st) < 0) { /* If user typed "ls -l", etc, and the client used NLST, do what * the user meant. */ if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) { retrieve("/bin/ls %s", dirname); return; } perror_reply(550, whichfiles); if (dout != NULL) { (void) fclose(dout); transflag = 0; data = -1; pdata = -1; } return; } if ((st.st_mode & S_IFMT) == S_IFREG) { if (dout == NULL) { dout = dataconn("file list", (off_t) - 1, "w"); if (dout == NULL) return; transflag++; } fprintf(dout, "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); byte_count += strlen(dirname) + 1; continue; } else if ((st.st_mode & S_IFMT) != S_IFDIR) continue; if ((dirp = opendir(dirname)) == NULL) continue; while ((dir = readdir(dirp)) != NULL) { char nbuf[MAXPATHLEN]; #ifndef HAVE_DIRENT /* does not have d_namlen */ if (dir->d_name[0] == '.' && dir->d_namlen == 1) #else if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1)) #endif continue; #ifndef HAVE_DIRENT /* does not have d_namlen */ if (dir->d_namlen == 2 && dir->d_name[0] == '.' && dir->d_name[1] == '.') #else if ((strlen(dir->d_name) == 2) && dir->d_name[0] == '.' && dir->d_name[1] == '.') #endif continue; sprintf(nbuf, "%s/%s", dirname, dir->d_name); /* We have to do a stat to insure it's not a directory or special * file. */ if (simple || (stat(nbuf, &st) == 0 && (st.st_mode & S_IFMT) == S_IFREG)) { if (dout == NULL) { dout = dataconn("file list", (off_t) - 1, "w"); if (dout == NULL) { (void) closedir(dirp); return; } transflag++; } if (nbuf[0] == '.' && nbuf[1] == '/') fprintf(dout, "%s%s\n", &nbuf[2], type == TYPE_A ? "\r" : ""); else fprintf(dout, "%s%s\n", nbuf, type == TYPE_A ? "\r" : ""); byte_count += strlen(nbuf) + 1; } } (void) closedir(dirp); } if (dout == NULL) reply(550, "No files found."); else if (ferror(dout) != 0) perror_reply(550, "Data connection"); else reply(226, "Transfer complete."); transflag = 0; if (dout != NULL) (void) fclose(dout); data = -1; pdata = -1; } /* ** SETPROCTITLE -- set process title for ps (from sendmail 8) ** ** Parameters: ** fmt -- a printf style format string. ** a, b, c -- possible parameters to fmt. ** ** Returns: ** none. ** ** Side Effects: ** Clobbers argv of our main procedure so ps(1) will ** display the title. */ #ifndef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV /* default type */ #endif #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN #if SPT_TYPE == SPT_PSTAT #include <sys/pstat.h> #endif #if SPT_TYPE == SPT_PSSTRINGS #include <machine/vmparam.h> #include <sys/exec.h> #ifndef PS_STRINGS /* hmmmm.... apparently not available after all */ #undef SPT_TYPE #define SPT_TYPE SPT_REUSEARGV #else #ifndef NKPDE /* FreeBSD 2.0 */ #define NKPDE 63 typedef unsigned int *pt_entry_t; #endif #endif #endif #if SPT_TYPE == SPT_PSSTRINGS #define SETPROC_STATIC static #else #define SETPROC_STATIC #endif #if SPT_TYPE == SPT_SYSMIPS #include <sys/sysmips.h> #include <sys/sysnews.h> #endif #if SPT_TYPE == SPT_SCO #include <sys/immu.h> #include <sys/dir.h> #include <sys/user.h> #include <sys/fs/s5param.h> #if PSARGSZ > 2048 #define SPT_BUFSIZE PSARGSZ #endif #endif #ifndef SPT_PADCHAR #define SPT_PADCHAR ' ' #endif #ifndef SPT_BUFSIZE #define SPT_BUFSIZE 2048 #endif #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */ #if SPT_TYPE != SPT_BUILTIN /*VARARGS1*/ void #ifdef __STDC__ setproctitle(const char *fmt, ...) #else setproctitle(fmt, va_alist) const char *fmt; va_dcl #endif { #if SPT_TYPE != SPT_NONE register char *p; register int i; SETPROC_STATIC char buf[SPT_BUFSIZE]; VA_LOCAL_DECL #if SPT_TYPE == SPT_PSTAT union pstun pst; #endif #if SPT_TYPE == SPT_SCO off_t seek_off; static int kmem = -1; static int kmempid = -1; #endif #if SPT_TYPE == SPT_REUSEARGV extern char **Argv; extern char *LastArgv; #endif p = buf; /* print ftpd: heading for grep */ (void) strcpy(p, "ftpd: "); p += strlen(p); /* print the argument string */ VA_START(fmt); (void) vsnprintf(p, sizeof buf - (p - buf), fmt, ap); VA_END; i = strlen(buf); #if SPT_TYPE == SPT_PSTAT pst.pst_command = buf; pstat(PSTAT_SETCMD, pst, i, 0, 0); #endif #if SPT_TYPE == SPT_PSSTRINGS if (PS_STRINGS->ps_argvstr) { static char *ps_argv[2]; ps_argv[0] = buf; ps_argv[1] = NULL; PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = ps_argv; } else { PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = buf; } #endif #if SPT_TYPE == SPT_SYSMIPS sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf); #endif #if SPT_TYPE == SPT_SCO if (kmem < 0 || kmempid != getpid()) { if (kmem >= 0) close(kmem); kmem = open(_PATH_KMEM, O_RDWR, 0); if (kmem < 0) return; (void) fcntl(kmem, F_SETFD, 1); kmempid = getpid(); } buf[PSARGSZ - 1] = '\0'; /* Remove trailing carriage returns and new lines */ while (buf[i - 1] == '\r' || buf[i - 1] == '\n') buf[--i] = '\0'; seek_off = UVUBLK + (off_t) &((struct user *)0)->u_psargs; if (lseek(kmem, seek_off, SEEK_SET) == seek_off) (void) write(kmem, buf, PSARGSZ); #endif #if SPT_TYPE == SPT_REUSEARGV /* This may fix problems in AIX, I don't really know since I don't have AIX */ /* Maybe someone will check it out and mail me, or someone will get me */ /* ready access to AIX */ #ifndef AIX if (i > LastArgv - Argv[0] - 2) { i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } #endif (void) strcpy(Argv[0], buf); #ifndef AIX p = &Argv[0][i]; while (p < LastArgv) *p++ = SPT_PADCHAR; Argv[1] = NULL; #endif #endif #endif /* SPT_TYPE != SPT_NONE */ } #endif /* SPT_TYPE != SPT_BUILTIN */ #ifdef KERBEROS /* thanks to gshapiro@wpi.wpi.edu for the following kerberosities */ void init_krb() { char hostname[100]; #ifdef HAVE_SYSINFO if (sysinfo(SI_HOSTNAME, hostname, sizeof (hostname)) < 0) { perror("sysinfo"); #else if (gethostname(hostname, sizeof(hostname)) < 0) { perror("gethostname"); #endif exit(1); } if (strchr(hostname, '.')) *(strchr(hostname, '.')) = 0; sprintf(krb_ticket_name, "/var/dss/kerberos/tkt/tkt.%d", getpid()); krb_set_tkt_string(krb_ticket_name); config_auth(); if (krb_svc_init("hesiod", hostname, (char *) NULL, 0, (char *) NULL, (char *) NULL) != KSUCCESS) { fprintf(stderr, "Couldn't initialize Kerberos\n"); exit(1); } } void end_krb() { unlink(krb_ticket_name); } #endif /* KERBEROS */ #ifdef ULTRIX_AUTH static int ultrix_check_pass(char *passwd, char *xpasswd) { struct svcinfo *svp; int auth_status; if ((svp = getsvc()) == (struct svcinfo *) NULL) { syslog(LOG_WARNING, "getsvc() failed in ultrix_check_pass"); return -1; } if (pw == (struct passwd *) NULL) { return -1; } if (((svp->svcauth.seclevel == SEC_UPGRADE) && (!strcmp(pw->pw_passwd, "*"))) || (svp->svcauth.seclevel == SEC_ENHANCED)) { if ((auth_status=authenticate_user(pw, passwd, "/dev/ttypXX")) >= 0) { /* Indicate successful validation */ return auth_status; } if (auth_status < 0 && errno == EPERM) { /* Log some information about the failed login attempt. */ switch(abs(auth_status)) { case A_EBADPASS: break; case A_ESOFTEXP: syslog(LOG_NOTICE, "password will expire soon for user %s", pw->pw_name); break; case A_EHARDEXP: syslog(LOG_NOTICE, "password has expired for user %s", pw->pw_name); break; case A_ENOLOGIN: syslog(LOG_NOTICE, "user %s attempted login to disabled acct", pw->pw_name); break; } } } else { if ((*pw->pw_passwd != '\0') && (!strcmp(xpasswd, pw->pw_passwd))) { /* passwd in /etc/passwd isn't empty && encrypted passwd matches */ return 0; } } return -1; } #endif /* ULTRIX_AUTH */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.