122499Sdist /* 244339Skarels * Copyright (c) 1985, 1988, 1990 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 622499Sdist */ 722499Sdist 810275Ssam #ifndef lint 922499Sdist char copyright[] = 1044339Skarels "@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\ 1122499Sdist All rights reserved.\n"; 1233738Sbostic #endif /* not lint */ 1310275Ssam 1422499Sdist #ifndef lint 15*52998Sbostic static char sccsid[] = "@(#)ftpd.c 5.41 (Berkeley) 03/18/92"; 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 3610275Ssam #include <signal.h> 3746669Sbostic #include <dirent.h> 3846669Sbostic #include <fcntl.h> 3946669Sbostic #include <time.h> 4010275Ssam #include <pwd.h> 4110275Ssam #include <setjmp.h> 4210275Ssam #include <netdb.h> 4310423Ssam #include <errno.h> 4426493Sminshall #include <syslog.h> 4536435Sbostic #include <varargs.h> 4646669Sbostic #include <unistd.h> 4746669Sbostic #include <stdio.h> 4846669Sbostic #include <ctype.h> 4946669Sbostic #include <stdlib.h> 5046669Sbostic #include <string.h> 5137459Skarels #include "pathnames.h" 5210275Ssam 5310695Ssam /* 5410695Ssam * File containing login names 5510695Ssam * NOT to be used on this machine. 5610695Ssam * Commonly used to disallow uucp. 5710695Ssam */ 5810275Ssam extern int errno; 5910275Ssam extern char *crypt(); 6010275Ssam extern char version[]; 6110275Ssam extern char *home; /* pointer to home directory for glob */ 6236276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 6336304Skarels extern int ftpd_pclose(), fclose(); 6426044Sminshall extern char *getline(); 6526044Sminshall extern char cbuf[]; 6637459Skarels extern off_t restart_point; 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 */ 8926044Sminshall int 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 11246669Sbostic void lostconn(), myoob(); 11310275Ssam FILE *getdatasock(), *dataconn(); 11410275Ssam 11536620Srick #ifdef SETPROCTITLE 11636620Srick char **Argv = NULL; /* pointer to argument vector */ 11736620Srick char *LastArgv = NULL; /* end of argv */ 11836933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 11936620Srick #endif /* SETPROCTITLE */ 12036620Srick 12136620Srick main(argc, argv, envp) 12210275Ssam int argc; 12310275Ssam char *argv[]; 12436620Srick char **envp; 12510275Ssam { 12644339Skarels int addrlen, on = 1, tos; 12710275Ssam char *cp; 12810275Ssam 12945028Skarels /* 13045028Skarels * LOG_NDELAY sets up the logging connection immediately, 13145028Skarels * necessary for anonymous ftp's that chroot and can't do it later. 13245028Skarels */ 13345028Skarels openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 13416339Skarels addrlen = sizeof (his_addr); 13536304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 13626493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 13710275Ssam exit(1); 13810275Ssam } 13916339Skarels addrlen = sizeof (ctrl_addr); 14036304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 14126493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 14216339Skarels exit(1); 14316339Skarels } 14444339Skarels #ifdef IP_TOS 14544339Skarels tos = IPTOS_LOWDELAY; 14644339Skarels if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 14744339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 14844339Skarels #endif 14916339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 15010275Ssam debug = 0; 15136620Srick #ifdef SETPROCTITLE 15236620Srick /* 15336620Srick * Save start and extent of argv for setproctitle. 15436620Srick */ 15536620Srick Argv = argv; 15636620Srick while (*envp) 15736620Srick envp++; 15836620Srick LastArgv = envp[-1] + strlen(envp[-1]); 15936620Srick #endif /* SETPROCTITLE */ 16036620Srick 16110275Ssam argc--, argv++; 16210275Ssam while (argc > 0 && *argv[0] == '-') { 16310275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 16410275Ssam 16511653Ssam case 'v': 16611653Ssam debug = 1; 16711653Ssam break; 16811653Ssam 16910275Ssam case 'd': 17010275Ssam debug = 1; 17110275Ssam break; 17210275Ssam 17311757Ssam case 'l': 17411757Ssam logging = 1; 17511757Ssam break; 17611757Ssam 17711653Ssam case 't': 17811653Ssam timeout = atoi(++cp); 17936933Skarels if (maxtimeout < timeout) 18036933Skarels maxtimeout = timeout; 18111653Ssam goto nextopt; 18211653Ssam 18336933Skarels case 'T': 18436933Skarels maxtimeout = atoi(++cp); 18536933Skarels if (timeout > maxtimeout) 18636933Skarels timeout = maxtimeout; 18736933Skarels goto nextopt; 18836933Skarels 18936933Skarels case 'u': 19036933Skarels { 19136933Skarels int val = 0; 19236933Skarels 19336933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 19436933Skarels val = val*8 + *cp - '0'; 19536933Skarels if (*cp) 19636933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 19736933Skarels else 19836933Skarels defumask = val; 19936933Skarels goto nextopt; 20036933Skarels } 20136933Skarels 20210275Ssam default: 20316339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 20416339Skarels *cp); 20510275Ssam break; 20610275Ssam } 20711653Ssam nextopt: 20810275Ssam argc--, argv++; 20910275Ssam } 21037459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 21126493Sminshall (void) signal(SIGPIPE, lostconn); 21226493Sminshall (void) signal(SIGCHLD, SIG_IGN); 21335691Sbostic if ((int)signal(SIGURG, myoob) < 0) 21426493Sminshall syslog(LOG_ERR, "signal: %m"); 21535691Sbostic 21638134Srick /* Try to handle urgent data inline */ 21727750Sminshall #ifdef SO_OOBINLINE 21836276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 21927750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 22036276Sbostic #endif 22138134Srick 22236933Skarels #ifdef F_SETOWN 22336304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 22436304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 22536933Skarels #endif 22616760Slepreau dolog(&his_addr); 22716339Skarels /* 22816339Skarels * Set up default state 22916339Skarels */ 23016339Skarels data = -1; 23116339Skarels type = TYPE_A; 23216339Skarels form = FORM_N; 23316339Skarels stru = STRU_F; 23416339Skarels mode = MODE_S; 23526044Sminshall tmpline[0] = '\0'; 23626493Sminshall (void) gethostname(hostname, sizeof (hostname)); 23736276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 23836304Skarels (void) setjmp(errcatch); 23936304Skarels for (;;) 24026493Sminshall (void) yyparse(); 24136620Srick /* NOTREACHED */ 24210275Ssam } 24310419Ssam 24446669Sbostic void 24510275Ssam lostconn() 24610275Ssam { 24714089Ssam if (debug) 24826493Sminshall syslog(LOG_DEBUG, "lost connection"); 24914089Ssam dologout(-1); 25010275Ssam } 25110275Ssam 25235672Sbostic static char ttyline[20]; 25335672Sbostic 25436185Sbostic /* 25536185Sbostic * Helper function for sgetpwnam(). 25636185Sbostic */ 25736185Sbostic char * 25836185Sbostic sgetsave(s) 25936185Sbostic char *s; 26036185Sbostic { 26136185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 26236933Skarels 26336185Sbostic if (new == NULL) { 26436620Srick perror_reply(421, "Local resource failure: malloc"); 26536185Sbostic dologout(1); 26636620Srick /* NOTREACHED */ 26736185Sbostic } 26836185Sbostic (void) strcpy(new, s); 26936185Sbostic return (new); 27036185Sbostic } 27136185Sbostic 27236185Sbostic /* 27336185Sbostic * Save the result of a getpwnam. Used for USER command, since 27436185Sbostic * the data returned must not be clobbered by any other command 27536185Sbostic * (e.g., globbing). 27636185Sbostic */ 27736185Sbostic struct passwd * 27836185Sbostic sgetpwnam(name) 27936185Sbostic char *name; 28036185Sbostic { 28136185Sbostic static struct passwd save; 28236185Sbostic register struct passwd *p; 28336185Sbostic char *sgetsave(); 28436185Sbostic 28536185Sbostic if ((p = getpwnam(name)) == NULL) 28636185Sbostic return (p); 28736185Sbostic if (save.pw_name) { 28836185Sbostic free(save.pw_name); 28936185Sbostic free(save.pw_passwd); 29036185Sbostic free(save.pw_gecos); 29136185Sbostic free(save.pw_dir); 29236185Sbostic free(save.pw_shell); 29336185Sbostic } 29436185Sbostic save = *p; 29536185Sbostic save.pw_name = sgetsave(p->pw_name); 29636185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 29736185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 29836185Sbostic save.pw_dir = sgetsave(p->pw_dir); 29936185Sbostic save.pw_shell = sgetsave(p->pw_shell); 30036185Sbostic return (&save); 30136185Sbostic } 30236185Sbostic 30336304Skarels int login_attempts; /* number of failed login attempts */ 30436304Skarels int askpasswd; /* had user command, ask for passwd */ 30536304Skarels 30636304Skarels /* 30736304Skarels * USER command. 30842327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 30942327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 31042327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 31142327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 31242327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 31342327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 31442327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 31542327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 31636304Skarels */ 31736304Skarels user(name) 31836304Skarels char *name; 31936304Skarels { 32036304Skarels register char *cp; 32136304Skarels char *shell; 32240183Smckusick char *getusershell(); 32336304Skarels 32436304Skarels if (logged_in) { 32536304Skarels if (guest) { 32636304Skarels reply(530, "Can't change user from guest login."); 32736304Skarels return; 32836304Skarels } 32936304Skarels end_login(); 33036304Skarels } 33136304Skarels 33236304Skarels guest = 0; 33336304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 33440183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 33540155Smckusick reply(530, "User %s access denied.", name); 33640155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 33736304Skarels guest = 1; 33836304Skarels askpasswd = 1; 33936304Skarels reply(331, "Guest login ok, send ident as password."); 34036933Skarels } else 34136304Skarels reply(530, "User %s unknown.", name); 34236304Skarels return; 34336304Skarels } 34436304Skarels if (pw = sgetpwnam(name)) { 34536304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 34637459Skarels shell = _PATH_BSHELL; 34736304Skarels while ((cp = getusershell()) != NULL) 34836304Skarels if (strcmp(cp, shell) == 0) 34936304Skarels break; 35036304Skarels endusershell(); 35140183Smckusick if (cp == NULL || checkuser(name)) { 35236304Skarels reply(530, "User %s access denied.", name); 35336933Skarels if (logging) 35436933Skarels syslog(LOG_NOTICE, 35536933Skarels "FTP LOGIN REFUSED FROM %s, %s", 35636933Skarels remotehost, name); 35736304Skarels pw = (struct passwd *) NULL; 35836304Skarels return; 35936304Skarels } 36036304Skarels } 36136304Skarels reply(331, "Password required for %s.", name); 36236304Skarels askpasswd = 1; 36336304Skarels /* 36436304Skarels * Delay before reading passwd after first failed 36536304Skarels * attempt to slow down passwd-guessing programs. 36636304Skarels */ 36736304Skarels if (login_attempts) 36836304Skarels sleep((unsigned) login_attempts); 36936304Skarels } 37036304Skarels 37136304Skarels /* 37240183Smckusick * Check if a user is in the file _PATH_FTPUSERS 37340183Smckusick */ 37440183Smckusick checkuser(name) 37540183Smckusick char *name; 37640183Smckusick { 37742327Sbostic register FILE *fd; 37842327Sbostic register char *p; 37942327Sbostic char line[BUFSIZ]; 38040183Smckusick 38140183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 38242327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 38342327Sbostic if ((p = index(line, '\n')) != NULL) { 38442327Sbostic *p = '\0'; 38542327Sbostic if (line[0] == '#') 38642327Sbostic continue; 38742327Sbostic if (strcmp(line, name) == 0) 38842327Sbostic return (1); 38942327Sbostic } 39040183Smckusick (void) fclose(fd); 39140183Smckusick } 39240183Smckusick return (0); 39340183Smckusick } 39440183Smckusick 39540183Smckusick /* 39636304Skarels * Terminate login as previous user, if any, resetting state; 39736304Skarels * used when USER command is given or login fails. 39836304Skarels */ 39936304Skarels end_login() 40036304Skarels { 40136304Skarels 40236304Skarels (void) seteuid((uid_t)0); 40336304Skarels if (logged_in) 40436304Skarels logwtmp(ttyline, "", ""); 40536304Skarels pw = NULL; 40636304Skarels logged_in = 0; 40736304Skarels guest = 0; 40836304Skarels } 40936304Skarels 41010275Ssam pass(passwd) 41110275Ssam char *passwd; 41210275Ssam { 41336304Skarels char *xpasswd, *salt; 41410275Ssam 41536304Skarels if (logged_in || askpasswd == 0) { 41610275Ssam reply(503, "Login with USER first."); 41710275Ssam return; 41810275Ssam } 41936304Skarels askpasswd = 0; 42010275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 42136304Skarels if (pw == NULL) 42236304Skarels salt = "xx"; 42336304Skarels else 42436304Skarels salt = pw->pw_passwd; 42536304Skarels xpasswd = crypt(passwd, salt); 42616760Slepreau /* The strcmp does not catch null passwords! */ 42736304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 42836304Skarels strcmp(xpasswd, pw->pw_passwd)) { 42910275Ssam reply(530, "Login incorrect."); 43010275Ssam pw = NULL; 43136304Skarels if (login_attempts++ >= 5) { 43236933Skarels syslog(LOG_NOTICE, 43336304Skarels "repeated login failures from %s", 43436304Skarels remotehost); 43536304Skarels exit(0); 43636304Skarels } 43710275Ssam return; 43810275Ssam } 43910275Ssam } 44036304Skarels login_attempts = 0; /* this time successful */ 44136304Skarels (void) setegid((gid_t)pw->pw_gid); 44236304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 44316033Sralph 44436192Sbostic /* open wtmp before chroot */ 44536192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 44636192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 44736192Sbostic logged_in = 1; 44836192Sbostic 44936446Sbostic if (guest) { 45036620Srick /* 45136933Skarels * We MUST do a chdir() after the chroot. Otherwise 45236933Skarels * the old current directory will be accessible as "." 45336933Skarels * outside the new root! 45436620Srick */ 45536620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 45636446Sbostic reply(550, "Can't set guest privileges."); 45736446Sbostic goto bad; 45836446Sbostic } 45936933Skarels } else if (chdir(pw->pw_dir) < 0) { 46036620Srick if (chdir("/") < 0) { 46136446Sbostic reply(530, "User %s: can't change directory to %s.", 46236446Sbostic pw->pw_name, pw->pw_dir); 46336446Sbostic goto bad; 46436933Skarels } else 46536446Sbostic lreply(230, "No directory! Logging in with home=/"); 46636620Srick } 46736304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 46836304Skarels reply(550, "Can't set uid."); 46936304Skarels goto bad; 47036304Skarels } 47136550Sbostic if (guest) { 47236192Sbostic reply(230, "Guest login ok, access restrictions apply."); 47336933Skarels #ifdef SETPROCTITLE 47436933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 47536933Skarels sizeof(proctitle) - sizeof(remotehost) - 47636933Skarels sizeof(": anonymous/"), passwd); 47736933Skarels setproctitle(proctitle); 47836933Skarels #endif /* SETPROCTITLE */ 47936933Skarels if (logging) 48036933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 48136933Skarels remotehost, passwd); 482*52998Sbostic home = "/"; /* guest home dir for globbing */ 48336933Skarels } else { 48410275Ssam reply(230, "User %s logged in.", pw->pw_name); 48536933Skarels #ifdef SETPROCTITLE 48636933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 48736933Skarels setproctitle(proctitle); 48836933Skarels #endif /* SETPROCTITLE */ 48936933Skarels if (logging) 49036933Skarels syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 49136933Skarels remotehost, pw->pw_name); 492*52998Sbostic home = pw->pw_dir; /* home dir for globbing */ 49336550Sbostic } 49436933Skarels (void) umask(defumask); 49510303Ssam return; 49610303Ssam bad: 49736304Skarels /* Forget all about it... */ 49836304Skarels end_login(); 49910275Ssam } 50010275Ssam 50110275Ssam retrieve(cmd, name) 50210275Ssam char *cmd, *name; 50310275Ssam { 50410275Ssam FILE *fin, *dout; 50510275Ssam struct stat st; 50636620Srick int (*closefunc)(); 50710275Ssam 50836557Sbostic if (cmd == 0) { 50936446Sbostic fin = fopen(name, "r"), closefunc = fclose; 51036557Sbostic st.st_size = 0; 51136557Sbostic } else { 51210275Ssam char line[BUFSIZ]; 51310275Ssam 51426493Sminshall (void) sprintf(line, cmd, name), name = line; 51536304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 51636557Sbostic st.st_size = -1; 51736933Skarels st.st_blksize = BUFSIZ; 51810275Ssam } 51910275Ssam if (fin == NULL) { 52013152Ssam if (errno != 0) 52136304Skarels perror_reply(550, name); 52210275Ssam return; 52310275Ssam } 52410275Ssam if (cmd == 0 && 52536933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 52610275Ssam reply(550, "%s: not a plain file.", name); 52710275Ssam goto done; 52810275Ssam } 52937459Skarels if (restart_point) { 53037459Skarels if (type == TYPE_A) { 53137459Skarels register int i, n, c; 53237459Skarels 53337459Skarels n = restart_point; 53437459Skarels i = 0; 53537459Skarels while (i++ < n) { 53637459Skarels if ((c=getc(fin)) == EOF) { 53737459Skarels perror_reply(550, name); 53837459Skarels goto done; 53937459Skarels } 54037459Skarels if (c == '\n') 54137459Skarels i++; 542*52998Sbostic } 54337459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 54437459Skarels perror_reply(550, name); 54537459Skarels goto done; 54637459Skarels } 54737459Skarels } 54810275Ssam dout = dataconn(name, st.st_size, "w"); 54910275Ssam if (dout == NULL) 55010275Ssam goto done; 55136620Srick send_data(fin, dout, st.st_blksize); 55226493Sminshall (void) fclose(dout); 55326044Sminshall data = -1; 55426044Sminshall pdata = -1; 55510275Ssam done: 55610275Ssam (*closefunc)(fin); 55710275Ssam } 55810275Ssam 55936304Skarels store(name, mode, unique) 56010275Ssam char *name, *mode; 56136304Skarels int unique; 56210275Ssam { 56310275Ssam FILE *fout, *din; 56436446Sbostic struct stat st; 56536620Srick int (*closefunc)(); 56636304Skarels char *gunique(); 56710275Ssam 56836446Sbostic if (unique && stat(name, &st) == 0 && 56936446Sbostic (name = gunique(name)) == NULL) 57036446Sbostic return; 57110303Ssam 57237459Skarels if (restart_point) 57337459Skarels mode = "r+w"; 57436620Srick fout = fopen(name, mode); 57536620Srick closefunc = fclose; 57610275Ssam if (fout == NULL) { 57736304Skarels perror_reply(553, name); 57810275Ssam return; 57910275Ssam } 58037459Skarels if (restart_point) { 58137459Skarels if (type == TYPE_A) { 58237459Skarels register int i, n, c; 58337459Skarels 58437459Skarels n = restart_point; 58537459Skarels i = 0; 58637459Skarels while (i++ < n) { 58737459Skarels if ((c=getc(fout)) == EOF) { 58837459Skarels perror_reply(550, name); 58937459Skarels goto done; 59037459Skarels } 59137459Skarels if (c == '\n') 59237459Skarels i++; 593*52998Sbostic } 59437459Skarels /* 59537459Skarels * We must do this seek to "current" position 59637459Skarels * because we are changing from reading to 59737459Skarels * writing. 59837459Skarels */ 59937459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 60037459Skarels perror_reply(550, name); 60137459Skarels goto done; 60237459Skarels } 60337459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 60437459Skarels perror_reply(550, name); 60537459Skarels goto done; 60637459Skarels } 60737459Skarels } 60836304Skarels din = dataconn(name, (off_t)-1, "r"); 60910275Ssam if (din == NULL) 61010275Ssam goto done; 61136620Srick if (receive_data(din, fout) == 0) { 61236620Srick if (unique) 61336304Skarels reply(226, "Transfer complete (unique file name:%s).", 61436933Skarels name); 61536304Skarels else 61636304Skarels reply(226, "Transfer complete."); 61726044Sminshall } 61826493Sminshall (void) fclose(din); 61926044Sminshall data = -1; 62026044Sminshall pdata = -1; 62110275Ssam done: 62210275Ssam (*closefunc)(fout); 62310275Ssam } 62410275Ssam 62510275Ssam FILE * 62610275Ssam getdatasock(mode) 62710275Ssam char *mode; 62810275Ssam { 62937459Skarels int s, on = 1, tries; 63010275Ssam 63110275Ssam if (data >= 0) 63210275Ssam return (fdopen(data, mode)); 63350391Skarels (void) seteuid((uid_t)0); 63413247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 63510602Ssam if (s < 0) 63650391Skarels goto bad; 63737459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 63837459Skarels (char *) &on, sizeof (on)) < 0) 63910602Ssam goto bad; 64013152Ssam /* anchor socket to avoid multi-homing problems */ 64113152Ssam data_source.sin_family = AF_INET; 64213152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 64337459Skarels for (tries = 1; ; tries++) { 64437459Skarels if (bind(s, (struct sockaddr *)&data_source, 64537459Skarels sizeof (data_source)) >= 0) 64637459Skarels break; 64737459Skarels if (errno != EADDRINUSE || tries > 10) 64837459Skarels goto bad; 64937459Skarels sleep(tries); 65037459Skarels } 65136304Skarels (void) seteuid((uid_t)pw->pw_uid); 65244339Skarels #ifdef IP_TOS 65344339Skarels on = IPTOS_THROUGHPUT; 65444339Skarels if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 65544339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 65644339Skarels #endif 65710275Ssam return (fdopen(s, mode)); 65810602Ssam bad: 65936304Skarels (void) seteuid((uid_t)pw->pw_uid); 66026493Sminshall (void) close(s); 66110602Ssam return (NULL); 66210275Ssam } 66310275Ssam 66410275Ssam FILE * 66510275Ssam dataconn(name, size, mode) 66610275Ssam char *name; 66711653Ssam off_t size; 66810275Ssam char *mode; 66910275Ssam { 67010275Ssam char sizebuf[32]; 67110275Ssam FILE *file; 67244339Skarels int retry = 0, tos; 67310275Ssam 67436933Skarels file_size = size; 67536933Skarels byte_count = 0; 67636304Skarels if (size != (off_t) -1) 67726493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 67810275Ssam else 67910275Ssam (void) strcpy(sizebuf, ""); 68036304Skarels if (pdata >= 0) { 68126044Sminshall struct sockaddr_in from; 68226044Sminshall int s, fromlen = sizeof(from); 68326044Sminshall 68436304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 68526044Sminshall if (s < 0) { 68626044Sminshall reply(425, "Can't open data connection."); 68726044Sminshall (void) close(pdata); 68826044Sminshall pdata = -1; 68926044Sminshall return(NULL); 69026044Sminshall } 69126044Sminshall (void) close(pdata); 69226044Sminshall pdata = s; 69344339Skarels #ifdef IP_TOS 69444339Skarels tos = IPTOS_LOWDELAY; 69544339Skarels (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 69644339Skarels sizeof(int)); 69744339Skarels #endif 69836235Skarels reply(150, "Opening %s mode data connection for %s%s.", 69936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 70026044Sminshall return(fdopen(pdata, mode)); 70126044Sminshall } 70210275Ssam if (data >= 0) { 70310275Ssam reply(125, "Using existing data connection for %s%s.", 70410275Ssam name, sizebuf); 70510321Ssam usedefault = 1; 70610275Ssam return (fdopen(data, mode)); 70710275Ssam } 70810566Ssam if (usedefault) 70910422Ssam data_dest = his_addr; 71010422Ssam usedefault = 1; 71110275Ssam file = getdatasock(mode); 71210275Ssam if (file == NULL) { 71310275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 71413247Ssam inet_ntoa(data_source.sin_addr), 71542412Sbostic ntohs(data_source.sin_port), strerror(errno)); 71610275Ssam return (NULL); 71710275Ssam } 71810275Ssam data = fileno(file); 71936304Skarels while (connect(data, (struct sockaddr *)&data_dest, 72036304Skarels sizeof (data_dest)) < 0) { 72111653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 72226493Sminshall sleep((unsigned) swaitint); 72311653Ssam retry += swaitint; 72411653Ssam continue; 72511653Ssam } 72636304Skarels perror_reply(425, "Can't build data connection"); 72710275Ssam (void) fclose(file); 72810275Ssam data = -1; 72910275Ssam return (NULL); 73010275Ssam } 73136235Skarels reply(150, "Opening %s mode data connection for %s%s.", 73236235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 73310275Ssam return (file); 73410275Ssam } 73510275Ssam 73610275Ssam /* 73710275Ssam * Tranfer the contents of "instr" to 73810275Ssam * "outstr" peer using the appropriate 73936933Skarels * encapsulation of the data subject 74010275Ssam * to Mode, Structure, and Type. 74110275Ssam * 74210275Ssam * NB: Form isn't handled. 74310275Ssam */ 74436446Sbostic send_data(instr, outstr, blksize) 74510275Ssam FILE *instr, *outstr; 74636446Sbostic off_t blksize; 74710275Ssam { 74836446Sbostic register int c, cnt; 74936446Sbostic register char *buf; 75036446Sbostic int netfd, filefd; 75110275Ssam 75226044Sminshall transflag++; 75326044Sminshall if (setjmp(urgcatch)) { 75426044Sminshall transflag = 0; 75536620Srick return; 75626044Sminshall } 75710275Ssam switch (type) { 75810275Ssam 75910275Ssam case TYPE_A: 76010275Ssam while ((c = getc(instr)) != EOF) { 76136933Skarels byte_count++; 76211220Ssam if (c == '\n') { 76336933Skarels if (ferror(outstr)) 76436620Srick goto data_err; 76527750Sminshall (void) putc('\r', outstr); 76611220Ssam } 76727750Sminshall (void) putc(c, outstr); 76810275Ssam } 76936933Skarels fflush(outstr); 77026044Sminshall transflag = 0; 77136933Skarels if (ferror(instr)) 77236933Skarels goto file_err; 77336933Skarels if (ferror(outstr)) 77436620Srick goto data_err; 77536620Srick reply(226, "Transfer complete."); 77636620Srick return; 77736446Sbostic 77810275Ssam case TYPE_I: 77910275Ssam case TYPE_L: 78036446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 78136446Sbostic transflag = 0; 78236933Skarels perror_reply(451, "Local resource failure: malloc"); 78336933Skarels return; 78436446Sbostic } 78510275Ssam netfd = fileno(outstr); 78610275Ssam filefd = fileno(instr); 78736933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 78836620Srick write(netfd, buf, cnt) == cnt) 78936933Skarels byte_count += cnt; 79026044Sminshall transflag = 0; 79136446Sbostic (void)free(buf); 79236933Skarels if (cnt != 0) { 79336933Skarels if (cnt < 0) 79436933Skarels goto file_err; 79536620Srick goto data_err; 79636933Skarels } 79736620Srick reply(226, "Transfer complete."); 79836620Srick return; 79936620Srick default: 80036620Srick transflag = 0; 80136620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 80236620Srick return; 80310275Ssam } 80436620Srick 80536620Srick data_err: 80626044Sminshall transflag = 0; 80736933Skarels perror_reply(426, "Data connection"); 80836933Skarels return; 80936933Skarels 81036933Skarels file_err: 81136933Skarels transflag = 0; 81236933Skarels perror_reply(551, "Error on input file"); 81310275Ssam } 81410275Ssam 81510275Ssam /* 81610275Ssam * Transfer data from peer to 81710275Ssam * "outstr" using the appropriate 81810275Ssam * encapulation of the data subject 81910275Ssam * to Mode, Structure, and Type. 82010275Ssam * 82110275Ssam * N.B.: Form isn't handled. 82210275Ssam */ 82310275Ssam receive_data(instr, outstr) 82410275Ssam FILE *instr, *outstr; 82510275Ssam { 82610275Ssam register int c; 82738134Srick int cnt, bare_lfs = 0; 82810275Ssam char buf[BUFSIZ]; 82910275Ssam 83026044Sminshall transflag++; 83126044Sminshall if (setjmp(urgcatch)) { 83226044Sminshall transflag = 0; 83336933Skarels return (-1); 83426044Sminshall } 83510275Ssam switch (type) { 83610275Ssam 83710275Ssam case TYPE_I: 83810275Ssam case TYPE_L: 83926044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 84036620Srick if (write(fileno(outstr), buf, cnt) != cnt) 84136933Skarels goto file_err; 84236933Skarels byte_count += cnt; 84326044Sminshall } 84436933Skarels if (cnt < 0) 84536620Srick goto data_err; 84626044Sminshall transflag = 0; 84736933Skarels return (0); 84810275Ssam 84910275Ssam case TYPE_E: 85027106Smckusick reply(553, "TYPE E not implemented."); 85126044Sminshall transflag = 0; 85227106Smckusick return (-1); 85310275Ssam 85410275Ssam case TYPE_A: 85510275Ssam while ((c = getc(instr)) != EOF) { 85636933Skarels byte_count++; 85738134Srick if (c == '\n') 85838134Srick bare_lfs++; 85927750Sminshall while (c == '\r') { 86036933Skarels if (ferror(outstr)) 86136620Srick goto data_err; 86236933Skarels if ((c = getc(instr)) != '\n') { 86327750Sminshall (void) putc ('\r', outstr); 86436933Skarels if (c == '\0' || c == EOF) 86536933Skarels goto contin2; 86636933Skarels } 86710275Ssam } 86836933Skarels (void) putc(c, outstr); 86936933Skarels contin2: ; 87010275Ssam } 87136620Srick fflush(outstr); 87236933Skarels if (ferror(instr)) 87336620Srick goto data_err; 87436933Skarels if (ferror(outstr)) 87536933Skarels goto file_err; 87626044Sminshall transflag = 0; 87738134Srick if (bare_lfs) { 87838134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 87938134Srick printf(" File may not have transferred correctly.\r\n"); 88038134Srick } 88110275Ssam return (0); 88236620Srick default: 88336620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 88436620Srick transflag = 0; 88536933Skarels return (-1); 88610275Ssam } 88736620Srick 88836620Srick data_err: 88926044Sminshall transflag = 0; 89036933Skarels perror_reply(426, "Data Connection"); 89136933Skarels return (-1); 89236933Skarels 89336933Skarels file_err: 89436933Skarels transflag = 0; 89536933Skarels perror_reply(452, "Error writing file"); 89636933Skarels return (-1); 89710275Ssam } 89810275Ssam 89936933Skarels statfilecmd(filename) 90036933Skarels char *filename; 90136933Skarels { 90236933Skarels char line[BUFSIZ]; 90336933Skarels FILE *fin; 90436933Skarels int c; 90536933Skarels 90636933Skarels (void) sprintf(line, "/bin/ls -lgA %s", filename); 90736933Skarels fin = ftpd_popen(line, "r"); 90836933Skarels lreply(211, "status of %s:", filename); 90936933Skarels while ((c = getc(fin)) != EOF) { 91036933Skarels if (c == '\n') { 91136933Skarels if (ferror(stdout)){ 91236933Skarels perror_reply(421, "control connection"); 91336933Skarels (void) ftpd_pclose(fin); 91436933Skarels dologout(1); 91536933Skarels /* NOTREACHED */ 91636933Skarels } 91736933Skarels if (ferror(fin)) { 91836933Skarels perror_reply(551, filename); 91936933Skarels (void) ftpd_pclose(fin); 92036933Skarels return; 92136933Skarels } 92236933Skarels (void) putc('\r', stdout); 92336933Skarels } 92436933Skarels (void) putc(c, stdout); 92536933Skarels } 92636933Skarels (void) ftpd_pclose(fin); 92736933Skarels reply(211, "End of Status"); 92836933Skarels } 92936933Skarels 93036933Skarels statcmd() 93136933Skarels { 93236933Skarels struct sockaddr_in *sin; 93336933Skarels u_char *a, *p; 93436933Skarels 93536933Skarels lreply(211, "%s FTP server status:", hostname, version); 93636933Skarels printf(" %s\r\n", version); 93736933Skarels printf(" Connected to %s", remotehost); 93838134Srick if (!isdigit(remotehost[0])) 93936933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 94036933Skarels printf("\r\n"); 94136933Skarels if (logged_in) { 94236933Skarels if (guest) 94336933Skarels printf(" Logged in anonymously\r\n"); 94436933Skarels else 94536933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 94636933Skarels } else if (askpasswd) 94736933Skarels printf(" Waiting for password\r\n"); 94836933Skarels else 94936933Skarels printf(" Waiting for user name\r\n"); 95036933Skarels printf(" TYPE: %s", typenames[type]); 95136933Skarels if (type == TYPE_A || type == TYPE_E) 95236933Skarels printf(", FORM: %s", formnames[form]); 95336933Skarels if (type == TYPE_L) 95436933Skarels #if NBBY == 8 95536933Skarels printf(" %d", NBBY); 95636933Skarels #else 95736933Skarels printf(" %d", bytesize); /* need definition! */ 95836933Skarels #endif 95936933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 96036933Skarels strunames[stru], modenames[mode]); 96136933Skarels if (data != -1) 96236933Skarels printf(" Data connection open\r\n"); 96336933Skarels else if (pdata != -1) { 96436933Skarels printf(" in Passive mode"); 96536933Skarels sin = &pasv_addr; 96636933Skarels goto printaddr; 96736933Skarels } else if (usedefault == 0) { 96836933Skarels printf(" PORT"); 96936933Skarels sin = &data_dest; 97036933Skarels printaddr: 97136933Skarels a = (u_char *) &sin->sin_addr; 97236933Skarels p = (u_char *) &sin->sin_port; 97336933Skarels #define UC(b) (((int) b) & 0xff) 97436933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 97536933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 97636933Skarels #undef UC 97736933Skarels } else 97836933Skarels printf(" No data connection\r\n"); 97936933Skarels reply(211, "End of status"); 98036933Skarels } 98136933Skarels 98210275Ssam fatal(s) 98310275Ssam char *s; 98410275Ssam { 98510275Ssam reply(451, "Error in server: %s\n", s); 98610275Ssam reply(221, "Closing connection due to server error."); 98713247Ssam dologout(0); 98836620Srick /* NOTREACHED */ 98910275Ssam } 99010275Ssam 99136446Sbostic /* VARARGS2 */ 99236446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 99310275Ssam int n; 99436446Sbostic char *fmt; 99510275Ssam { 99610275Ssam printf("%d ", n); 99736446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 99810275Ssam printf("\r\n"); 99936435Sbostic (void)fflush(stdout); 100010275Ssam if (debug) { 100126493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 100236446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 100310275Ssam } 100436620Srick } 100510275Ssam 100636446Sbostic /* VARARGS2 */ 100736446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 100810275Ssam int n; 100936446Sbostic char *fmt; 101010275Ssam { 101136446Sbostic printf("%d- ", n); 101236446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 101336446Sbostic printf("\r\n"); 101436435Sbostic (void)fflush(stdout); 101536446Sbostic if (debug) { 101636446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 101736446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 101836446Sbostic } 101910275Ssam } 102010275Ssam 102110275Ssam ack(s) 102210275Ssam char *s; 102310275Ssam { 102427106Smckusick reply(250, "%s command successful.", s); 102510275Ssam } 102610275Ssam 102710275Ssam nack(s) 102810275Ssam char *s; 102910275Ssam { 103010275Ssam reply(502, "%s command not implemented.", s); 103110275Ssam } 103210275Ssam 103336304Skarels /* ARGSUSED */ 103426493Sminshall yyerror(s) 103526493Sminshall char *s; 103610275Ssam { 103726044Sminshall char *cp; 103826044Sminshall 103936551Sbostic if (cp = index(cbuf,'\n')) 104036551Sbostic *cp = '\0'; 104136933Skarels reply(500, "'%s': command not understood.", cbuf); 104210275Ssam } 104310275Ssam 104410275Ssam delete(name) 104510275Ssam char *name; 104610275Ssam { 104710275Ssam struct stat st; 104810275Ssam 104910275Ssam if (stat(name, &st) < 0) { 105036304Skarels perror_reply(550, name); 105110275Ssam return; 105210275Ssam } 105310275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 105410275Ssam if (rmdir(name) < 0) { 105536304Skarels perror_reply(550, name); 105610275Ssam return; 105710275Ssam } 105810275Ssam goto done; 105910275Ssam } 106010275Ssam if (unlink(name) < 0) { 106136304Skarels perror_reply(550, name); 106210275Ssam return; 106310275Ssam } 106410275Ssam done: 106510275Ssam ack("DELE"); 106610275Ssam } 106710275Ssam 106810275Ssam cwd(path) 106910275Ssam char *path; 107010275Ssam { 107136620Srick if (chdir(path) < 0) 107236304Skarels perror_reply(550, path); 107336620Srick else 107436620Srick ack("CWD"); 107510275Ssam } 107610275Ssam 107710303Ssam makedir(name) 107810275Ssam char *name; 107910275Ssam { 108036276Sbostic if (mkdir(name, 0777) < 0) 108136304Skarels perror_reply(550, name); 108236276Sbostic else 108336276Sbostic reply(257, "MKD command successful."); 108410275Ssam } 108510275Ssam 108610303Ssam removedir(name) 108710275Ssam char *name; 108810275Ssam { 108936620Srick if (rmdir(name) < 0) 109036304Skarels perror_reply(550, name); 109136620Srick else 109236620Srick ack("RMD"); 109310275Ssam } 109410275Ssam 109510303Ssam pwd() 109610275Ssam { 109710303Ssam char path[MAXPATHLEN + 1]; 109836304Skarels extern char *getwd(); 109910275Ssam 110036620Srick if (getwd(path) == (char *)NULL) 110127106Smckusick reply(550, "%s.", path); 110236620Srick else 110336620Srick reply(257, "\"%s\" is current directory.", path); 110410275Ssam } 110510275Ssam 110610275Ssam char * 110710275Ssam renamefrom(name) 110810275Ssam char *name; 110910275Ssam { 111010275Ssam struct stat st; 111110275Ssam 111210275Ssam if (stat(name, &st) < 0) { 111336304Skarels perror_reply(550, name); 111410275Ssam return ((char *)0); 111510275Ssam } 111610303Ssam reply(350, "File exists, ready for destination name"); 111710275Ssam return (name); 111810275Ssam } 111910275Ssam 112010275Ssam renamecmd(from, to) 112110275Ssam char *from, *to; 112210275Ssam { 112336620Srick if (rename(from, to) < 0) 112436304Skarels perror_reply(550, "rename"); 112536620Srick else 112636620Srick ack("RNTO"); 112710275Ssam } 112810275Ssam 112910275Ssam dolog(sin) 113010275Ssam struct sockaddr_in *sin; 113110275Ssam { 113236304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 113310275Ssam sizeof (struct in_addr), AF_INET); 113410275Ssam 113536304Skarels if (hp) 113626493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 113736304Skarels else 113826493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 113913247Ssam sizeof (remotehost)); 114036620Srick #ifdef SETPROCTITLE 114136933Skarels sprintf(proctitle, "%s: connected", remotehost); 114236933Skarels setproctitle(proctitle); 114336620Srick #endif /* SETPROCTITLE */ 114436933Skarels 1145*52998Sbostic if (logging) 1146*52998Sbostic syslog(LOG_INFO, "connection from %s", remotehost); 114710275Ssam } 114810695Ssam 114910695Ssam /* 115013247Ssam * Record logout in wtmp file 115113247Ssam * and exit with supplied status. 115213247Ssam */ 115313247Ssam dologout(status) 115413247Ssam int status; 115513247Ssam { 115617580Ssam if (logged_in) { 115736304Skarels (void) seteuid((uid_t)0); 115835672Sbostic logwtmp(ttyline, "", ""); 115913247Ssam } 116014436Ssam /* beware of flushing buffers after a SIGPIPE */ 116114436Ssam _exit(status); 116213247Ssam } 116313247Ssam 116446669Sbostic void 116526044Sminshall myoob() 116626044Sminshall { 116727750Sminshall char *cp; 116826044Sminshall 116927750Sminshall /* only process if transfer occurring */ 117036304Skarels if (!transflag) 117126044Sminshall return; 117227750Sminshall cp = tmpline; 117327750Sminshall if (getline(cp, 7, stdin) == NULL) { 117436304Skarels reply(221, "You could at least say goodbye."); 117527750Sminshall dologout(0); 117626044Sminshall } 117726044Sminshall upper(cp); 117836933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 117936933Skarels tmpline[0] = '\0'; 118036933Skarels reply(426, "Transfer aborted. Data connection closed."); 118136933Skarels reply(226, "Abort successful"); 118236933Skarels longjmp(urgcatch, 1); 118336933Skarels } 118436933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 118536933Skarels if (file_size != (off_t) -1) 118636933Skarels reply(213, "Status: %lu of %lu bytes transferred", 118736933Skarels byte_count, file_size); 118836933Skarels else 118936933Skarels reply(213, "Status: %lu bytes transferred", byte_count); 119036933Skarels } 119126044Sminshall } 119226044Sminshall 119327106Smckusick /* 119436620Srick * Note: a response of 425 is not mentioned as a possible response to 1195*52998Sbostic * the PASV command in RFC959. However, it has been blessed as 1196*52998Sbostic * a legitimate response by Jon Postel in a telephone conversation 119736620Srick * with Rick Adams on 25 Jan 89. 119827106Smckusick */ 119926044Sminshall passive() 120026044Sminshall { 120126044Sminshall int len; 120226044Sminshall register char *p, *a; 120326044Sminshall 120426044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 120526044Sminshall if (pdata < 0) { 120636620Srick perror_reply(425, "Can't open passive connection"); 120726044Sminshall return; 120826044Sminshall } 120936933Skarels pasv_addr = ctrl_addr; 121036933Skarels pasv_addr.sin_port = 0; 121136304Skarels (void) seteuid((uid_t)0); 121236933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 121336304Skarels (void) seteuid((uid_t)pw->pw_uid); 121436620Srick goto pasv_error; 121526044Sminshall } 121636304Skarels (void) seteuid((uid_t)pw->pw_uid); 121736933Skarels len = sizeof(pasv_addr); 121836933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 121936620Srick goto pasv_error; 122036620Srick if (listen(pdata, 1) < 0) 122136620Srick goto pasv_error; 122236933Skarels a = (char *) &pasv_addr.sin_addr; 122336933Skarels p = (char *) &pasv_addr.sin_port; 122426044Sminshall 122526044Sminshall #define UC(b) (((int) b) & 0xff) 122626044Sminshall 122726044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 122826044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 122936620Srick return; 123036620Srick 123136620Srick pasv_error: 123236620Srick (void) close(pdata); 123336620Srick pdata = -1; 123436620Srick perror_reply(425, "Can't open passive connection"); 123536620Srick return; 123626044Sminshall } 123726044Sminshall 123836304Skarels /* 123936304Skarels * Generate unique name for file with basename "local". 124036304Skarels * The file named "local" is already known to exist. 124136304Skarels * Generates failure reply on error. 124236304Skarels */ 124326044Sminshall char * 124426044Sminshall gunique(local) 124526044Sminshall char *local; 124626044Sminshall { 124726044Sminshall static char new[MAXPATHLEN]; 124836304Skarels struct stat st; 124926044Sminshall char *cp = rindex(local, '/'); 125036933Skarels int count = 0; 125126044Sminshall 125236304Skarels if (cp) 125326044Sminshall *cp = '\0'; 125436620Srick if (stat(cp ? local : ".", &st) < 0) { 125536933Skarels perror_reply(553, cp ? local : "."); 125626044Sminshall return((char *) 0); 125726044Sminshall } 125836620Srick if (cp) 125936620Srick *cp = '/'; 126026044Sminshall (void) strcpy(new, local); 126126044Sminshall cp = new + strlen(new); 126226044Sminshall *cp++ = '.'; 126336304Skarels for (count = 1; count < 100; count++) { 126436304Skarels (void) sprintf(cp, "%d", count); 126536304Skarels if (stat(new, &st) < 0) 126636304Skarels return(new); 126726044Sminshall } 126836304Skarels reply(452, "Unique file name cannot be created."); 126936304Skarels return((char *) 0); 127026044Sminshall } 127136304Skarels 127236304Skarels /* 127336304Skarels * Format and send reply containing system error number. 127436304Skarels */ 127536304Skarels perror_reply(code, string) 127636304Skarels int code; 127736304Skarels char *string; 127836304Skarels { 127942412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 128036304Skarels } 128136620Srick 128236620Srick static char *onefile[] = { 128336620Srick "", 128436620Srick 0 128536620Srick }; 128636620Srick 128736620Srick send_file_list(whichfiles) 128836620Srick char *whichfiles; 128936620Srick { 129036620Srick struct stat st; 129136620Srick DIR *dirp = NULL; 129246669Sbostic struct dirent *dir; 129336620Srick FILE *dout = NULL; 129436620Srick register char **dirlist, *dirname; 129537459Skarels int simple = 0; 129636620Srick char *strpbrk(); 129736620Srick 129836620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 129946669Sbostic extern char **ftpglob(), *globerr; 130036933Skarels 130136620Srick globerr = NULL; 130246669Sbostic dirlist = ftpglob(whichfiles); 130336620Srick if (globerr != NULL) { 130436620Srick reply(550, globerr); 130536620Srick return; 130636620Srick } else if (dirlist == NULL) { 130736620Srick errno = ENOENT; 130836620Srick perror_reply(550, whichfiles); 130936620Srick return; 131036620Srick } 131136620Srick } else { 131236620Srick onefile[0] = whichfiles; 131336620Srick dirlist = onefile; 131437459Skarels simple = 1; 131536620Srick } 131636933Skarels 131736933Skarels if (setjmp(urgcatch)) { 131836933Skarels transflag = 0; 131936933Skarels return; 132036933Skarels } 132136620Srick while (dirname = *dirlist++) { 132236620Srick if (stat(dirname, &st) < 0) { 132336933Skarels /* 132436933Skarels * If user typed "ls -l", etc, and the client 132536933Skarels * used NLST, do what the user meant. 132636933Skarels */ 132736933Skarels if (dirname[0] == '-' && *dirlist == NULL && 132836933Skarels transflag == 0) { 132936933Skarels retrieve("/bin/ls %s", dirname); 133036933Skarels return; 133136933Skarels } 133236620Srick perror_reply(550, whichfiles); 133336620Srick if (dout != NULL) { 133436620Srick (void) fclose(dout); 133536933Skarels transflag = 0; 133636620Srick data = -1; 133736620Srick pdata = -1; 133836620Srick } 133936620Srick return; 134036620Srick } 134136933Skarels 134236620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 134336620Srick if (dout == NULL) { 134437459Skarels dout = dataconn("file list", (off_t)-1, "w"); 134536620Srick if (dout == NULL) 134636620Srick return; 134736933Skarels transflag++; 134836620Srick } 134938158Srick fprintf(dout, "%s%s\n", dirname, 135038158Srick type == TYPE_A ? "\r" : ""); 135136933Skarels byte_count += strlen(dirname) + 1; 135236620Srick continue; 135336933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 135436620Srick continue; 135536620Srick 135636620Srick if ((dirp = opendir(dirname)) == NULL) 135736620Srick continue; 135836620Srick 135936620Srick while ((dir = readdir(dirp)) != NULL) { 136036933Skarels char nbuf[MAXPATHLEN]; 136136620Srick 136236620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 136336620Srick continue; 136436933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 136536933Skarels dir->d_namlen == 2) 136636620Srick continue; 136736620Srick 136836933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 136936933Skarels 137036620Srick /* 137136933Skarels * We have to do a stat to insure it's 137236933Skarels * not a directory or special file. 137336620Srick */ 137437459Skarels if (simple || (stat(nbuf, &st) == 0 && 137537459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 137636620Srick if (dout == NULL) { 137737459Skarels dout = dataconn("file list", (off_t)-1, 137836620Srick "w"); 137936620Srick if (dout == NULL) 138036620Srick return; 138136933Skarels transflag++; 138236620Srick } 138336620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 138438158Srick fprintf(dout, "%s%s\n", &nbuf[2], 138538158Srick type == TYPE_A ? "\r" : ""); 138636620Srick else 138738158Srick fprintf(dout, "%s%s\n", nbuf, 138838158Srick type == TYPE_A ? "\r" : ""); 138936933Skarels byte_count += strlen(nbuf) + 1; 139036620Srick } 139136620Srick } 139236620Srick (void) closedir(dirp); 139336620Srick } 139436620Srick 139536933Skarels if (dout == NULL) 139636933Skarels reply(550, "No files found."); 139736933Skarels else if (ferror(dout) != 0) 139836933Skarels perror_reply(550, "Data connection"); 139936933Skarels else 140036620Srick reply(226, "Transfer complete."); 140136620Srick 140236933Skarels transflag = 0; 140336933Skarels if (dout != NULL) 140436620Srick (void) fclose(dout); 140536620Srick data = -1; 140636620Srick pdata = -1; 140736620Srick } 140836620Srick 140936620Srick #ifdef SETPROCTITLE 141036620Srick /* 141136620Srick * clobber argv so ps will show what we're doing. 141236620Srick * (stolen from sendmail) 141336620Srick * warning, since this is usually started from inetd.conf, it 141436620Srick * often doesn't have much of an environment or arglist to overwrite. 141536620Srick */ 141636620Srick 141736620Srick /*VARARGS1*/ 141836620Srick setproctitle(fmt, a, b, c) 141936620Srick char *fmt; 142036620Srick { 142136620Srick register char *p, *bp, ch; 142236620Srick register int i; 142336620Srick char buf[BUFSIZ]; 142436620Srick 142536620Srick (void) sprintf(buf, fmt, a, b, c); 142636620Srick 142736620Srick /* make ps print our process name */ 142836620Srick p = Argv[0]; 142936620Srick *p++ = '-'; 143036620Srick 143136620Srick i = strlen(buf); 143236620Srick if (i > LastArgv - p - 2) { 143336620Srick i = LastArgv - p - 2; 143436620Srick buf[i] = '\0'; 143536620Srick } 143636620Srick bp = buf; 143736620Srick while (ch = *bp++) 143836620Srick if (ch != '\n' && ch != '\r') 143936620Srick *p++ = ch; 144036620Srick while (p < LastArgv) 144136620Srick *p++ = ' '; 144236620Srick } 144336620Srick #endif /* SETPROCTITLE */ 1444