122499Sdist /* 236304Skarels * Copyright (c) 1985, 1988 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 533738Sbostic * Redistribution and use in source and binary forms are permitted 634769Sbostic * provided that the above copyright notice and this paragraph are 734769Sbostic * duplicated in all such forms and that any documentation, 834769Sbostic * advertising materials, and other materials related to such 934769Sbostic * distribution and use acknowledge that the software was developed 1034769Sbostic * by the University of California, Berkeley. The name of the 1134769Sbostic * University may not be used to endorse or promote products derived 1234769Sbostic * from this software without specific prior written permission. 1334769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534769Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622499Sdist */ 1722499Sdist 1810275Ssam #ifndef lint 1922499Sdist char copyright[] = 2036304Skarels "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\ 2122499Sdist All rights reserved.\n"; 2233738Sbostic #endif /* not lint */ 2310275Ssam 2422499Sdist #ifndef lint 25*36557Sbostic static char sccsid[] = "@(#)ftpd.c 5.25 (Berkeley) 01/16/89"; 2633738Sbostic #endif /* not lint */ 2722499Sdist 2810275Ssam /* 2910275Ssam * FTP server. 3010275Ssam */ 3110303Ssam #include <sys/param.h> 3210275Ssam #include <sys/stat.h> 3310275Ssam #include <sys/ioctl.h> 3410275Ssam #include <sys/socket.h> 3513247Ssam #include <sys/file.h> 3613595Ssam #include <sys/wait.h> 3710275Ssam 3810275Ssam #include <netinet/in.h> 3910275Ssam 4013034Ssam #include <arpa/ftp.h> 4113211Sroot #include <arpa/inet.h> 4226044Sminshall #include <arpa/telnet.h> 4313034Ssam 4410275Ssam #include <stdio.h> 4510275Ssam #include <signal.h> 4610275Ssam #include <pwd.h> 4710275Ssam #include <setjmp.h> 4810275Ssam #include <netdb.h> 4910423Ssam #include <errno.h> 5026044Sminshall #include <strings.h> 5126493Sminshall #include <syslog.h> 5236435Sbostic #include <varargs.h> 5310275Ssam 5410695Ssam /* 5510695Ssam * File containing login names 5610695Ssam * NOT to be used on this machine. 5710695Ssam * Commonly used to disallow uucp. 5810695Ssam */ 5910695Ssam #define FTPUSERS "/etc/ftpusers" 6010695Ssam 6110275Ssam extern int errno; 6210275Ssam extern char *sys_errlist[]; 6336304Skarels extern int sys_nerr; 6410275Ssam extern char *crypt(); 6510275Ssam extern char version[]; 6610275Ssam extern char *home; /* pointer to home directory for glob */ 6736276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 6836304Skarels extern int ftpd_pclose(), fclose(); 6926044Sminshall extern char *getline(); 7026044Sminshall extern char cbuf[]; 7136551Sbostic extern off_t restart_point; 7210275Ssam 7310275Ssam struct sockaddr_in ctrl_addr; 7410275Ssam struct sockaddr_in data_source; 7510275Ssam struct sockaddr_in data_dest; 7610275Ssam struct sockaddr_in his_addr; 7710275Ssam 7810275Ssam int data; 7926044Sminshall jmp_buf errcatch, urgcatch; 8010275Ssam int logged_in; 8110275Ssam struct passwd *pw; 8210275Ssam int debug; 8326493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8411757Ssam int logging; 8510275Ssam int guest; 8610275Ssam int type; 8710275Ssam int form; 8810275Ssam int stru; /* avoid C keyword */ 8910275Ssam int mode; 9010321Ssam int usedefault = 1; /* for data transfers */ 9136304Skarels int pdata = -1; /* for passive mode */ 9226044Sminshall int transflag; 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 10810275Ssam int lostconn(); 10926044Sminshall int myoob(); 11010275Ssam FILE *getdatasock(), *dataconn(); 11110275Ssam 11210275Ssam main(argc, argv) 11310275Ssam int argc; 11410275Ssam char *argv[]; 11510275Ssam { 11627750Sminshall int addrlen, on = 1; 11710275Ssam char *cp; 11810275Ssam 11916339Skarels addrlen = sizeof (his_addr); 12036304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 12126493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12210275Ssam exit(1); 12310275Ssam } 12416339Skarels addrlen = sizeof (ctrl_addr); 12536304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 12626493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 12716339Skarels exit(1); 12816339Skarels } 12916339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 13010275Ssam debug = 0; 13126493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 13210275Ssam argc--, argv++; 13310275Ssam while (argc > 0 && *argv[0] == '-') { 13410275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 13510275Ssam 13611653Ssam case 'v': 13711653Ssam debug = 1; 13811653Ssam break; 13911653Ssam 14010275Ssam case 'd': 14110275Ssam debug = 1; 14210275Ssam break; 14310275Ssam 14411757Ssam case 'l': 14511757Ssam logging = 1; 14611757Ssam break; 14711757Ssam 14811653Ssam case 't': 14911653Ssam timeout = atoi(++cp); 15011653Ssam goto nextopt; 15111653Ssam 15210275Ssam default: 15316339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 15416339Skarels *cp); 15510275Ssam break; 15610275Ssam } 15711653Ssam nextopt: 15810275Ssam argc--, argv++; 15910275Ssam } 16030944Scsvsj (void) freopen("/dev/null", "w", stderr); 16126493Sminshall (void) signal(SIGPIPE, lostconn); 16226493Sminshall (void) signal(SIGCHLD, SIG_IGN); 16335691Sbostic if ((int)signal(SIGURG, myoob) < 0) 16426493Sminshall syslog(LOG_ERR, "signal: %m"); 16535691Sbostic 16627750Sminshall /* handle urgent data inline */ 16736276Sbostic /* Sequent defines this, but it doesn't work */ 16827750Sminshall #ifdef SO_OOBINLINE 16936276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 17027750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 17136276Sbostic #endif 17236304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 17336304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 17416760Slepreau dolog(&his_addr); 17516339Skarels /* do telnet option negotiation here */ 17616339Skarels /* 17716339Skarels * Set up default state 17816339Skarels */ 17916339Skarels data = -1; 18016339Skarels type = TYPE_A; 18116339Skarels form = FORM_N; 18216339Skarels stru = STRU_F; 18316339Skarels mode = MODE_S; 18426044Sminshall tmpline[0] = '\0'; 18526493Sminshall (void) gethostname(hostname, sizeof (hostname)); 18636276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 18736304Skarels (void) setjmp(errcatch); 18836304Skarels for (;;) 18926493Sminshall (void) yyparse(); 19010275Ssam } 19110419Ssam 19210275Ssam lostconn() 19310275Ssam { 19410275Ssam 19514089Ssam if (debug) 19626493Sminshall syslog(LOG_DEBUG, "lost connection"); 19714089Ssam dologout(-1); 19810275Ssam } 19910275Ssam 20035672Sbostic static char ttyline[20]; 20135672Sbostic 20236185Sbostic /* 20336185Sbostic * Helper function for sgetpwnam(). 20436185Sbostic */ 20536185Sbostic char * 20636185Sbostic sgetsave(s) 20736185Sbostic char *s; 20836185Sbostic { 20936185Sbostic char *malloc(); 21036185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 21136185Sbostic 21236185Sbostic if (new == NULL) { 21336276Sbostic reply(553, "Local resource failure: malloc"); 21436185Sbostic dologout(1); 21536185Sbostic } 21636185Sbostic (void) strcpy(new, s); 21736185Sbostic return (new); 21836185Sbostic } 21936185Sbostic 22036185Sbostic /* 22136185Sbostic * Save the result of a getpwnam. Used for USER command, since 22236185Sbostic * the data returned must not be clobbered by any other command 22336185Sbostic * (e.g., globbing). 22436185Sbostic */ 22536185Sbostic struct passwd * 22636185Sbostic sgetpwnam(name) 22736185Sbostic char *name; 22836185Sbostic { 22936185Sbostic static struct passwd save; 23036185Sbostic register struct passwd *p; 23136185Sbostic char *sgetsave(); 23236185Sbostic 23336185Sbostic if ((p = getpwnam(name)) == NULL) 23436185Sbostic return (p); 23536185Sbostic if (save.pw_name) { 23636185Sbostic free(save.pw_name); 23736185Sbostic free(save.pw_passwd); 23836185Sbostic free(save.pw_comment); 23936185Sbostic free(save.pw_gecos); 24036185Sbostic free(save.pw_dir); 24136185Sbostic free(save.pw_shell); 24236185Sbostic } 24336185Sbostic save = *p; 24436185Sbostic save.pw_name = sgetsave(p->pw_name); 24536185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 24636185Sbostic save.pw_comment = sgetsave(p->pw_comment); 24736185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 24836185Sbostic save.pw_dir = sgetsave(p->pw_dir); 24936185Sbostic save.pw_shell = sgetsave(p->pw_shell); 25036185Sbostic return (&save); 25136185Sbostic } 25236185Sbostic 25336304Skarels int login_attempts; /* number of failed login attempts */ 25436304Skarels int askpasswd; /* had user command, ask for passwd */ 25536304Skarels 25636304Skarels /* 25736304Skarels * USER command. 25836304Skarels * Sets global passwd pointer pw if named account exists 25936304Skarels * and is acceptable; sets askpasswd if a PASS command is 26036304Skarels * expected. If logged in previously, need to reset state. 26136304Skarels * If name is "ftp" or "anonymous" and ftp account exists, 26236304Skarels * set guest and pw, then just return. 26336304Skarels * If account doesn't exist, ask for passwd anyway. 26436304Skarels * Otherwise, check user requesting login privileges. 26536304Skarels * Disallow anyone who does not have a standard 26636304Skarels * shell returned by getusershell() (/etc/shells). 26736304Skarels * Disallow anyone mentioned in the file FTPUSERS 26836304Skarels * to allow people such as root and uucp to be avoided. 26936304Skarels */ 27036304Skarels user(name) 27136304Skarels char *name; 27236304Skarels { 27336304Skarels register char *cp; 27436304Skarels FILE *fd; 27536304Skarels char *shell; 27636551Sbostic char line[BUFSIZ], *getusershell(); 27736304Skarels 27836304Skarels if (logged_in) { 27936304Skarels if (guest) { 28036304Skarels reply(530, "Can't change user from guest login."); 28136304Skarels return; 28236304Skarels } 28336304Skarels end_login(); 28436304Skarels } 28536304Skarels 28636304Skarels guest = 0; 28736304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 28836304Skarels if ((pw = sgetpwnam("ftp")) != NULL) { 28936304Skarels guest = 1; 29036304Skarels askpasswd = 1; 29136304Skarels reply(331, "Guest login ok, send ident as password."); 29236551Sbostic } 29336551Sbostic else 29436304Skarels reply(530, "User %s unknown.", name); 29536304Skarels return; 29636304Skarels } 29736304Skarels if (pw = sgetpwnam(name)) { 29836304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 29936304Skarels shell = "/bin/sh"; 30036304Skarels while ((cp = getusershell()) != NULL) 30136304Skarels if (strcmp(cp, shell) == 0) 30236304Skarels break; 30336304Skarels endusershell(); 30436304Skarels if (cp == NULL) { 30536304Skarels reply(530, "User %s access denied.", name); 30636550Sbostic syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s", 30736550Sbostic remotehost, name); 30836304Skarels pw = (struct passwd *) NULL; 30936304Skarels return; 31036304Skarels } 31136304Skarels if ((fd = fopen(FTPUSERS, "r")) != NULL) { 31236304Skarels while (fgets(line, sizeof (line), fd) != NULL) { 31336304Skarels if ((cp = index(line, '\n')) != NULL) 31436304Skarels *cp = '\0'; 31536304Skarels if (strcmp(line, name) == 0) { 31636304Skarels reply(530, "User %s access denied.", name); 31736550Sbostic syslog(LOG_ERR, "FTP LOGIN REFUSED FROM %s, %s", 31836550Sbostic remotehost, name); 31936304Skarels pw = (struct passwd *) NULL; 32036304Skarels return; 32136304Skarels } 32236304Skarels } 32336304Skarels } 32436304Skarels (void) fclose(fd); 32536304Skarels } 32636304Skarels reply(331, "Password required for %s.", name); 32736304Skarels askpasswd = 1; 32836304Skarels /* 32936304Skarels * Delay before reading passwd after first failed 33036304Skarels * attempt to slow down passwd-guessing programs. 33136304Skarels */ 33236304Skarels if (login_attempts) 33336304Skarels sleep((unsigned) login_attempts); 33436304Skarels } 33536304Skarels 33636304Skarels /* 33736304Skarels * Terminate login as previous user, if any, resetting state; 33836304Skarels * used when USER command is given or login fails. 33936304Skarels */ 34036304Skarels end_login() 34136304Skarels { 34236304Skarels 34336304Skarels (void) seteuid((uid_t)0); 34436304Skarels if (logged_in) 34536304Skarels logwtmp(ttyline, "", ""); 34636304Skarels pw = NULL; 34736304Skarels logged_in = 0; 34836304Skarels guest = 0; 34936304Skarels } 35036304Skarels 35110275Ssam pass(passwd) 35210275Ssam char *passwd; 35310275Ssam { 35436304Skarels char *xpasswd, *salt; 35510275Ssam 35636304Skarels if (logged_in || askpasswd == 0) { 35710275Ssam reply(503, "Login with USER first."); 35810275Ssam return; 35910275Ssam } 36036304Skarels askpasswd = 0; 36110275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 36236304Skarels if (pw == NULL) 36336304Skarels salt = "xx"; 36436304Skarels else 36536304Skarels salt = pw->pw_passwd; 36636304Skarels xpasswd = crypt(passwd, salt); 36716760Slepreau /* The strcmp does not catch null passwords! */ 36836304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 36936304Skarels strcmp(xpasswd, pw->pw_passwd)) { 37010275Ssam reply(530, "Login incorrect."); 37110275Ssam pw = NULL; 37236304Skarels if (login_attempts++ >= 5) { 37336304Skarels syslog(LOG_ERR, 37436304Skarels "repeated login failures from %s", 37536304Skarels remotehost); 37636304Skarels exit(0); 37736304Skarels } 37810275Ssam return; 37910275Ssam } 38010275Ssam } 38136304Skarels login_attempts = 0; /* this time successful */ 38236304Skarels (void) setegid((gid_t)pw->pw_gid); 38336304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 38416033Sralph 38536192Sbostic /* open wtmp before chroot */ 38636192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 38736192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 38836192Sbostic logged_in = 1; 38936192Sbostic 39036446Sbostic if (guest) { 39136446Sbostic if (chroot(pw->pw_dir) < 0) { 39236446Sbostic reply(550, "Can't set guest privileges."); 39336446Sbostic goto bad; 39436446Sbostic } 39536304Skarels } 39636446Sbostic else if (chdir(pw->pw_dir)) 39736446Sbostic if (chdir("/")) { 39836446Sbostic reply(530, "User %s: can't change directory to %s.", 39936446Sbostic pw->pw_name, pw->pw_dir); 40036446Sbostic goto bad; 40136446Sbostic } 40236446Sbostic else 40336446Sbostic lreply(230, "No directory! Logging in with home=/"); 40436304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 40536304Skarels reply(550, "Can't set uid."); 40636304Skarels goto bad; 40736304Skarels } 40836550Sbostic if (guest) { 40936192Sbostic reply(230, "Guest login ok, access restrictions apply."); 41036550Sbostic syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 41136550Sbostic remotehost, passwd); 41236551Sbostic } 41336551Sbostic else { 41410275Ssam reply(230, "User %s logged in.", pw->pw_name); 41536550Sbostic syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", 41636550Sbostic remotehost, pw->pw_name); 41736550Sbostic } 41810303Ssam home = pw->pw_dir; /* home dir for globbing */ 41910303Ssam return; 42010303Ssam bad: 42136304Skarels /* Forget all about it... */ 42236304Skarels end_login(); 42310275Ssam } 42410275Ssam 42510275Ssam retrieve(cmd, name) 42610275Ssam char *cmd, *name; 42710275Ssam { 42810275Ssam FILE *fin, *dout; 42910275Ssam struct stat st; 43026044Sminshall int (*closefunc)(), tmp; 43110275Ssam 432*36557Sbostic if (cmd == 0) { 43336446Sbostic fin = fopen(name, "r"), closefunc = fclose; 434*36557Sbostic st.st_size = 0; 435*36557Sbostic } else { 43610275Ssam char line[BUFSIZ]; 43710275Ssam 43826493Sminshall (void) sprintf(line, cmd, name), name = line; 43936304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 440*36557Sbostic st.st_size = -1; 44110275Ssam } 44210275Ssam if (fin == NULL) { 44313152Ssam if (errno != 0) 44436304Skarels perror_reply(550, name); 44510275Ssam return; 44610275Ssam } 44710275Ssam if (cmd == 0 && 44810275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 44910275Ssam reply(550, "%s: not a plain file.", name); 45010275Ssam goto done; 45110275Ssam } 45236551Sbostic if (restart_point) 45336551Sbostic if (type == TYPE_A) { 45436551Sbostic if (fseek(fin, restart_point, L_SET) < 0) 45536551Sbostic perror_reply(550, name); 45636551Sbostic } 45736551Sbostic else if (lseek(fileno(fin), restart_point, L_SET) < 0) 45836551Sbostic perror_reply(550, name); 45910275Ssam dout = dataconn(name, st.st_size, "w"); 46010275Ssam if (dout == NULL) 46110275Ssam goto done; 46236446Sbostic if ((tmp = send_data(fin, dout, st.st_blksize)) > 0 || 46336551Sbostic ferror(dout) > 0) 46436304Skarels perror_reply(550, name); 46536551Sbostic else if (tmp == 0) 46610275Ssam reply(226, "Transfer complete."); 46726493Sminshall (void) fclose(dout); 46826044Sminshall data = -1; 46926044Sminshall pdata = -1; 47010275Ssam done: 47110275Ssam (*closefunc)(fin); 47210275Ssam } 47310275Ssam 47436304Skarels store(name, mode, unique) 47510275Ssam char *name, *mode; 47636304Skarels int unique; 47710275Ssam { 47810275Ssam FILE *fout, *din; 47936446Sbostic struct stat st; 48036304Skarels int (*closefunc)(), tmp; 48136304Skarels char *gunique(); 48210275Ssam 48336446Sbostic if (unique && stat(name, &st) == 0 && 48436446Sbostic (name = gunique(name)) == NULL) 48536446Sbostic return; 48610303Ssam 48736446Sbostic fout = fopen(name, mode), closefunc = fclose; 48810275Ssam if (fout == NULL) { 48936304Skarels perror_reply(553, name); 49010275Ssam return; 49110275Ssam } 49236551Sbostic if (restart_point) 49336551Sbostic if (type == TYPE_A) { 49436551Sbostic if (fseek(fout, restart_point, L_SET) < 0) 49536551Sbostic perror_reply(550, name); 49636551Sbostic } 49736551Sbostic else if (lseek(fileno(fout), restart_point, L_SET) < 0) 49836551Sbostic perror_reply(550, name); 49936304Skarels din = dataconn(name, (off_t)-1, "r"); 50010275Ssam if (din == NULL) 50110275Ssam goto done; 50236304Skarels if ((tmp = receive_data(din, fout)) > 0) 50336304Skarels perror_reply(552, name); 50436304Skarels else if (tmp == 0) { 50536304Skarels if (ferror(fout) > 0) 50636304Skarels perror_reply(552, name); 50736304Skarels else if (unique) 50836304Skarels reply(226, "Transfer complete (unique file name:%s).", 50936304Skarels name); 51036304Skarels else 51136304Skarels reply(226, "Transfer complete."); 51226044Sminshall } 51326493Sminshall (void) fclose(din); 51426044Sminshall data = -1; 51526044Sminshall pdata = -1; 51610275Ssam done: 51710275Ssam (*closefunc)(fout); 51810275Ssam } 51910275Ssam 52010275Ssam FILE * 52110275Ssam getdatasock(mode) 52210275Ssam char *mode; 52310275Ssam { 52417157Ssam int s, on = 1; 52510275Ssam 52610275Ssam if (data >= 0) 52710275Ssam return (fdopen(data, mode)); 52813247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 52910602Ssam if (s < 0) 53010275Ssam return (NULL); 53136304Skarels (void) seteuid((uid_t)0); 53226493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 53310602Ssam goto bad; 53413152Ssam /* anchor socket to avoid multi-homing problems */ 53513152Ssam data_source.sin_family = AF_INET; 53613152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 53736304Skarels if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) 53810602Ssam goto bad; 53936304Skarels (void) seteuid((uid_t)pw->pw_uid); 54010275Ssam return (fdopen(s, mode)); 54110602Ssam bad: 54236304Skarels (void) seteuid((uid_t)pw->pw_uid); 54326493Sminshall (void) close(s); 54410602Ssam return (NULL); 54510275Ssam } 54610275Ssam 54710275Ssam FILE * 54810275Ssam dataconn(name, size, mode) 54910275Ssam char *name; 55011653Ssam off_t size; 55110275Ssam char *mode; 55210275Ssam { 55310275Ssam char sizebuf[32]; 55410275Ssam FILE *file; 55511653Ssam int retry = 0; 55610275Ssam 55736304Skarels if (size != (off_t) -1) 55826493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 55910275Ssam else 56010275Ssam (void) strcpy(sizebuf, ""); 56136304Skarels if (pdata >= 0) { 56226044Sminshall struct sockaddr_in from; 56326044Sminshall int s, fromlen = sizeof(from); 56426044Sminshall 56536304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 56626044Sminshall if (s < 0) { 56726044Sminshall reply(425, "Can't open data connection."); 56826044Sminshall (void) close(pdata); 56926044Sminshall pdata = -1; 57026044Sminshall return(NULL); 57126044Sminshall } 57226044Sminshall (void) close(pdata); 57326044Sminshall pdata = s; 57436235Skarels reply(150, "Opening %s mode data connection for %s%s.", 57536235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 57626044Sminshall return(fdopen(pdata, mode)); 57726044Sminshall } 57810275Ssam if (data >= 0) { 57910275Ssam reply(125, "Using existing data connection for %s%s.", 58010275Ssam name, sizebuf); 58110321Ssam usedefault = 1; 58210275Ssam return (fdopen(data, mode)); 58310275Ssam } 58410566Ssam if (usedefault) 58510422Ssam data_dest = his_addr; 58610422Ssam usedefault = 1; 58710275Ssam file = getdatasock(mode); 58810275Ssam if (file == NULL) { 58910275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 59013247Ssam inet_ntoa(data_source.sin_addr), 59110275Ssam ntohs(data_source.sin_port), 59236304Skarels errno < sys_nerr ? sys_errlist[errno] : "unknown error"); 59310275Ssam return (NULL); 59410275Ssam } 59510275Ssam data = fileno(file); 59636304Skarels while (connect(data, (struct sockaddr *)&data_dest, 59736304Skarels sizeof (data_dest)) < 0) { 59811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 59926493Sminshall sleep((unsigned) swaitint); 60011653Ssam retry += swaitint; 60111653Ssam continue; 60211653Ssam } 60336304Skarels perror_reply(425, "Can't build data connection"); 60410275Ssam (void) fclose(file); 60510275Ssam data = -1; 60610275Ssam return (NULL); 60710275Ssam } 60836235Skarels reply(150, "Opening %s mode data connection for %s%s.", 60936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 61010275Ssam return (file); 61110275Ssam } 61210275Ssam 61310275Ssam /* 61410275Ssam * Tranfer the contents of "instr" to 61510275Ssam * "outstr" peer using the appropriate 61610275Ssam * encapulation of the date subject 61710275Ssam * to Mode, Structure, and Type. 61810275Ssam * 61910275Ssam * NB: Form isn't handled. 62010275Ssam */ 62136446Sbostic send_data(instr, outstr, blksize) 62210275Ssam FILE *instr, *outstr; 62336446Sbostic off_t blksize; 62410275Ssam { 62536446Sbostic register int c, cnt; 62636446Sbostic register char *buf; 62736446Sbostic int netfd, filefd; 62810275Ssam 62926044Sminshall transflag++; 63026044Sminshall if (setjmp(urgcatch)) { 63126044Sminshall transflag = 0; 63226044Sminshall return(-1); 63326044Sminshall } 63410275Ssam switch (type) { 63510275Ssam 63610275Ssam case TYPE_A: 63710275Ssam while ((c = getc(instr)) != EOF) { 63811220Ssam if (c == '\n') { 63926044Sminshall if (ferror (outstr)) { 64026044Sminshall transflag = 0; 64111220Ssam return (1); 64226044Sminshall } 64327750Sminshall (void) putc('\r', outstr); 64411220Ssam } 64527750Sminshall (void) putc(c, outstr); 64610275Ssam } 64726044Sminshall transflag = 0; 64826044Sminshall if (ferror (instr) || ferror (outstr)) { 64911220Ssam return (1); 65026044Sminshall } 65110275Ssam return (0); 65236446Sbostic 65310275Ssam case TYPE_I: 65410275Ssam case TYPE_L: 65536446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 65636446Sbostic transflag = 0; 65736446Sbostic return (1); 65836446Sbostic } 65910275Ssam netfd = fileno(outstr); 66010275Ssam filefd = fileno(instr); 66136446Sbostic while ((cnt = read(filefd, buf, sizeof(buf))) > 0 && 66236446Sbostic write(netfd, buf, cnt) == cnt); 66326044Sminshall transflag = 0; 66436446Sbostic (void)free(buf); 66536446Sbostic return (cnt != 0); 66610275Ssam } 66727106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 66826044Sminshall transflag = 0; 66927106Smckusick return (-1); 67010275Ssam } 67110275Ssam 67210275Ssam /* 67310275Ssam * Transfer data from peer to 67410275Ssam * "outstr" using the appropriate 67510275Ssam * encapulation of the data subject 67610275Ssam * to Mode, Structure, and Type. 67710275Ssam * 67810275Ssam * N.B.: Form isn't handled. 67910275Ssam */ 68010275Ssam receive_data(instr, outstr) 68110275Ssam FILE *instr, *outstr; 68210275Ssam { 68310275Ssam register int c; 68411220Ssam int cnt; 68510275Ssam char buf[BUFSIZ]; 68610275Ssam 68710275Ssam 68826044Sminshall transflag++; 68926044Sminshall if (setjmp(urgcatch)) { 69026044Sminshall transflag = 0; 69126044Sminshall return(-1); 69226044Sminshall } 69310275Ssam switch (type) { 69410275Ssam 69510275Ssam case TYPE_I: 69610275Ssam case TYPE_L: 69726044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 69826044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 69926044Sminshall transflag = 0; 70010275Ssam return (1); 70126044Sminshall } 70226044Sminshall } 70326044Sminshall transflag = 0; 70410275Ssam return (cnt < 0); 70510275Ssam 70610275Ssam case TYPE_E: 70727106Smckusick reply(553, "TYPE E not implemented."); 70826044Sminshall transflag = 0; 70927106Smckusick return (-1); 71010275Ssam 71110275Ssam case TYPE_A: 71210275Ssam while ((c = getc(instr)) != EOF) { 71327750Sminshall while (c == '\r') { 71426044Sminshall if (ferror (outstr)) { 71526044Sminshall transflag = 0; 71611220Ssam return (1); 71726044Sminshall } 71811220Ssam if ((c = getc(instr)) != '\n') 71927750Sminshall (void) putc ('\r', outstr); 72026044Sminshall /* if (c == '\0') */ 72126044Sminshall /* continue; */ 72210275Ssam } 72327750Sminshall (void) putc (c, outstr); 72410275Ssam } 72526044Sminshall transflag = 0; 72611220Ssam if (ferror (instr) || ferror (outstr)) 72711220Ssam return (1); 72810275Ssam return (0); 72910275Ssam } 73026044Sminshall transflag = 0; 73110275Ssam fatal("Unknown type in receive_data."); 73210275Ssam /*NOTREACHED*/ 73310275Ssam } 73410275Ssam 73510275Ssam fatal(s) 73610275Ssam char *s; 73710275Ssam { 73810275Ssam reply(451, "Error in server: %s\n", s); 73910275Ssam reply(221, "Closing connection due to server error."); 74013247Ssam dologout(0); 74110275Ssam } 74210275Ssam 74336446Sbostic /* VARARGS2 */ 74436446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 74510275Ssam int n; 74636446Sbostic char *fmt; 74710275Ssam { 74810275Ssam printf("%d ", n); 74936446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 75010275Ssam printf("\r\n"); 75136435Sbostic (void)fflush(stdout); 75210275Ssam if (debug) { 75326493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 75436446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 75510275Ssam } 75610275Ssam } 75710275Ssam 75836446Sbostic /* VARARGS2 */ 75936446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 76010275Ssam int n; 76136446Sbostic char *fmt; 76210275Ssam { 76336446Sbostic printf("%d- ", n); 76436446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 76536446Sbostic printf("\r\n"); 76636435Sbostic (void)fflush(stdout); 76736446Sbostic if (debug) { 76836446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 76936446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 77036446Sbostic } 77110275Ssam } 77210275Ssam 77310275Ssam ack(s) 77410275Ssam char *s; 77510275Ssam { 77627106Smckusick reply(250, "%s command successful.", s); 77710275Ssam } 77810275Ssam 77910275Ssam nack(s) 78010275Ssam char *s; 78110275Ssam { 78210275Ssam reply(502, "%s command not implemented.", s); 78310275Ssam } 78410275Ssam 78536304Skarels /* ARGSUSED */ 78626493Sminshall yyerror(s) 78726493Sminshall char *s; 78810275Ssam { 78926044Sminshall char *cp; 79026044Sminshall 79136551Sbostic if (cp = index(cbuf,'\n')) 79236551Sbostic *cp = '\0'; 79326044Sminshall reply(500, "'%s': command not understood.",cbuf); 79410275Ssam } 79510275Ssam 79610275Ssam delete(name) 79710275Ssam char *name; 79810275Ssam { 79910275Ssam struct stat st; 80010275Ssam 80110275Ssam if (stat(name, &st) < 0) { 80236304Skarels perror_reply(550, name); 80310275Ssam return; 80410275Ssam } 80510275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 80610275Ssam if (rmdir(name) < 0) { 80736304Skarels perror_reply(550, name); 80810275Ssam return; 80910275Ssam } 81010275Ssam goto done; 81110275Ssam } 81210275Ssam if (unlink(name) < 0) { 81336304Skarels perror_reply(550, name); 81410275Ssam return; 81510275Ssam } 81610275Ssam done: 81710275Ssam ack("DELE"); 81810275Ssam } 81910275Ssam 82010275Ssam cwd(path) 82110275Ssam char *path; 82210275Ssam { 82310275Ssam 82410275Ssam if (chdir(path) < 0) { 82536304Skarels perror_reply(550, path); 82610275Ssam return; 82710275Ssam } 82810275Ssam ack("CWD"); 82910275Ssam } 83010275Ssam 83110303Ssam makedir(name) 83210275Ssam char *name; 83310275Ssam { 83436276Sbostic if (mkdir(name, 0777) < 0) 83536304Skarels perror_reply(550, name); 83636276Sbostic else 83736276Sbostic reply(257, "MKD command successful."); 83810275Ssam } 83910275Ssam 84010303Ssam removedir(name) 84110275Ssam char *name; 84210275Ssam { 84310275Ssam 84410275Ssam if (rmdir(name) < 0) { 84536304Skarels perror_reply(550, name); 84610275Ssam return; 84710275Ssam } 84827106Smckusick ack("RMD"); 84910275Ssam } 85010275Ssam 85110303Ssam pwd() 85210275Ssam { 85310303Ssam char path[MAXPATHLEN + 1]; 85436304Skarels extern char *getwd(); 85510275Ssam 85636304Skarels if (getwd(path) == (char *)NULL) { 85727106Smckusick reply(550, "%s.", path); 85810275Ssam return; 85910275Ssam } 86027106Smckusick reply(257, "\"%s\" is current directory.", path); 86110275Ssam } 86210275Ssam 86310275Ssam char * 86410275Ssam renamefrom(name) 86510275Ssam char *name; 86610275Ssam { 86710275Ssam struct stat st; 86810275Ssam 86910275Ssam if (stat(name, &st) < 0) { 87036304Skarels perror_reply(550, name); 87110275Ssam return ((char *)0); 87210275Ssam } 87310303Ssam reply(350, "File exists, ready for destination name"); 87410275Ssam return (name); 87510275Ssam } 87610275Ssam 87710275Ssam renamecmd(from, to) 87810275Ssam char *from, *to; 87910275Ssam { 88010275Ssam 88110275Ssam if (rename(from, to) < 0) { 88236304Skarels perror_reply(550, "rename"); 88310275Ssam return; 88410275Ssam } 88510275Ssam ack("RNTO"); 88610275Ssam } 88710275Ssam 88810275Ssam dolog(sin) 88910275Ssam struct sockaddr_in *sin; 89010275Ssam { 89136304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 89210275Ssam sizeof (struct in_addr), AF_INET); 89336304Skarels time_t t, time(); 89426493Sminshall extern char *ctime(); 89510275Ssam 89636304Skarels if (hp) 89726493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 89836304Skarels else 89926493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 90013247Ssam sizeof (remotehost)); 90113247Ssam if (!logging) 90213247Ssam return; 90326493Sminshall t = time((time_t *) 0); 90436304Skarels syslog(LOG_INFO, "connection from %s at %s", 90536304Skarels remotehost, ctime(&t)); 90610275Ssam } 90710695Ssam 90810695Ssam /* 90913247Ssam * Record logout in wtmp file 91013247Ssam * and exit with supplied status. 91113247Ssam */ 91213247Ssam dologout(status) 91313247Ssam int status; 91413247Ssam { 91517580Ssam if (logged_in) { 91636304Skarels (void) seteuid((uid_t)0); 91735672Sbostic logwtmp(ttyline, "", ""); 91813247Ssam } 91914436Ssam /* beware of flushing buffers after a SIGPIPE */ 92014436Ssam _exit(status); 92113247Ssam } 92213247Ssam 92326044Sminshall myoob() 92426044Sminshall { 92527750Sminshall char *cp; 92626044Sminshall 92727750Sminshall /* only process if transfer occurring */ 92836304Skarels if (!transflag) 92926044Sminshall return; 93027750Sminshall cp = tmpline; 93127750Sminshall if (getline(cp, 7, stdin) == NULL) { 93236304Skarels reply(221, "You could at least say goodbye."); 93327750Sminshall dologout(0); 93426044Sminshall } 93526044Sminshall upper(cp); 93626227Ssam if (strcmp(cp, "ABOR\r\n")) 93726044Sminshall return; 93826044Sminshall tmpline[0] = '\0'; 93926044Sminshall reply(426,"Transfer aborted. Data connection closed."); 94026044Sminshall reply(226,"Abort successful"); 94126044Sminshall longjmp(urgcatch, 1); 94226044Sminshall } 94326044Sminshall 94427106Smckusick /* 94527106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 94627106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 94727106Smckusick */ 94826044Sminshall passive() 94926044Sminshall { 95026044Sminshall int len; 95126044Sminshall struct sockaddr_in tmp; 95226044Sminshall register char *p, *a; 95326044Sminshall 95426044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 95526044Sminshall if (pdata < 0) { 95627106Smckusick reply(530, "Can't open passive connection"); 95726044Sminshall return; 95826044Sminshall } 95926044Sminshall tmp = ctrl_addr; 96026044Sminshall tmp.sin_port = 0; 96136304Skarels (void) seteuid((uid_t)0); 96226493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 96336304Skarels (void) seteuid((uid_t)pw->pw_uid); 96426044Sminshall (void) close(pdata); 96526044Sminshall pdata = -1; 96627106Smckusick reply(530, "Can't open passive connection"); 96726044Sminshall return; 96826044Sminshall } 96936304Skarels (void) seteuid((uid_t)pw->pw_uid); 97026044Sminshall len = sizeof(tmp); 97136304Skarels if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) { 97226044Sminshall (void) close(pdata); 97326044Sminshall pdata = -1; 97427106Smckusick reply(530, "Can't open passive connection"); 97526044Sminshall return; 97626044Sminshall } 97726044Sminshall if (listen(pdata, 1) < 0) { 97826044Sminshall (void) close(pdata); 97926044Sminshall pdata = -1; 98027106Smckusick reply(530, "Can't open passive connection"); 98126044Sminshall return; 98226044Sminshall } 98326044Sminshall a = (char *) &tmp.sin_addr; 98426044Sminshall p = (char *) &tmp.sin_port; 98526044Sminshall 98626044Sminshall #define UC(b) (((int) b) & 0xff) 98726044Sminshall 98826044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 98926044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 99026044Sminshall } 99126044Sminshall 99236304Skarels /* 99336304Skarels * Generate unique name for file with basename "local". 99436304Skarels * The file named "local" is already known to exist. 99536304Skarels * Generates failure reply on error. 99636304Skarels */ 99726044Sminshall char * 99826044Sminshall gunique(local) 99926044Sminshall char *local; 100026044Sminshall { 100126044Sminshall static char new[MAXPATHLEN]; 100236304Skarels struct stat st; 100326044Sminshall char *cp = rindex(local, '/'); 100426044Sminshall int d, count=0; 100526044Sminshall 100636304Skarels if (cp) 100726044Sminshall *cp = '\0'; 100836304Skarels d = stat(cp ? local : ".", &st); 100936304Skarels if (cp) 101026044Sminshall *cp = '/'; 101126044Sminshall if (d < 0) { 101236304Skarels perror_reply(553, local); 101326044Sminshall return((char *) 0); 101426044Sminshall } 101526044Sminshall (void) strcpy(new, local); 101626044Sminshall cp = new + strlen(new); 101726044Sminshall *cp++ = '.'; 101836304Skarels for (count = 1; count < 100; count++) { 101936304Skarels (void) sprintf(cp, "%d", count); 102036304Skarels if (stat(new, &st) < 0) 102136304Skarels return(new); 102226044Sminshall } 102336304Skarels reply(452, "Unique file name cannot be created."); 102436304Skarels return((char *) 0); 102526044Sminshall } 102636304Skarels 102736304Skarels /* 102836304Skarels * Format and send reply containing system error number. 102936304Skarels */ 103036304Skarels perror_reply(code, string) 103136304Skarels int code; 103236304Skarels char *string; 103336304Skarels { 103436304Skarels 103536304Skarels if (errno < sys_nerr) 103636304Skarels reply(code, "%s: %s.", string, sys_errlist[errno]); 103736304Skarels else 103836304Skarels reply(code, "%s: unknown error %d.", string, errno); 103936304Skarels } 1040