122499Sdist /* 2*61427Sbostic * Copyright (c) 1985, 1988, 1990, 1992, 1993 3*61427Sbostic * The Regents of the University of California. All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 622499Sdist */ 722499Sdist 810275Ssam #ifndef lint 9*61427Sbostic static char copyright[] = 10*61427Sbostic "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993\n\ 11*61427Sbostic The Regents of the University of California. All rights reserved.\n"; 1233738Sbostic #endif /* not lint */ 1310275Ssam 1422499Sdist #ifndef lint 15*61427Sbostic static char sccsid[] = "@(#)ftpd.c 8.1 (Berkeley) 06/04/93"; 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> 4546669Sbostic #include <unistd.h> 4646669Sbostic #include <stdio.h> 4746669Sbostic #include <ctype.h> 4846669Sbostic #include <stdlib.h> 4946669Sbostic #include <string.h> 5037459Skarels #include "pathnames.h" 5155258Sandrew #include "extern.h" 5210275Ssam 5355258Sandrew #if __STDC__ 5455258Sandrew #include <stdarg.h> 5555258Sandrew #else 5655258Sandrew #include <varargs.h> 5755258Sandrew #endif 5855258Sandrew 5955258Sandrew extern off_t restart_point; 6010275Ssam extern char *home; /* pointer to home directory for glob */ 6126044Sminshall extern char cbuf[]; 6255258Sandrew extern char version[]; 6310275Ssam 6410275Ssam struct sockaddr_in ctrl_addr; 6510275Ssam struct sockaddr_in data_source; 6610275Ssam struct sockaddr_in data_dest; 6710275Ssam struct sockaddr_in his_addr; 6836933Skarels struct sockaddr_in pasv_addr; 6910275Ssam 7010275Ssam int data; 7126044Sminshall jmp_buf errcatch, urgcatch; 7210275Ssam int logged_in; 7310275Ssam struct passwd *pw; 7410275Ssam int debug; 7526493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 7636933Skarels int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 7711757Ssam int logging; 7810275Ssam int guest; 7910275Ssam int type; 8010275Ssam int form; 8110275Ssam int stru; /* avoid C keyword */ 8210275Ssam int mode; 8310321Ssam int usedefault = 1; /* for data transfers */ 8436304Skarels int pdata = -1; /* for passive mode */ 8526044Sminshall int transflag; 8636933Skarels off_t file_size; 8736933Skarels off_t byte_count; 8836933Skarels #if !defined(CMASK) || CMASK == 0 8936933Skarels #undef CMASK 9036933Skarels #define CMASK 027 9136933Skarels #endif 9236933Skarels int defumask = CMASK; /* default umask value */ 9326044Sminshall char tmpline[7]; 9436276Sbostic char hostname[MAXHOSTNAMELEN]; 9536276Sbostic char remotehost[MAXHOSTNAMELEN]; 9610275Ssam 9711653Ssam /* 9811653Ssam * Timeout intervals for retrying connections 9911653Ssam * to hosts that don't accept PORT cmds. This 10011653Ssam * is a kludge, but given the problems with TCP... 10111653Ssam */ 10211653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10311653Ssam #define SWAITINT 5 /* interval between retries */ 10411653Ssam 10511653Ssam int swaitmax = SWAITMAX; 10611653Ssam int swaitint = SWAITINT; 10711653Ssam 10836620Srick #ifdef SETPROCTITLE 10936620Srick char **Argv = NULL; /* pointer to argument vector */ 11036620Srick char *LastArgv = NULL; /* end of argv */ 11136933Skarels char proctitle[BUFSIZ]; /* initial part of title */ 11236620Srick #endif /* SETPROCTITLE */ 11336620Srick 11455258Sandrew #define MAXLINE 256 11555258Sandrew 11655258Sandrew #define LOGCMD(cmd, file) \ 11755258Sandrew if (logging > 1) \ 11855258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \ 11955258Sandrew *(file) == '/' ? "" : curdir(), file); 12055258Sandrew #define LOGCMD2(cmd, file1, file2) \ 12155258Sandrew if (logging > 1) \ 12255258Sandrew syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 12355258Sandrew *(file1) == '/' ? "" : curdir(), file1, \ 12455258Sandrew *(file2) == '/' ? "" : curdir(), file2); 12555258Sandrew #define LOGBYTES(cmd, file, cnt) \ 12655258Sandrew if (logging > 1) { \ 12755258Sandrew if (cnt == (off_t)-1) \ 12855258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \ 12955258Sandrew *(file) == '/' ? "" : curdir(), file); \ 13055258Sandrew else \ 13155258Sandrew syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 13255258Sandrew cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 13355258Sandrew } 13455258Sandrew 13555258Sandrew static void ack __P((char *)); 13655258Sandrew static void myoob __P((int)); 13755258Sandrew static int checkuser __P((char *)); 13855258Sandrew static FILE *dataconn __P((char *, off_t, char *)); 13955258Sandrew static void dolog __P((struct sockaddr_in *)); 14055258Sandrew static char *curdir __P((void)); 14155258Sandrew static void end_login __P((void)); 14255258Sandrew static FILE *getdatasock __P((char *)); 14355258Sandrew static char *gunique __P((char *)); 14455258Sandrew static void lostconn __P((int)); 14555258Sandrew static int receive_data __P((FILE *, FILE *)); 14655258Sandrew static void send_data __P((FILE *, FILE *, off_t)); 14755258Sandrew static struct passwd * 14855258Sandrew sgetpwnam __P((char *)); 14955258Sandrew static char *sgetsave __P((char *)); 15055258Sandrew 15155258Sandrew static char * 15255258Sandrew curdir() 15355258Sandrew { 15455258Sandrew static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 15555258Sandrew 15655258Sandrew if (getcwd(path, sizeof(path)-2) == NULL) 15755258Sandrew return (""); 15855258Sandrew if (path[1] != '\0') /* special case for root dir. */ 15955258Sandrew strcat(path, "/"); 16055258Sandrew /* For guest account, skip / since it's chrooted */ 16155258Sandrew return (guest ? path+1 : path); 16255258Sandrew } 16355258Sandrew 16455258Sandrew int 16536620Srick main(argc, argv, envp) 16610275Ssam int argc; 16710275Ssam char *argv[]; 16836620Srick char **envp; 16910275Ssam { 17044339Skarels int addrlen, on = 1, tos; 17155258Sandrew char *cp, line[MAXLINE]; 17255258Sandrew FILE *fd; 17310275Ssam 17445028Skarels /* 17545028Skarels * LOG_NDELAY sets up the logging connection immediately, 17645028Skarels * necessary for anonymous ftp's that chroot and can't do it later. 17745028Skarels */ 17855258Sandrew openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 17916339Skarels addrlen = sizeof (his_addr); 18036304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 18126493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 18210275Ssam exit(1); 18310275Ssam } 18416339Skarels addrlen = sizeof (ctrl_addr); 18536304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 18626493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 18716339Skarels exit(1); 18816339Skarels } 18944339Skarels #ifdef IP_TOS 19044339Skarels tos = IPTOS_LOWDELAY; 19144339Skarels if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 19244339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 19344339Skarels #endif 19416339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 19510275Ssam debug = 0; 19636620Srick #ifdef SETPROCTITLE 19736620Srick /* 19836620Srick * Save start and extent of argv for setproctitle. 19936620Srick */ 20036620Srick Argv = argv; 20136620Srick while (*envp) 20236620Srick envp++; 20336620Srick LastArgv = envp[-1] + strlen(envp[-1]); 20436620Srick #endif /* SETPROCTITLE */ 20536620Srick 20610275Ssam argc--, argv++; 20710275Ssam while (argc > 0 && *argv[0] == '-') { 20810275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 20910275Ssam 21011653Ssam case 'v': 21111653Ssam debug = 1; 21211653Ssam break; 21311653Ssam 21410275Ssam case 'd': 21510275Ssam debug = 1; 21610275Ssam break; 21710275Ssam 21811757Ssam case 'l': 21955258Sandrew logging++; /* > 1 == extra logging */ 22011757Ssam break; 22111757Ssam 22211653Ssam case 't': 22311653Ssam timeout = atoi(++cp); 22436933Skarels if (maxtimeout < timeout) 22536933Skarels maxtimeout = timeout; 22611653Ssam goto nextopt; 22711653Ssam 22836933Skarels case 'T': 22936933Skarels maxtimeout = atoi(++cp); 23036933Skarels if (timeout > maxtimeout) 23136933Skarels timeout = maxtimeout; 23236933Skarels goto nextopt; 23336933Skarels 23436933Skarels case 'u': 23536933Skarels { 23636933Skarels int val = 0; 23736933Skarels 23836933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 23936933Skarels val = val*8 + *cp - '0'; 24036933Skarels if (*cp) 24136933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 24236933Skarels else 24336933Skarels defumask = val; 24436933Skarels goto nextopt; 24536933Skarels } 24636933Skarels 24710275Ssam default: 24816339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 24916339Skarels *cp); 25010275Ssam break; 25110275Ssam } 25211653Ssam nextopt: 25310275Ssam argc--, argv++; 25410275Ssam } 25537459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 25626493Sminshall (void) signal(SIGPIPE, lostconn); 25726493Sminshall (void) signal(SIGCHLD, SIG_IGN); 25835691Sbostic if ((int)signal(SIGURG, myoob) < 0) 25926493Sminshall syslog(LOG_ERR, "signal: %m"); 26035691Sbostic 26138134Srick /* Try to handle urgent data inline */ 26227750Sminshall #ifdef SO_OOBINLINE 26336276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 26427750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 26536276Sbostic #endif 26638134Srick 26736933Skarels #ifdef F_SETOWN 26836304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 26936304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 27036933Skarels #endif 27116760Slepreau dolog(&his_addr); 27216339Skarels /* 27316339Skarels * Set up default state 27416339Skarels */ 27516339Skarels data = -1; 27616339Skarels type = TYPE_A; 27716339Skarels form = FORM_N; 27816339Skarels stru = STRU_F; 27916339Skarels mode = MODE_S; 28026044Sminshall tmpline[0] = '\0'; 28155258Sandrew 28255258Sandrew /* If logins are disabled, print out the message. */ 28355258Sandrew if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 28455258Sandrew while (fgets(line, sizeof (line), fd) != NULL) { 28560087Sbostic if ((cp = strchr(line, '\n')) != NULL) 28655258Sandrew *cp = '\0'; 28755258Sandrew lreply(530, "%s", line); 28855258Sandrew } 28955258Sandrew (void) fflush(stdout); 29055258Sandrew (void) fclose(fd); 29155258Sandrew reply(530, "System not available."); 29255258Sandrew exit(0); 29355258Sandrew } 29455258Sandrew if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 29555258Sandrew while (fgets(line, sizeof (line), fd) != NULL) { 29660087Sbostic if ((cp = strchr(line, '\n')) != NULL) 29755258Sandrew *cp = '\0'; 29855258Sandrew lreply(220, "%s", line); 29955258Sandrew } 30055258Sandrew (void) fflush(stdout); 30155258Sandrew (void) fclose(fd); 30255258Sandrew /* reply(220,) must follow */ 30355258Sandrew } 30426493Sminshall (void) gethostname(hostname, sizeof (hostname)); 30536276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 30636304Skarels (void) setjmp(errcatch); 30736304Skarels for (;;) 30826493Sminshall (void) yyparse(); 30936620Srick /* NOTREACHED */ 31010275Ssam } 31110419Ssam 31255258Sandrew static void 31355258Sandrew lostconn(signo) 31455258Sandrew int signo; 31510275Ssam { 31614089Ssam if (debug) 31726493Sminshall syslog(LOG_DEBUG, "lost connection"); 31814089Ssam dologout(-1); 31910275Ssam } 32010275Ssam 32135672Sbostic static char ttyline[20]; 32235672Sbostic 32336185Sbostic /* 32436185Sbostic * Helper function for sgetpwnam(). 32536185Sbostic */ 32655258Sandrew static char * 32736185Sbostic sgetsave(s) 32836185Sbostic char *s; 32936185Sbostic { 33036185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 33136933Skarels 33236185Sbostic if (new == NULL) { 33336620Srick perror_reply(421, "Local resource failure: malloc"); 33436185Sbostic dologout(1); 33536620Srick /* NOTREACHED */ 33636185Sbostic } 33736185Sbostic (void) strcpy(new, s); 33836185Sbostic return (new); 33936185Sbostic } 34036185Sbostic 34136185Sbostic /* 34236185Sbostic * Save the result of a getpwnam. Used for USER command, since 34336185Sbostic * the data returned must not be clobbered by any other command 34436185Sbostic * (e.g., globbing). 34536185Sbostic */ 34655258Sandrew static struct passwd * 34736185Sbostic sgetpwnam(name) 34836185Sbostic char *name; 34936185Sbostic { 35036185Sbostic static struct passwd save; 35136185Sbostic register struct passwd *p; 35236185Sbostic char *sgetsave(); 35336185Sbostic 35436185Sbostic if ((p = getpwnam(name)) == NULL) 35536185Sbostic return (p); 35636185Sbostic if (save.pw_name) { 35736185Sbostic free(save.pw_name); 35836185Sbostic free(save.pw_passwd); 35936185Sbostic free(save.pw_gecos); 36036185Sbostic free(save.pw_dir); 36136185Sbostic free(save.pw_shell); 36236185Sbostic } 36336185Sbostic save = *p; 36436185Sbostic save.pw_name = sgetsave(p->pw_name); 36536185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 36636185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 36736185Sbostic save.pw_dir = sgetsave(p->pw_dir); 36836185Sbostic save.pw_shell = sgetsave(p->pw_shell); 36936185Sbostic return (&save); 37036185Sbostic } 37136185Sbostic 37255258Sandrew static int login_attempts; /* number of failed login attempts */ 37355258Sandrew static int askpasswd; /* had user command, ask for passwd */ 37455258Sandrew static char curname[10]; /* current USER name */ 37536304Skarels 37636304Skarels /* 37736304Skarels * USER command. 37842327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 37942327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 38042327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 38142327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 38242327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 38342327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 38442327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 38542327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 38636304Skarels */ 38755258Sandrew void 38836304Skarels user(name) 38936304Skarels char *name; 39036304Skarels { 39136304Skarels register char *cp; 39236304Skarels char *shell; 39336304Skarels 39436304Skarels if (logged_in) { 39536304Skarels if (guest) { 39636304Skarels reply(530, "Can't change user from guest login."); 39736304Skarels return; 39836304Skarels } 39936304Skarels end_login(); 40036304Skarels } 40136304Skarels 40236304Skarels guest = 0; 40336304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 40440183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 40540155Smckusick reply(530, "User %s access denied.", name); 40640155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 40736304Skarels guest = 1; 40836304Skarels askpasswd = 1; 40955258Sandrew reply(331, 41055258Sandrew "Guest login ok, type your name as password."); 41136933Skarels } else 41236304Skarels reply(530, "User %s unknown.", name); 41355258Sandrew if (!askpasswd && logging) 41455258Sandrew syslog(LOG_NOTICE, 41555258Sandrew "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 41636304Skarels return; 41736304Skarels } 41836304Skarels if (pw = sgetpwnam(name)) { 41936304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 42037459Skarels shell = _PATH_BSHELL; 42136304Skarels while ((cp = getusershell()) != NULL) 42236304Skarels if (strcmp(cp, shell) == 0) 42336304Skarels break; 42436304Skarels endusershell(); 42555258Sandrew 42640183Smckusick if (cp == NULL || checkuser(name)) { 42736304Skarels reply(530, "User %s access denied.", name); 42836933Skarels if (logging) 42936933Skarels syslog(LOG_NOTICE, 43036933Skarels "FTP LOGIN REFUSED FROM %s, %s", 43136933Skarels remotehost, name); 43236304Skarels pw = (struct passwd *) NULL; 43336304Skarels return; 43436304Skarels } 43536304Skarels } 43655258Sandrew if (logging) 43755258Sandrew strncpy(curname, name, sizeof(curname)-1); 43836304Skarels reply(331, "Password required for %s.", name); 43936304Skarels askpasswd = 1; 44036304Skarels /* 44136304Skarels * Delay before reading passwd after first failed 44236304Skarels * attempt to slow down passwd-guessing programs. 44336304Skarels */ 44436304Skarels if (login_attempts) 44536304Skarels sleep((unsigned) login_attempts); 44636304Skarels } 44736304Skarels 44836304Skarels /* 44940183Smckusick * Check if a user is in the file _PATH_FTPUSERS 45040183Smckusick */ 45155258Sandrew static int 45240183Smckusick checkuser(name) 45340183Smckusick char *name; 45440183Smckusick { 45542327Sbostic register FILE *fd; 45642327Sbostic register char *p; 45742327Sbostic char line[BUFSIZ]; 45855258Sandrew int found = 0; 45940183Smckusick 46040183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 46142327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 46260087Sbostic if ((p = strchr(line, '\n')) != NULL) { 46342327Sbostic *p = '\0'; 46442327Sbostic if (line[0] == '#') 46542327Sbostic continue; 46655258Sandrew if (strcmp(p, name) == 0) { 46755258Sandrew found = 1; 46855258Sandrew break; 46955258Sandrew } 47042327Sbostic } 47140183Smckusick (void) fclose(fd); 47240183Smckusick } 47355258Sandrew return (found); 47440183Smckusick } 47540183Smckusick 47640183Smckusick /* 47736304Skarels * Terminate login as previous user, if any, resetting state; 47836304Skarels * used when USER command is given or login fails. 47936304Skarels */ 48055258Sandrew static void 48136304Skarels end_login() 48236304Skarels { 48336304Skarels 48436304Skarels (void) seteuid((uid_t)0); 48536304Skarels if (logged_in) 48636304Skarels logwtmp(ttyline, "", ""); 48736304Skarels pw = NULL; 48836304Skarels logged_in = 0; 48936304Skarels guest = 0; 49036304Skarels } 49136304Skarels 49255258Sandrew void 49310275Ssam pass(passwd) 49410275Ssam char *passwd; 49510275Ssam { 49636304Skarels char *xpasswd, *salt; 49755258Sandrew FILE *fd; 49810275Ssam 49936304Skarels if (logged_in || askpasswd == 0) { 50010275Ssam reply(503, "Login with USER first."); 50110275Ssam return; 50210275Ssam } 50336304Skarels askpasswd = 0; 50410275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 50536304Skarels if (pw == NULL) 50636304Skarels salt = "xx"; 50736304Skarels else 50836304Skarels salt = pw->pw_passwd; 50936304Skarels xpasswd = crypt(passwd, salt); 51016760Slepreau /* The strcmp does not catch null passwords! */ 51136304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 51236304Skarels strcmp(xpasswd, pw->pw_passwd)) { 51310275Ssam reply(530, "Login incorrect."); 51455258Sandrew if (logging) 51555258Sandrew syslog(LOG_NOTICE, 51655258Sandrew "FTP LOGIN FAILED FROM %s, %s", 51755258Sandrew remotehost, curname); 51810275Ssam pw = NULL; 51936304Skarels if (login_attempts++ >= 5) { 52036933Skarels syslog(LOG_NOTICE, 52136304Skarels "repeated login failures from %s", 52236304Skarels remotehost); 52336304Skarels exit(0); 52436304Skarels } 52510275Ssam return; 52610275Ssam } 52710275Ssam } 52836304Skarels login_attempts = 0; /* this time successful */ 52955258Sandrew if (setegid((gid_t)pw->pw_gid) < 0) { 53055258Sandrew reply(550, "Can't set gid."); 53155258Sandrew return; 53255258Sandrew } 53336304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 53416033Sralph 53536192Sbostic /* open wtmp before chroot */ 53636192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 53736192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 53836192Sbostic logged_in = 1; 53936192Sbostic 54036446Sbostic if (guest) { 54136620Srick /* 54236933Skarels * We MUST do a chdir() after the chroot. Otherwise 54336933Skarels * the old current directory will be accessible as "." 54436933Skarels * outside the new root! 54536620Srick */ 54636620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 54736446Sbostic reply(550, "Can't set guest privileges."); 54836446Sbostic goto bad; 54936446Sbostic } 55036933Skarels } else if (chdir(pw->pw_dir) < 0) { 55136620Srick if (chdir("/") < 0) { 55236446Sbostic reply(530, "User %s: can't change directory to %s.", 55336446Sbostic pw->pw_name, pw->pw_dir); 55436446Sbostic goto bad; 55536933Skarels } else 55636446Sbostic lreply(230, "No directory! Logging in with home=/"); 55736620Srick } 55836304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 55936304Skarels reply(550, "Can't set uid."); 56036304Skarels goto bad; 56136304Skarels } 56255258Sandrew /* 56355258Sandrew * Display a login message, if it exists. 56455258Sandrew * N.B. reply(230,) must follow the message. 56555258Sandrew */ 56655258Sandrew if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 56755258Sandrew char *cp, line[MAXLINE]; 56855258Sandrew 56955258Sandrew while (fgets(line, sizeof (line), fd) != NULL) { 57060087Sbostic if ((cp = strchr(line, '\n')) != NULL) 57155258Sandrew *cp = '\0'; 57255258Sandrew lreply(230, "%s", line); 57355258Sandrew } 57455258Sandrew (void) fflush(stdout); 57555258Sandrew (void) fclose(fd); 57655258Sandrew } 57736550Sbostic if (guest) { 57836192Sbostic reply(230, "Guest login ok, access restrictions apply."); 57936933Skarels #ifdef SETPROCTITLE 58036933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 58136933Skarels sizeof(proctitle) - sizeof(remotehost) - 58236933Skarels sizeof(": anonymous/"), passwd); 58336933Skarels setproctitle(proctitle); 58436933Skarels #endif /* SETPROCTITLE */ 58536933Skarels if (logging) 58636933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 58736933Skarels remotehost, passwd); 58852998Sbostic home = "/"; /* guest home dir for globbing */ 58936933Skarels } else { 59010275Ssam reply(230, "User %s logged in.", pw->pw_name); 59136933Skarels #ifdef SETPROCTITLE 59236933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 59336933Skarels setproctitle(proctitle); 59436933Skarels #endif /* SETPROCTITLE */ 59536933Skarels if (logging) 59655258Sandrew syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 59736933Skarels remotehost, pw->pw_name); 59852998Sbostic home = pw->pw_dir; /* home dir for globbing */ 59936550Sbostic } 60036933Skarels (void) umask(defumask); 60110303Ssam return; 60210303Ssam bad: 60336304Skarels /* Forget all about it... */ 60436304Skarels end_login(); 60510275Ssam } 60610275Ssam 60755258Sandrew void 60810275Ssam retrieve(cmd, name) 60910275Ssam char *cmd, *name; 61010275Ssam { 61110275Ssam FILE *fin, *dout; 61210275Ssam struct stat st; 61336620Srick int (*closefunc)(); 61410275Ssam 61536557Sbostic if (cmd == 0) { 61636446Sbostic fin = fopen(name, "r"), closefunc = fclose; 61736557Sbostic st.st_size = 0; 61836557Sbostic } else { 61910275Ssam char line[BUFSIZ]; 62010275Ssam 62126493Sminshall (void) sprintf(line, cmd, name), name = line; 62236304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 62336557Sbostic st.st_size = -1; 62436933Skarels st.st_blksize = BUFSIZ; 62510275Ssam } 62610275Ssam if (fin == NULL) { 62755258Sandrew if (errno != 0) { 62836304Skarels perror_reply(550, name); 62955258Sandrew if (cmd == 0) { 63055258Sandrew LOGCMD("get", name); 63155258Sandrew } 63255258Sandrew } 63310275Ssam return; 63410275Ssam } 63555258Sandrew byte_count = -1; 63610275Ssam if (cmd == 0 && 63736933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 63810275Ssam reply(550, "%s: not a plain file.", name); 63910275Ssam goto done; 64010275Ssam } 64137459Skarels if (restart_point) { 64237459Skarels if (type == TYPE_A) { 64337459Skarels register int i, n, c; 64437459Skarels 64537459Skarels n = restart_point; 64637459Skarels i = 0; 64737459Skarels while (i++ < n) { 64837459Skarels if ((c=getc(fin)) == EOF) { 64937459Skarels perror_reply(550, name); 65037459Skarels goto done; 65137459Skarels } 65237459Skarels if (c == '\n') 65337459Skarels i++; 65452998Sbostic } 65537459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 65637459Skarels perror_reply(550, name); 65737459Skarels goto done; 65837459Skarels } 65937459Skarels } 66010275Ssam dout = dataconn(name, st.st_size, "w"); 66110275Ssam if (dout == NULL) 66210275Ssam goto done; 66336620Srick send_data(fin, dout, st.st_blksize); 66426493Sminshall (void) fclose(dout); 66526044Sminshall data = -1; 66626044Sminshall pdata = -1; 66710275Ssam done: 66855258Sandrew if (cmd == 0) 66955258Sandrew LOGBYTES("get", name, byte_count); 67010275Ssam (*closefunc)(fin); 67110275Ssam } 67210275Ssam 67355258Sandrew void 67436304Skarels store(name, mode, unique) 67510275Ssam char *name, *mode; 67636304Skarels int unique; 67710275Ssam { 67810275Ssam FILE *fout, *din; 67936446Sbostic struct stat st; 68036620Srick int (*closefunc)(); 68110275Ssam 68236446Sbostic if (unique && stat(name, &st) == 0 && 68355258Sandrew (name = gunique(name)) == NULL) { 68455258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name); 68536446Sbostic return; 68655258Sandrew } 68710303Ssam 68837459Skarels if (restart_point) 68937459Skarels mode = "r+w"; 69036620Srick fout = fopen(name, mode); 69136620Srick closefunc = fclose; 69210275Ssam if (fout == NULL) { 69336304Skarels perror_reply(553, name); 69455258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name); 69510275Ssam return; 69610275Ssam } 69755258Sandrew byte_count = -1; 69837459Skarels if (restart_point) { 69937459Skarels if (type == TYPE_A) { 70037459Skarels register int i, n, c; 70137459Skarels 70237459Skarels n = restart_point; 70337459Skarels i = 0; 70437459Skarels while (i++ < n) { 70537459Skarels if ((c=getc(fout)) == EOF) { 70637459Skarels perror_reply(550, name); 70737459Skarels goto done; 70837459Skarels } 70937459Skarels if (c == '\n') 71037459Skarels i++; 71152998Sbostic } 71237459Skarels /* 71337459Skarels * We must do this seek to "current" position 71437459Skarels * because we are changing from reading to 71537459Skarels * writing. 71637459Skarels */ 71737459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 71837459Skarels perror_reply(550, name); 71937459Skarels goto done; 72037459Skarels } 72137459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 72237459Skarels perror_reply(550, name); 72337459Skarels goto done; 72437459Skarels } 72537459Skarels } 72636304Skarels din = dataconn(name, (off_t)-1, "r"); 72710275Ssam if (din == NULL) 72810275Ssam goto done; 72936620Srick if (receive_data(din, fout) == 0) { 73036620Srick if (unique) 73136304Skarels reply(226, "Transfer complete (unique file name:%s).", 73236933Skarels name); 73336304Skarels else 73436304Skarels reply(226, "Transfer complete."); 73526044Sminshall } 73626493Sminshall (void) fclose(din); 73726044Sminshall data = -1; 73826044Sminshall pdata = -1; 73910275Ssam done: 74055258Sandrew LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 74110275Ssam (*closefunc)(fout); 74210275Ssam } 74310275Ssam 74455258Sandrew static FILE * 74510275Ssam getdatasock(mode) 74610275Ssam char *mode; 74710275Ssam { 74837459Skarels int s, on = 1, tries; 74955258Sandrew int t; 75010275Ssam 75110275Ssam if (data >= 0) 75210275Ssam return (fdopen(data, mode)); 75350391Skarels (void) seteuid((uid_t)0); 75413247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 75510602Ssam if (s < 0) 75650391Skarels goto bad; 75737459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 75837459Skarels (char *) &on, sizeof (on)) < 0) 75910602Ssam goto bad; 76013152Ssam /* anchor socket to avoid multi-homing problems */ 76113152Ssam data_source.sin_family = AF_INET; 76213152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 76337459Skarels for (tries = 1; ; tries++) { 76437459Skarels if (bind(s, (struct sockaddr *)&data_source, 76537459Skarels sizeof (data_source)) >= 0) 76637459Skarels break; 76737459Skarels if (errno != EADDRINUSE || tries > 10) 76837459Skarels goto bad; 76937459Skarels sleep(tries); 77037459Skarels } 77136304Skarels (void) seteuid((uid_t)pw->pw_uid); 77244339Skarels #ifdef IP_TOS 77344339Skarels on = IPTOS_THROUGHPUT; 77444339Skarels if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 77544339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 77644339Skarels #endif 77710275Ssam return (fdopen(s, mode)); 77810602Ssam bad: 77955258Sandrew /* Return the real value of errno (close may change it) */ 78055258Sandrew t = errno; 78136304Skarels (void) seteuid((uid_t)pw->pw_uid); 78226493Sminshall (void) close(s); 78355258Sandrew errno = t; 78410602Ssam return (NULL); 78510275Ssam } 78610275Ssam 78755258Sandrew static FILE * 78810275Ssam dataconn(name, size, mode) 78910275Ssam char *name; 79011653Ssam off_t size; 79110275Ssam char *mode; 79210275Ssam { 79310275Ssam char sizebuf[32]; 79410275Ssam FILE *file; 79544339Skarels int retry = 0, tos; 79610275Ssam 79736933Skarels file_size = size; 79836933Skarels byte_count = 0; 79936304Skarels if (size != (off_t) -1) 80055258Sandrew (void) sprintf (sizebuf, " (%qd bytes)", size); 80110275Ssam else 80210275Ssam (void) strcpy(sizebuf, ""); 80336304Skarels if (pdata >= 0) { 80426044Sminshall struct sockaddr_in from; 80526044Sminshall int s, fromlen = sizeof(from); 80626044Sminshall 80736304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 80826044Sminshall if (s < 0) { 80926044Sminshall reply(425, "Can't open data connection."); 81026044Sminshall (void) close(pdata); 81126044Sminshall pdata = -1; 81226044Sminshall return(NULL); 81326044Sminshall } 81426044Sminshall (void) close(pdata); 81526044Sminshall pdata = s; 81644339Skarels #ifdef IP_TOS 81744339Skarels tos = IPTOS_LOWDELAY; 81844339Skarels (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 81944339Skarels sizeof(int)); 82044339Skarels #endif 82155258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.", 82236235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 82326044Sminshall return(fdopen(pdata, mode)); 82426044Sminshall } 82510275Ssam if (data >= 0) { 82655258Sandrew reply(125, "Using existing data connection for '%s'%s.", 82710275Ssam name, sizebuf); 82810321Ssam usedefault = 1; 82910275Ssam return (fdopen(data, mode)); 83010275Ssam } 83110566Ssam if (usedefault) 83210422Ssam data_dest = his_addr; 83310422Ssam usedefault = 1; 83410275Ssam file = getdatasock(mode); 83510275Ssam if (file == NULL) { 83610275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 83713247Ssam inet_ntoa(data_source.sin_addr), 83842412Sbostic ntohs(data_source.sin_port), strerror(errno)); 83910275Ssam return (NULL); 84010275Ssam } 84110275Ssam data = fileno(file); 84236304Skarels while (connect(data, (struct sockaddr *)&data_dest, 84336304Skarels sizeof (data_dest)) < 0) { 84411653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 84526493Sminshall sleep((unsigned) swaitint); 84611653Ssam retry += swaitint; 84711653Ssam continue; 84811653Ssam } 84936304Skarels perror_reply(425, "Can't build data connection"); 85010275Ssam (void) fclose(file); 85110275Ssam data = -1; 85210275Ssam return (NULL); 85310275Ssam } 85455258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.", 85536235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 85610275Ssam return (file); 85710275Ssam } 85810275Ssam 85910275Ssam /* 86055258Sandrew * Tranfer the contents of "instr" to "outstr" peer using the appropriate 86155258Sandrew * encapsulation of the data subject * to Mode, Structure, and Type. 86210275Ssam * 86310275Ssam * NB: Form isn't handled. 86410275Ssam */ 86555258Sandrew static void 86636446Sbostic send_data(instr, outstr, blksize) 86710275Ssam FILE *instr, *outstr; 86836446Sbostic off_t blksize; 86910275Ssam { 87036446Sbostic register int c, cnt; 87136446Sbostic register char *buf; 87236446Sbostic int netfd, filefd; 87310275Ssam 87426044Sminshall transflag++; 87526044Sminshall if (setjmp(urgcatch)) { 87626044Sminshall transflag = 0; 87736620Srick return; 87826044Sminshall } 87910275Ssam switch (type) { 88010275Ssam 88110275Ssam case TYPE_A: 88210275Ssam while ((c = getc(instr)) != EOF) { 88336933Skarels byte_count++; 88411220Ssam if (c == '\n') { 88536933Skarels if (ferror(outstr)) 88636620Srick goto data_err; 88727750Sminshall (void) putc('\r', outstr); 88811220Ssam } 88927750Sminshall (void) putc(c, outstr); 89010275Ssam } 89136933Skarels fflush(outstr); 89226044Sminshall transflag = 0; 89336933Skarels if (ferror(instr)) 89436933Skarels goto file_err; 89536933Skarels if (ferror(outstr)) 89636620Srick goto data_err; 89736620Srick reply(226, "Transfer complete."); 89836620Srick return; 89936446Sbostic 90010275Ssam case TYPE_I: 90110275Ssam case TYPE_L: 90236446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 90336446Sbostic transflag = 0; 90436933Skarels perror_reply(451, "Local resource failure: malloc"); 90536933Skarels return; 90636446Sbostic } 90710275Ssam netfd = fileno(outstr); 90810275Ssam filefd = fileno(instr); 90936933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 91036620Srick write(netfd, buf, cnt) == cnt) 91136933Skarels byte_count += cnt; 91226044Sminshall transflag = 0; 91336446Sbostic (void)free(buf); 91436933Skarels if (cnt != 0) { 91536933Skarels if (cnt < 0) 91636933Skarels goto file_err; 91736620Srick goto data_err; 91836933Skarels } 91936620Srick reply(226, "Transfer complete."); 92036620Srick return; 92136620Srick default: 92236620Srick transflag = 0; 92336620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 92436620Srick return; 92510275Ssam } 92636620Srick 92736620Srick data_err: 92826044Sminshall transflag = 0; 92936933Skarels perror_reply(426, "Data connection"); 93036933Skarels return; 93136933Skarels 93236933Skarels file_err: 93336933Skarels transflag = 0; 93436933Skarels perror_reply(551, "Error on input file"); 93510275Ssam } 93610275Ssam 93710275Ssam /* 93855258Sandrew * Transfer data from peer to "outstr" using the appropriate encapulation of 93955258Sandrew * the data subject to Mode, Structure, and Type. 94010275Ssam * 94110275Ssam * N.B.: Form isn't handled. 94210275Ssam */ 94355258Sandrew static int 94410275Ssam receive_data(instr, outstr) 94510275Ssam FILE *instr, *outstr; 94610275Ssam { 94710275Ssam register int c; 94838134Srick int cnt, bare_lfs = 0; 94910275Ssam char buf[BUFSIZ]; 95010275Ssam 95126044Sminshall transflag++; 95226044Sminshall if (setjmp(urgcatch)) { 95326044Sminshall transflag = 0; 95436933Skarels return (-1); 95526044Sminshall } 95610275Ssam switch (type) { 95710275Ssam 95810275Ssam case TYPE_I: 95910275Ssam case TYPE_L: 96026044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 96136620Srick if (write(fileno(outstr), buf, cnt) != cnt) 96236933Skarels goto file_err; 96336933Skarels byte_count += cnt; 96426044Sminshall } 96536933Skarels if (cnt < 0) 96636620Srick goto data_err; 96726044Sminshall transflag = 0; 96836933Skarels return (0); 96910275Ssam 97010275Ssam case TYPE_E: 97127106Smckusick reply(553, "TYPE E not implemented."); 97226044Sminshall transflag = 0; 97327106Smckusick return (-1); 97410275Ssam 97510275Ssam case TYPE_A: 97610275Ssam while ((c = getc(instr)) != EOF) { 97736933Skarels byte_count++; 97838134Srick if (c == '\n') 97938134Srick bare_lfs++; 98027750Sminshall while (c == '\r') { 98136933Skarels if (ferror(outstr)) 98236620Srick goto data_err; 98336933Skarels if ((c = getc(instr)) != '\n') { 98427750Sminshall (void) putc ('\r', outstr); 98536933Skarels if (c == '\0' || c == EOF) 98636933Skarels goto contin2; 98736933Skarels } 98810275Ssam } 98936933Skarels (void) putc(c, outstr); 99036933Skarels contin2: ; 99110275Ssam } 99236620Srick fflush(outstr); 99336933Skarels if (ferror(instr)) 99436620Srick goto data_err; 99536933Skarels if (ferror(outstr)) 99636933Skarels goto file_err; 99726044Sminshall transflag = 0; 99838134Srick if (bare_lfs) { 99938134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 100038134Srick printf(" File may not have transferred correctly.\r\n"); 100138134Srick } 100210275Ssam return (0); 100336620Srick default: 100436620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 100536620Srick transflag = 0; 100636933Skarels return (-1); 100710275Ssam } 100836620Srick 100936620Srick data_err: 101026044Sminshall transflag = 0; 101136933Skarels perror_reply(426, "Data Connection"); 101236933Skarels return (-1); 101336933Skarels 101436933Skarels file_err: 101536933Skarels transflag = 0; 101636933Skarels perror_reply(452, "Error writing file"); 101736933Skarels return (-1); 101810275Ssam } 101910275Ssam 102055258Sandrew void 102136933Skarels statfilecmd(filename) 102236933Skarels char *filename; 102336933Skarels { 102436933Skarels char line[BUFSIZ]; 102536933Skarels FILE *fin; 102636933Skarels int c; 102736933Skarels 102855258Sandrew (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); 102936933Skarels fin = ftpd_popen(line, "r"); 103036933Skarels lreply(211, "status of %s:", filename); 103136933Skarels while ((c = getc(fin)) != EOF) { 103236933Skarels if (c == '\n') { 103336933Skarels if (ferror(stdout)){ 103436933Skarels perror_reply(421, "control connection"); 103536933Skarels (void) ftpd_pclose(fin); 103636933Skarels dologout(1); 103736933Skarels /* NOTREACHED */ 103836933Skarels } 103936933Skarels if (ferror(fin)) { 104036933Skarels perror_reply(551, filename); 104136933Skarels (void) ftpd_pclose(fin); 104236933Skarels return; 104336933Skarels } 104436933Skarels (void) putc('\r', stdout); 104536933Skarels } 104636933Skarels (void) putc(c, stdout); 104736933Skarels } 104836933Skarels (void) ftpd_pclose(fin); 104936933Skarels reply(211, "End of Status"); 105036933Skarels } 105136933Skarels 105255258Sandrew void 105336933Skarels statcmd() 105436933Skarels { 105536933Skarels struct sockaddr_in *sin; 105636933Skarels u_char *a, *p; 105736933Skarels 105836933Skarels lreply(211, "%s FTP server status:", hostname, version); 105936933Skarels printf(" %s\r\n", version); 106036933Skarels printf(" Connected to %s", remotehost); 106138134Srick if (!isdigit(remotehost[0])) 106236933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 106336933Skarels printf("\r\n"); 106436933Skarels if (logged_in) { 106536933Skarels if (guest) 106636933Skarels printf(" Logged in anonymously\r\n"); 106736933Skarels else 106836933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 106936933Skarels } else if (askpasswd) 107036933Skarels printf(" Waiting for password\r\n"); 107136933Skarels else 107236933Skarels printf(" Waiting for user name\r\n"); 107336933Skarels printf(" TYPE: %s", typenames[type]); 107436933Skarels if (type == TYPE_A || type == TYPE_E) 107536933Skarels printf(", FORM: %s", formnames[form]); 107636933Skarels if (type == TYPE_L) 107736933Skarels #if NBBY == 8 107836933Skarels printf(" %d", NBBY); 107936933Skarels #else 108036933Skarels printf(" %d", bytesize); /* need definition! */ 108136933Skarels #endif 108236933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 108336933Skarels strunames[stru], modenames[mode]); 108436933Skarels if (data != -1) 108536933Skarels printf(" Data connection open\r\n"); 108636933Skarels else if (pdata != -1) { 108736933Skarels printf(" in Passive mode"); 108836933Skarels sin = &pasv_addr; 108936933Skarels goto printaddr; 109036933Skarels } else if (usedefault == 0) { 109136933Skarels printf(" PORT"); 109236933Skarels sin = &data_dest; 109336933Skarels printaddr: 109436933Skarels a = (u_char *) &sin->sin_addr; 109536933Skarels p = (u_char *) &sin->sin_port; 109636933Skarels #define UC(b) (((int) b) & 0xff) 109736933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 109836933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 109936933Skarels #undef UC 110036933Skarels } else 110136933Skarels printf(" No data connection\r\n"); 110236933Skarels reply(211, "End of status"); 110336933Skarels } 110436933Skarels 110555258Sandrew void 110610275Ssam fatal(s) 110710275Ssam char *s; 110810275Ssam { 110910275Ssam reply(451, "Error in server: %s\n", s); 111010275Ssam reply(221, "Closing connection due to server error."); 111113247Ssam dologout(0); 111236620Srick /* NOTREACHED */ 111310275Ssam } 111410275Ssam 111555258Sandrew void 111655258Sandrew #if __STDC__ 111755258Sandrew reply(int n, const char *fmt, ...) 111855258Sandrew #else 111955258Sandrew reply(n, fmt, va_alist) 112010275Ssam int n; 112136446Sbostic char *fmt; 112255258Sandrew va_dcl 112355258Sandrew #endif 112410275Ssam { 112555258Sandrew va_list ap; 112655258Sandrew #if __STDC__ 112755258Sandrew va_start(ap, fmt); 112855258Sandrew #else 112955258Sandrew va_start(ap); 113055258Sandrew #endif 113155258Sandrew (void)printf("%d ", n); 113255258Sandrew (void)vprintf(fmt, ap); 113355258Sandrew (void)printf("\r\n"); 113436435Sbostic (void)fflush(stdout); 113510275Ssam if (debug) { 113626493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 113755258Sandrew vsyslog(LOG_DEBUG, fmt, ap); 113855258Sandrew } 113910275Ssam } 114010275Ssam 114155258Sandrew void 114255258Sandrew #if __STDC__ 114355258Sandrew lreply(int n, const char *fmt, ...) 114455258Sandrew #else 114555258Sandrew lreply(n, fmt, va_alist) 114610275Ssam int n; 114736446Sbostic char *fmt; 114855258Sandrew va_dcl 114955258Sandrew #endif 115010275Ssam { 115155258Sandrew va_list ap; 115255258Sandrew #if __STDC__ 115355258Sandrew va_start(ap, fmt); 115455258Sandrew #else 115555258Sandrew va_start(ap); 115655258Sandrew #endif 115755258Sandrew (void)printf("%d- ", n); 115855258Sandrew (void)vprintf(fmt, ap); 115955258Sandrew (void)printf("\r\n"); 116036435Sbostic (void)fflush(stdout); 116136446Sbostic if (debug) { 116236446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 116355258Sandrew vsyslog(LOG_DEBUG, fmt, ap); 116436446Sbostic } 116510275Ssam } 116610275Ssam 116755258Sandrew static void 116810275Ssam ack(s) 116910275Ssam char *s; 117010275Ssam { 117127106Smckusick reply(250, "%s command successful.", s); 117210275Ssam } 117310275Ssam 117455258Sandrew void 117510275Ssam nack(s) 117610275Ssam char *s; 117710275Ssam { 117810275Ssam reply(502, "%s command not implemented.", s); 117910275Ssam } 118010275Ssam 118136304Skarels /* ARGSUSED */ 118255258Sandrew char * 118326493Sminshall yyerror(s) 118426493Sminshall char *s; 118510275Ssam { 118626044Sminshall char *cp; 118726044Sminshall 118860087Sbostic if (cp = strchr(cbuf,'\n')) 118936551Sbostic *cp = '\0'; 119036933Skarels reply(500, "'%s': command not understood.", cbuf); 119110275Ssam } 119210275Ssam 119355258Sandrew void 119410275Ssam delete(name) 119510275Ssam char *name; 119610275Ssam { 119710275Ssam struct stat st; 119810275Ssam 119955258Sandrew LOGCMD("delete", name); 120010275Ssam if (stat(name, &st) < 0) { 120136304Skarels perror_reply(550, name); 120210275Ssam return; 120310275Ssam } 120410275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 120510275Ssam if (rmdir(name) < 0) { 120636304Skarels perror_reply(550, name); 120710275Ssam return; 120810275Ssam } 120910275Ssam goto done; 121010275Ssam } 121110275Ssam if (unlink(name) < 0) { 121236304Skarels perror_reply(550, name); 121310275Ssam return; 121410275Ssam } 121510275Ssam done: 121610275Ssam ack("DELE"); 121710275Ssam } 121810275Ssam 121955258Sandrew void 122010275Ssam cwd(path) 122110275Ssam char *path; 122210275Ssam { 122336620Srick if (chdir(path) < 0) 122436304Skarels perror_reply(550, path); 122536620Srick else 122636620Srick ack("CWD"); 122710275Ssam } 122810275Ssam 122955258Sandrew void 123010303Ssam makedir(name) 123110275Ssam char *name; 123210275Ssam { 123355258Sandrew LOGCMD("mkdir", name); 123436276Sbostic if (mkdir(name, 0777) < 0) 123536304Skarels perror_reply(550, name); 123636276Sbostic else 123736276Sbostic reply(257, "MKD command successful."); 123810275Ssam } 123910275Ssam 124055258Sandrew void 124110303Ssam removedir(name) 124210275Ssam char *name; 124310275Ssam { 124455258Sandrew LOGCMD("rmdir", name); 124536620Srick if (rmdir(name) < 0) 124636304Skarels perror_reply(550, name); 124736620Srick else 124836620Srick ack("RMD"); 124910275Ssam } 125010275Ssam 125155258Sandrew void 125210303Ssam pwd() 125310275Ssam { 125410303Ssam char path[MAXPATHLEN + 1]; 125510275Ssam 125636620Srick if (getwd(path) == (char *)NULL) 125727106Smckusick reply(550, "%s.", path); 125836620Srick else 125936620Srick reply(257, "\"%s\" is current directory.", path); 126010275Ssam } 126110275Ssam 126210275Ssam char * 126310275Ssam renamefrom(name) 126410275Ssam char *name; 126510275Ssam { 126610275Ssam struct stat st; 126710275Ssam 126810275Ssam if (stat(name, &st) < 0) { 126936304Skarels perror_reply(550, name); 127010275Ssam return ((char *)0); 127110275Ssam } 127210303Ssam reply(350, "File exists, ready for destination name"); 127310275Ssam return (name); 127410275Ssam } 127510275Ssam 127655258Sandrew void 127710275Ssam renamecmd(from, to) 127810275Ssam char *from, *to; 127910275Ssam { 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, 129210275Ssam sizeof (struct in_addr), AF_INET); 129310275Ssam 129436304Skarels if (hp) 129526493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 129636304Skarels else 129726493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 129813247Ssam sizeof (remotehost)); 129936620Srick #ifdef SETPROCTITLE 130036933Skarels sprintf(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 { 131617580Ssam if (logged_in) { 131736304Skarels (void) seteuid((uid_t)0); 131835672Sbostic logwtmp(ttyline, "", ""); 131913247Ssam } 132014436Ssam /* beware of flushing buffers after a SIGPIPE */ 132114436Ssam _exit(status); 132213247Ssam } 132313247Ssam 132455258Sandrew static void 132555258Sandrew myoob(signo) 132655258Sandrew int signo; 132726044Sminshall { 132827750Sminshall char *cp; 132926044Sminshall 133027750Sminshall /* only process if transfer occurring */ 133136304Skarels if (!transflag) 133226044Sminshall return; 133327750Sminshall cp = tmpline; 133427750Sminshall if (getline(cp, 7, stdin) == NULL) { 133536304Skarels reply(221, "You could at least say goodbye."); 133627750Sminshall dologout(0); 133726044Sminshall } 133826044Sminshall upper(cp); 133936933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 134036933Skarels tmpline[0] = '\0'; 134136933Skarels reply(426, "Transfer aborted. Data connection closed."); 134236933Skarels reply(226, "Abort successful"); 134336933Skarels longjmp(urgcatch, 1); 134436933Skarels } 134536933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 134636933Skarels if (file_size != (off_t) -1) 134755258Sandrew reply(213, "Status: %qd of %qd bytes transferred", 134836933Skarels byte_count, file_size); 134936933Skarels else 135055258Sandrew reply(213, "Status: %qd bytes transferred", byte_count); 135136933Skarels } 135226044Sminshall } 135326044Sminshall 135427106Smckusick /* 135536620Srick * Note: a response of 425 is not mentioned as a possible response to 135652998Sbostic * the PASV command in RFC959. However, it has been blessed as 135752998Sbostic * a legitimate response by Jon Postel in a telephone conversation 135836620Srick * with Rick Adams on 25 Jan 89. 135927106Smckusick */ 136055258Sandrew void 136126044Sminshall passive() 136226044Sminshall { 136326044Sminshall int len; 136426044Sminshall register char *p, *a; 136526044Sminshall 136626044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 136726044Sminshall if (pdata < 0) { 136836620Srick perror_reply(425, "Can't open passive connection"); 136926044Sminshall return; 137026044Sminshall } 137136933Skarels pasv_addr = ctrl_addr; 137236933Skarels pasv_addr.sin_port = 0; 137336304Skarels (void) seteuid((uid_t)0); 137436933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 137536304Skarels (void) seteuid((uid_t)pw->pw_uid); 137636620Srick goto pasv_error; 137726044Sminshall } 137836304Skarels (void) seteuid((uid_t)pw->pw_uid); 137936933Skarels len = sizeof(pasv_addr); 138036933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 138136620Srick goto pasv_error; 138236620Srick if (listen(pdata, 1) < 0) 138336620Srick goto pasv_error; 138436933Skarels a = (char *) &pasv_addr.sin_addr; 138536933Skarels p = (char *) &pasv_addr.sin_port; 138626044Sminshall 138726044Sminshall #define UC(b) (((int) b) & 0xff) 138826044Sminshall 138926044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 139026044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 139136620Srick return; 139236620Srick 139336620Srick pasv_error: 139436620Srick (void) close(pdata); 139536620Srick pdata = -1; 139636620Srick perror_reply(425, "Can't open passive connection"); 139736620Srick return; 139826044Sminshall } 139926044Sminshall 140036304Skarels /* 140136304Skarels * Generate unique name for file with basename "local". 140236304Skarels * The file named "local" is already known to exist. 140336304Skarels * Generates failure reply on error. 140436304Skarels */ 140555258Sandrew static char * 140626044Sminshall gunique(local) 140726044Sminshall char *local; 140826044Sminshall { 140926044Sminshall static char new[MAXPATHLEN]; 141036304Skarels struct stat st; 141155258Sandrew int count; 141255258Sandrew char *cp; 141326044Sminshall 141460087Sbostic cp = strrchr(local, '/'); 141536304Skarels if (cp) 141626044Sminshall *cp = '\0'; 141736620Srick if (stat(cp ? local : ".", &st) < 0) { 141836933Skarels perror_reply(553, cp ? local : "."); 141926044Sminshall return((char *) 0); 142026044Sminshall } 142136620Srick if (cp) 142236620Srick *cp = '/'; 142326044Sminshall (void) strcpy(new, local); 142426044Sminshall cp = new + strlen(new); 142526044Sminshall *cp++ = '.'; 142636304Skarels for (count = 1; count < 100; count++) { 142755258Sandrew (void)sprintf(cp, "%d", count); 142836304Skarels if (stat(new, &st) < 0) 142936304Skarels return(new); 143026044Sminshall } 143136304Skarels reply(452, "Unique file name cannot be created."); 143255258Sandrew return(NULL); 143326044Sminshall } 143436304Skarels 143536304Skarels /* 143636304Skarels * Format and send reply containing system error number. 143736304Skarels */ 143855258Sandrew void 143936304Skarels perror_reply(code, string) 144036304Skarels int code; 144136304Skarels char *string; 144236304Skarels { 144342412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 144436304Skarels } 144536620Srick 144636620Srick static char *onefile[] = { 144736620Srick "", 144836620Srick 0 144936620Srick }; 145036620Srick 145155258Sandrew void 145236620Srick send_file_list(whichfiles) 145336620Srick char *whichfiles; 145436620Srick { 145536620Srick struct stat st; 145636620Srick DIR *dirp = NULL; 145746669Sbostic struct dirent *dir; 145836620Srick FILE *dout = NULL; 145936620Srick register char **dirlist, *dirname; 146037459Skarels int simple = 0; 146136620Srick 146236620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 146355258Sandrew extern char *globerr; 146436933Skarels 146536620Srick globerr = NULL; 146646669Sbostic dirlist = ftpglob(whichfiles); 146736620Srick if (globerr != NULL) { 146836620Srick reply(550, globerr); 146936620Srick return; 147036620Srick } else if (dirlist == NULL) { 147136620Srick errno = ENOENT; 147236620Srick perror_reply(550, whichfiles); 147336620Srick return; 147436620Srick } 147536620Srick } else { 147636620Srick onefile[0] = whichfiles; 147736620Srick dirlist = onefile; 147837459Skarels simple = 1; 147936620Srick } 148036933Skarels 148136933Skarels if (setjmp(urgcatch)) { 148236933Skarels transflag = 0; 148336933Skarels return; 148436933Skarels } 148536620Srick while (dirname = *dirlist++) { 148636620Srick if (stat(dirname, &st) < 0) { 148736933Skarels /* 148836933Skarels * If user typed "ls -l", etc, and the client 148936933Skarels * used NLST, do what the user meant. 149036933Skarels */ 149136933Skarels if (dirname[0] == '-' && *dirlist == NULL && 149236933Skarels transflag == 0) { 149336933Skarels retrieve("/bin/ls %s", dirname); 149436933Skarels return; 149536933Skarels } 149636620Srick perror_reply(550, whichfiles); 149736620Srick if (dout != NULL) { 149836620Srick (void) fclose(dout); 149936933Skarels transflag = 0; 150036620Srick data = -1; 150136620Srick pdata = -1; 150236620Srick } 150336620Srick return; 150436620Srick } 150536933Skarels 150636620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 150736620Srick if (dout == NULL) { 150837459Skarels dout = dataconn("file list", (off_t)-1, "w"); 150936620Srick if (dout == NULL) 151036620Srick return; 151136933Skarels transflag++; 151236620Srick } 151338158Srick fprintf(dout, "%s%s\n", dirname, 151438158Srick type == TYPE_A ? "\r" : ""); 151536933Skarels byte_count += strlen(dirname) + 1; 151636620Srick continue; 151736933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 151836620Srick continue; 151936620Srick 152036620Srick if ((dirp = opendir(dirname)) == NULL) 152136620Srick continue; 152236620Srick 152336620Srick while ((dir = readdir(dirp)) != NULL) { 152436933Skarels char nbuf[MAXPATHLEN]; 152536620Srick 152636620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 152736620Srick continue; 152836933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 152936933Skarels dir->d_namlen == 2) 153036620Srick continue; 153136620Srick 153236933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 153336933Skarels 153436620Srick /* 153536933Skarels * We have to do a stat to insure it's 153636933Skarels * not a directory or special file. 153736620Srick */ 153837459Skarels if (simple || (stat(nbuf, &st) == 0 && 153937459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 154036620Srick if (dout == NULL) { 154137459Skarels dout = dataconn("file list", (off_t)-1, 154236620Srick "w"); 154336620Srick if (dout == NULL) 154436620Srick return; 154536933Skarels transflag++; 154636620Srick } 154736620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 154838158Srick fprintf(dout, "%s%s\n", &nbuf[2], 154938158Srick type == TYPE_A ? "\r" : ""); 155036620Srick else 155138158Srick fprintf(dout, "%s%s\n", nbuf, 155238158Srick type == TYPE_A ? "\r" : ""); 155336933Skarels byte_count += strlen(nbuf) + 1; 155436620Srick } 155536620Srick } 155636620Srick (void) closedir(dirp); 155736620Srick } 155836620Srick 155936933Skarels if (dout == NULL) 156036933Skarels reply(550, "No files found."); 156136933Skarels else if (ferror(dout) != 0) 156236933Skarels perror_reply(550, "Data connection"); 156336933Skarels else 156436620Srick reply(226, "Transfer complete."); 156536620Srick 156636933Skarels transflag = 0; 156736933Skarels if (dout != NULL) 156836620Srick (void) fclose(dout); 156936620Srick data = -1; 157036620Srick pdata = -1; 157136620Srick } 157236620Srick 157336620Srick #ifdef SETPROCTITLE 157436620Srick /* 157555258Sandrew * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 157655258Sandrew * Warning, since this is usually started from inetd.conf, it often doesn't 157755258Sandrew * have much of an environment or arglist to overwrite. 157836620Srick */ 157955258Sandrew void 158055258Sandrew #if __STDC__ 158155258Sandrew setproctitle(const char *fmt, ...) 158255258Sandrew #else 158355258Sandrew setproctitle(fmt, va_alist) 158455258Sandrew char *fmt; 158555258Sandrew va_dcl 158655258Sandrew #endif 158736620Srick { 158836620Srick register char *p, *bp, ch; 158936620Srick register int i; 159055258Sandrew va_list ap; 159136620Srick char buf[BUFSIZ]; 159255258Sandrew #if __STDC__ 159355258Sandrew va_start(ap, fmt); 159455258Sandrew #else 159555258Sandrew va_start(ap); 159655258Sandrew #endif 159755258Sandrew (void)vsnprintf(buf, sizeof(buf), fmt, ap); 159836620Srick 159936620Srick /* make ps print our process name */ 160036620Srick p = Argv[0]; 160136620Srick *p++ = '-'; 160236620Srick 160336620Srick i = strlen(buf); 160436620Srick if (i > LastArgv - p - 2) { 160536620Srick i = LastArgv - p - 2; 160636620Srick buf[i] = '\0'; 160736620Srick } 160836620Srick bp = buf; 160936620Srick while (ch = *bp++) 161036620Srick if (ch != '\n' && ch != '\r') 161136620Srick *p++ = ch; 161236620Srick while (p < LastArgv) 161336620Srick *p++ = ' '; 161436620Srick } 161536620Srick #endif /* SETPROCTITLE */ 1616