122499Sdist /* 2*55258Sandrew * Copyright (c) 1985, 1988, 1990, 1992 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 542666Sbostic * %sccs.include.redist.c% 622499Sdist */ 722499Sdist 810275Ssam #ifndef lint 922499Sdist char copyright[] = 10*55258Sandrew "@(#) Copyright (c) 1985, 1988, 1990, 1992 Regents of the University of California.\n\ 1122499Sdist All rights reserved.\n"; 1233738Sbostic #endif /* not lint */ 1310275Ssam 1422499Sdist #ifndef lint 15*55258Sandrew static char sccsid[] = "@(#)ftpd.c 5.42 (Berkeley) 07/15/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> 4546669Sbostic #include <unistd.h> 4646669Sbostic #include <stdio.h> 4746669Sbostic #include <ctype.h> 4846669Sbostic #include <stdlib.h> 4946669Sbostic #include <string.h> 5037459Skarels #include "pathnames.h" 51*55258Sandrew #include "extern.h" 5210275Ssam 53*55258Sandrew #if __STDC__ 54*55258Sandrew #include <stdarg.h> 55*55258Sandrew #else 56*55258Sandrew #include <varargs.h> 57*55258Sandrew #endif 58*55258Sandrew 59*55258Sandrew extern off_t restart_point; 6010275Ssam extern char *home; /* pointer to home directory for glob */ 6126044Sminshall extern char cbuf[]; 62*55258Sandrew 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 114*55258Sandrew #define MAXLINE 256 115*55258Sandrew 116*55258Sandrew #define LOGCMD(cmd, file) \ 117*55258Sandrew if (logging > 1) \ 118*55258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \ 119*55258Sandrew *(file) == '/' ? "" : curdir(), file); 120*55258Sandrew #define LOGCMD2(cmd, file1, file2) \ 121*55258Sandrew if (logging > 1) \ 122*55258Sandrew syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 123*55258Sandrew *(file1) == '/' ? "" : curdir(), file1, \ 124*55258Sandrew *(file2) == '/' ? "" : curdir(), file2); 125*55258Sandrew #define LOGBYTES(cmd, file, cnt) \ 126*55258Sandrew if (logging > 1) { \ 127*55258Sandrew if (cnt == (off_t)-1) \ 128*55258Sandrew syslog(LOG_INFO,"%s %s%s", cmd, \ 129*55258Sandrew *(file) == '/' ? "" : curdir(), file); \ 130*55258Sandrew else \ 131*55258Sandrew syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 132*55258Sandrew cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 133*55258Sandrew } 134*55258Sandrew 135*55258Sandrew static void ack __P((char *)); 136*55258Sandrew static void myoob __P((int)); 137*55258Sandrew static int checkuser __P((char *)); 138*55258Sandrew static FILE *dataconn __P((char *, off_t, char *)); 139*55258Sandrew static void dolog __P((struct sockaddr_in *)); 140*55258Sandrew static char *curdir __P((void)); 141*55258Sandrew static void end_login __P((void)); 142*55258Sandrew static FILE *getdatasock __P((char *)); 143*55258Sandrew static char *gunique __P((char *)); 144*55258Sandrew static void lostconn __P((int)); 145*55258Sandrew static void pass __P((char *)); 146*55258Sandrew static int receive_data __P((FILE *, FILE *)); 147*55258Sandrew static void send_data __P((FILE *, FILE *, off_t)); 148*55258Sandrew static struct passwd * 149*55258Sandrew sgetpwnam __P((char *)); 150*55258Sandrew static char *sgetsave __P((char *)); 151*55258Sandrew static void user __P((char *)); 152*55258Sandrew 153*55258Sandrew static char * 154*55258Sandrew curdir() 155*55258Sandrew { 156*55258Sandrew static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 157*55258Sandrew 158*55258Sandrew if (getcwd(path, sizeof(path)-2) == NULL) 159*55258Sandrew return (""); 160*55258Sandrew if (path[1] != '\0') /* special case for root dir. */ 161*55258Sandrew strcat(path, "/"); 162*55258Sandrew /* For guest account, skip / since it's chrooted */ 163*55258Sandrew return (guest ? path+1 : path); 164*55258Sandrew } 165*55258Sandrew 166*55258Sandrew int 16736620Srick main(argc, argv, envp) 16810275Ssam int argc; 16910275Ssam char *argv[]; 17036620Srick char **envp; 17110275Ssam { 17244339Skarels int addrlen, on = 1, tos; 173*55258Sandrew char *cp, line[MAXLINE]; 174*55258Sandrew FILE *fd; 17510275Ssam 17645028Skarels /* 17745028Skarels * LOG_NDELAY sets up the logging connection immediately, 17845028Skarels * necessary for anonymous ftp's that chroot and can't do it later. 17945028Skarels */ 180*55258Sandrew openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 18116339Skarels addrlen = sizeof (his_addr); 18236304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 18326493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 18410275Ssam exit(1); 18510275Ssam } 18616339Skarels addrlen = sizeof (ctrl_addr); 18736304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 18826493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 18916339Skarels exit(1); 19016339Skarels } 19144339Skarels #ifdef IP_TOS 19244339Skarels tos = IPTOS_LOWDELAY; 19344339Skarels if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 19444339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 19544339Skarels #endif 19616339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 19710275Ssam debug = 0; 19836620Srick #ifdef SETPROCTITLE 19936620Srick /* 20036620Srick * Save start and extent of argv for setproctitle. 20136620Srick */ 20236620Srick Argv = argv; 20336620Srick while (*envp) 20436620Srick envp++; 20536620Srick LastArgv = envp[-1] + strlen(envp[-1]); 20636620Srick #endif /* SETPROCTITLE */ 20736620Srick 20810275Ssam argc--, argv++; 20910275Ssam while (argc > 0 && *argv[0] == '-') { 21010275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 21110275Ssam 21211653Ssam case 'v': 21311653Ssam debug = 1; 21411653Ssam break; 21511653Ssam 21610275Ssam case 'd': 21710275Ssam debug = 1; 21810275Ssam break; 21910275Ssam 22011757Ssam case 'l': 221*55258Sandrew logging++; /* > 1 == extra logging */ 22211757Ssam break; 22311757Ssam 22411653Ssam case 't': 22511653Ssam timeout = atoi(++cp); 22636933Skarels if (maxtimeout < timeout) 22736933Skarels maxtimeout = timeout; 22811653Ssam goto nextopt; 22911653Ssam 23036933Skarels case 'T': 23136933Skarels maxtimeout = atoi(++cp); 23236933Skarels if (timeout > maxtimeout) 23336933Skarels timeout = maxtimeout; 23436933Skarels goto nextopt; 23536933Skarels 23636933Skarels case 'u': 23736933Skarels { 23836933Skarels int val = 0; 23936933Skarels 24036933Skarels while (*++cp && *cp >= '0' && *cp <= '9') 24136933Skarels val = val*8 + *cp - '0'; 24236933Skarels if (*cp) 24336933Skarels fprintf(stderr, "ftpd: Bad value for -u\n"); 24436933Skarels else 24536933Skarels defumask = val; 24636933Skarels goto nextopt; 24736933Skarels } 24836933Skarels 24910275Ssam default: 25016339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 25116339Skarels *cp); 25210275Ssam break; 25310275Ssam } 25411653Ssam nextopt: 25510275Ssam argc--, argv++; 25610275Ssam } 25737459Skarels (void) freopen(_PATH_DEVNULL, "w", stderr); 25826493Sminshall (void) signal(SIGPIPE, lostconn); 25926493Sminshall (void) signal(SIGCHLD, SIG_IGN); 26035691Sbostic if ((int)signal(SIGURG, myoob) < 0) 26126493Sminshall syslog(LOG_ERR, "signal: %m"); 26235691Sbostic 26338134Srick /* Try to handle urgent data inline */ 26427750Sminshall #ifdef SO_OOBINLINE 26536276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 26627750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 26736276Sbostic #endif 26838134Srick 26936933Skarels #ifdef F_SETOWN 27036304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 27136304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 27236933Skarels #endif 27316760Slepreau dolog(&his_addr); 27416339Skarels /* 27516339Skarels * Set up default state 27616339Skarels */ 27716339Skarels data = -1; 27816339Skarels type = TYPE_A; 27916339Skarels form = FORM_N; 28016339Skarels stru = STRU_F; 28116339Skarels mode = MODE_S; 28226044Sminshall tmpline[0] = '\0'; 283*55258Sandrew 284*55258Sandrew /* If logins are disabled, print out the message. */ 285*55258Sandrew if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 286*55258Sandrew while (fgets(line, sizeof (line), fd) != NULL) { 287*55258Sandrew if ((cp = index(line, '\n')) != NULL) 288*55258Sandrew *cp = '\0'; 289*55258Sandrew lreply(530, "%s", line); 290*55258Sandrew } 291*55258Sandrew (void) fflush(stdout); 292*55258Sandrew (void) fclose(fd); 293*55258Sandrew reply(530, "System not available."); 294*55258Sandrew exit(0); 295*55258Sandrew } 296*55258Sandrew if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 297*55258Sandrew while (fgets(line, sizeof (line), fd) != NULL) { 298*55258Sandrew if ((cp = index(line, '\n')) != NULL) 299*55258Sandrew *cp = '\0'; 300*55258Sandrew lreply(220, "%s", line); 301*55258Sandrew } 302*55258Sandrew (void) fflush(stdout); 303*55258Sandrew (void) fclose(fd); 304*55258Sandrew /* reply(220,) must follow */ 305*55258Sandrew } 30626493Sminshall (void) gethostname(hostname, sizeof (hostname)); 30736276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 30836304Skarels (void) setjmp(errcatch); 30936304Skarels for (;;) 31026493Sminshall (void) yyparse(); 31136620Srick /* NOTREACHED */ 31210275Ssam } 31310419Ssam 314*55258Sandrew static void 315*55258Sandrew lostconn(signo) 316*55258Sandrew int signo; 31710275Ssam { 31814089Ssam if (debug) 31926493Sminshall syslog(LOG_DEBUG, "lost connection"); 32014089Ssam dologout(-1); 32110275Ssam } 32210275Ssam 32335672Sbostic static char ttyline[20]; 32435672Sbostic 32536185Sbostic /* 32636185Sbostic * Helper function for sgetpwnam(). 32736185Sbostic */ 328*55258Sandrew static char * 32936185Sbostic sgetsave(s) 33036185Sbostic char *s; 33136185Sbostic { 33236185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 33336933Skarels 33436185Sbostic if (new == NULL) { 33536620Srick perror_reply(421, "Local resource failure: malloc"); 33636185Sbostic dologout(1); 33736620Srick /* NOTREACHED */ 33836185Sbostic } 33936185Sbostic (void) strcpy(new, s); 34036185Sbostic return (new); 34136185Sbostic } 34236185Sbostic 34336185Sbostic /* 34436185Sbostic * Save the result of a getpwnam. Used for USER command, since 34536185Sbostic * the data returned must not be clobbered by any other command 34636185Sbostic * (e.g., globbing). 34736185Sbostic */ 348*55258Sandrew static struct passwd * 34936185Sbostic sgetpwnam(name) 35036185Sbostic char *name; 35136185Sbostic { 35236185Sbostic static struct passwd save; 35336185Sbostic register struct passwd *p; 35436185Sbostic char *sgetsave(); 35536185Sbostic 35636185Sbostic if ((p = getpwnam(name)) == NULL) 35736185Sbostic return (p); 35836185Sbostic if (save.pw_name) { 35936185Sbostic free(save.pw_name); 36036185Sbostic free(save.pw_passwd); 36136185Sbostic free(save.pw_gecos); 36236185Sbostic free(save.pw_dir); 36336185Sbostic free(save.pw_shell); 36436185Sbostic } 36536185Sbostic save = *p; 36636185Sbostic save.pw_name = sgetsave(p->pw_name); 36736185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 36836185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 36936185Sbostic save.pw_dir = sgetsave(p->pw_dir); 37036185Sbostic save.pw_shell = sgetsave(p->pw_shell); 37136185Sbostic return (&save); 37236185Sbostic } 37336185Sbostic 374*55258Sandrew static int login_attempts; /* number of failed login attempts */ 375*55258Sandrew static int askpasswd; /* had user command, ask for passwd */ 376*55258Sandrew static char curname[10]; /* current USER name */ 37736304Skarels 37836304Skarels /* 37936304Skarels * USER command. 38042327Sbostic * Sets global passwd pointer pw if named account exists and is acceptable; 38142327Sbostic * sets askpasswd if a PASS command is expected. If logged in previously, 38242327Sbostic * need to reset state. If name is "ftp" or "anonymous", the name is not in 38342327Sbostic * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 38442327Sbostic * If account doesn't exist, ask for passwd anyway. Otherwise, check user 38542327Sbostic * requesting login privileges. Disallow anyone who does not have a standard 38642327Sbostic * shell as returned by getusershell(). Disallow anyone mentioned in the file 38742327Sbostic * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 38836304Skarels */ 389*55258Sandrew void 39036304Skarels user(name) 39136304Skarels char *name; 39236304Skarels { 39336304Skarels register char *cp; 39436304Skarels char *shell; 39536304Skarels 39636304Skarels if (logged_in) { 39736304Skarels if (guest) { 39836304Skarels reply(530, "Can't change user from guest login."); 39936304Skarels return; 40036304Skarels } 40136304Skarels end_login(); 40236304Skarels } 40336304Skarels 40436304Skarels guest = 0; 40536304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 40640183Smckusick if (checkuser("ftp") || checkuser("anonymous")) 40740155Smckusick reply(530, "User %s access denied.", name); 40840155Smckusick else if ((pw = sgetpwnam("ftp")) != NULL) { 40936304Skarels guest = 1; 41036304Skarels askpasswd = 1; 411*55258Sandrew reply(331, 412*55258Sandrew "Guest login ok, type your name as password."); 41336933Skarels } else 41436304Skarels reply(530, "User %s unknown.", name); 415*55258Sandrew if (!askpasswd && logging) 416*55258Sandrew syslog(LOG_NOTICE, 417*55258Sandrew "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 41836304Skarels return; 41936304Skarels } 42036304Skarels if (pw = sgetpwnam(name)) { 42136304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 42237459Skarels shell = _PATH_BSHELL; 42336304Skarels while ((cp = getusershell()) != NULL) 42436304Skarels if (strcmp(cp, shell) == 0) 42536304Skarels break; 42636304Skarels endusershell(); 427*55258Sandrew 42840183Smckusick if (cp == NULL || checkuser(name)) { 42936304Skarels reply(530, "User %s access denied.", name); 43036933Skarels if (logging) 43136933Skarels syslog(LOG_NOTICE, 43236933Skarels "FTP LOGIN REFUSED FROM %s, %s", 43336933Skarels remotehost, name); 43436304Skarels pw = (struct passwd *) NULL; 43536304Skarels return; 43636304Skarels } 43736304Skarels } 438*55258Sandrew if (logging) 439*55258Sandrew strncpy(curname, name, sizeof(curname)-1); 44036304Skarels reply(331, "Password required for %s.", name); 44136304Skarels askpasswd = 1; 44236304Skarels /* 44336304Skarels * Delay before reading passwd after first failed 44436304Skarels * attempt to slow down passwd-guessing programs. 44536304Skarels */ 44636304Skarels if (login_attempts) 44736304Skarels sleep((unsigned) login_attempts); 44836304Skarels } 44936304Skarels 45036304Skarels /* 45140183Smckusick * Check if a user is in the file _PATH_FTPUSERS 45240183Smckusick */ 453*55258Sandrew static int 45440183Smckusick checkuser(name) 45540183Smckusick char *name; 45640183Smckusick { 45742327Sbostic register FILE *fd; 45842327Sbostic register char *p; 45942327Sbostic char line[BUFSIZ]; 460*55258Sandrew int found = 0; 46140183Smckusick 46240183Smckusick if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { 46342327Sbostic while (fgets(line, sizeof(line), fd) != NULL) 46442327Sbostic if ((p = index(line, '\n')) != NULL) { 46542327Sbostic *p = '\0'; 46642327Sbostic if (line[0] == '#') 46742327Sbostic continue; 468*55258Sandrew if (strcmp(p, name) == 0) { 469*55258Sandrew found = 1; 470*55258Sandrew break; 471*55258Sandrew } 47242327Sbostic } 47340183Smckusick (void) fclose(fd); 47440183Smckusick } 475*55258Sandrew return (found); 47640183Smckusick } 47740183Smckusick 47840183Smckusick /* 47936304Skarels * Terminate login as previous user, if any, resetting state; 48036304Skarels * used when USER command is given or login fails. 48136304Skarels */ 482*55258Sandrew static void 48336304Skarels end_login() 48436304Skarels { 48536304Skarels 48636304Skarels (void) seteuid((uid_t)0); 48736304Skarels if (logged_in) 48836304Skarels logwtmp(ttyline, "", ""); 48936304Skarels pw = NULL; 49036304Skarels logged_in = 0; 49136304Skarels guest = 0; 49236304Skarels } 49336304Skarels 494*55258Sandrew void 49510275Ssam pass(passwd) 49610275Ssam char *passwd; 49710275Ssam { 49836304Skarels char *xpasswd, *salt; 499*55258Sandrew FILE *fd; 50010275Ssam 50136304Skarels if (logged_in || askpasswd == 0) { 50210275Ssam reply(503, "Login with USER first."); 50310275Ssam return; 50410275Ssam } 50536304Skarels askpasswd = 0; 50610275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 50736304Skarels if (pw == NULL) 50836304Skarels salt = "xx"; 50936304Skarels else 51036304Skarels salt = pw->pw_passwd; 51136304Skarels xpasswd = crypt(passwd, salt); 51216760Slepreau /* The strcmp does not catch null passwords! */ 51336304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 51436304Skarels strcmp(xpasswd, pw->pw_passwd)) { 51510275Ssam reply(530, "Login incorrect."); 516*55258Sandrew if (logging) 517*55258Sandrew syslog(LOG_NOTICE, 518*55258Sandrew "FTP LOGIN FAILED FROM %s, %s", 519*55258Sandrew remotehost, curname); 52010275Ssam pw = NULL; 52136304Skarels if (login_attempts++ >= 5) { 52236933Skarels syslog(LOG_NOTICE, 52336304Skarels "repeated login failures from %s", 52436304Skarels remotehost); 52536304Skarels exit(0); 52636304Skarels } 52710275Ssam return; 52810275Ssam } 52910275Ssam } 53036304Skarels login_attempts = 0; /* this time successful */ 531*55258Sandrew if (setegid((gid_t)pw->pw_gid) < 0) { 532*55258Sandrew reply(550, "Can't set gid."); 533*55258Sandrew return; 534*55258Sandrew } 53536304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 53616033Sralph 53736192Sbostic /* open wtmp before chroot */ 53836192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 53936192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 54036192Sbostic logged_in = 1; 54136192Sbostic 54236446Sbostic if (guest) { 54336620Srick /* 54436933Skarels * We MUST do a chdir() after the chroot. Otherwise 54536933Skarels * the old current directory will be accessible as "." 54636933Skarels * outside the new root! 54736620Srick */ 54836620Srick if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 54936446Sbostic reply(550, "Can't set guest privileges."); 55036446Sbostic goto bad; 55136446Sbostic } 55236933Skarels } else if (chdir(pw->pw_dir) < 0) { 55336620Srick if (chdir("/") < 0) { 55436446Sbostic reply(530, "User %s: can't change directory to %s.", 55536446Sbostic pw->pw_name, pw->pw_dir); 55636446Sbostic goto bad; 55736933Skarels } else 55836446Sbostic lreply(230, "No directory! Logging in with home=/"); 55936620Srick } 56036304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 56136304Skarels reply(550, "Can't set uid."); 56236304Skarels goto bad; 56336304Skarels } 564*55258Sandrew /* 565*55258Sandrew * Display a login message, if it exists. 566*55258Sandrew * N.B. reply(230,) must follow the message. 567*55258Sandrew */ 568*55258Sandrew if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 569*55258Sandrew char *cp, line[MAXLINE]; 570*55258Sandrew 571*55258Sandrew while (fgets(line, sizeof (line), fd) != NULL) { 572*55258Sandrew if ((cp = index(line, '\n')) != NULL) 573*55258Sandrew *cp = '\0'; 574*55258Sandrew lreply(230, "%s", line); 575*55258Sandrew } 576*55258Sandrew (void) fflush(stdout); 577*55258Sandrew (void) fclose(fd); 578*55258Sandrew } 57936550Sbostic if (guest) { 58036192Sbostic reply(230, "Guest login ok, access restrictions apply."); 58136933Skarels #ifdef SETPROCTITLE 58236933Skarels sprintf(proctitle, "%s: anonymous/%.*s", remotehost, 58336933Skarels sizeof(proctitle) - sizeof(remotehost) - 58436933Skarels sizeof(": anonymous/"), passwd); 58536933Skarels setproctitle(proctitle); 58636933Skarels #endif /* SETPROCTITLE */ 58736933Skarels if (logging) 58836933Skarels syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 58936933Skarels remotehost, passwd); 59052998Sbostic home = "/"; /* guest home dir for globbing */ 59136933Skarels } else { 59210275Ssam reply(230, "User %s logged in.", pw->pw_name); 59336933Skarels #ifdef SETPROCTITLE 59436933Skarels sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); 59536933Skarels setproctitle(proctitle); 59636933Skarels #endif /* SETPROCTITLE */ 59736933Skarels if (logging) 598*55258Sandrew syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 59936933Skarels remotehost, pw->pw_name); 60052998Sbostic home = pw->pw_dir; /* home dir for globbing */ 60136550Sbostic } 60236933Skarels (void) umask(defumask); 60310303Ssam return; 60410303Ssam bad: 60536304Skarels /* Forget all about it... */ 60636304Skarels end_login(); 60710275Ssam } 60810275Ssam 609*55258Sandrew void 61010275Ssam retrieve(cmd, name) 61110275Ssam char *cmd, *name; 61210275Ssam { 61310275Ssam FILE *fin, *dout; 61410275Ssam struct stat st; 61536620Srick int (*closefunc)(); 61610275Ssam 61736557Sbostic if (cmd == 0) { 61836446Sbostic fin = fopen(name, "r"), closefunc = fclose; 61936557Sbostic st.st_size = 0; 62036557Sbostic } else { 62110275Ssam char line[BUFSIZ]; 62210275Ssam 62326493Sminshall (void) sprintf(line, cmd, name), name = line; 62436304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 62536557Sbostic st.st_size = -1; 62636933Skarels st.st_blksize = BUFSIZ; 62710275Ssam } 62810275Ssam if (fin == NULL) { 629*55258Sandrew if (errno != 0) { 63036304Skarels perror_reply(550, name); 631*55258Sandrew if (cmd == 0) { 632*55258Sandrew LOGCMD("get", name); 633*55258Sandrew } 634*55258Sandrew } 63510275Ssam return; 63610275Ssam } 637*55258Sandrew byte_count = -1; 63810275Ssam if (cmd == 0 && 63936933Skarels (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 64010275Ssam reply(550, "%s: not a plain file.", name); 64110275Ssam goto done; 64210275Ssam } 64337459Skarels if (restart_point) { 64437459Skarels if (type == TYPE_A) { 64537459Skarels register int i, n, c; 64637459Skarels 64737459Skarels n = restart_point; 64837459Skarels i = 0; 64937459Skarels while (i++ < n) { 65037459Skarels if ((c=getc(fin)) == EOF) { 65137459Skarels perror_reply(550, name); 65237459Skarels goto done; 65337459Skarels } 65437459Skarels if (c == '\n') 65537459Skarels i++; 65652998Sbostic } 65737459Skarels } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 65837459Skarels perror_reply(550, name); 65937459Skarels goto done; 66037459Skarels } 66137459Skarels } 66210275Ssam dout = dataconn(name, st.st_size, "w"); 66310275Ssam if (dout == NULL) 66410275Ssam goto done; 66536620Srick send_data(fin, dout, st.st_blksize); 66626493Sminshall (void) fclose(dout); 66726044Sminshall data = -1; 66826044Sminshall pdata = -1; 66910275Ssam done: 670*55258Sandrew if (cmd == 0) 671*55258Sandrew LOGBYTES("get", name, byte_count); 67210275Ssam (*closefunc)(fin); 67310275Ssam } 67410275Ssam 675*55258Sandrew void 67636304Skarels store(name, mode, unique) 67710275Ssam char *name, *mode; 67836304Skarels int unique; 67910275Ssam { 68010275Ssam FILE *fout, *din; 68136446Sbostic struct stat st; 68236620Srick int (*closefunc)(); 68310275Ssam 68436446Sbostic if (unique && stat(name, &st) == 0 && 685*55258Sandrew (name = gunique(name)) == NULL) { 686*55258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name); 68736446Sbostic return; 688*55258Sandrew } 68910303Ssam 69037459Skarels if (restart_point) 69137459Skarels mode = "r+w"; 69236620Srick fout = fopen(name, mode); 69336620Srick closefunc = fclose; 69410275Ssam if (fout == NULL) { 69536304Skarels perror_reply(553, name); 696*55258Sandrew LOGCMD(*mode == 'w' ? "put" : "append", name); 69710275Ssam return; 69810275Ssam } 699*55258Sandrew byte_count = -1; 70037459Skarels if (restart_point) { 70137459Skarels if (type == TYPE_A) { 70237459Skarels register int i, n, c; 70337459Skarels 70437459Skarels n = restart_point; 70537459Skarels i = 0; 70637459Skarels while (i++ < n) { 70737459Skarels if ((c=getc(fout)) == EOF) { 70837459Skarels perror_reply(550, name); 70937459Skarels goto done; 71037459Skarels } 71137459Skarels if (c == '\n') 71237459Skarels i++; 71352998Sbostic } 71437459Skarels /* 71537459Skarels * We must do this seek to "current" position 71637459Skarels * because we are changing from reading to 71737459Skarels * writing. 71837459Skarels */ 71937459Skarels if (fseek(fout, 0L, L_INCR) < 0) { 72037459Skarels perror_reply(550, name); 72137459Skarels goto done; 72237459Skarels } 72337459Skarels } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 72437459Skarels perror_reply(550, name); 72537459Skarels goto done; 72637459Skarels } 72737459Skarels } 72836304Skarels din = dataconn(name, (off_t)-1, "r"); 72910275Ssam if (din == NULL) 73010275Ssam goto done; 73136620Srick if (receive_data(din, fout) == 0) { 73236620Srick if (unique) 73336304Skarels reply(226, "Transfer complete (unique file name:%s).", 73436933Skarels name); 73536304Skarels else 73636304Skarels reply(226, "Transfer complete."); 73726044Sminshall } 73826493Sminshall (void) fclose(din); 73926044Sminshall data = -1; 74026044Sminshall pdata = -1; 74110275Ssam done: 742*55258Sandrew LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 74310275Ssam (*closefunc)(fout); 74410275Ssam } 74510275Ssam 746*55258Sandrew static FILE * 74710275Ssam getdatasock(mode) 74810275Ssam char *mode; 74910275Ssam { 75037459Skarels int s, on = 1, tries; 751*55258Sandrew int t; 75210275Ssam 75310275Ssam if (data >= 0) 75410275Ssam return (fdopen(data, mode)); 75550391Skarels (void) seteuid((uid_t)0); 75613247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 75710602Ssam if (s < 0) 75850391Skarels goto bad; 75937459Skarels if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 76037459Skarels (char *) &on, sizeof (on)) < 0) 76110602Ssam goto bad; 76213152Ssam /* anchor socket to avoid multi-homing problems */ 76313152Ssam data_source.sin_family = AF_INET; 76413152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 76537459Skarels for (tries = 1; ; tries++) { 76637459Skarels if (bind(s, (struct sockaddr *)&data_source, 76737459Skarels sizeof (data_source)) >= 0) 76837459Skarels break; 76937459Skarels if (errno != EADDRINUSE || tries > 10) 77037459Skarels goto bad; 77137459Skarels sleep(tries); 77237459Skarels } 77336304Skarels (void) seteuid((uid_t)pw->pw_uid); 77444339Skarels #ifdef IP_TOS 77544339Skarels on = IPTOS_THROUGHPUT; 77644339Skarels if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 77744339Skarels syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 77844339Skarels #endif 77910275Ssam return (fdopen(s, mode)); 78010602Ssam bad: 781*55258Sandrew /* Return the real value of errno (close may change it) */ 782*55258Sandrew t = errno; 78336304Skarels (void) seteuid((uid_t)pw->pw_uid); 78426493Sminshall (void) close(s); 785*55258Sandrew errno = t; 78610602Ssam return (NULL); 78710275Ssam } 78810275Ssam 789*55258Sandrew static FILE * 79010275Ssam dataconn(name, size, mode) 79110275Ssam char *name; 79211653Ssam off_t size; 79310275Ssam char *mode; 79410275Ssam { 79510275Ssam char sizebuf[32]; 79610275Ssam FILE *file; 79744339Skarels int retry = 0, tos; 79810275Ssam 79936933Skarels file_size = size; 80036933Skarels byte_count = 0; 80136304Skarels if (size != (off_t) -1) 802*55258Sandrew (void) sprintf (sizebuf, " (%qd bytes)", size); 80310275Ssam else 80410275Ssam (void) strcpy(sizebuf, ""); 80536304Skarels if (pdata >= 0) { 80626044Sminshall struct sockaddr_in from; 80726044Sminshall int s, fromlen = sizeof(from); 80826044Sminshall 80936304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 81026044Sminshall if (s < 0) { 81126044Sminshall reply(425, "Can't open data connection."); 81226044Sminshall (void) close(pdata); 81326044Sminshall pdata = -1; 81426044Sminshall return(NULL); 81526044Sminshall } 81626044Sminshall (void) close(pdata); 81726044Sminshall pdata = s; 81844339Skarels #ifdef IP_TOS 81944339Skarels tos = IPTOS_LOWDELAY; 82044339Skarels (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 82144339Skarels sizeof(int)); 82244339Skarels #endif 823*55258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.", 82436235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 82526044Sminshall return(fdopen(pdata, mode)); 82626044Sminshall } 82710275Ssam if (data >= 0) { 828*55258Sandrew reply(125, "Using existing data connection for '%s'%s.", 82910275Ssam name, sizebuf); 83010321Ssam usedefault = 1; 83110275Ssam return (fdopen(data, mode)); 83210275Ssam } 83310566Ssam if (usedefault) 83410422Ssam data_dest = his_addr; 83510422Ssam usedefault = 1; 83610275Ssam file = getdatasock(mode); 83710275Ssam if (file == NULL) { 83810275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 83913247Ssam inet_ntoa(data_source.sin_addr), 84042412Sbostic ntohs(data_source.sin_port), strerror(errno)); 84110275Ssam return (NULL); 84210275Ssam } 84310275Ssam data = fileno(file); 84436304Skarels while (connect(data, (struct sockaddr *)&data_dest, 84536304Skarels sizeof (data_dest)) < 0) { 84611653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 84726493Sminshall sleep((unsigned) swaitint); 84811653Ssam retry += swaitint; 84911653Ssam continue; 85011653Ssam } 85136304Skarels perror_reply(425, "Can't build data connection"); 85210275Ssam (void) fclose(file); 85310275Ssam data = -1; 85410275Ssam return (NULL); 85510275Ssam } 856*55258Sandrew reply(150, "Opening %s mode data connection for '%s'%s.", 85736235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 85810275Ssam return (file); 85910275Ssam } 86010275Ssam 86110275Ssam /* 862*55258Sandrew * Tranfer the contents of "instr" to "outstr" peer using the appropriate 863*55258Sandrew * encapsulation of the data subject * to Mode, Structure, and Type. 86410275Ssam * 86510275Ssam * NB: Form isn't handled. 86610275Ssam */ 867*55258Sandrew static void 86836446Sbostic send_data(instr, outstr, blksize) 86910275Ssam FILE *instr, *outstr; 87036446Sbostic off_t blksize; 87110275Ssam { 87236446Sbostic register int c, cnt; 87336446Sbostic register char *buf; 87436446Sbostic int netfd, filefd; 87510275Ssam 87626044Sminshall transflag++; 87726044Sminshall if (setjmp(urgcatch)) { 87826044Sminshall transflag = 0; 87936620Srick return; 88026044Sminshall } 88110275Ssam switch (type) { 88210275Ssam 88310275Ssam case TYPE_A: 88410275Ssam while ((c = getc(instr)) != EOF) { 88536933Skarels byte_count++; 88611220Ssam if (c == '\n') { 88736933Skarels if (ferror(outstr)) 88836620Srick goto data_err; 88927750Sminshall (void) putc('\r', outstr); 89011220Ssam } 89127750Sminshall (void) putc(c, outstr); 89210275Ssam } 89336933Skarels fflush(outstr); 89426044Sminshall transflag = 0; 89536933Skarels if (ferror(instr)) 89636933Skarels goto file_err; 89736933Skarels if (ferror(outstr)) 89836620Srick goto data_err; 89936620Srick reply(226, "Transfer complete."); 90036620Srick return; 90136446Sbostic 90210275Ssam case TYPE_I: 90310275Ssam case TYPE_L: 90436446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 90536446Sbostic transflag = 0; 90636933Skarels perror_reply(451, "Local resource failure: malloc"); 90736933Skarels return; 90836446Sbostic } 90910275Ssam netfd = fileno(outstr); 91010275Ssam filefd = fileno(instr); 91136933Skarels while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 91236620Srick write(netfd, buf, cnt) == cnt) 91336933Skarels byte_count += cnt; 91426044Sminshall transflag = 0; 91536446Sbostic (void)free(buf); 91636933Skarels if (cnt != 0) { 91736933Skarels if (cnt < 0) 91836933Skarels goto file_err; 91936620Srick goto data_err; 92036933Skarels } 92136620Srick reply(226, "Transfer complete."); 92236620Srick return; 92336620Srick default: 92436620Srick transflag = 0; 92536620Srick reply(550, "Unimplemented TYPE %d in send_data", type); 92636620Srick return; 92710275Ssam } 92836620Srick 92936620Srick data_err: 93026044Sminshall transflag = 0; 93136933Skarels perror_reply(426, "Data connection"); 93236933Skarels return; 93336933Skarels 93436933Skarels file_err: 93536933Skarels transflag = 0; 93636933Skarels perror_reply(551, "Error on input file"); 93710275Ssam } 93810275Ssam 93910275Ssam /* 940*55258Sandrew * Transfer data from peer to "outstr" using the appropriate encapulation of 941*55258Sandrew * the data subject to Mode, Structure, and Type. 94210275Ssam * 94310275Ssam * N.B.: Form isn't handled. 94410275Ssam */ 945*55258Sandrew static int 94610275Ssam receive_data(instr, outstr) 94710275Ssam FILE *instr, *outstr; 94810275Ssam { 94910275Ssam register int c; 95038134Srick int cnt, bare_lfs = 0; 95110275Ssam char buf[BUFSIZ]; 95210275Ssam 95326044Sminshall transflag++; 95426044Sminshall if (setjmp(urgcatch)) { 95526044Sminshall transflag = 0; 95636933Skarels return (-1); 95726044Sminshall } 95810275Ssam switch (type) { 95910275Ssam 96010275Ssam case TYPE_I: 96110275Ssam case TYPE_L: 96226044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 96336620Srick if (write(fileno(outstr), buf, cnt) != cnt) 96436933Skarels goto file_err; 96536933Skarels byte_count += cnt; 96626044Sminshall } 96736933Skarels if (cnt < 0) 96836620Srick goto data_err; 96926044Sminshall transflag = 0; 97036933Skarels return (0); 97110275Ssam 97210275Ssam case TYPE_E: 97327106Smckusick reply(553, "TYPE E not implemented."); 97426044Sminshall transflag = 0; 97527106Smckusick return (-1); 97610275Ssam 97710275Ssam case TYPE_A: 97810275Ssam while ((c = getc(instr)) != EOF) { 97936933Skarels byte_count++; 98038134Srick if (c == '\n') 98138134Srick bare_lfs++; 98227750Sminshall while (c == '\r') { 98336933Skarels if (ferror(outstr)) 98436620Srick goto data_err; 98536933Skarels if ((c = getc(instr)) != '\n') { 98627750Sminshall (void) putc ('\r', outstr); 98736933Skarels if (c == '\0' || c == EOF) 98836933Skarels goto contin2; 98936933Skarels } 99010275Ssam } 99136933Skarels (void) putc(c, outstr); 99236933Skarels contin2: ; 99310275Ssam } 99436620Srick fflush(outstr); 99536933Skarels if (ferror(instr)) 99636620Srick goto data_err; 99736933Skarels if (ferror(outstr)) 99836933Skarels goto file_err; 99926044Sminshall transflag = 0; 100038134Srick if (bare_lfs) { 100138134Srick lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); 100238134Srick printf(" File may not have transferred correctly.\r\n"); 100338134Srick } 100410275Ssam return (0); 100536620Srick default: 100636620Srick reply(550, "Unimplemented TYPE %d in receive_data", type); 100736620Srick transflag = 0; 100836933Skarels return (-1); 100910275Ssam } 101036620Srick 101136620Srick data_err: 101226044Sminshall transflag = 0; 101336933Skarels perror_reply(426, "Data Connection"); 101436933Skarels return (-1); 101536933Skarels 101636933Skarels file_err: 101736933Skarels transflag = 0; 101836933Skarels perror_reply(452, "Error writing file"); 101936933Skarels return (-1); 102010275Ssam } 102110275Ssam 1022*55258Sandrew void 102336933Skarels statfilecmd(filename) 102436933Skarels char *filename; 102536933Skarels { 102636933Skarels char line[BUFSIZ]; 102736933Skarels FILE *fin; 102836933Skarels int c; 102936933Skarels 1030*55258Sandrew (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); 103136933Skarels fin = ftpd_popen(line, "r"); 103236933Skarels lreply(211, "status of %s:", filename); 103336933Skarels while ((c = getc(fin)) != EOF) { 103436933Skarels if (c == '\n') { 103536933Skarels if (ferror(stdout)){ 103636933Skarels perror_reply(421, "control connection"); 103736933Skarels (void) ftpd_pclose(fin); 103836933Skarels dologout(1); 103936933Skarels /* NOTREACHED */ 104036933Skarels } 104136933Skarels if (ferror(fin)) { 104236933Skarels perror_reply(551, filename); 104336933Skarels (void) ftpd_pclose(fin); 104436933Skarels return; 104536933Skarels } 104636933Skarels (void) putc('\r', stdout); 104736933Skarels } 104836933Skarels (void) putc(c, stdout); 104936933Skarels } 105036933Skarels (void) ftpd_pclose(fin); 105136933Skarels reply(211, "End of Status"); 105236933Skarels } 105336933Skarels 1054*55258Sandrew void 105536933Skarels statcmd() 105636933Skarels { 105736933Skarels struct sockaddr_in *sin; 105836933Skarels u_char *a, *p; 105936933Skarels 106036933Skarels lreply(211, "%s FTP server status:", hostname, version); 106136933Skarels printf(" %s\r\n", version); 106236933Skarels printf(" Connected to %s", remotehost); 106338134Srick if (!isdigit(remotehost[0])) 106436933Skarels printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 106536933Skarels printf("\r\n"); 106636933Skarels if (logged_in) { 106736933Skarels if (guest) 106836933Skarels printf(" Logged in anonymously\r\n"); 106936933Skarels else 107036933Skarels printf(" Logged in as %s\r\n", pw->pw_name); 107136933Skarels } else if (askpasswd) 107236933Skarels printf(" Waiting for password\r\n"); 107336933Skarels else 107436933Skarels printf(" Waiting for user name\r\n"); 107536933Skarels printf(" TYPE: %s", typenames[type]); 107636933Skarels if (type == TYPE_A || type == TYPE_E) 107736933Skarels printf(", FORM: %s", formnames[form]); 107836933Skarels if (type == TYPE_L) 107936933Skarels #if NBBY == 8 108036933Skarels printf(" %d", NBBY); 108136933Skarels #else 108236933Skarels printf(" %d", bytesize); /* need definition! */ 108336933Skarels #endif 108436933Skarels printf("; STRUcture: %s; transfer MODE: %s\r\n", 108536933Skarels strunames[stru], modenames[mode]); 108636933Skarels if (data != -1) 108736933Skarels printf(" Data connection open\r\n"); 108836933Skarels else if (pdata != -1) { 108936933Skarels printf(" in Passive mode"); 109036933Skarels sin = &pasv_addr; 109136933Skarels goto printaddr; 109236933Skarels } else if (usedefault == 0) { 109336933Skarels printf(" PORT"); 109436933Skarels sin = &data_dest; 109536933Skarels printaddr: 109636933Skarels a = (u_char *) &sin->sin_addr; 109736933Skarels p = (u_char *) &sin->sin_port; 109836933Skarels #define UC(b) (((int) b) & 0xff) 109936933Skarels printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 110036933Skarels UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 110136933Skarels #undef UC 110236933Skarels } else 110336933Skarels printf(" No data connection\r\n"); 110436933Skarels reply(211, "End of status"); 110536933Skarels } 110636933Skarels 1107*55258Sandrew void 110810275Ssam fatal(s) 110910275Ssam char *s; 111010275Ssam { 111110275Ssam reply(451, "Error in server: %s\n", s); 111210275Ssam reply(221, "Closing connection due to server error."); 111313247Ssam dologout(0); 111436620Srick /* NOTREACHED */ 111510275Ssam } 111610275Ssam 1117*55258Sandrew void 1118*55258Sandrew #if __STDC__ 1119*55258Sandrew reply(int n, const char *fmt, ...) 1120*55258Sandrew #else 1121*55258Sandrew reply(n, fmt, va_alist) 112210275Ssam int n; 112336446Sbostic char *fmt; 1124*55258Sandrew va_dcl 1125*55258Sandrew #endif 112610275Ssam { 1127*55258Sandrew va_list ap; 1128*55258Sandrew #if __STDC__ 1129*55258Sandrew va_start(ap, fmt); 1130*55258Sandrew #else 1131*55258Sandrew va_start(ap); 1132*55258Sandrew #endif 1133*55258Sandrew (void)printf("%d ", n); 1134*55258Sandrew (void)vprintf(fmt, ap); 1135*55258Sandrew (void)printf("\r\n"); 113636435Sbostic (void)fflush(stdout); 113710275Ssam if (debug) { 113826493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 1139*55258Sandrew vsyslog(LOG_DEBUG, fmt, ap); 1140*55258Sandrew } 114110275Ssam } 114210275Ssam 1143*55258Sandrew void 1144*55258Sandrew #if __STDC__ 1145*55258Sandrew lreply(int n, const char *fmt, ...) 1146*55258Sandrew #else 1147*55258Sandrew lreply(n, fmt, va_alist) 114810275Ssam int n; 114936446Sbostic char *fmt; 1150*55258Sandrew va_dcl 1151*55258Sandrew #endif 115210275Ssam { 1153*55258Sandrew va_list ap; 1154*55258Sandrew #if __STDC__ 1155*55258Sandrew va_start(ap, fmt); 1156*55258Sandrew #else 1157*55258Sandrew va_start(ap); 1158*55258Sandrew #endif 1159*55258Sandrew (void)printf("%d- ", n); 1160*55258Sandrew (void)vprintf(fmt, ap); 1161*55258Sandrew (void)printf("\r\n"); 116236435Sbostic (void)fflush(stdout); 116336446Sbostic if (debug) { 116436446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 1165*55258Sandrew vsyslog(LOG_DEBUG, fmt, ap); 116636446Sbostic } 116710275Ssam } 116810275Ssam 1169*55258Sandrew static void 117010275Ssam ack(s) 117110275Ssam char *s; 117210275Ssam { 117327106Smckusick reply(250, "%s command successful.", s); 117410275Ssam } 117510275Ssam 1176*55258Sandrew void 117710275Ssam nack(s) 117810275Ssam char *s; 117910275Ssam { 118010275Ssam reply(502, "%s command not implemented.", s); 118110275Ssam } 118210275Ssam 118336304Skarels /* ARGSUSED */ 1184*55258Sandrew char * 118526493Sminshall yyerror(s) 118626493Sminshall char *s; 118710275Ssam { 118826044Sminshall char *cp; 118926044Sminshall 119036551Sbostic if (cp = index(cbuf,'\n')) 119136551Sbostic *cp = '\0'; 119236933Skarels reply(500, "'%s': command not understood.", cbuf); 119310275Ssam } 119410275Ssam 1195*55258Sandrew void 119610275Ssam delete(name) 119710275Ssam char *name; 119810275Ssam { 119910275Ssam struct stat st; 120010275Ssam 1201*55258Sandrew LOGCMD("delete", name); 120210275Ssam if (stat(name, &st) < 0) { 120336304Skarels perror_reply(550, name); 120410275Ssam return; 120510275Ssam } 120610275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 120710275Ssam if (rmdir(name) < 0) { 120836304Skarels perror_reply(550, name); 120910275Ssam return; 121010275Ssam } 121110275Ssam goto done; 121210275Ssam } 121310275Ssam if (unlink(name) < 0) { 121436304Skarels perror_reply(550, name); 121510275Ssam return; 121610275Ssam } 121710275Ssam done: 121810275Ssam ack("DELE"); 121910275Ssam } 122010275Ssam 1221*55258Sandrew void 122210275Ssam cwd(path) 122310275Ssam char *path; 122410275Ssam { 122536620Srick if (chdir(path) < 0) 122636304Skarels perror_reply(550, path); 122736620Srick else 122836620Srick ack("CWD"); 122910275Ssam } 123010275Ssam 1231*55258Sandrew void 123210303Ssam makedir(name) 123310275Ssam char *name; 123410275Ssam { 1235*55258Sandrew LOGCMD("mkdir", name); 123636276Sbostic if (mkdir(name, 0777) < 0) 123736304Skarels perror_reply(550, name); 123836276Sbostic else 123936276Sbostic reply(257, "MKD command successful."); 124010275Ssam } 124110275Ssam 1242*55258Sandrew void 124310303Ssam removedir(name) 124410275Ssam char *name; 124510275Ssam { 1246*55258Sandrew LOGCMD("rmdir", name); 124736620Srick if (rmdir(name) < 0) 124836304Skarels perror_reply(550, name); 124936620Srick else 125036620Srick ack("RMD"); 125110275Ssam } 125210275Ssam 1253*55258Sandrew void 125410303Ssam pwd() 125510275Ssam { 125610303Ssam char path[MAXPATHLEN + 1]; 125710275Ssam 125836620Srick if (getwd(path) == (char *)NULL) 125927106Smckusick reply(550, "%s.", path); 126036620Srick else 126136620Srick reply(257, "\"%s\" is current directory.", path); 126210275Ssam } 126310275Ssam 126410275Ssam char * 126510275Ssam renamefrom(name) 126610275Ssam char *name; 126710275Ssam { 126810275Ssam struct stat st; 126910275Ssam 127010275Ssam if (stat(name, &st) < 0) { 127136304Skarels perror_reply(550, name); 127210275Ssam return ((char *)0); 127310275Ssam } 127410303Ssam reply(350, "File exists, ready for destination name"); 127510275Ssam return (name); 127610275Ssam } 127710275Ssam 1278*55258Sandrew void 127910275Ssam renamecmd(from, to) 128010275Ssam char *from, *to; 128110275Ssam { 1282*55258Sandrew LOGCMD2("rename", from, to); 128336620Srick if (rename(from, to) < 0) 128436304Skarels perror_reply(550, "rename"); 128536620Srick else 128636620Srick ack("RNTO"); 128710275Ssam } 128810275Ssam 1289*55258Sandrew static void 129010275Ssam dolog(sin) 129110275Ssam struct sockaddr_in *sin; 129210275Ssam { 129336304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 129410275Ssam sizeof (struct in_addr), AF_INET); 129510275Ssam 129636304Skarels if (hp) 129726493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 129836304Skarels else 129926493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 130013247Ssam sizeof (remotehost)); 130136620Srick #ifdef SETPROCTITLE 130236933Skarels sprintf(proctitle, "%s: connected", remotehost); 130336933Skarels setproctitle(proctitle); 130436620Srick #endif /* SETPROCTITLE */ 130536933Skarels 130652998Sbostic if (logging) 130752998Sbostic syslog(LOG_INFO, "connection from %s", remotehost); 130810275Ssam } 130910695Ssam 131010695Ssam /* 131113247Ssam * Record logout in wtmp file 131213247Ssam * and exit with supplied status. 131313247Ssam */ 1314*55258Sandrew void 131513247Ssam dologout(status) 131613247Ssam int status; 131713247Ssam { 131817580Ssam if (logged_in) { 131936304Skarels (void) seteuid((uid_t)0); 132035672Sbostic logwtmp(ttyline, "", ""); 132113247Ssam } 132214436Ssam /* beware of flushing buffers after a SIGPIPE */ 132314436Ssam _exit(status); 132413247Ssam } 132513247Ssam 1326*55258Sandrew static void 1327*55258Sandrew myoob(signo) 1328*55258Sandrew int signo; 132926044Sminshall { 133027750Sminshall char *cp; 133126044Sminshall 133227750Sminshall /* only process if transfer occurring */ 133336304Skarels if (!transflag) 133426044Sminshall return; 133527750Sminshall cp = tmpline; 133627750Sminshall if (getline(cp, 7, stdin) == NULL) { 133736304Skarels reply(221, "You could at least say goodbye."); 133827750Sminshall dologout(0); 133926044Sminshall } 134026044Sminshall upper(cp); 134136933Skarels if (strcmp(cp, "ABOR\r\n") == 0) { 134236933Skarels tmpline[0] = '\0'; 134336933Skarels reply(426, "Transfer aborted. Data connection closed."); 134436933Skarels reply(226, "Abort successful"); 134536933Skarels longjmp(urgcatch, 1); 134636933Skarels } 134736933Skarels if (strcmp(cp, "STAT\r\n") == 0) { 134836933Skarels if (file_size != (off_t) -1) 1349*55258Sandrew reply(213, "Status: %qd of %qd bytes transferred", 135036933Skarels byte_count, file_size); 135136933Skarels else 1352*55258Sandrew reply(213, "Status: %qd bytes transferred", byte_count); 135336933Skarels } 135426044Sminshall } 135526044Sminshall 135627106Smckusick /* 135736620Srick * Note: a response of 425 is not mentioned as a possible response to 135852998Sbostic * the PASV command in RFC959. However, it has been blessed as 135952998Sbostic * a legitimate response by Jon Postel in a telephone conversation 136036620Srick * with Rick Adams on 25 Jan 89. 136127106Smckusick */ 1362*55258Sandrew void 136326044Sminshall passive() 136426044Sminshall { 136526044Sminshall int len; 136626044Sminshall register char *p, *a; 136726044Sminshall 136826044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 136926044Sminshall if (pdata < 0) { 137036620Srick perror_reply(425, "Can't open passive connection"); 137126044Sminshall return; 137226044Sminshall } 137336933Skarels pasv_addr = ctrl_addr; 137436933Skarels pasv_addr.sin_port = 0; 137536304Skarels (void) seteuid((uid_t)0); 137636933Skarels if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 137736304Skarels (void) seteuid((uid_t)pw->pw_uid); 137836620Srick goto pasv_error; 137926044Sminshall } 138036304Skarels (void) seteuid((uid_t)pw->pw_uid); 138136933Skarels len = sizeof(pasv_addr); 138236933Skarels if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 138336620Srick goto pasv_error; 138436620Srick if (listen(pdata, 1) < 0) 138536620Srick goto pasv_error; 138636933Skarels a = (char *) &pasv_addr.sin_addr; 138736933Skarels p = (char *) &pasv_addr.sin_port; 138826044Sminshall 138926044Sminshall #define UC(b) (((int) b) & 0xff) 139026044Sminshall 139126044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 139226044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 139336620Srick return; 139436620Srick 139536620Srick pasv_error: 139636620Srick (void) close(pdata); 139736620Srick pdata = -1; 139836620Srick perror_reply(425, "Can't open passive connection"); 139936620Srick return; 140026044Sminshall } 140126044Sminshall 140236304Skarels /* 140336304Skarels * Generate unique name for file with basename "local". 140436304Skarels * The file named "local" is already known to exist. 140536304Skarels * Generates failure reply on error. 140636304Skarels */ 1407*55258Sandrew static char * 140826044Sminshall gunique(local) 140926044Sminshall char *local; 141026044Sminshall { 141126044Sminshall static char new[MAXPATHLEN]; 141236304Skarels struct stat st; 1413*55258Sandrew int count; 1414*55258Sandrew char *cp; 141526044Sminshall 1416*55258Sandrew cp = rindex(local, '/'); 141736304Skarels if (cp) 141826044Sminshall *cp = '\0'; 141936620Srick if (stat(cp ? local : ".", &st) < 0) { 142036933Skarels perror_reply(553, cp ? local : "."); 142126044Sminshall return((char *) 0); 142226044Sminshall } 142336620Srick if (cp) 142436620Srick *cp = '/'; 142526044Sminshall (void) strcpy(new, local); 142626044Sminshall cp = new + strlen(new); 142726044Sminshall *cp++ = '.'; 142836304Skarels for (count = 1; count < 100; count++) { 1429*55258Sandrew (void)sprintf(cp, "%d", count); 143036304Skarels if (stat(new, &st) < 0) 143136304Skarels return(new); 143226044Sminshall } 143336304Skarels reply(452, "Unique file name cannot be created."); 1434*55258Sandrew return(NULL); 143526044Sminshall } 143636304Skarels 143736304Skarels /* 143836304Skarels * Format and send reply containing system error number. 143936304Skarels */ 1440*55258Sandrew void 144136304Skarels perror_reply(code, string) 144236304Skarels int code; 144336304Skarels char *string; 144436304Skarels { 144542412Sbostic reply(code, "%s: %s.", string, strerror(errno)); 144636304Skarels } 144736620Srick 144836620Srick static char *onefile[] = { 144936620Srick "", 145036620Srick 0 145136620Srick }; 145236620Srick 1453*55258Sandrew void 145436620Srick send_file_list(whichfiles) 145536620Srick char *whichfiles; 145636620Srick { 145736620Srick struct stat st; 145836620Srick DIR *dirp = NULL; 145946669Sbostic struct dirent *dir; 146036620Srick FILE *dout = NULL; 146136620Srick register char **dirlist, *dirname; 146237459Skarels int simple = 0; 146336620Srick 146436620Srick if (strpbrk(whichfiles, "~{[*?") != NULL) { 1465*55258Sandrew extern char *globerr; 146636933Skarels 146736620Srick globerr = NULL; 146846669Sbostic dirlist = ftpglob(whichfiles); 146936620Srick if (globerr != NULL) { 147036620Srick reply(550, globerr); 147136620Srick return; 147236620Srick } else if (dirlist == NULL) { 147336620Srick errno = ENOENT; 147436620Srick perror_reply(550, whichfiles); 147536620Srick return; 147636620Srick } 147736620Srick } else { 147836620Srick onefile[0] = whichfiles; 147936620Srick dirlist = onefile; 148037459Skarels simple = 1; 148136620Srick } 148236933Skarels 148336933Skarels if (setjmp(urgcatch)) { 148436933Skarels transflag = 0; 148536933Skarels return; 148636933Skarels } 148736620Srick while (dirname = *dirlist++) { 148836620Srick if (stat(dirname, &st) < 0) { 148936933Skarels /* 149036933Skarels * If user typed "ls -l", etc, and the client 149136933Skarels * used NLST, do what the user meant. 149236933Skarels */ 149336933Skarels if (dirname[0] == '-' && *dirlist == NULL && 149436933Skarels transflag == 0) { 149536933Skarels retrieve("/bin/ls %s", dirname); 149636933Skarels return; 149736933Skarels } 149836620Srick perror_reply(550, whichfiles); 149936620Srick if (dout != NULL) { 150036620Srick (void) fclose(dout); 150136933Skarels transflag = 0; 150236620Srick data = -1; 150336620Srick pdata = -1; 150436620Srick } 150536620Srick return; 150636620Srick } 150736933Skarels 150836620Srick if ((st.st_mode&S_IFMT) == S_IFREG) { 150936620Srick if (dout == NULL) { 151037459Skarels dout = dataconn("file list", (off_t)-1, "w"); 151136620Srick if (dout == NULL) 151236620Srick return; 151336933Skarels transflag++; 151436620Srick } 151538158Srick fprintf(dout, "%s%s\n", dirname, 151638158Srick type == TYPE_A ? "\r" : ""); 151736933Skarels byte_count += strlen(dirname) + 1; 151836620Srick continue; 151936933Skarels } else if ((st.st_mode&S_IFMT) != S_IFDIR) 152036620Srick continue; 152136620Srick 152236620Srick if ((dirp = opendir(dirname)) == NULL) 152336620Srick continue; 152436620Srick 152536620Srick while ((dir = readdir(dirp)) != NULL) { 152636933Skarels char nbuf[MAXPATHLEN]; 152736620Srick 152836620Srick if (dir->d_name[0] == '.' && dir->d_namlen == 1) 152936620Srick continue; 153036933Skarels if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 153136933Skarels dir->d_namlen == 2) 153236620Srick continue; 153336620Srick 153436933Skarels sprintf(nbuf, "%s/%s", dirname, dir->d_name); 153536933Skarels 153636620Srick /* 153736933Skarels * We have to do a stat to insure it's 153836933Skarels * not a directory or special file. 153936620Srick */ 154037459Skarels if (simple || (stat(nbuf, &st) == 0 && 154137459Skarels (st.st_mode&S_IFMT) == S_IFREG)) { 154236620Srick if (dout == NULL) { 154337459Skarels dout = dataconn("file list", (off_t)-1, 154436620Srick "w"); 154536620Srick if (dout == NULL) 154636620Srick return; 154736933Skarels transflag++; 154836620Srick } 154936620Srick if (nbuf[0] == '.' && nbuf[1] == '/') 155038158Srick fprintf(dout, "%s%s\n", &nbuf[2], 155138158Srick type == TYPE_A ? "\r" : ""); 155236620Srick else 155338158Srick fprintf(dout, "%s%s\n", nbuf, 155438158Srick type == TYPE_A ? "\r" : ""); 155536933Skarels byte_count += strlen(nbuf) + 1; 155636620Srick } 155736620Srick } 155836620Srick (void) closedir(dirp); 155936620Srick } 156036620Srick 156136933Skarels if (dout == NULL) 156236933Skarels reply(550, "No files found."); 156336933Skarels else if (ferror(dout) != 0) 156436933Skarels perror_reply(550, "Data connection"); 156536933Skarels else 156636620Srick reply(226, "Transfer complete."); 156736620Srick 156836933Skarels transflag = 0; 156936933Skarels if (dout != NULL) 157036620Srick (void) fclose(dout); 157136620Srick data = -1; 157236620Srick pdata = -1; 157336620Srick } 157436620Srick 157536620Srick #ifdef SETPROCTITLE 157636620Srick /* 1577*55258Sandrew * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) 1578*55258Sandrew * Warning, since this is usually started from inetd.conf, it often doesn't 1579*55258Sandrew * have much of an environment or arglist to overwrite. 158036620Srick */ 1581*55258Sandrew void 1582*55258Sandrew #if __STDC__ 1583*55258Sandrew setproctitle(const char *fmt, ...) 1584*55258Sandrew #else 1585*55258Sandrew setproctitle(fmt, va_alist) 1586*55258Sandrew char *fmt; 1587*55258Sandrew va_dcl 1588*55258Sandrew #endif 158936620Srick { 159036620Srick register char *p, *bp, ch; 159136620Srick register int i; 1592*55258Sandrew va_list ap; 159336620Srick char buf[BUFSIZ]; 1594*55258Sandrew #if __STDC__ 1595*55258Sandrew va_start(ap, fmt); 1596*55258Sandrew #else 1597*55258Sandrew va_start(ap); 1598*55258Sandrew #endif 1599*55258Sandrew (void)vsnprintf(buf, sizeof(buf), fmt, ap); 160036620Srick 160136620Srick /* make ps print our process name */ 160236620Srick p = Argv[0]; 160336620Srick *p++ = '-'; 160436620Srick 160536620Srick i = strlen(buf); 160636620Srick if (i > LastArgv - p - 2) { 160736620Srick i = LastArgv - p - 2; 160836620Srick buf[i] = '\0'; 160936620Srick } 161036620Srick bp = buf; 161136620Srick while (ch = *bp++) 161236620Srick if (ch != '\n' && ch != '\r') 161336620Srick *p++ = ch; 161436620Srick while (p < LastArgv) 161536620Srick *p++ = ' '; 161636620Srick } 161736620Srick #endif /* SETPROCTITLE */ 1618