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*36446Sbostic static char sccsid[] = "@(#)ftpd.c 5.22 (Berkeley) 12/19/88"; 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[]; 7110275Ssam 7210275Ssam struct sockaddr_in ctrl_addr; 7310275Ssam struct sockaddr_in data_source; 7410275Ssam struct sockaddr_in data_dest; 7510275Ssam struct sockaddr_in his_addr; 7610275Ssam 7710275Ssam int data; 7826044Sminshall jmp_buf errcatch, urgcatch; 7910275Ssam int logged_in; 8010275Ssam struct passwd *pw; 8110275Ssam int debug; 8226493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8311757Ssam int logging; 8410275Ssam int guest; 8510275Ssam int type; 8610275Ssam int form; 8710275Ssam int stru; /* avoid C keyword */ 8810275Ssam int mode; 8910321Ssam int usedefault = 1; /* for data transfers */ 9036304Skarels int pdata = -1; /* for passive mode */ 9126044Sminshall int transflag; 9226044Sminshall char tmpline[7]; 9336276Sbostic char hostname[MAXHOSTNAMELEN]; 9436276Sbostic char remotehost[MAXHOSTNAMELEN]; 9510275Ssam 9611653Ssam /* 9711653Ssam * Timeout intervals for retrying connections 9811653Ssam * to hosts that don't accept PORT cmds. This 9911653Ssam * is a kludge, but given the problems with TCP... 10011653Ssam */ 10111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10211653Ssam #define SWAITINT 5 /* interval between retries */ 10311653Ssam 10411653Ssam int swaitmax = SWAITMAX; 10511653Ssam int swaitint = SWAITINT; 10611653Ssam 10710275Ssam int lostconn(); 10826044Sminshall int myoob(); 10910275Ssam FILE *getdatasock(), *dataconn(); 11010275Ssam 11110275Ssam main(argc, argv) 11210275Ssam int argc; 11310275Ssam char *argv[]; 11410275Ssam { 11527750Sminshall int addrlen, on = 1; 11610275Ssam char *cp; 11710275Ssam 11816339Skarels addrlen = sizeof (his_addr); 11936304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 12026493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12110275Ssam exit(1); 12210275Ssam } 12316339Skarels addrlen = sizeof (ctrl_addr); 12436304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 12526493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 12616339Skarels exit(1); 12716339Skarels } 12816339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 12910275Ssam debug = 0; 13026493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 13110275Ssam argc--, argv++; 13210275Ssam while (argc > 0 && *argv[0] == '-') { 13310275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 13410275Ssam 13511653Ssam case 'v': 13611653Ssam debug = 1; 13711653Ssam break; 13811653Ssam 13910275Ssam case 'd': 14010275Ssam debug = 1; 14110275Ssam break; 14210275Ssam 14311757Ssam case 'l': 14411757Ssam logging = 1; 14511757Ssam break; 14611757Ssam 14711653Ssam case 't': 14811653Ssam timeout = atoi(++cp); 14911653Ssam goto nextopt; 15011653Ssam 15110275Ssam default: 15216339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 15316339Skarels *cp); 15410275Ssam break; 15510275Ssam } 15611653Ssam nextopt: 15710275Ssam argc--, argv++; 15810275Ssam } 15930944Scsvsj (void) freopen("/dev/null", "w", stderr); 16026493Sminshall (void) signal(SIGPIPE, lostconn); 16126493Sminshall (void) signal(SIGCHLD, SIG_IGN); 16235691Sbostic if ((int)signal(SIGURG, myoob) < 0) 16326493Sminshall syslog(LOG_ERR, "signal: %m"); 16435691Sbostic 16527750Sminshall /* handle urgent data inline */ 16636276Sbostic /* Sequent defines this, but it doesn't work */ 16727750Sminshall #ifdef SO_OOBINLINE 16836276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 16927750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 17036276Sbostic #endif 17136304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 17236304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 17316760Slepreau dolog(&his_addr); 17416339Skarels /* do telnet option negotiation here */ 17516339Skarels /* 17616339Skarels * Set up default state 17716339Skarels */ 17816339Skarels data = -1; 17916339Skarels type = TYPE_A; 18016339Skarels form = FORM_N; 18116339Skarels stru = STRU_F; 18216339Skarels mode = MODE_S; 18326044Sminshall tmpline[0] = '\0'; 18426493Sminshall (void) gethostname(hostname, sizeof (hostname)); 18536276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 18636304Skarels (void) setjmp(errcatch); 18736304Skarels for (;;) 18826493Sminshall (void) yyparse(); 18910275Ssam } 19010419Ssam 19110275Ssam lostconn() 19210275Ssam { 19310275Ssam 19414089Ssam if (debug) 19526493Sminshall syslog(LOG_DEBUG, "lost connection"); 19614089Ssam dologout(-1); 19710275Ssam } 19810275Ssam 19935672Sbostic static char ttyline[20]; 20035672Sbostic 20136185Sbostic /* 20236185Sbostic * Helper function for sgetpwnam(). 20336185Sbostic */ 20436185Sbostic char * 20536185Sbostic sgetsave(s) 20636185Sbostic char *s; 20736185Sbostic { 20836185Sbostic char *malloc(); 20936185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 21036185Sbostic 21136185Sbostic if (new == NULL) { 21236276Sbostic reply(553, "Local resource failure: malloc"); 21336185Sbostic dologout(1); 21436185Sbostic } 21536185Sbostic (void) strcpy(new, s); 21636185Sbostic return (new); 21736185Sbostic } 21836185Sbostic 21936185Sbostic /* 22036185Sbostic * Save the result of a getpwnam. Used for USER command, since 22136185Sbostic * the data returned must not be clobbered by any other command 22236185Sbostic * (e.g., globbing). 22336185Sbostic */ 22436185Sbostic struct passwd * 22536185Sbostic sgetpwnam(name) 22636185Sbostic char *name; 22736185Sbostic { 22836185Sbostic static struct passwd save; 22936185Sbostic register struct passwd *p; 23036185Sbostic char *sgetsave(); 23136185Sbostic 23236185Sbostic if ((p = getpwnam(name)) == NULL) 23336185Sbostic return (p); 23436185Sbostic if (save.pw_name) { 23536185Sbostic free(save.pw_name); 23636185Sbostic free(save.pw_passwd); 23736185Sbostic free(save.pw_comment); 23836185Sbostic free(save.pw_gecos); 23936185Sbostic free(save.pw_dir); 24036185Sbostic free(save.pw_shell); 24136185Sbostic } 24236185Sbostic save = *p; 24336185Sbostic save.pw_name = sgetsave(p->pw_name); 24436185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 24536185Sbostic save.pw_comment = sgetsave(p->pw_comment); 24636185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 24736185Sbostic save.pw_dir = sgetsave(p->pw_dir); 24836185Sbostic save.pw_shell = sgetsave(p->pw_shell); 24936185Sbostic return (&save); 25036185Sbostic } 25136185Sbostic 25236304Skarels int login_attempts; /* number of failed login attempts */ 25336304Skarels int askpasswd; /* had user command, ask for passwd */ 25436304Skarels 25536304Skarels /* 25636304Skarels * USER command. 25736304Skarels * Sets global passwd pointer pw if named account exists 25836304Skarels * and is acceptable; sets askpasswd if a PASS command is 25936304Skarels * expected. If logged in previously, need to reset state. 26036304Skarels * If name is "ftp" or "anonymous" and ftp account exists, 26136304Skarels * set guest and pw, then just return. 26236304Skarels * If account doesn't exist, ask for passwd anyway. 26336304Skarels * Otherwise, check user requesting login privileges. 26436304Skarels * Disallow anyone who does not have a standard 26536304Skarels * shell returned by getusershell() (/etc/shells). 26636304Skarels * Disallow anyone mentioned in the file FTPUSERS 26736304Skarels * to allow people such as root and uucp to be avoided. 26836304Skarels */ 26936304Skarels user(name) 27036304Skarels char *name; 27136304Skarels { 27236304Skarels register char *cp; 27336304Skarels FILE *fd; 27436304Skarels char *shell; 27536304Skarels char line[BUFSIZ], *index(), *getusershell(); 27636304Skarels 27736304Skarels if (logged_in) { 27836304Skarels if (guest) { 27936304Skarels reply(530, "Can't change user from guest login."); 28036304Skarels return; 28136304Skarels } 28236304Skarels end_login(); 28336304Skarels } 28436304Skarels 28536304Skarels guest = 0; 28636304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 28736304Skarels if ((pw = sgetpwnam("ftp")) != NULL) { 28836304Skarels guest = 1; 28936304Skarels askpasswd = 1; 29036304Skarels reply(331, "Guest login ok, send ident as password."); 29136304Skarels } else 29236304Skarels reply(530, "User %s unknown.", name); 29336304Skarels return; 29436304Skarels } 29536304Skarels if (pw = sgetpwnam(name)) { 29636304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 29736304Skarels shell = "/bin/sh"; 29836304Skarels while ((cp = getusershell()) != NULL) 29936304Skarels if (strcmp(cp, shell) == 0) 30036304Skarels break; 30136304Skarels endusershell(); 30236304Skarels if (cp == NULL) { 30336304Skarels reply(530, "User %s access denied.", name); 30436304Skarels pw = (struct passwd *) NULL; 30536304Skarels return; 30636304Skarels } 30736304Skarels if ((fd = fopen(FTPUSERS, "r")) != NULL) { 30836304Skarels while (fgets(line, sizeof (line), fd) != NULL) { 30936304Skarels if ((cp = index(line, '\n')) != NULL) 31036304Skarels *cp = '\0'; 31136304Skarels if (strcmp(line, name) == 0) { 31236304Skarels reply(530, "User %s access denied.", name); 31336304Skarels pw = (struct passwd *) NULL; 31436304Skarels return; 31536304Skarels } 31636304Skarels } 31736304Skarels } 31836304Skarels (void) fclose(fd); 31936304Skarels } 32036304Skarels reply(331, "Password required for %s.", name); 32136304Skarels askpasswd = 1; 32236304Skarels /* 32336304Skarels * Delay before reading passwd after first failed 32436304Skarels * attempt to slow down passwd-guessing programs. 32536304Skarels */ 32636304Skarels if (login_attempts) 32736304Skarels sleep((unsigned) login_attempts); 32836304Skarels } 32936304Skarels 33036304Skarels /* 33136304Skarels * Terminate login as previous user, if any, resetting state; 33236304Skarels * used when USER command is given or login fails. 33336304Skarels */ 33436304Skarels end_login() 33536304Skarels { 33636304Skarels 33736304Skarels (void) seteuid((uid_t)0); 33836304Skarels if (logged_in) 33936304Skarels logwtmp(ttyline, "", ""); 34036304Skarels pw = NULL; 34136304Skarels logged_in = 0; 34236304Skarels guest = 0; 34336304Skarels } 34436304Skarels 34510275Ssam pass(passwd) 34610275Ssam char *passwd; 34710275Ssam { 34836304Skarels char *xpasswd, *salt; 34910275Ssam 35036304Skarels if (logged_in || askpasswd == 0) { 35110275Ssam reply(503, "Login with USER first."); 35210275Ssam return; 35310275Ssam } 35436304Skarels askpasswd = 0; 35510275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 35636304Skarels if (pw == NULL) 35736304Skarels salt = "xx"; 35836304Skarels else 35936304Skarels salt = pw->pw_passwd; 36036304Skarels xpasswd = crypt(passwd, salt); 36116760Slepreau /* The strcmp does not catch null passwords! */ 36236304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 36336304Skarels strcmp(xpasswd, pw->pw_passwd)) { 36410275Ssam reply(530, "Login incorrect."); 36510275Ssam pw = NULL; 36636304Skarels if (login_attempts++ >= 5) { 36736304Skarels syslog(LOG_ERR, 36836304Skarels "repeated login failures from %s", 36936304Skarels remotehost); 37036304Skarels exit(0); 37136304Skarels } 37210275Ssam return; 37310275Ssam } 37410275Ssam } 37536304Skarels login_attempts = 0; /* this time successful */ 37636304Skarels (void) setegid((gid_t)pw->pw_gid); 37736304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 37816033Sralph 37936192Sbostic /* open wtmp before chroot */ 38036192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 38136192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 38236192Sbostic logged_in = 1; 38336192Sbostic 384*36446Sbostic if (guest) { 385*36446Sbostic if (chroot(pw->pw_dir) < 0) { 386*36446Sbostic reply(550, "Can't set guest privileges."); 387*36446Sbostic goto bad; 388*36446Sbostic } 38936304Skarels } 390*36446Sbostic else if (chdir(pw->pw_dir)) 391*36446Sbostic if (chdir("/")) { 392*36446Sbostic reply(530, "User %s: can't change directory to %s.", 393*36446Sbostic pw->pw_name, pw->pw_dir); 394*36446Sbostic goto bad; 395*36446Sbostic } 396*36446Sbostic else 397*36446Sbostic lreply(230, "No directory! Logging in with home=/"); 39836304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 39936304Skarels reply(550, "Can't set uid."); 40036304Skarels goto bad; 40136304Skarels } 40236304Skarels if (guest) 40336192Sbostic reply(230, "Guest login ok, access restrictions apply."); 40436304Skarels else 40510275Ssam reply(230, "User %s logged in.", pw->pw_name); 40610303Ssam home = pw->pw_dir; /* home dir for globbing */ 40710303Ssam return; 40810303Ssam bad: 40936304Skarels /* Forget all about it... */ 41036304Skarels end_login(); 41110275Ssam } 41210275Ssam 41310275Ssam retrieve(cmd, name) 41410275Ssam char *cmd, *name; 41510275Ssam { 41610275Ssam FILE *fin, *dout; 41710275Ssam struct stat st; 41826044Sminshall int (*closefunc)(), tmp; 41910275Ssam 420*36446Sbostic if (cmd == 0) 421*36446Sbostic fin = fopen(name, "r"), closefunc = fclose; 422*36446Sbostic else { 42310275Ssam char line[BUFSIZ]; 42410275Ssam 42526493Sminshall (void) sprintf(line, cmd, name), name = line; 42636304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 42710275Ssam } 42810275Ssam if (fin == NULL) { 42913152Ssam if (errno != 0) 43036304Skarels perror_reply(550, name); 43110275Ssam return; 43210275Ssam } 43310275Ssam st.st_size = 0; 43410275Ssam if (cmd == 0 && 43510275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 43610275Ssam reply(550, "%s: not a plain file.", name); 43710275Ssam goto done; 43810275Ssam } 43910275Ssam dout = dataconn(name, st.st_size, "w"); 44010275Ssam if (dout == NULL) 44110275Ssam goto done; 442*36446Sbostic if ((tmp = send_data(fin, dout, st.st_blksize)) > 0 || 443*36446Sbostic ferror(dout) > 0) { 44436304Skarels perror_reply(550, name); 44526044Sminshall } 44626044Sminshall else if (tmp == 0) { 44710275Ssam reply(226, "Transfer complete."); 44826044Sminshall } 44926493Sminshall (void) fclose(dout); 45026044Sminshall data = -1; 45126044Sminshall pdata = -1; 45210275Ssam done: 45310275Ssam (*closefunc)(fin); 45410275Ssam } 45510275Ssam 45636304Skarels store(name, mode, unique) 45710275Ssam char *name, *mode; 45836304Skarels int unique; 45910275Ssam { 46010275Ssam FILE *fout, *din; 461*36446Sbostic struct stat st; 46236304Skarels int (*closefunc)(), tmp; 46336304Skarels char *gunique(); 46410275Ssam 465*36446Sbostic if (unique && stat(name, &st) == 0 && 466*36446Sbostic (name = gunique(name)) == NULL) 467*36446Sbostic return; 46810303Ssam 469*36446Sbostic fout = fopen(name, mode), closefunc = fclose; 47010275Ssam if (fout == NULL) { 47136304Skarels perror_reply(553, name); 47210275Ssam return; 47310275Ssam } 47436304Skarels din = dataconn(name, (off_t)-1, "r"); 47510275Ssam if (din == NULL) 47610275Ssam goto done; 47736304Skarels if ((tmp = receive_data(din, fout)) > 0) 47836304Skarels perror_reply(552, name); 47936304Skarels else if (tmp == 0) { 48036304Skarels if (ferror(fout) > 0) 48136304Skarels perror_reply(552, name); 48236304Skarels else if (unique) 48336304Skarels reply(226, "Transfer complete (unique file name:%s).", 48436304Skarels name); 48536304Skarels else 48636304Skarels reply(226, "Transfer complete."); 48726044Sminshall } 48826493Sminshall (void) fclose(din); 48926044Sminshall data = -1; 49026044Sminshall pdata = -1; 49110275Ssam done: 49210275Ssam (*closefunc)(fout); 49310275Ssam } 49410275Ssam 49510275Ssam FILE * 49610275Ssam getdatasock(mode) 49710275Ssam char *mode; 49810275Ssam { 49917157Ssam int s, on = 1; 50010275Ssam 50110275Ssam if (data >= 0) 50210275Ssam return (fdopen(data, mode)); 50313247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 50410602Ssam if (s < 0) 50510275Ssam return (NULL); 50636304Skarels (void) seteuid((uid_t)0); 50726493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 50810602Ssam goto bad; 50913152Ssam /* anchor socket to avoid multi-homing problems */ 51013152Ssam data_source.sin_family = AF_INET; 51113152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 51236304Skarels if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) 51310602Ssam goto bad; 51436304Skarels (void) seteuid((uid_t)pw->pw_uid); 51510275Ssam return (fdopen(s, mode)); 51610602Ssam bad: 51736304Skarels (void) seteuid((uid_t)pw->pw_uid); 51826493Sminshall (void) close(s); 51910602Ssam return (NULL); 52010275Ssam } 52110275Ssam 52210275Ssam FILE * 52310275Ssam dataconn(name, size, mode) 52410275Ssam char *name; 52511653Ssam off_t size; 52610275Ssam char *mode; 52710275Ssam { 52810275Ssam char sizebuf[32]; 52910275Ssam FILE *file; 53011653Ssam int retry = 0; 53110275Ssam 53236304Skarels if (size != (off_t) -1) 53326493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 53410275Ssam else 53510275Ssam (void) strcpy(sizebuf, ""); 53636304Skarels if (pdata >= 0) { 53726044Sminshall struct sockaddr_in from; 53826044Sminshall int s, fromlen = sizeof(from); 53926044Sminshall 54036304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 54126044Sminshall if (s < 0) { 54226044Sminshall reply(425, "Can't open data connection."); 54326044Sminshall (void) close(pdata); 54426044Sminshall pdata = -1; 54526044Sminshall return(NULL); 54626044Sminshall } 54726044Sminshall (void) close(pdata); 54826044Sminshall pdata = s; 54936235Skarels reply(150, "Opening %s mode data connection for %s%s.", 55036235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 55126044Sminshall return(fdopen(pdata, mode)); 55226044Sminshall } 55310275Ssam if (data >= 0) { 55410275Ssam reply(125, "Using existing data connection for %s%s.", 55510275Ssam name, sizebuf); 55610321Ssam usedefault = 1; 55710275Ssam return (fdopen(data, mode)); 55810275Ssam } 55910566Ssam if (usedefault) 56010422Ssam data_dest = his_addr; 56110422Ssam usedefault = 1; 56210275Ssam file = getdatasock(mode); 56310275Ssam if (file == NULL) { 56410275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 56513247Ssam inet_ntoa(data_source.sin_addr), 56610275Ssam ntohs(data_source.sin_port), 56736304Skarels errno < sys_nerr ? sys_errlist[errno] : "unknown error"); 56810275Ssam return (NULL); 56910275Ssam } 57010275Ssam data = fileno(file); 57136304Skarels while (connect(data, (struct sockaddr *)&data_dest, 57236304Skarels sizeof (data_dest)) < 0) { 57311653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 57426493Sminshall sleep((unsigned) swaitint); 57511653Ssam retry += swaitint; 57611653Ssam continue; 57711653Ssam } 57836304Skarels perror_reply(425, "Can't build data connection"); 57910275Ssam (void) fclose(file); 58010275Ssam data = -1; 58110275Ssam return (NULL); 58210275Ssam } 58336235Skarels reply(150, "Opening %s mode data connection for %s%s.", 58436235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 58510275Ssam return (file); 58610275Ssam } 58710275Ssam 58810275Ssam /* 58910275Ssam * Tranfer the contents of "instr" to 59010275Ssam * "outstr" peer using the appropriate 59110275Ssam * encapulation of the date subject 59210275Ssam * to Mode, Structure, and Type. 59310275Ssam * 59410275Ssam * NB: Form isn't handled. 59510275Ssam */ 596*36446Sbostic send_data(instr, outstr, blksize) 59710275Ssam FILE *instr, *outstr; 598*36446Sbostic off_t blksize; 59910275Ssam { 600*36446Sbostic register int c, cnt; 601*36446Sbostic register char *buf; 602*36446Sbostic int netfd, filefd; 60310275Ssam 60426044Sminshall transflag++; 60526044Sminshall if (setjmp(urgcatch)) { 60626044Sminshall transflag = 0; 60726044Sminshall return(-1); 60826044Sminshall } 60910275Ssam switch (type) { 61010275Ssam 61110275Ssam case TYPE_A: 61210275Ssam while ((c = getc(instr)) != EOF) { 61311220Ssam if (c == '\n') { 61426044Sminshall if (ferror (outstr)) { 61526044Sminshall transflag = 0; 61611220Ssam return (1); 61726044Sminshall } 61827750Sminshall (void) putc('\r', outstr); 61911220Ssam } 62027750Sminshall (void) putc(c, outstr); 62110275Ssam } 62226044Sminshall transflag = 0; 62326044Sminshall if (ferror (instr) || ferror (outstr)) { 62411220Ssam return (1); 62526044Sminshall } 62610275Ssam return (0); 627*36446Sbostic 62810275Ssam case TYPE_I: 62910275Ssam case TYPE_L: 630*36446Sbostic if ((buf = malloc((u_int)blksize)) == NULL) { 631*36446Sbostic transflag = 0; 632*36446Sbostic return (1); 633*36446Sbostic } 63410275Ssam netfd = fileno(outstr); 63510275Ssam filefd = fileno(instr); 636*36446Sbostic while ((cnt = read(filefd, buf, sizeof(buf))) > 0 && 637*36446Sbostic write(netfd, buf, cnt) == cnt); 63826044Sminshall transflag = 0; 639*36446Sbostic (void)free(buf); 640*36446Sbostic return (cnt != 0); 64110275Ssam } 64227106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 64326044Sminshall transflag = 0; 64427106Smckusick return (-1); 64510275Ssam } 64610275Ssam 64710275Ssam /* 64810275Ssam * Transfer data from peer to 64910275Ssam * "outstr" using the appropriate 65010275Ssam * encapulation of the data subject 65110275Ssam * to Mode, Structure, and Type. 65210275Ssam * 65310275Ssam * N.B.: Form isn't handled. 65410275Ssam */ 65510275Ssam receive_data(instr, outstr) 65610275Ssam FILE *instr, *outstr; 65710275Ssam { 65810275Ssam register int c; 65911220Ssam int cnt; 66010275Ssam char buf[BUFSIZ]; 66110275Ssam 66210275Ssam 66326044Sminshall transflag++; 66426044Sminshall if (setjmp(urgcatch)) { 66526044Sminshall transflag = 0; 66626044Sminshall return(-1); 66726044Sminshall } 66810275Ssam switch (type) { 66910275Ssam 67010275Ssam case TYPE_I: 67110275Ssam case TYPE_L: 67226044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 67326044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 67426044Sminshall transflag = 0; 67510275Ssam return (1); 67626044Sminshall } 67726044Sminshall } 67826044Sminshall transflag = 0; 67910275Ssam return (cnt < 0); 68010275Ssam 68110275Ssam case TYPE_E: 68227106Smckusick reply(553, "TYPE E not implemented."); 68326044Sminshall transflag = 0; 68427106Smckusick return (-1); 68510275Ssam 68610275Ssam case TYPE_A: 68710275Ssam while ((c = getc(instr)) != EOF) { 68827750Sminshall while (c == '\r') { 68926044Sminshall if (ferror (outstr)) { 69026044Sminshall transflag = 0; 69111220Ssam return (1); 69226044Sminshall } 69311220Ssam if ((c = getc(instr)) != '\n') 69427750Sminshall (void) putc ('\r', outstr); 69526044Sminshall /* if (c == '\0') */ 69626044Sminshall /* continue; */ 69710275Ssam } 69827750Sminshall (void) putc (c, outstr); 69910275Ssam } 70026044Sminshall transflag = 0; 70111220Ssam if (ferror (instr) || ferror (outstr)) 70211220Ssam return (1); 70310275Ssam return (0); 70410275Ssam } 70526044Sminshall transflag = 0; 70610275Ssam fatal("Unknown type in receive_data."); 70710275Ssam /*NOTREACHED*/ 70810275Ssam } 70910275Ssam 71010275Ssam fatal(s) 71110275Ssam char *s; 71210275Ssam { 71310275Ssam reply(451, "Error in server: %s\n", s); 71410275Ssam reply(221, "Closing connection due to server error."); 71513247Ssam dologout(0); 71610275Ssam } 71710275Ssam 718*36446Sbostic /* VARARGS2 */ 719*36446Sbostic reply(n, fmt, p0, p1, p2, p3, p4, p5) 72010275Ssam int n; 721*36446Sbostic char *fmt; 72210275Ssam { 72310275Ssam printf("%d ", n); 724*36446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 72510275Ssam printf("\r\n"); 72636435Sbostic (void)fflush(stdout); 72710275Ssam if (debug) { 72826493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 729*36446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 73010275Ssam } 73110275Ssam } 73210275Ssam 733*36446Sbostic /* VARARGS2 */ 734*36446Sbostic lreply(n, fmt, p0, p1, p2, p3, p4, p5) 73510275Ssam int n; 736*36446Sbostic char *fmt; 73710275Ssam { 738*36446Sbostic printf("%d- ", n); 739*36446Sbostic printf(fmt, p0, p1, p2, p3, p4, p5); 740*36446Sbostic printf("\r\n"); 74136435Sbostic (void)fflush(stdout); 742*36446Sbostic if (debug) { 743*36446Sbostic syslog(LOG_DEBUG, "<--- %d- ", n); 744*36446Sbostic syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); 745*36446Sbostic } 74610275Ssam } 74710275Ssam 74810275Ssam ack(s) 74910275Ssam char *s; 75010275Ssam { 75127106Smckusick reply(250, "%s command successful.", s); 75210275Ssam } 75310275Ssam 75410275Ssam nack(s) 75510275Ssam char *s; 75610275Ssam { 75710275Ssam reply(502, "%s command not implemented.", s); 75810275Ssam } 75910275Ssam 76036304Skarels /* ARGSUSED */ 76126493Sminshall yyerror(s) 76226493Sminshall char *s; 76310275Ssam { 76426044Sminshall char *cp; 76526044Sminshall 76626044Sminshall cp = index(cbuf,'\n'); 76726044Sminshall *cp = '\0'; 76826044Sminshall reply(500, "'%s': command not understood.",cbuf); 76910275Ssam } 77010275Ssam 77110275Ssam delete(name) 77210275Ssam char *name; 77310275Ssam { 77410275Ssam struct stat st; 77510275Ssam 77610275Ssam if (stat(name, &st) < 0) { 77736304Skarels perror_reply(550, name); 77810275Ssam return; 77910275Ssam } 78010275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 78110275Ssam if (rmdir(name) < 0) { 78236304Skarels perror_reply(550, name); 78310275Ssam return; 78410275Ssam } 78510275Ssam goto done; 78610275Ssam } 78710275Ssam if (unlink(name) < 0) { 78836304Skarels perror_reply(550, name); 78910275Ssam return; 79010275Ssam } 79110275Ssam done: 79210275Ssam ack("DELE"); 79310275Ssam } 79410275Ssam 79510275Ssam cwd(path) 79610275Ssam char *path; 79710275Ssam { 79810275Ssam 79910275Ssam if (chdir(path) < 0) { 80036304Skarels perror_reply(550, path); 80110275Ssam return; 80210275Ssam } 80310275Ssam ack("CWD"); 80410275Ssam } 80510275Ssam 80610303Ssam makedir(name) 80710275Ssam char *name; 80810275Ssam { 80936276Sbostic if (mkdir(name, 0777) < 0) 81036304Skarels perror_reply(550, name); 81136276Sbostic else 81236276Sbostic reply(257, "MKD command successful."); 81310275Ssam } 81410275Ssam 81510303Ssam removedir(name) 81610275Ssam char *name; 81710275Ssam { 81810275Ssam 81910275Ssam if (rmdir(name) < 0) { 82036304Skarels perror_reply(550, name); 82110275Ssam return; 82210275Ssam } 82327106Smckusick ack("RMD"); 82410275Ssam } 82510275Ssam 82610303Ssam pwd() 82710275Ssam { 82810303Ssam char path[MAXPATHLEN + 1]; 82936304Skarels extern char *getwd(); 83010275Ssam 83136304Skarels if (getwd(path) == (char *)NULL) { 83227106Smckusick reply(550, "%s.", path); 83310275Ssam return; 83410275Ssam } 83527106Smckusick reply(257, "\"%s\" is current directory.", path); 83610275Ssam } 83710275Ssam 83810275Ssam char * 83910275Ssam renamefrom(name) 84010275Ssam char *name; 84110275Ssam { 84210275Ssam struct stat st; 84310275Ssam 84410275Ssam if (stat(name, &st) < 0) { 84536304Skarels perror_reply(550, name); 84610275Ssam return ((char *)0); 84710275Ssam } 84810303Ssam reply(350, "File exists, ready for destination name"); 84910275Ssam return (name); 85010275Ssam } 85110275Ssam 85210275Ssam renamecmd(from, to) 85310275Ssam char *from, *to; 85410275Ssam { 85510275Ssam 85610275Ssam if (rename(from, to) < 0) { 85736304Skarels perror_reply(550, "rename"); 85810275Ssam return; 85910275Ssam } 86010275Ssam ack("RNTO"); 86110275Ssam } 86210275Ssam 86310275Ssam dolog(sin) 86410275Ssam struct sockaddr_in *sin; 86510275Ssam { 86636304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 86710275Ssam sizeof (struct in_addr), AF_INET); 86836304Skarels time_t t, time(); 86926493Sminshall extern char *ctime(); 87010275Ssam 87136304Skarels if (hp) 87226493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 87336304Skarels else 87426493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 87513247Ssam sizeof (remotehost)); 87613247Ssam if (!logging) 87713247Ssam return; 87826493Sminshall t = time((time_t *) 0); 87936304Skarels syslog(LOG_INFO, "connection from %s at %s", 88036304Skarels remotehost, ctime(&t)); 88110275Ssam } 88210695Ssam 88310695Ssam /* 88413247Ssam * Record logout in wtmp file 88513247Ssam * and exit with supplied status. 88613247Ssam */ 88713247Ssam dologout(status) 88813247Ssam int status; 88913247Ssam { 89017580Ssam if (logged_in) { 89136304Skarels (void) seteuid((uid_t)0); 89235672Sbostic logwtmp(ttyline, "", ""); 89313247Ssam } 89414436Ssam /* beware of flushing buffers after a SIGPIPE */ 89514436Ssam _exit(status); 89613247Ssam } 89713247Ssam 89826044Sminshall myoob() 89926044Sminshall { 90027750Sminshall char *cp; 90126044Sminshall 90227750Sminshall /* only process if transfer occurring */ 90336304Skarels if (!transflag) 90426044Sminshall return; 90527750Sminshall cp = tmpline; 90627750Sminshall if (getline(cp, 7, stdin) == NULL) { 90736304Skarels reply(221, "You could at least say goodbye."); 90827750Sminshall dologout(0); 90926044Sminshall } 91026044Sminshall upper(cp); 91126227Ssam if (strcmp(cp, "ABOR\r\n")) 91226044Sminshall return; 91326044Sminshall tmpline[0] = '\0'; 91426044Sminshall reply(426,"Transfer aborted. Data connection closed."); 91526044Sminshall reply(226,"Abort successful"); 91626044Sminshall longjmp(urgcatch, 1); 91726044Sminshall } 91826044Sminshall 91927106Smckusick /* 92027106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 92127106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 92227106Smckusick */ 92326044Sminshall passive() 92426044Sminshall { 92526044Sminshall int len; 92626044Sminshall struct sockaddr_in tmp; 92726044Sminshall register char *p, *a; 92826044Sminshall 92926044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 93026044Sminshall if (pdata < 0) { 93127106Smckusick reply(530, "Can't open passive connection"); 93226044Sminshall return; 93326044Sminshall } 93426044Sminshall tmp = ctrl_addr; 93526044Sminshall tmp.sin_port = 0; 93636304Skarels (void) seteuid((uid_t)0); 93726493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 93836304Skarels (void) seteuid((uid_t)pw->pw_uid); 93926044Sminshall (void) close(pdata); 94026044Sminshall pdata = -1; 94127106Smckusick reply(530, "Can't open passive connection"); 94226044Sminshall return; 94326044Sminshall } 94436304Skarels (void) seteuid((uid_t)pw->pw_uid); 94526044Sminshall len = sizeof(tmp); 94636304Skarels if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) { 94726044Sminshall (void) close(pdata); 94826044Sminshall pdata = -1; 94927106Smckusick reply(530, "Can't open passive connection"); 95026044Sminshall return; 95126044Sminshall } 95226044Sminshall if (listen(pdata, 1) < 0) { 95326044Sminshall (void) close(pdata); 95426044Sminshall pdata = -1; 95527106Smckusick reply(530, "Can't open passive connection"); 95626044Sminshall return; 95726044Sminshall } 95826044Sminshall a = (char *) &tmp.sin_addr; 95926044Sminshall p = (char *) &tmp.sin_port; 96026044Sminshall 96126044Sminshall #define UC(b) (((int) b) & 0xff) 96226044Sminshall 96326044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 96426044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 96526044Sminshall } 96626044Sminshall 96736304Skarels /* 96836304Skarels * Generate unique name for file with basename "local". 96936304Skarels * The file named "local" is already known to exist. 97036304Skarels * Generates failure reply on error. 97136304Skarels */ 97226044Sminshall char * 97326044Sminshall gunique(local) 97426044Sminshall char *local; 97526044Sminshall { 97626044Sminshall static char new[MAXPATHLEN]; 97736304Skarels struct stat st; 97826044Sminshall char *cp = rindex(local, '/'); 97926044Sminshall int d, count=0; 98026044Sminshall 98136304Skarels if (cp) 98226044Sminshall *cp = '\0'; 98336304Skarels d = stat(cp ? local : ".", &st); 98436304Skarels if (cp) 98526044Sminshall *cp = '/'; 98626044Sminshall if (d < 0) { 98736304Skarels perror_reply(553, local); 98826044Sminshall return((char *) 0); 98926044Sminshall } 99026044Sminshall (void) strcpy(new, local); 99126044Sminshall cp = new + strlen(new); 99226044Sminshall *cp++ = '.'; 99336304Skarels for (count = 1; count < 100; count++) { 99436304Skarels (void) sprintf(cp, "%d", count); 99536304Skarels if (stat(new, &st) < 0) 99636304Skarels return(new); 99726044Sminshall } 99836304Skarels reply(452, "Unique file name cannot be created."); 99936304Skarels return((char *) 0); 100026044Sminshall } 100136304Skarels 100236304Skarels /* 100336304Skarels * Format and send reply containing system error number. 100436304Skarels */ 100536304Skarels perror_reply(code, string) 100636304Skarels int code; 100736304Skarels char *string; 100836304Skarels { 100936304Skarels 101036304Skarels if (errno < sys_nerr) 101136304Skarels reply(code, "%s: %s.", string, sys_errlist[errno]); 101236304Skarels else 101336304Skarels reply(code, "%s: unknown error %d.", string, errno); 101436304Skarels } 1015