122499Sdist /* 2*66711Spendry * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 361427Sbostic * The Regents of the University of California. All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 622499Sdist */ 722499Sdist 810275Ssam #ifndef lint 961427Sbostic static char copyright[] = 10*66711Spendry "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 1161427Sbostic The Regents of the University of California. All rights reserved.\n"; 1233738Sbostic #endif /* not lint */ 1310275Ssam 1422499Sdist #ifndef lint 15*66711Spendry static char sccsid[] = "@(#)ftpd.c 8.2 (Berkeley) 04/04/94"; 1633738Sbostic #endif /* not lint */ 1722499Sdist 1810275Ssam /* 1910275Ssam * FTP server. 2010275Ssam */ 2110303Ssam #include <sys/param.h> 2210275Ssam #include <sys/stat.h> 2310275Ssam #include <sys/ioctl.h> 2410275Ssam #include <sys/socket.h> 2513595Ssam #include <sys/wait.h> 2610275Ssam 2710275Ssam #include <netinet/in.h> 2844339Skarels #include <netinet/in_systm.h> 2944339Skarels #include <netinet/ip.h> 3010275Ssam 3136933Skarels #define FTP_NAMES 3213034Ssam #include <arpa/ftp.h> 3313211Sroot #include <arpa/inet.h> 3426044Sminshall #include <arpa/telnet.h> 3513034Ssam 36*66711Spendry #include <ctype.h> 3746669Sbostic #include <dirent.h> 38*66711Spendry #include <err.h> 39*66711Spendry #include <errno.h> 4046669Sbostic #include <fcntl.h> 41*66711Spendry #include <glob.h> 42*66711Spendry #include <limits.h> 43*66711Spendry #include <netdb.h> 4410275Ssam #include <pwd.h> 4510275Ssam #include <setjmp.h> 46*66711Spendry #include <signal.h> 4746669Sbostic #include <stdio.h> 4846669Sbostic #include <stdlib.h> 4946669Sbostic #include <string.h> 50*66711Spendry #include <syslog.h> 51*66711Spendry #include <time.h> 52*66711Spendry #include <unistd.h> 53*66711Spendry 5437459Skarels #include "pathnames.h" 5555258Sandrew #include "extern.h" 5610275Ssam 5755258Sandrew #if __STDC__ 5855258Sandrew #include <stdarg.h> 5955258Sandrew #else 6055258Sandrew #include <varargs.h> 6155258Sandrew #endif 6255258Sandrew 63*66711Spendry static char version[] = "Version 6.00"; 64*66711Spendry 6555258Sandrew extern off_t restart_point; 6626044Sminshall extern char cbuf[]; 6710275Ssam 6810275Ssam struct sockaddr_in ctrl_addr; 6910275Ssam struct sockaddr_in data_source; 7010275Ssam struct sockaddr_in data_dest; 7110275Ssam struct sockaddr_in his_addr; 7236933Skarels struct sockaddr_in pasv_addr; 7310275Ssam 7410275Ssam int data; 7526044Sminshall jmp_buf errcatch, urgcatch; 7610275Ssam int logged_in; 7710275Ssam struct passwd *pw; 7810275Ssam int debug; 7926493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8036933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 8111757Ssam int logging; 8210275Ssam int guest; 8310275Ssam int type; 8410275Ssam int form; 8510275Ssam int stru; /* avoid C keyword */ 8610275Ssam int mode; 8710321Ssam int usedefault = 1; /* for data transfers */ 8836304Skarels int pdata = -1; /* for passive mode */ 89*66711Spendry sig_atomic_t transflag; 9036933Skarels off_t file_size; 9136933Skarels off_t byte_count; 9236933Skarels #if !defined(CMASK) || CMASK == 0 9336933Skarels #undef CMASK 9436933Skarels #define CMASK 027 9536933Skarels #endif 9636933Skarels int defumask = CMASK; /* default umask value */ 9726044Sminshall char tmpline[7]; 9836276Sbostic char hostname[MAXHOSTNAMELEN]; 9936276Sbostic char remotehost[MAXHOSTNAMELEN]; 10010275Ssam 10111653Ssam /* 10211653Ssam * Timeout intervals for retrying connections 10311653Ssam * to hosts that don't accept PORT cmds. This 10411653Ssam * is a kludge, but given the problems with TCP... 10511653Ssam */ 10611653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10711653Ssam #define SWAITINT 5 /* interval between retries */ 10811653Ssam 10911653Ssam int swaitmax = SWAITMAX; 11011653Ssam int swaitint = SWAITINT; 11111653Ssam 11236620Srick #ifdef SETPROCTITLE 11336620Srick char **Argv = NULL; /* pointer to argument vector */ 11436620Srick char *LastArgv = NULL; /* end of argv */ 115*66711Spendry char proctitle[LINE_MAX]; /* initial part of title */ 11636620Srick #endif /* SETPROCTITLE */ 11736620Srick 11855258Sandrew #define LOGCMD(cmd, file) \ 11955258Sandrew if (logging > 1) \ 12055258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \ 12155258Sandrew *(file) == '/' ? "" : curdir(), file); 12255258Sandrew #define LOGCMD2(cmd, file1, file2) \ 12355258Sandrew if (logging > 1) \ 12455258Sandrew syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 12555258Sandrew *(file1) == '/' ? "" : curdir(), file1, \ 12655258Sandrew *(file2) == '/' ? "" : curdir(), file2); 12755258Sandrew #define LOGBYTES(cmd, file, cnt) \ 12855258Sandrew if (logging > 1) { \ 12955258Sandrew if (cnt == (off_t)-1) \ 13055258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \ 13155258Sandrew *(file) == '/' ? "" : curdir(), file); \ 13255258Sandrew else \ 13355258Sandrew syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 13455258Sandrew cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 13555258Sandrew } 13655258Sandrew 13755258Sandrew static void ack __P((char *)); 13855258Sandrew static void myoob __P((int)); 13955258Sandrew static int checkuser __P((char *)); 14055258Sandrew static FILE *dataconn __P((char *, off_t, char *)); 14155258Sandrew static void dolog __P((struct sockaddr_in *)); 14255258Sandrew static char *curdir __P((void)); 14355258Sandrew static void end_login __P((void)); 14455258Sandrew static FILE *getdatasock __P((char *)); 14555258Sandrew static char *gunique __P((char *)); 14655258Sandrew static void lostconn __P((int)); 14755258Sandrew static int receive_data __P((FILE *, FILE *)); 14855258Sandrew static void send_data __P((FILE *, FILE *, off_t)); 14955258Sandrew static struct passwd * 15055258Sandrew sgetpwnam __P((char *)); 15155258Sandrew static char *sgetsave __P((char *)); 15255258Sandrew 15355258Sandrew static char * 15455258Sandrew curdir() 15555258Sandrew { 15655258Sandrew static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 15755258Sandrew 15855258Sandrew if (getcwd(path, sizeof(path)-2) == NULL) 15955258Sandrew return (""); 16055258Sandrew if (path[1] != '\0') /* special case for root dir. */ 16155258Sandrew strcat(path, "/"); 16255258Sandrew /* For guest account, skip / since it's chrooted */ 16355258Sandrew return (guest ? path+1 : path); 16455258Sandrew } 16555258Sandrew 16655258Sandrew int 16736620Srick main(argc, argv, envp) 16810275Ssam int argc; 16910275Ssam char *argv[]; 17036620Srick char **envp; 17110275Ssam { 172*66711Spendry int addrlen, ch, on = 1, tos; 173*66711Spendry char *cp, line[LINE_MAX]; 17455258Sandrew FILE *fd; 17510275Ssam 17645028Skarels /* 17745028Skarels * LOG_NDELAY sets up the logging connection immediately, 17845028Skarels * necessary for anonymous ftp's that chroot and can't do it later. 17945028Skarels */ 18055258Sandrew openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 181*66711Spendry addrlen = sizeof(his_addr); 18236304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 18326493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 18410275Ssam exit(1); 18510275Ssam } 186*66711Spendry addrlen = sizeof(ctrl_addr); 18736304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 18826493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 18916339Skarels exit(1); 19016339Skarels } 19144339Skarels #ifdef IP_TOS 19244339Skarels tos = IPTOS_LOWDELAY; 19344339Skarels if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 19444339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 19544339Skarels #endif 19616339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 19710275Ssam debug = 0; 19836620Srick #ifdef SETPROCTITLE 19936620Srick /* 20036620Srick * Save start and extent of argv for setproctitle. 20136620Srick */ 20236620Srick Argv = argv; 20336620Srick while (*envp) 20436620Srick envp++; 20536620Srick LastArgv = envp[-1] + strlen(envp[-1]); 20636620Srick #endif /* SETPROCTITLE */ 20736620Srick 208*66711Spendry while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) { 209*66711Spendry switch (ch) { 21010275Ssam case 'd': 21110275Ssam debug = 1; 21210275Ssam break; 21310275Ssam 21411757Ssam case 'l': 21555258Sandrew logging++; /* > 1 == extra logging */ 21611757Ssam break; 21711757Ssam 21811653Ssam case 't': 219*66711Spendry timeout = atoi(optarg); 22036933Skarels if (maxtimeout < timeout) 22136933Skarels maxtimeout = timeout; 222*66711Spendry break; 22311653Ssam 22436933Skarels case 'T': 225*66711Spendry maxtimeout = atoi(optarg); 22636933Skarels if (timeout > maxtimeout) 22736933Skarels timeout = maxtimeout; 228*66711Spendry break; 22936933Skarels 23036933Skarels case 'u': 23136933Skarels { 232*66711Spendry long val = 0; 23336933Skarels 234*66711Spendry val = strtol(optarg, &optarg, 8); 235*66711Spendry if (*optarg != '\0' || val < 0) 236*66711Spendry warnx("bad value for -u"); 23736933Skarels else 23836933Skarels defumask = val; 239*66711Spendry break; 24036933Skarels } 24136933Skarels 242*66711Spendry case 'v': 243*66711Spendry debug = 1; 244*66711Spendry break; 245*66711Spendry 24610275Ssam default: 247*66711Spendry warnx("unknown flag -%c ignored", optopt); 24810275Ssam break; 24910275Ssam } 25010275Ssam } 25137459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 25226493Sminshall (void) signal(SIGPIPE, lostconn); 25326493Sminshall (void) signal(SIGCHLD, SIG_IGN); 25435691Sbostic if ((int)signal(SIGURG, myoob) < 0) 25526493Sminshall syslog(LOG_ERR, "signal: %m"); 25635691Sbostic 25738134Srick /* Try to handle urgent data inline */ 25827750Sminshall #ifdef SO_OOBINLINE 25936276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 26027750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 26136276Sbostic #endif 26238134Srick 26336933Skarels #ifdef F_SETOWN 26436304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 26536304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 26636933Skarels #endif 26716760Slepreau dolog(&his_addr); 26816339Skarels /* 26916339Skarels * Set up default state 27016339Skarels */ 27116339Skarels data = -1; 27216339Skarels type = TYPE_A; 27316339Skarels form = FORM_N; 27416339Skarels stru = STRU_F; 27516339Skarels mode = MODE_S; 27626044Sminshall tmpline[0] = '\0'; 27755258Sandrew 27855258Sandrew /* If logins are disabled, print out the message. */ 27955258Sandrew if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 280*66711Spendry while (fgets(line, sizeof(line), fd) != NULL) { 28160087Sbostic if ((cp = strchr(line, '\n')) != NULL) 28255258Sandrew *cp = '\0'; 28355258Sandrew lreply(530, "%s", line); 28455258Sandrew } 28555258Sandrew (void) fflush(stdout); 28655258Sandrew (void) fclose(fd); 28755258Sandrew reply(530, "System not available."); 28855258Sandrew exit(0); 28955258Sandrew } 29055258Sandrew if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 291*66711Spendry while (fgets(line, sizeof(line), fd) != NULL) { 29260087Sbostic if ((cp = strchr(line, '\n')) != NULL) 29355258Sandrew *cp = '\0'; 29455258Sandrew lreply(220, "%s", line); 29555258Sandrew } 29655258Sandrew (void) fflush(stdout); 29755258Sandrew (void) fclose(fd); 29855258Sandrew /* reply(220,) must follow */ 29955258Sandrew } 300*66711Spendry (void) gethostname(hostname, sizeof(hostname)); 30136276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 30236304Skarels (void) setjmp(errcatch); 30336304Skarels for (;;) 30426493Sminshall (void) yyparse(); 30536620Srick /* NOTREACHED */ 30610275Ssam } 30710419Ssam 30855258Sandrew static void 30955258Sandrew lostconn(signo) 31055258Sandrew int signo; 31110275Ssam { 312*66711Spendry 31314089Ssam if (debug) 31426493Sminshall syslog(LOG_DEBUG, "lost connection"); 31514089Ssam dologout(-1); 31610275Ssam } 31710275Ssam 31835672Sbostic static char ttyline[20]; 31935672Sbostic 32036185Sbostic /* 32136185Sbostic * Helper function for sgetpwnam(). 32236185Sbostic */ 32355258Sandrew static char * 32436185Sbostic sgetsave(s) 32536185Sbostic char *s; 32636185Sbostic { 32736185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 32836933Skarels 32936185Sbostic if (new == NULL) { 33036620Srick perror_reply(421, "Local resource failure: malloc"); 33136185Sbostic dologout(1); 33236620Srick /* NOTREACHED */ 33336185Sbostic } 33436185Sbostic (void) strcpy(new, s); 33536185Sbostic return (new); 33636185Sbostic } 33736185Sbostic 33836185Sbostic /* 33936185Sbostic * Save the result of a getpwnam. Used for USER command, since 34036185Sbostic * the data returned must not be clobbered by any other command 34136185Sbostic * (e.g., globbing). 34236185Sbostic */ 34355258Sandrew static struct passwd * 34436185Sbostic sgetpwnam(name) 34536185Sbostic char *name; 34636185Sbostic { 34736185Sbostic static struct passwd save; 348*66711Spendry struct passwd *p; 34936185Sbostic 35036185Sbostic if ((p = getpwnam(name)) == NULL) 35136185Sbostic return (p); 35236185Sbostic if (save.pw_name) { 35336185Sbostic free(save.pw_name); 35436185Sbostic free(save.pw_passwd); 35536185Sbostic free(save.pw_gecos); 35636185Sbostic free(save.pw_dir); 35736185Sbostic free(save.pw_shell); 35836185Sbostic } 35936185Sbostic save = *p; 36036185Sbostic save.pw_name = sgetsave(p->pw_name); 36136185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 36236185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 36336185Sbostic save.pw_dir = sgetsave(p->pw_dir); 36436185Sbostic save.pw_shell = sgetsave(p->pw_shell); 36536185Sbostic return (&save); 36636185Sbostic } 36736185Sbostic 36855258Sandrew static int login_attempts; /* number of failed login attempts */ 36955258Sandrew static int askpasswd; /* had user command, ask for passwd */ 37055258Sandrew static char curname[10]; /* current USER name */ 37136304Skarels 37236304Skarels /* 37336304Skarels * USER command. 37442327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 37542327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 37642327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 37742327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 37842327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 37942327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 38042327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 38142327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 38236304Skarels */ 38355258Sandrew void 38436304Skarels user(name) 38536304Skarels char *name; 38636304Skarels { 387*66711Spendry char *cp, *shell; 38836304Skarels 38936304Skarels if (logged_in) { 39036304Skarels if (guest) { 39136304Skarels reply(530, "Can't change user from guest login."); 39236304Skarels return; 39336304Skarels } 39436304Skarels end_login(); 39536304Skarels } 39636304Skarels 39736304Skarels guest = 0; 39836304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 39940183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 40040155Smckusick reply(530, "User %s access denied.", name); 40140155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 40236304Skarels guest = 1; 40336304Skarels askpasswd = 1; 40455258Sandrew reply(331, 40555258Sandrew "Guest login ok, type your name as password."); 40636933Skarels } else 40736304Skarels reply(530, "User %s unknown.", name); 40855258Sandrew if (!askpasswd && logging) 40955258Sandrew syslog(LOG_NOTICE, 41055258Sandrew "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 41136304Skarels return; 41236304Skarels } 41336304Skarels if (pw = sgetpwnam(name)) { 41436304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 41537459Skarels shell = _PATH_BSHELL; 41636304Skarels while ((cp = getusershell()) != NULL) 41736304Skarels if (strcmp(cp, shell) == 0) 41836304Skarels break; 41936304Skarels endusershell(); 42055258Sandrew 42140183Smckusick if (cp == NULL || checkuser(name)) { 42236304Skarels reply(530, "User %s access denied.", name); 42336933Skarels if (logging) 42436933Skarels syslog(LOG_NOTICE, 42536933Skarels "FTP LOGIN REFUSED FROM %s, %s", 42636933Skarels remotehost, name); 42736304Skarels pw = (struct passwd *) NULL; 42836304Skarels return; 42936304Skarels } 43036304Skarels } 43155258Sandrew if (logging) 43255258Sandrew strncpy(curname, name, sizeof(curname)-1); 43336304Skarels reply(331, "Password required for %s.", name); 43436304Skarels askpasswd = 1; 43536304Skarels /* 43636304Skarels * Delay before reading passwd after first failed 43736304Skarels * attempt to slow down passwd-guessing programs. 43836304Skarels */ 43936304Skarels if (login_attempts) 44036304Skarels sleep((unsigned) login_attempts); 44136304Skarels } 44236304Skarels 44336304Skarels /* 44440183Smckusick * Check if a user is in the file _PATH_FTPUSERS 44540183Smckusick */ 44655258Sandrew static int 44740183Smckusick checkuser(name) 44840183Smckusick char *name; 44940183Smckusick { 450*66711Spendry FILE *fd; 45155258Sandrew int found = 0; 452*66711Spendry char *p, line[BUFSIZ]; 45340183Smckusick 45440183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 45542327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 45660087Sbostic if ((p = strchr(line, '\n')) != NULL) { 45742327Sbostic *p = '\0'; 45842327Sbostic if (line[0] == '#') 45942327Sbostic continue; 46055258Sandrew if (strcmp(p, name) == 0) { 46155258Sandrew found = 1; 46255258Sandrew break; 46355258Sandrew } 46442327Sbostic } 46540183Smckusick (void) fclose(fd); 46640183Smckusick } 46755258Sandrew return (found); 46840183Smckusick } 46940183Smckusick 47040183Smckusick /* 47136304Skarels * Terminate login as previous user, if any, resetting state; 47236304Skarels * used when USER command is given or login fails. 47336304Skarels */ 47455258Sandrew static void 47536304Skarels end_login() 47636304Skarels { 47736304Skarels 47836304Skarels (void) seteuid((uid_t)0); 47936304Skarels if (logged_in) 48036304Skarels logwtmp(ttyline, "", ""); 48136304Skarels pw = NULL; 48236304Skarels logged_in = 0; 48336304Skarels guest = 0; 48436304Skarels } 48536304Skarels 48655258Sandrew void 48710275Ssam pass(passwd) 48810275Ssam char *passwd; 48910275Ssam { 490*66711Spendry char *salt, *xpasswd; 49155258Sandrew FILE *fd; 49210275Ssam 49336304Skarels if (logged_in || askpasswd == 0) { 49410275Ssam reply(503, "Login with USER first."); 49510275Ssam return; 49610275Ssam } 49736304Skarels askpasswd = 0; 49810275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 49936304Skarels if (pw == NULL) 50036304Skarels salt = "xx"; 50136304Skarels else 50236304Skarels salt = pw->pw_passwd; 50336304Skarels xpasswd = crypt(passwd, salt); 50416760Slepreau /* The strcmp does not catch null passwords! */ 50536304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 50636304Skarels strcmp(xpasswd, pw->pw_passwd)) { 50710275Ssam reply(530, "Login incorrect."); 50855258Sandrew if (logging) 50955258Sandrew syslog(LOG_NOTICE, 51055258Sandrew "FTP LOGIN FAILED FROM %s, %s", 51155258Sandrew remotehost, curname); 51210275Ssam pw = NULL; 51336304Skarels if (login_attempts++ >= 5) { 51436933Skarels syslog(LOG_NOTICE, 51536304Skarels "repeated login failures from %s", 51636304Skarels remotehost); 51736304Skarels exit(0); 51836304Skarels } 51910275Ssam return; 52010275Ssam } 52110275Ssam } 52236304Skarels login_attempts = 0; /* this time successful */ 52355258Sandrew if (setegid((gid_t)pw->pw_gid) < 0) { 52455258Sandrew reply(550, "Can't set gid."); 52555258Sandrew return; 52655258Sandrew } 52736304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 52816033Sralph 52936192Sbostic /* open wtmp before chroot */ 53036192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 53136192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 53236192Sbostic logged_in = 1; 53336192Sbostic 53436446Sbostic if (guest) { 53536620Srick /* 53636933Skarels * We MUST do a chdir() after the chroot. Otherwise 53736933Skarels * the old current directory will be accessible as "." 53836933Skarels * outside the new root! 53936620Srick */ 54036620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 54136446Sbostic reply(550, "Can't set guest privileges."); 54236446Sbostic goto bad; 54336446Sbostic } 54436933Skarels } else if (chdir(pw->pw_dir) < 0) { 54536620Srick if (chdir("/") < 0) { 54636446Sbostic reply(530, "User %s: can't change directory to %s.", 54736446Sbostic pw->pw_name, pw->pw_dir); 54836446Sbostic goto bad; 54936933Skarels } else 55036446Sbostic lreply(230, "No directory! Logging in with home=/"); 55136620Srick } 55236304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 55336304Skarels reply(550, "Can't set uid."); 55436304Skarels goto bad; 55536304Skarels } 55655258Sandrew /* 55755258Sandrew * Display a login message, if it exists. 55855258Sandrew * N.B. reply(230,) must follow the message. 55955258Sandrew */ 56055258Sandrew if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 561*66711Spendry char *cp, line[LINE_MAX]; 56255258Sandrew 563*66711Spendry while (fgets(line, sizeof(line), fd) != NULL) { 56460087Sbostic if ((cp = strchr(line, '\n')) != NULL) 56555258Sandrew *cp = '\0'; 56655258Sandrew lreply(230, "%s", line); 56755258Sandrew } 56855258Sandrew (void) fflush(stdout); 56955258Sandrew (void) fclose(fd); 57055258Sandrew } 57136550Sbostic if (guest) { 57236192Sbostic reply(230, "Guest login ok, access restrictions apply."); 57336933Skarels #ifdef SETPROCTITLE 574*66711Spendry snprintf(proctitle, sizeof(proctitle), 575*66711Spendry "%s: anonymous/%.*s", remotehost, 57636933Skarels sizeof(proctitle) - sizeof(remotehost) - 57736933Skarels sizeof(": anonymous/"), passwd); 57836933Skarels setproctitle(proctitle); 57936933Skarels #endif /* SETPROCTITLE */ 58036933Skarels if (logging) 58136933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 58236933Skarels remotehost, passwd); 58336933Skarels } else { 58410275Ssam reply(230, "User %s logged in.", pw->pw_name); 58536933Skarels #ifdef SETPROCTITLE 586*66711Spendry snprintf(proctitle, sizeof(proctitle), 587*66711Spendry "%s: %s", remotehost, pw->pw_name); 58836933Skarels setproctitle(proctitle); 58936933Skarels #endif /* SETPROCTITLE */ 59036933Skarels if (logging) 59155258Sandrew syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 59236933Skarels remotehost, pw->pw_name); 59336550Sbostic } 59436933Skarels (void) umask(defumask); 59510303Ssam return; 59610303Ssam bad: 59736304Skarels /* Forget all about it... */ 59836304Skarels end_login(); 59910275Ssam } 60010275Ssam 60155258Sandrew void 60210275Ssam retrieve(cmd, name) 60310275Ssam char *cmd, *name; 60410275Ssam { 60510275Ssam FILE *fin, *dout; 60610275Ssam struct stat st; 607*66711Spendry int (*closefunc) __P((FILE *)); 60810275Ssam 60936557Sbostic if (cmd == 0) { 61036446Sbostic fin = fopen(name, "r"), closefunc = fclose; 61136557Sbostic st.st_size = 0; 61236557Sbostic } else { 61310275Ssam char line[BUFSIZ]; 61410275Ssam 61526493Sminshall (void) sprintf(line, cmd, name), name = line; 61636304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 61736557Sbostic st.st_size = -1; 61836933Skarels st.st_blksize = BUFSIZ; 61910275Ssam } 62010275Ssam if (fin == NULL) { 62155258Sandrew if (errno != 0) { 62236304Skarels perror_reply(550, name); 62355258Sandrew if (cmd == 0) { 62455258Sandrew LOGCMD("get", name); 62555258Sandrew } 62655258Sandrew } 62710275Ssam return; 62810275Ssam } 62955258Sandrew byte_count = -1; 630*66711Spendry if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { 63110275Ssam reply(550, "%s: not a plain file.", name); 63210275Ssam goto done; 63310275Ssam } 63437459Skarels if (restart_point) { 63537459Skarels if (type == TYPE_A) { 636*66711Spendry off_t i, n; 637*66711Spendry int c; 63837459Skarels 63937459Skarels n = restart_point; 64037459Skarels i = 0; 64137459Skarels while (i++ < n) { 64237459Skarels if ((c=getc(fin)) == EOF) { 64337459Skarels perror_reply(550, name); 64437459Skarels goto done; 64537459Skarels } 64637459Skarels if (c == '\n') 64737459Skarels i++; 64852998Sbostic } 64937459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 65037459Skarels perror_reply(550, name); 65137459Skarels goto done; 65237459Skarels } 65337459Skarels } 65410275Ssam dout = dataconn(name, st.st_size, "w"); 65510275Ssam if (dout == NULL) 65610275Ssam goto done; 65736620Srick send_data(fin, dout, st.st_blksize); 65826493Sminshall (void) fclose(dout); 65926044Sminshall data = -1; 66026044Sminshall pdata = -1; 66110275Ssam done: 66255258Sandrew if (cmd == 0) 66355258Sandrew LOGBYTES("get", name, byte_count); 66410275Ssam (*closefunc)(fin); 66510275Ssam } 66610275Ssam 66755258Sandrew void 66836304Skarels store(name, mode, unique) 66910275Ssam char *name, *mode; 67036304Skarels int unique; 67110275Ssam { 67210275Ssam FILE *fout, *din; 67336446Sbostic struct stat st; 674*66711Spendry int (*closefunc) __P((FILE *)); 67510275Ssam 67636446Sbostic if (unique && stat(name, &st) == 0 && 67755258Sandrew (name = gunique(name)) == NULL) { 67855258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name); 67936446Sbostic return; 68055258Sandrew } 68110303Ssam 68237459Skarels if (restart_point) 683*66711Spendry mode = "r+"; 68436620Srick fout = fopen(name, mode); 68536620Srick closefunc = fclose; 68610275Ssam if (fout == NULL) { 68736304Skarels perror_reply(553, name); 68855258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name); 68910275Ssam return; 69010275Ssam } 69155258Sandrew byte_count = -1; 69237459Skarels if (restart_point) { 69337459Skarels if (type == TYPE_A) { 694*66711Spendry off_t i, n; 695*66711Spendry int c; 69637459Skarels 69737459Skarels n = restart_point; 69837459Skarels i = 0; 69937459Skarels while (i++ < n) { 70037459Skarels if ((c=getc(fout)) == EOF) { 70137459Skarels perror_reply(550, name); 70237459Skarels goto done; 70337459Skarels } 70437459Skarels if (c == '\n') 70537459Skarels i++; 70652998Sbostic } 70737459Skarels /* 70837459Skarels * We must do this seek to "current" position 70937459Skarels * because we are changing from reading to 71037459Skarels * writing. 71137459Skarels */ 71237459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 71337459Skarels perror_reply(550, name); 71437459Skarels goto done; 71537459Skarels } 71637459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 71737459Skarels perror_reply(550, name); 71837459Skarels goto done; 71937459Skarels } 72037459Skarels } 72136304Skarels din = dataconn(name, (off_t)-1, "r"); 72210275Ssam if (din == NULL) 72310275Ssam goto done; 72436620Srick if (receive_data(din, fout) == 0) { 72536620Srick if (unique) 72636304Skarels reply(226, "Transfer complete (unique file name:%s).", 72736933Skarels name); 72836304Skarels else 72936304Skarels reply(226, "Transfer complete."); 73026044Sminshall } 73126493Sminshall (void) fclose(din); 73226044Sminshall data = -1; 73326044Sminshall pdata = -1; 73410275Ssam done: 73555258Sandrew LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 73610275Ssam (*closefunc)(fout); 73710275Ssam } 73810275Ssam 73955258Sandrew static FILE * 74010275Ssam getdatasock(mode) 74110275Ssam char *mode; 74210275Ssam { 743*66711Spendry int on = 1, s, t, tries; 74410275Ssam 74510275Ssam if (data >= 0) 74610275Ssam return (fdopen(data, mode)); 74750391Skarels (void) seteuid((uid_t)0); 74813247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 74910602Ssam if (s < 0) 75050391Skarels goto bad; 75137459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 752*66711Spendry (char *) &on, sizeof(on)) < 0) 75310602Ssam goto bad; 75413152Ssam /* anchor socket to avoid multi-homing problems */ 75513152Ssam data_source.sin_family = AF_INET; 75613152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 75737459Skarels for (tries = 1; ; tries++) { 75837459Skarels if (bind(s, (struct sockaddr *)&data_source, 759*66711Spendry sizeof(data_source)) >= 0) 76037459Skarels break; 76137459Skarels if (errno != EADDRINUSE || tries > 10) 76237459Skarels goto bad; 76337459Skarels sleep(tries); 76437459Skarels } 76536304Skarels (void) seteuid((uid_t)pw->pw_uid); 76644339Skarels #ifdef IP_TOS 76744339Skarels on = IPTOS_THROUGHPUT; 76844339Skarels if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 76944339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 77044339Skarels #endif 77110275Ssam return (fdopen(s, mode)); 77210602Ssam bad: 77355258Sandrew /* Return the real value of errno (close may change it) */ 77455258Sandrew t = errno; 77536304Skarels (void) seteuid((uid_t)pw->pw_uid); 77626493Sminshall (void) close(s); 77755258Sandrew errno = t; 77810602Ssam return (NULL); 77910275Ssam } 78010275Ssam 78155258Sandrew static FILE * 78210275Ssam dataconn(name, size, mode) 78310275Ssam char *name; 78411653Ssam off_t size; 78510275Ssam char *mode; 78610275Ssam { 78710275Ssam char sizebuf[32]; 78810275Ssam FILE *file; 78944339Skarels int retry = 0, tos; 79010275Ssam 79136933Skarels file_size = size; 79236933Skarels byte_count = 0; 79336304Skarels if (size != (off_t) -1) 794*66711Spendry (void) sprintf(sizebuf, " (%qd bytes)", size); 79510275Ssam else 79610275Ssam (void) strcpy(sizebuf, ""); 79736304Skarels if (pdata >= 0) { 79826044Sminshall struct sockaddr_in from; 79926044Sminshall int s, fromlen = sizeof(from); 80026044Sminshall 80136304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 80226044Sminshall if (s < 0) { 80326044Sminshall reply(425, "Can't open data connection."); 80426044Sminshall (void) close(pdata); 80526044Sminshall pdata = -1; 806*66711Spendry return (NULL); 80726044Sminshall } 80826044Sminshall (void) close(pdata); 80926044Sminshall pdata = s; 81044339Skarels #ifdef IP_TOS 81144339Skarels tos = IPTOS_LOWDELAY; 81244339Skarels (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 81344339Skarels sizeof(int)); 81444339Skarels #endif 81555258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.", 81636235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 817*66711Spendry return (fdopen(pdata, mode)); 81826044Sminshall } 81910275Ssam if (data >= 0) { 82055258Sandrew reply(125, "Using existing data connection for '%s'%s.", 82110275Ssam name, sizebuf); 82210321Ssam usedefault = 1; 82310275Ssam return (fdopen(data, mode)); 82410275Ssam } 82510566Ssam if (usedefault) 82610422Ssam data_dest = his_addr; 82710422Ssam usedefault = 1; 82810275Ssam file = getdatasock(mode); 82910275Ssam if (file == NULL) { 83010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 83113247Ssam inet_ntoa(data_source.sin_addr), 83242412Sbostic ntohs(data_source.sin_port), strerror(errno)); 83310275Ssam return (NULL); 83410275Ssam } 83510275Ssam data = fileno(file); 83636304Skarels while (connect(data, (struct sockaddr *)&data_dest, 837*66711Spendry sizeof(data_dest)) < 0) { 83811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 83926493Sminshall sleep((unsigned) swaitint); 84011653Ssam retry += swaitint; 84111653Ssam continue; 84211653Ssam } 84336304Skarels perror_reply(425, "Can't build data connection"); 84410275Ssam (void) fclose(file); 84510275Ssam data = -1; 84610275Ssam return (NULL); 84710275Ssam } 84855258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.", 84936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 85010275Ssam return (file); 85110275Ssam } 85210275Ssam 85310275Ssam /* 85455258Sandrew * Tranfer the contents of "instr" to "outstr" peer using the appropriate 85555258Sandrew * encapsulation of the data subject * to Mode, Structure, and Type. 85610275Ssam * 85710275Ssam * NB: Form isn't handled. 85810275Ssam */ 85955258Sandrew static void 86036446Sbostic send_data(instr, outstr, blksize) 86110275Ssam FILE *instr, *outstr; 86236446Sbostic off_t blksize; 86310275Ssam { 864*66711Spendry int c, cnt, filefd, netfd; 865*66711Spendry char *buf; 86610275Ssam 86726044Sminshall transflag++; 86826044Sminshall if (setjmp(urgcatch)) { 86926044Sminshall transflag = 0; 87036620Srick return; 87126044Sminshall } 87210275Ssam switch (type) { 87310275Ssam 87410275Ssam case TYPE_A: 87510275Ssam while ((c = getc(instr)) != EOF) { 87636933Skarels byte_count++; 87711220Ssam if (c == '\n') { 87836933Skarels if (ferror(outstr)) 87936620Srick goto data_err; 88027750Sminshall (void) putc('\r', outstr); 88111220Ssam } 88227750Sminshall (void) putc(c, outstr); 88310275Ssam } 88436933Skarels fflush(outstr); 88526044Sminshall transflag = 0; 88636933Skarels if (ferror(instr)) 88736933Skarels goto file_err; 88836933Skarels if (ferror(outstr)) 88936620Srick goto data_err; 89036620Srick reply(226, "Transfer complete."); 89136620Srick return; 89236446Sbostic 89310275Ssam case TYPE_I: 89410275Ssam case TYPE_L: 89536446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 89636446Sbostic transflag = 0; 89736933Skarels perror_reply(451, "Local resource failure: malloc"); 89836933Skarels return; 89936446Sbostic } 90010275Ssam netfd = fileno(outstr); 90110275Ssam filefd = fileno(instr); 90236933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 90336620Srick write(netfd, buf, cnt) == cnt) 90436933Skarels byte_count += cnt; 90526044Sminshall transflag = 0; 90636446Sbostic (void)free(buf); 90736933Skarels if (cnt != 0) { 90836933Skarels if (cnt < 0) 90936933Skarels goto file_err; 91036620Srick goto data_err; 91136933Skarels } 91236620Srick reply(226, "Transfer complete."); 91336620Srick return; 91436620Srick default: 91536620Srick transflag = 0; 91636620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 91736620Srick return; 91810275Ssam } 91936620Srick 92036620Srick data_err: 92126044Sminshall transflag = 0; 92236933Skarels perror_reply(426, "Data connection"); 92336933Skarels return; 92436933Skarels 92536933Skarels file_err: 92636933Skarels transflag = 0; 92736933Skarels perror_reply(551, "Error on input file"); 92810275Ssam } 92910275Ssam 93010275Ssam /* 93155258Sandrew * Transfer data from peer to "outstr" using the appropriate encapulation of 93255258Sandrew * the data subject to Mode, Structure, and Type. 93310275Ssam * 93410275Ssam * N.B.: Form isn't handled. 93510275Ssam */ 93655258Sandrew static int 93710275Ssam receive_data(instr, outstr) 93810275Ssam FILE *instr, *outstr; 93910275Ssam { 940*66711Spendry int c; 94138134Srick int cnt, bare_lfs = 0; 94210275Ssam char buf[BUFSIZ]; 94310275Ssam 94426044Sminshall transflag++; 94526044Sminshall if (setjmp(urgcatch)) { 94626044Sminshall transflag = 0; 94736933Skarels return (-1); 94826044Sminshall } 94910275Ssam switch (type) { 95010275Ssam 95110275Ssam case TYPE_I: 95210275Ssam case TYPE_L: 953*66711Spendry while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 95436620Srick if (write(fileno(outstr), buf, cnt) != cnt) 95536933Skarels goto file_err; 95636933Skarels byte_count += cnt; 95726044Sminshall } 95836933Skarels if (cnt < 0) 95936620Srick goto data_err; 96026044Sminshall transflag = 0; 96136933Skarels return (0); 96210275Ssam 96310275Ssam case TYPE_E: 96427106Smckusick reply(553, "TYPE E not implemented."); 96526044Sminshall transflag = 0; 96627106Smckusick return (-1); 96710275Ssam 96810275Ssam case TYPE_A: 96910275Ssam while ((c = getc(instr)) != EOF) { 97036933Skarels byte_count++; 97138134Srick if (c == '\n') 97238134Srick bare_lfs++; 97327750Sminshall while (c == '\r') { 97436933Skarels if (ferror(outstr)) 97536620Srick goto data_err; 97636933Skarels if ((c = getc(instr)) != '\n') { 97727750Sminshall (void) putc ('\r', outstr); 97836933Skarels if (c == '\0' || c == EOF) 97936933Skarels goto contin2; 98036933Skarels } 98110275Ssam } 98236933Skarels (void) putc(c, outstr); 98336933Skarels contin2: ; 98410275Ssam } 98536620Srick fflush(outstr); 98636933Skarels if (ferror(instr)) 98736620Srick goto data_err; 98836933Skarels if (ferror(outstr)) 98936933Skarels goto file_err; 99026044Sminshall transflag = 0; 99138134Srick if (bare_lfs) { 99238134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 99338134Srick printf(" File may not have transferred correctly.\r\n"); 99438134Srick } 99510275Ssam return (0); 99636620Srick default: 99736620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 99836620Srick transflag = 0; 99936933Skarels return (-1); 100010275Ssam } 100136620Srick 100236620Srick data_err: 100326044Sminshall transflag = 0; 100436933Skarels perror_reply(426, "Data Connection"); 100536933Skarels return (-1); 100636933Skarels 100736933Skarels file_err: 100836933Skarels transflag = 0; 100936933Skarels perror_reply(452, "Error writing file"); 101036933Skarels return (-1); 101110275Ssam } 101210275Ssam 101355258Sandrew void 101436933Skarels statfilecmd(filename) 101536933Skarels char *filename; 101636933Skarels { 101736933Skarels FILE *fin; 101836933Skarels int c; 1019*66711Spendry char line[LINE_MAX]; 102036933Skarels 102155258Sandrew (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); 102236933Skarels fin = ftpd_popen(line, "r"); 102336933Skarels lreply(211, "status of %s:", filename); 102436933Skarels while ((c = getc(fin)) != EOF) { 102536933Skarels if (c == '\n') { 102636933Skarels if (ferror(stdout)){ 102736933Skarels perror_reply(421, "control connection"); 102836933Skarels (void) ftpd_pclose(fin); 102936933Skarels dologout(1); 103036933Skarels /* NOTREACHED */ 103136933Skarels } 103236933Skarels if (ferror(fin)) { 103336933Skarels perror_reply(551, filename); 103436933Skarels (void) ftpd_pclose(fin); 103536933Skarels return; 103636933Skarels } 103736933Skarels (void) putc('\r', stdout); 103836933Skarels } 103936933Skarels (void) putc(c, stdout); 104036933Skarels } 104136933Skarels (void) ftpd_pclose(fin); 104236933Skarels reply(211, "End of Status"); 104336933Skarels } 104436933Skarels 104555258Sandrew void 104636933Skarels statcmd() 104736933Skarels { 104836933Skarels struct sockaddr_in *sin; 104936933Skarels u_char *a, *p; 105036933Skarels 105136933Skarels lreply(211, "%s FTP server status:", hostname, version); 105236933Skarels printf(" %s\r\n", version); 105336933Skarels printf(" Connected to %s", remotehost); 105438134Srick if (!isdigit(remotehost[0])) 105536933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 105636933Skarels printf("\r\n"); 105736933Skarels if (logged_in) { 105836933Skarels if (guest) 105936933Skarels printf(" Logged in anonymously\r\n"); 106036933Skarels else 106136933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 106236933Skarels } else if (askpasswd) 106336933Skarels printf(" Waiting for password\r\n"); 106436933Skarels else 106536933Skarels printf(" Waiting for user name\r\n"); 106636933Skarels printf(" TYPE: %s", typenames[type]); 106736933Skarels if (type == TYPE_A || type == TYPE_E) 106836933Skarels printf(", FORM: %s", formnames[form]); 106936933Skarels if (type == TYPE_L) 107036933Skarels #if NBBY == 8 107136933Skarels printf(" %d", NBBY); 107236933Skarels #else 107336933Skarels printf(" %d", bytesize); /* need definition! */ 107436933Skarels #endif 107536933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 107636933Skarels strunames[stru], modenames[mode]); 107736933Skarels if (data != -1) 107836933Skarels printf(" Data connection open\r\n"); 107936933Skarels else if (pdata != -1) { 108036933Skarels printf(" in Passive mode"); 108136933Skarels sin = &pasv_addr; 108236933Skarels goto printaddr; 108336933Skarels } else if (usedefault == 0) { 108436933Skarels printf(" PORT"); 108536933Skarels sin = &data_dest; 108636933Skarels printaddr: 108736933Skarels a = (u_char *) &sin->sin_addr; 108836933Skarels p = (u_char *) &sin->sin_port; 108936933Skarels #define UC(b) (((int) b) & 0xff) 109036933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 109136933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 109236933Skarels #undef UC 109336933Skarels } else 109436933Skarels printf(" No data connection\r\n"); 109536933Skarels reply(211, "End of status"); 109636933Skarels } 109736933Skarels 109855258Sandrew void 109910275Ssam fatal(s) 110010275Ssam char *s; 110110275Ssam { 1102*66711Spendry 110310275Ssam reply(451, "Error in server: %s\n", s); 110410275Ssam reply(221, "Closing connection due to server error."); 110513247Ssam dologout(0); 110636620Srick /* NOTREACHED */ 110710275Ssam } 110810275Ssam 110955258Sandrew void 111055258Sandrew #if __STDC__ 111155258Sandrew reply(int n, const char *fmt, ...) 111255258Sandrew #else 111355258Sandrew reply(n, fmt, va_alist) 111410275Ssam int n; 111536446Sbostic char *fmt; 111655258Sandrew va_dcl 111755258Sandrew #endif 111810275Ssam { 111955258Sandrew va_list ap; 112055258Sandrew #if __STDC__ 112155258Sandrew va_start(ap, fmt); 112255258Sandrew #else 112355258Sandrew va_start(ap); 112455258Sandrew #endif 112555258Sandrew (void)printf("%d ", n); 112655258Sandrew (void)vprintf(fmt, ap); 112755258Sandrew (void)printf("\r\n"); 112836435Sbostic (void)fflush(stdout); 112910275Ssam if (debug) { 113026493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 113155258Sandrew vsyslog(LOG_DEBUG, fmt, ap); 113255258Sandrew } 113310275Ssam } 113410275Ssam 113555258Sandrew void 113655258Sandrew #if __STDC__ 113755258Sandrew lreply(int n, const char *fmt, ...) 113855258Sandrew #else 113955258Sandrew lreply(n, fmt, va_alist) 114010275Ssam int n; 114136446Sbostic char *fmt; 114255258Sandrew va_dcl 114355258Sandrew #endif 114410275Ssam { 114555258Sandrew va_list ap; 114655258Sandrew #if __STDC__ 114755258Sandrew va_start(ap, fmt); 114855258Sandrew #else 114955258Sandrew va_start(ap); 115055258Sandrew #endif 115155258Sandrew (void)printf("%d- ", n); 115255258Sandrew (void)vprintf(fmt, ap); 115355258Sandrew (void)printf("\r\n"); 115436435Sbostic (void)fflush(stdout); 115536446Sbostic if (debug) { 115636446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 115755258Sandrew vsyslog(LOG_DEBUG, fmt, ap); 115836446Sbostic } 115910275Ssam } 116010275Ssam 116155258Sandrew static void 116210275Ssam ack(s) 116310275Ssam char *s; 116410275Ssam { 1165*66711Spendry 116627106Smckusick reply(250, "%s command successful.", s); 116710275Ssam } 116810275Ssam 116955258Sandrew void 117010275Ssam nack(s) 117110275Ssam char *s; 117210275Ssam { 1173*66711Spendry 117410275Ssam reply(502, "%s command not implemented.", s); 117510275Ssam } 117610275Ssam 117736304Skarels /* ARGSUSED */ 1178*66711Spendry void 117926493Sminshall yyerror(s) 118026493Sminshall char *s; 118110275Ssam { 118226044Sminshall char *cp; 118326044Sminshall 118460087Sbostic if (cp = strchr(cbuf,'\n')) 118536551Sbostic *cp = '\0'; 118636933Skarels reply(500, "'%s': command not understood.", cbuf); 118710275Ssam } 118810275Ssam 118955258Sandrew void 119010275Ssam delete(name) 119110275Ssam char *name; 119210275Ssam { 119310275Ssam struct stat st; 119410275Ssam 119555258Sandrew LOGCMD("delete", name); 119610275Ssam if (stat(name, &st) < 0) { 119736304Skarels perror_reply(550, name); 119810275Ssam return; 119910275Ssam } 120010275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 120110275Ssam if (rmdir(name) < 0) { 120236304Skarels perror_reply(550, name); 120310275Ssam return; 120410275Ssam } 120510275Ssam goto done; 120610275Ssam } 120710275Ssam if (unlink(name) < 0) { 120836304Skarels perror_reply(550, name); 120910275Ssam return; 121010275Ssam } 121110275Ssam done: 121210275Ssam ack("DELE"); 121310275Ssam } 121410275Ssam 121555258Sandrew void 121610275Ssam cwd(path) 121710275Ssam char *path; 121810275Ssam { 1219*66711Spendry 122036620Srick if (chdir(path) < 0) 122136304Skarels perror_reply(550, path); 122236620Srick else 122336620Srick ack("CWD"); 122410275Ssam } 122510275Ssam 122655258Sandrew void 122710303Ssam makedir(name) 122810275Ssam char *name; 122910275Ssam { 1230*66711Spendry 123155258Sandrew LOGCMD("mkdir", name); 123236276Sbostic if (mkdir(name, 0777) < 0) 123336304Skarels perror_reply(550, name); 123436276Sbostic else 123536276Sbostic reply(257, "MKD command successful."); 123610275Ssam } 123710275Ssam 123855258Sandrew void 123910303Ssam removedir(name) 124010275Ssam char *name; 124110275Ssam { 1242*66711Spendry 124355258Sandrew LOGCMD("rmdir", name); 124436620Srick if (rmdir(name) < 0) 124536304Skarels perror_reply(550, name); 124636620Srick else 124736620Srick ack("RMD"); 124810275Ssam } 124910275Ssam 125055258Sandrew void 125110303Ssam pwd() 125210275Ssam { 125310303Ssam char path[MAXPATHLEN + 1]; 125410275Ssam 125536620Srick if (getwd(path) == (char *)NULL) 125627106Smckusick reply(550, "%s.", path); 125736620Srick else 125836620Srick reply(257, "\"%s\" is current directory.", path); 125910275Ssam } 126010275Ssam 126110275Ssam char * 126210275Ssam renamefrom(name) 126310275Ssam char *name; 126410275Ssam { 126510275Ssam struct stat st; 126610275Ssam 126710275Ssam if (stat(name, &st) < 0) { 126836304Skarels perror_reply(550, name); 126910275Ssam return ((char *)0); 127010275Ssam } 127110303Ssam reply(350, "File exists, ready for destination name"); 127210275Ssam return (name); 127310275Ssam } 127410275Ssam 127555258Sandrew void 127610275Ssam renamecmd(from, to) 127710275Ssam char *from, *to; 127810275Ssam { 1279*66711Spendry 128055258Sandrew LOGCMD2("rename", from, to); 128136620Srick if (rename(from, to) < 0) 128236304Skarels perror_reply(550, "rename"); 128336620Srick else 128436620Srick ack("RNTO"); 128510275Ssam } 128610275Ssam 128755258Sandrew static void 128810275Ssam dolog(sin) 128910275Ssam struct sockaddr_in *sin; 129010275Ssam { 129136304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 1292*66711Spendry sizeof(struct in_addr), AF_INET); 129310275Ssam 129436304Skarels if (hp) 1295*66711Spendry (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); 129636304Skarels else 129726493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 1298*66711Spendry sizeof(remotehost)); 129936620Srick #ifdef SETPROCTITLE 1300*66711Spendry snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 130136933Skarels setproctitle(proctitle); 130236620Srick #endif /* SETPROCTITLE */ 130336933Skarels 130452998Sbostic if (logging) 130552998Sbostic syslog(LOG_INFO, "connection from %s", remotehost); 130610275Ssam } 130710695Ssam 130810695Ssam /* 130913247Ssam * Record logout in wtmp file 131013247Ssam * and exit with supplied status. 131113247Ssam */ 131255258Sandrew void 131313247Ssam dologout(status) 131413247Ssam int status; 131513247Ssam { 1316*66711Spendry 131717580Ssam if (logged_in) { 131836304Skarels (void) seteuid((uid_t)0); 131935672Sbostic logwtmp(ttyline, "", ""); 132013247Ssam } 132114436Ssam /* beware of flushing buffers after a SIGPIPE */ 132214436Ssam _exit(status); 132313247Ssam } 132413247Ssam 132555258Sandrew static void 132655258Sandrew myoob(signo) 132755258Sandrew int signo; 132826044Sminshall { 132927750Sminshall char *cp; 133026044Sminshall 133127750Sminshall /* only process if transfer occurring */ 133236304Skarels if (!transflag) 133326044Sminshall return; 133427750Sminshall cp = tmpline; 133527750Sminshall if (getline(cp, 7, stdin) == NULL) { 133636304Skarels reply(221, "You could at least say goodbye."); 133727750Sminshall dologout(0); 133826044Sminshall } 133926044Sminshall upper(cp); 134036933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 134136933Skarels tmpline[0] = '\0'; 134236933Skarels reply(426, "Transfer aborted. Data connection closed."); 134336933Skarels reply(226, "Abort successful"); 134436933Skarels longjmp(urgcatch, 1); 134536933Skarels } 134636933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 134736933Skarels if (file_size != (off_t) -1) 134855258Sandrew reply(213, "Status: %qd of %qd bytes transferred", 134936933Skarels byte_count, file_size); 135036933Skarels else 135155258Sandrew reply(213, "Status: %qd bytes transferred", byte_count); 135236933Skarels } 135326044Sminshall } 135426044Sminshall 135527106Smckusick /* 135636620Srick * Note: a response of 425 is not mentioned as a possible response to 135752998Sbostic * the PASV command in RFC959. However, it has been blessed as 135852998Sbostic * a legitimate response by Jon Postel in a telephone conversation 135936620Srick * with Rick Adams on 25 Jan 89. 136027106Smckusick */ 136155258Sandrew void 136226044Sminshall passive() 136326044Sminshall { 136426044Sminshall int len; 1365*66711Spendry char *p, *a; 136626044Sminshall 136726044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 136826044Sminshall if (pdata < 0) { 136936620Srick perror_reply(425, "Can't open passive connection"); 137026044Sminshall return; 137126044Sminshall } 137236933Skarels pasv_addr = ctrl_addr; 137336933Skarels pasv_addr.sin_port = 0; 137436304Skarels (void) seteuid((uid_t)0); 137536933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 137636304Skarels (void) seteuid((uid_t)pw->pw_uid); 137736620Srick goto pasv_error; 137826044Sminshall } 137936304Skarels (void) seteuid((uid_t)pw->pw_uid); 138036933Skarels len = sizeof(pasv_addr); 138136933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 138236620Srick goto pasv_error; 138336620Srick if (listen(pdata, 1) < 0) 138436620Srick goto pasv_error; 138536933Skarels a = (char *) &pasv_addr.sin_addr; 138636933Skarels p = (char *) &pasv_addr.sin_port; 138726044Sminshall 138826044Sminshall #define UC(b) (((int) b) & 0xff) 138926044Sminshall 139026044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 139126044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 139236620Srick return; 139336620Srick 139436620Srick pasv_error: 139536620Srick (void) close(pdata); 139636620Srick pdata = -1; 139736620Srick perror_reply(425, "Can't open passive connection"); 139836620Srick return; 139926044Sminshall } 140026044Sminshall 140136304Skarels /* 140236304Skarels * Generate unique name for file with basename "local". 140336304Skarels * The file named "local" is already known to exist. 140436304Skarels * Generates failure reply on error. 140536304Skarels */ 140655258Sandrew static char * 140726044Sminshall gunique(local) 140826044Sminshall char *local; 140926044Sminshall { 141026044Sminshall static char new[MAXPATHLEN]; 141136304Skarels struct stat st; 141255258Sandrew int count; 141355258Sandrew char *cp; 141426044Sminshall 141560087Sbostic cp = strrchr(local, '/'); 141636304Skarels if (cp) 141726044Sminshall *cp = '\0'; 141836620Srick if (stat(cp ? local : ".", &st) < 0) { 141936933Skarels perror_reply(553, cp ? local : "."); 1420*66711Spendry return ((char *) 0); 142126044Sminshall } 142236620Srick if (cp) 142336620Srick *cp = '/'; 142426044Sminshall (void) strcpy(new, local); 142526044Sminshall cp = new + strlen(new); 142626044Sminshall *cp++ = '.'; 142736304Skarels for (count = 1; count < 100; count++) { 142855258Sandrew (void)sprintf(cp, "%d", count); 142936304Skarels if (stat(new, &st) < 0) 1430*66711Spendry return (new); 143126044Sminshall } 143236304Skarels reply(452, "Unique file name cannot be created."); 1433*66711Spendry return (NULL); 143426044Sminshall } 143536304Skarels 143636304Skarels /* 143736304Skarels * Format and send reply containing system error number. 143836304Skarels */ 143955258Sandrew void 144036304Skarels perror_reply(code, string) 144136304Skarels int code; 144236304Skarels char *string; 144336304Skarels { 1444*66711Spendry 144542412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 144636304Skarels } 144736620Srick 144836620Srick static char *onefile[] = { 144936620Srick "", 145036620Srick 0 145136620Srick }; 145236620Srick 145355258Sandrew void 1454*66711Spendry send_file_list(whichf) 1455*66711Spendry char *whichf; 145636620Srick { 145736620Srick struct stat st; 145836620Srick DIR *dirp = NULL; 145946669Sbostic struct dirent *dir; 146036620Srick FILE *dout = NULL; 1461*66711Spendry char **dirlist, *dirname; 146237459Skarels int simple = 0; 1463*66711Spendry int freeglob = 0; 1464*66711Spendry glob_t gl; 146536620Srick 1466*66711Spendry if (strpbrk(whichf, "~{[*?") != NULL) { 146736933Skarels 1468*66711Spendry memset(&gl, 0, sizeof(gl)); 1469*66711Spendry freeglob = 1; 1470*66711Spendry if (glob(whichf, GLOB_BRACE|GLOB_QUOTE|GLOB_TILDE, 0, &gl)) { 1471*66711Spendry reply(550, "not found"); 1472*66711Spendry goto out; 1473*66711Spendry } else if (gl.gl_pathc == 0) { 147436620Srick errno = ENOENT; 1475*66711Spendry perror_reply(550, whichf); 1476*66711Spendry goto out; 147736620Srick } 1478*66711Spendry dirlist = gl.gl_pathv; 147936620Srick } else { 1480*66711Spendry onefile[0] = whichf; 148136620Srick dirlist = onefile; 148237459Skarels simple = 1; 148336620Srick } 148436933Skarels 148536933Skarels if (setjmp(urgcatch)) { 148636933Skarels transflag = 0; 1487*66711Spendry goto out; 148836933Skarels } 148936620Srick while (dirname = *dirlist++) { 149036620Srick if (stat(dirname, &st) < 0) { 149136933Skarels /* 149236933Skarels * If user typed "ls -l", etc, and the client 149336933Skarels * used NLST, do what the user meant. 149436933Skarels */ 149536933Skarels if (dirname[0] == '-' && *dirlist == NULL && 149636933Skarels transflag == 0) { 149736933Skarels retrieve("/bin/ls %s", dirname); 1498*66711Spendry goto out; 149936933Skarels } 1500*66711Spendry perror_reply(550, whichf); 150136620Srick if (dout != NULL) { 150236620Srick (void) fclose(dout); 150336933Skarels transflag = 0; 150436620Srick data = -1; 150536620Srick pdata = -1; 150636620Srick } 1507*66711Spendry goto out; 150836620Srick } 150936933Skarels 1510*66711Spendry if (S_ISREG(st.st_mode)) { 151136620Srick if (dout == NULL) { 151237459Skarels dout = dataconn("file list", (off_t)-1, "w"); 151336620Srick if (dout == NULL) 1514*66711Spendry goto out; 151536933Skarels transflag++; 151636620Srick } 151738158Srick fprintf(dout, "%s%s\n", dirname, 151838158Srick type == TYPE_A ? "\r" : ""); 151936933Skarels byte_count += strlen(dirname) + 1; 152036620Srick continue; 1521*66711Spendry } else if (!S_ISDIR(st.st_mode)) 152236620Srick continue; 152336620Srick 152436620Srick if ((dirp = opendir(dirname)) == NULL) 152536620Srick continue; 152636620Srick 152736620Srick while ((dir = readdir(dirp)) != NULL) { 152836933Skarels char nbuf[MAXPATHLEN]; 152936620Srick 153036620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 153136620Srick continue; 153236933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 153336933Skarels dir->d_namlen == 2) 153436620Srick continue; 153536620Srick 153636933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 153736933Skarels 153836620Srick /* 153936933Skarels * We have to do a stat to insure it's 154036933Skarels * not a directory or special file. 154136620Srick */ 154237459Skarels if (simple || (stat(nbuf, &st) == 0 && 1543*66711Spendry S_ISREG(st.st_mode))) { 154436620Srick if (dout == NULL) { 154537459Skarels dout = dataconn("file list", (off_t)-1, 154636620Srick "w"); 154736620Srick if (dout == NULL) 1548*66711Spendry goto out; 154936933Skarels transflag++; 155036620Srick } 155136620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 155238158Srick fprintf(dout, "%s%s\n", &nbuf[2], 155338158Srick type == TYPE_A ? "\r" : ""); 155436620Srick else 155538158Srick fprintf(dout, "%s%s\n", nbuf, 155638158Srick type == TYPE_A ? "\r" : ""); 155736933Skarels byte_count += strlen(nbuf) + 1; 155836620Srick } 155936620Srick } 156036620Srick (void) closedir(dirp); 156136620Srick } 156236620Srick 156336933Skarels if (dout == NULL) 156436933Skarels reply(550, "No files found."); 156536933Skarels else if (ferror(dout) != 0) 156636933Skarels perror_reply(550, "Data connection"); 156736933Skarels else 156836620Srick reply(226, "Transfer complete."); 156936620Srick 157036933Skarels transflag = 0; 157136933Skarels if (dout != NULL) 157236620Srick (void) fclose(dout); 157336620Srick data = -1; 157436620Srick pdata = -1; 1575*66711Spendry out: 1576*66711Spendry if (freeglob) { 1577*66711Spendry freeglob = 0; 1578*66711Spendry globfree(&gl); 1579*66711Spendry } 158036620Srick } 158136620Srick 158236620Srick #ifdef SETPROCTITLE 158336620Srick /* 158455258Sandrew * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 158555258Sandrew * Warning, since this is usually started from inetd.conf, it often doesn't 158655258Sandrew * have much of an environment or arglist to overwrite. 158736620Srick */ 158855258Sandrew void 158955258Sandrew #if __STDC__ 159055258Sandrew setproctitle(const char *fmt, ...) 159155258Sandrew #else 159255258Sandrew setproctitle(fmt, va_alist) 159355258Sandrew char *fmt; 159455258Sandrew va_dcl 159555258Sandrew #endif 159636620Srick { 1597*66711Spendry int i; 159855258Sandrew va_list ap; 1599*66711Spendry char *p, *bp, ch; 1600*66711Spendry char buf[LINE_MAX]; 1601*66711Spendry 160255258Sandrew #if __STDC__ 160355258Sandrew va_start(ap, fmt); 160455258Sandrew #else 160555258Sandrew va_start(ap); 160655258Sandrew #endif 160755258Sandrew (void)vsnprintf(buf, sizeof(buf), fmt, ap); 160836620Srick 160936620Srick /* make ps print our process name */ 161036620Srick p = Argv[0]; 161136620Srick *p++ = '-'; 161236620Srick 161336620Srick i = strlen(buf); 161436620Srick if (i > LastArgv - p - 2) { 161536620Srick i = LastArgv - p - 2; 161636620Srick buf[i] = '\0'; 161736620Srick } 161836620Srick bp = buf; 161936620Srick while (ch = *bp++) 162036620Srick if (ch != '\n' && ch != '\r') 162136620Srick *p++ = ch; 162236620Srick while (p < LastArgv) 162336620Srick *p++ = ' '; 162436620Srick } 162536620Srick #endif /* SETPROCTITLE */ 1626