110275Ssam #ifndef lint 2*17580Ssam static char sccsid[] = "@(#)ftpd.c 4.33 (Berkeley) 12/23/84"; 310275Ssam #endif 410275Ssam 510275Ssam /* 610275Ssam * FTP server. 710275Ssam */ 810303Ssam #include <sys/param.h> 910275Ssam #include <sys/stat.h> 1010275Ssam #include <sys/ioctl.h> 1110275Ssam #include <sys/socket.h> 1213247Ssam #include <sys/file.h> 1313595Ssam #include <sys/wait.h> 1410275Ssam 1510275Ssam #include <netinet/in.h> 1610275Ssam 1713034Ssam #include <arpa/ftp.h> 1813211Sroot #include <arpa/inet.h> 1913034Ssam 2010275Ssam #include <stdio.h> 2110275Ssam #include <signal.h> 2210275Ssam #include <pwd.h> 2310275Ssam #include <setjmp.h> 2410275Ssam #include <netdb.h> 2510423Ssam #include <errno.h> 2610275Ssam 2710695Ssam /* 2810695Ssam * File containing login names 2910695Ssam * NOT to be used on this machine. 3010695Ssam * Commonly used to disallow uucp. 3110695Ssam */ 3210695Ssam #define FTPUSERS "/etc/ftpusers" 3310695Ssam 3410275Ssam extern int errno; 3510275Ssam extern char *sys_errlist[]; 3610275Ssam extern char *crypt(); 3710275Ssam extern char version[]; 3810275Ssam extern char *home; /* pointer to home directory for glob */ 3910275Ssam extern FILE *popen(), *fopen(); 4010275Ssam extern int pclose(), fclose(); 4110275Ssam 4210275Ssam struct sockaddr_in ctrl_addr; 4310275Ssam struct sockaddr_in data_source; 4410275Ssam struct sockaddr_in data_dest; 4510275Ssam struct sockaddr_in his_addr; 4610275Ssam 4710275Ssam struct hostent *hp; 4810275Ssam 4910275Ssam int data; 5010275Ssam jmp_buf errcatch; 5110275Ssam int logged_in; 5210275Ssam struct passwd *pw; 5310275Ssam int debug; 5411653Ssam int timeout; 5511757Ssam int logging; 5610275Ssam int guest; 5716033Sralph int wtmp; 5810275Ssam int type; 5910275Ssam int form; 6010275Ssam int stru; /* avoid C keyword */ 6110275Ssam int mode; 6210321Ssam int usedefault = 1; /* for data transfers */ 6310275Ssam char hostname[32]; 6413247Ssam char remotehost[32]; 6510275Ssam 6611653Ssam /* 6711653Ssam * Timeout intervals for retrying connections 6811653Ssam * to hosts that don't accept PORT cmds. This 6911653Ssam * is a kludge, but given the problems with TCP... 7011653Ssam */ 7111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 7211653Ssam #define SWAITINT 5 /* interval between retries */ 7311653Ssam 7411653Ssam int swaitmax = SWAITMAX; 7511653Ssam int swaitint = SWAITINT; 7611653Ssam 7710275Ssam int lostconn(); 7810419Ssam int reapchild(); 7910275Ssam FILE *getdatasock(), *dataconn(); 8010275Ssam 8110275Ssam main(argc, argv) 8210275Ssam int argc; 8310275Ssam char *argv[]; 8410275Ssam { 8516339Skarels int options = 0, addrlen; 8610275Ssam char *cp; 8710275Ssam 8816339Skarels addrlen = sizeof (his_addr); 8916339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 9016339Skarels fprintf(stderr, "%s: ", argv[0]); 9116339Skarels perror("getpeername"); 9210275Ssam exit(1); 9310275Ssam } 9416339Skarels addrlen = sizeof (ctrl_addr); 9516339Skarels if (getsockname(0, &ctrl_addr, &addrlen) < 0) { 9616339Skarels fprintf(stderr, "%s: ", argv[0]); 9716339Skarels perror("getsockname"); 9816339Skarels exit(1); 9916339Skarels } 10016339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 10110275Ssam debug = 0; 10210275Ssam argc--, argv++; 10310275Ssam while (argc > 0 && *argv[0] == '-') { 10410275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 10510275Ssam 10611653Ssam case 'v': 10711653Ssam debug = 1; 10811653Ssam break; 10911653Ssam 11010275Ssam case 'd': 11110275Ssam debug = 1; 11210275Ssam options |= SO_DEBUG; 11310275Ssam break; 11410275Ssam 11511757Ssam case 'l': 11611757Ssam logging = 1; 11711757Ssam break; 11811757Ssam 11911653Ssam case 't': 12011653Ssam timeout = atoi(++cp); 12111653Ssam goto nextopt; 12211653Ssam break; 12311653Ssam 12410275Ssam default: 12516339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 12616339Skarels *cp); 12710275Ssam break; 12810275Ssam } 12911653Ssam nextopt: 13010275Ssam argc--, argv++; 13110275Ssam } 13216339Skarels signal(SIGPIPE, lostconn); 13316339Skarels signal(SIGCHLD, SIG_IGN); 13416760Slepreau dolog(&his_addr); 13516339Skarels /* do telnet option negotiation here */ 13616339Skarels /* 13716339Skarels * Set up default state 13816339Skarels */ 13916339Skarels logged_in = 0; 14016339Skarels data = -1; 14116339Skarels type = TYPE_A; 14216339Skarels form = FORM_N; 14316339Skarels stru = STRU_F; 14416339Skarels mode = MODE_S; 14516339Skarels gethostname(hostname, sizeof (hostname)); 14616339Skarels reply(220, "%s FTP server (%s) ready.", 14716339Skarels hostname, version); 14810275Ssam for (;;) { 14916339Skarels setjmp(errcatch); 15016339Skarels yyparse(); 15110275Ssam } 15210275Ssam } 15310275Ssam 15410419Ssam reapchild() 15510419Ssam { 15610419Ssam union wait status; 15710419Ssam 15810419Ssam while (wait3(&status, WNOHANG, 0) > 0) 15910419Ssam ; 16010419Ssam } 16110419Ssam 16210275Ssam lostconn() 16310275Ssam { 16410275Ssam 16514089Ssam if (debug) 16614089Ssam fprintf(stderr, "Lost connection.\n"); 16714089Ssam dologout(-1); 16810275Ssam } 16910275Ssam 17010275Ssam pass(passwd) 17110275Ssam char *passwd; 17210275Ssam { 17310303Ssam char *xpasswd, *savestr(); 17410303Ssam static struct passwd save; 17510275Ssam 17610275Ssam if (logged_in || pw == NULL) { 17710275Ssam reply(503, "Login with USER first."); 17810275Ssam return; 17910275Ssam } 18010275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 18110275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 18216760Slepreau /* The strcmp does not catch null passwords! */ 18316760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 18410275Ssam reply(530, "Login incorrect."); 18510275Ssam pw = NULL; 18610275Ssam return; 18710275Ssam } 18810275Ssam } 18910303Ssam setegid(pw->pw_gid); 19010275Ssam initgroups(pw->pw_name, pw->pw_gid); 19110275Ssam if (chdir(pw->pw_dir)) { 19210275Ssam reply(550, "User %s: can't change directory to $s.", 19310275Ssam pw->pw_name, pw->pw_dir); 19410303Ssam goto bad; 19510275Ssam } 19616033Sralph 19716760Slepreau /* grab wtmp before chroot */ 19816760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 19910303Ssam if (guest && chroot(pw->pw_dir) < 0) { 20010275Ssam reply(550, "Can't set guest privileges."); 20116760Slepreau if (wtmp >= 0) { 20216760Slepreau (void) close(wtmp); 20316760Slepreau wtmp = -1; 20416760Slepreau } 20510303Ssam goto bad; 20610275Ssam } 20710275Ssam if (!guest) 20810275Ssam reply(230, "User %s logged in.", pw->pw_name); 20910275Ssam else 21010275Ssam reply(230, "Guest login ok, access restrictions apply."); 21110275Ssam logged_in = 1; 21213247Ssam dologin(pw); 21310303Ssam seteuid(pw->pw_uid); 21410303Ssam /* 21510303Ssam * Save everything so globbing doesn't 21610303Ssam * clobber the fields. 21710303Ssam */ 21810303Ssam save = *pw; 21910303Ssam save.pw_name = savestr(pw->pw_name); 22010303Ssam save.pw_passwd = savestr(pw->pw_passwd); 22110303Ssam save.pw_comment = savestr(pw->pw_comment); 22210303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 22310303Ssam save.pw_dir = savestr(pw->pw_dir); 22410303Ssam save.pw_shell = savestr(pw->pw_shell); 22510303Ssam pw = &save; 22610303Ssam home = pw->pw_dir; /* home dir for globbing */ 22710303Ssam return; 22810303Ssam bad: 22910303Ssam seteuid(0); 23010303Ssam pw = NULL; 23110275Ssam } 23210275Ssam 23310303Ssam char * 23410303Ssam savestr(s) 23510303Ssam char *s; 23610303Ssam { 23710303Ssam char *malloc(); 23810303Ssam char *new = malloc(strlen(s) + 1); 23910303Ssam 24010303Ssam if (new != NULL) 24110303Ssam strcpy(new, s); 24211347Ssam return (new); 24310303Ssam } 24410303Ssam 24510275Ssam retrieve(cmd, name) 24610275Ssam char *cmd, *name; 24710275Ssam { 24810275Ssam FILE *fin, *dout; 24910275Ssam struct stat st; 25010275Ssam int (*closefunc)(); 25110275Ssam 25210275Ssam if (cmd == 0) { 25310317Ssam #ifdef notdef 25410317Ssam /* no remote command execution -- it's a security hole */ 25511653Ssam if (*name == '|') 25610275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 25710275Ssam else 25810317Ssam #endif 25910275Ssam fin = fopen(name, "r"), closefunc = fclose; 26010275Ssam } else { 26110275Ssam char line[BUFSIZ]; 26210275Ssam 26310422Ssam sprintf(line, cmd, name), name = line; 26410275Ssam fin = popen(line, "r"), closefunc = pclose; 26510275Ssam } 26610275Ssam if (fin == NULL) { 26713152Ssam if (errno != 0) 26813152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 26910275Ssam return; 27010275Ssam } 27110275Ssam st.st_size = 0; 27210275Ssam if (cmd == 0 && 27310275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 27410275Ssam reply(550, "%s: not a plain file.", name); 27510275Ssam goto done; 27610275Ssam } 27710275Ssam dout = dataconn(name, st.st_size, "w"); 27810275Ssam if (dout == NULL) 27910275Ssam goto done; 28010303Ssam if (send_data(fin, dout) || ferror(dout)) 28110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 28210275Ssam else 28310275Ssam reply(226, "Transfer complete."); 28410303Ssam fclose(dout), data = -1; 28510275Ssam done: 28610275Ssam (*closefunc)(fin); 28710275Ssam } 28810275Ssam 28910275Ssam store(name, mode) 29010275Ssam char *name, *mode; 29110275Ssam { 29210275Ssam FILE *fout, *din; 29310303Ssam int (*closefunc)(), dochown = 0; 29410275Ssam 29510317Ssam #ifdef notdef 29610317Ssam /* no remote command execution -- it's a security hole */ 29711653Ssam if (name[0] == '|') 29810275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 29910317Ssam else 30010317Ssam #endif 30110317Ssam { 30210303Ssam struct stat st; 30310303Ssam 30410303Ssam if (stat(name, &st) < 0) 30510303Ssam dochown++; 30610275Ssam fout = fopen(name, mode), closefunc = fclose; 30710303Ssam } 30810275Ssam if (fout == NULL) { 30910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31010275Ssam return; 31110275Ssam } 31211653Ssam din = dataconn(name, (off_t)-1, "r"); 31310275Ssam if (din == NULL) 31410275Ssam goto done; 31510303Ssam if (receive_data(din, fout) || ferror(fout)) 31610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31710275Ssam else 31810275Ssam reply(226, "Transfer complete."); 31910275Ssam fclose(din), data = -1; 32010275Ssam done: 32110303Ssam if (dochown) 32210303Ssam (void) chown(name, pw->pw_uid, -1); 32310275Ssam (*closefunc)(fout); 32410275Ssam } 32510275Ssam 32610275Ssam FILE * 32710275Ssam getdatasock(mode) 32810275Ssam char *mode; 32910275Ssam { 33017157Ssam int s, on = 1; 33110275Ssam 33210275Ssam if (data >= 0) 33310275Ssam return (fdopen(data, mode)); 33413247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 33510602Ssam if (s < 0) 33610275Ssam return (NULL); 33710275Ssam seteuid(0); 33817157Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) 33910602Ssam goto bad; 34013152Ssam /* anchor socket to avoid multi-homing problems */ 34113152Ssam data_source.sin_family = AF_INET; 34213152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 34310602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 34410602Ssam goto bad; 34510311Ssam seteuid(pw->pw_uid); 34610275Ssam return (fdopen(s, mode)); 34710602Ssam bad: 34810602Ssam seteuid(pw->pw_uid); 34910602Ssam close(s); 35010602Ssam return (NULL); 35110275Ssam } 35210275Ssam 35310275Ssam FILE * 35410275Ssam dataconn(name, size, mode) 35510275Ssam char *name; 35611653Ssam off_t size; 35710275Ssam char *mode; 35810275Ssam { 35910275Ssam char sizebuf[32]; 36010275Ssam FILE *file; 36111653Ssam int retry = 0; 36210275Ssam 36310275Ssam if (size >= 0) 36411653Ssam sprintf (sizebuf, " (%ld bytes)", size); 36510275Ssam else 36610275Ssam (void) strcpy(sizebuf, ""); 36710275Ssam if (data >= 0) { 36810275Ssam reply(125, "Using existing data connection for %s%s.", 36910275Ssam name, sizebuf); 37010321Ssam usedefault = 1; 37110275Ssam return (fdopen(data, mode)); 37210275Ssam } 37310566Ssam if (usedefault) 37410422Ssam data_dest = his_addr; 37510422Ssam usedefault = 1; 37610275Ssam file = getdatasock(mode); 37710275Ssam if (file == NULL) { 37810275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 37913247Ssam inet_ntoa(data_source.sin_addr), 38010275Ssam ntohs(data_source.sin_port), 38110275Ssam sys_errlist[errno]); 38210275Ssam return (NULL); 38310275Ssam } 38410602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 38513247Ssam name, inet_ntoa(data_dest.sin_addr.s_addr), 38610602Ssam ntohs(data_dest.sin_port), sizebuf); 38710275Ssam data = fileno(file); 38811653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 38911653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 39011653Ssam sleep(swaitint); 39111653Ssam retry += swaitint; 39211653Ssam continue; 39311653Ssam } 39410275Ssam reply(425, "Can't build data connection: %s.", 39510275Ssam sys_errlist[errno]); 39610275Ssam (void) fclose(file); 39710275Ssam data = -1; 39810275Ssam return (NULL); 39910275Ssam } 40010275Ssam return (file); 40110275Ssam } 40210275Ssam 40310275Ssam /* 40410275Ssam * Tranfer the contents of "instr" to 40510275Ssam * "outstr" peer using the appropriate 40610275Ssam * encapulation of the date subject 40710275Ssam * to Mode, Structure, and Type. 40810275Ssam * 40910275Ssam * NB: Form isn't handled. 41010275Ssam */ 41110275Ssam send_data(instr, outstr) 41210275Ssam FILE *instr, *outstr; 41310275Ssam { 41410275Ssam register int c; 41510275Ssam int netfd, filefd, cnt; 41610275Ssam char buf[BUFSIZ]; 41710275Ssam 41810275Ssam switch (type) { 41910275Ssam 42010275Ssam case TYPE_A: 42110275Ssam while ((c = getc(instr)) != EOF) { 42211220Ssam if (c == '\n') { 42311220Ssam if (ferror (outstr)) 42411220Ssam return (1); 42510275Ssam putc('\r', outstr); 42611220Ssam } 42711220Ssam putc(c, outstr); 42811220Ssam if (c == '\r') 42911220Ssam putc ('\0', outstr); 43010275Ssam } 43111220Ssam if (ferror (instr) || ferror (outstr)) 43211220Ssam return (1); 43310275Ssam return (0); 43410275Ssam 43510275Ssam case TYPE_I: 43610275Ssam case TYPE_L: 43710275Ssam netfd = fileno(outstr); 43810275Ssam filefd = fileno(instr); 43910275Ssam 44010303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 44110275Ssam if (write(netfd, buf, cnt) < 0) 44210275Ssam return (1); 44310275Ssam return (cnt < 0); 44410275Ssam } 44510275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 44610275Ssam return (1); 44710275Ssam } 44810275Ssam 44910275Ssam /* 45010275Ssam * Transfer data from peer to 45110275Ssam * "outstr" using the appropriate 45210275Ssam * encapulation of the data subject 45310275Ssam * to Mode, Structure, and Type. 45410275Ssam * 45510275Ssam * N.B.: Form isn't handled. 45610275Ssam */ 45710275Ssam receive_data(instr, outstr) 45810275Ssam FILE *instr, *outstr; 45910275Ssam { 46010275Ssam register int c; 46111220Ssam int cnt; 46210275Ssam char buf[BUFSIZ]; 46310275Ssam 46410275Ssam 46510275Ssam switch (type) { 46610275Ssam 46710275Ssam case TYPE_I: 46810275Ssam case TYPE_L: 46910616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 47010616Ssam if (write(fileno(outstr), buf, cnt) < 0) 47110275Ssam return (1); 47210275Ssam return (cnt < 0); 47310275Ssam 47410275Ssam case TYPE_E: 47510275Ssam reply(504, "TYPE E not implemented."); 47610275Ssam return (1); 47710275Ssam 47810275Ssam case TYPE_A: 47910275Ssam while ((c = getc(instr)) != EOF) { 48010275Ssam if (c == '\r') { 48111220Ssam if (ferror (outstr)) 48211220Ssam return (1); 48311220Ssam if ((c = getc(instr)) != '\n') 48411220Ssam putc ('\r', outstr); 48511220Ssam if (c == '\0') 48611220Ssam continue; 48710275Ssam } 48811220Ssam putc (c, outstr); 48910275Ssam } 49011220Ssam if (ferror (instr) || ferror (outstr)) 49111220Ssam return (1); 49210275Ssam return (0); 49310275Ssam } 49410275Ssam fatal("Unknown type in receive_data."); 49510275Ssam /*NOTREACHED*/ 49610275Ssam } 49710275Ssam 49810275Ssam fatal(s) 49910275Ssam char *s; 50010275Ssam { 50110275Ssam reply(451, "Error in server: %s\n", s); 50210275Ssam reply(221, "Closing connection due to server error."); 50313247Ssam dologout(0); 50410275Ssam } 50510275Ssam 50610275Ssam reply(n, s, args) 50710275Ssam int n; 50810275Ssam char *s; 50910275Ssam { 51010275Ssam 51110275Ssam printf("%d ", n); 51210275Ssam _doprnt(s, &args, stdout); 51310275Ssam printf("\r\n"); 51410275Ssam fflush(stdout); 51510275Ssam if (debug) { 51610275Ssam fprintf(stderr, "<--- %d ", n); 51710275Ssam _doprnt(s, &args, stderr); 51810275Ssam fprintf(stderr, "\n"); 51910275Ssam fflush(stderr); 52010275Ssam } 52110275Ssam } 52210275Ssam 52310275Ssam lreply(n, s, args) 52410275Ssam int n; 52510275Ssam char *s; 52610275Ssam { 52710275Ssam printf("%d-", n); 52810275Ssam _doprnt(s, &args, stdout); 52910275Ssam printf("\r\n"); 53010275Ssam fflush(stdout); 53110275Ssam if (debug) { 53210275Ssam fprintf(stderr, "<--- %d-", n); 53310275Ssam _doprnt(s, &args, stderr); 53410275Ssam fprintf(stderr, "\n"); 53510275Ssam } 53610275Ssam } 53710275Ssam 53810275Ssam replystr(s) 53910275Ssam char *s; 54010275Ssam { 54110275Ssam printf("%s\r\n", s); 54210275Ssam fflush(stdout); 54310275Ssam if (debug) 54410275Ssam fprintf(stderr, "<--- %s\n", s); 54510275Ssam } 54610275Ssam 54710275Ssam ack(s) 54810275Ssam char *s; 54910275Ssam { 55010275Ssam reply(200, "%s command okay.", s); 55110275Ssam } 55210275Ssam 55310275Ssam nack(s) 55410275Ssam char *s; 55510275Ssam { 55610275Ssam reply(502, "%s command not implemented.", s); 55710275Ssam } 55810275Ssam 55910275Ssam yyerror() 56010275Ssam { 56110275Ssam reply(500, "Command not understood."); 56210275Ssam } 56310275Ssam 56410275Ssam delete(name) 56510275Ssam char *name; 56610275Ssam { 56710275Ssam struct stat st; 56810275Ssam 56910275Ssam if (stat(name, &st) < 0) { 57010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57110275Ssam return; 57210275Ssam } 57310275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 57410275Ssam if (rmdir(name) < 0) { 57510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57610275Ssam return; 57710275Ssam } 57810275Ssam goto done; 57910275Ssam } 58010275Ssam if (unlink(name) < 0) { 58110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 58210275Ssam return; 58310275Ssam } 58410275Ssam done: 58510275Ssam ack("DELE"); 58610275Ssam } 58710275Ssam 58810275Ssam cwd(path) 58910275Ssam char *path; 59010275Ssam { 59110275Ssam 59210275Ssam if (chdir(path) < 0) { 59310275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 59410275Ssam return; 59510275Ssam } 59610275Ssam ack("CWD"); 59710275Ssam } 59810275Ssam 59910303Ssam makedir(name) 60010275Ssam char *name; 60110275Ssam { 60210303Ssam struct stat st; 60310303Ssam int dochown = stat(name, &st) < 0; 60410275Ssam 60510275Ssam if (mkdir(name, 0777) < 0) { 60610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60710275Ssam return; 60810275Ssam } 60910303Ssam if (dochown) 61010303Ssam (void) chown(name, pw->pw_uid, -1); 61110275Ssam ack("MKDIR"); 61210275Ssam } 61310275Ssam 61410303Ssam removedir(name) 61510275Ssam char *name; 61610275Ssam { 61710275Ssam 61810275Ssam if (rmdir(name) < 0) { 61910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 62010275Ssam return; 62110275Ssam } 62210275Ssam ack("RMDIR"); 62310275Ssam } 62410275Ssam 62510303Ssam pwd() 62610275Ssam { 62710303Ssam char path[MAXPATHLEN + 1]; 62810275Ssam 62910275Ssam if (getwd(path) == NULL) { 63010275Ssam reply(451, "%s.", path); 63110275Ssam return; 63210275Ssam } 63310275Ssam reply(251, "\"%s\" is current directory.", path); 63410275Ssam } 63510275Ssam 63610275Ssam char * 63710275Ssam renamefrom(name) 63810275Ssam char *name; 63910275Ssam { 64010275Ssam struct stat st; 64110275Ssam 64210275Ssam if (stat(name, &st) < 0) { 64310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64410275Ssam return ((char *)0); 64510275Ssam } 64610303Ssam reply(350, "File exists, ready for destination name"); 64710275Ssam return (name); 64810275Ssam } 64910275Ssam 65010275Ssam renamecmd(from, to) 65110275Ssam char *from, *to; 65210275Ssam { 65310275Ssam 65410275Ssam if (rename(from, to) < 0) { 65510275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 65610275Ssam return; 65710275Ssam } 65810275Ssam ack("RNTO"); 65910275Ssam } 66010275Ssam 66110275Ssam dolog(sin) 66210275Ssam struct sockaddr_in *sin; 66310275Ssam { 66410275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 66510275Ssam sizeof (struct in_addr), AF_INET); 66610275Ssam time_t t; 66710275Ssam 66813247Ssam if (hp) { 66913247Ssam strncpy(remotehost, hp->h_name, sizeof (remotehost)); 67013247Ssam endhostent(); 67113247Ssam } else 67213247Ssam strncpy(remotehost, inet_ntoa(sin->sin_addr), 67313247Ssam sizeof (remotehost)); 67413247Ssam if (!logging) 67513247Ssam return; 67610275Ssam t = time(0); 67711757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 67810275Ssam fflush(stderr); 67910275Ssam } 68010695Ssam 68113247Ssam #include <utmp.h> 68213247Ssam 68313247Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 68413247Ssam struct utmp utmp; 68513247Ssam 68610695Ssam /* 68713247Ssam * Record login in wtmp file. 68813247Ssam */ 68913247Ssam dologin(pw) 69013247Ssam struct passwd *pw; 69113247Ssam { 69213247Ssam char line[32]; 69313247Ssam 69413247Ssam if (wtmp >= 0) { 69513247Ssam /* hack, but must be unique and no tty line */ 69613247Ssam sprintf(line, "ftp%d", getpid()); 69713247Ssam SCPYN(utmp.ut_line, line); 69813247Ssam SCPYN(utmp.ut_name, pw->pw_name); 69913247Ssam SCPYN(utmp.ut_host, remotehost); 70013247Ssam utmp.ut_time = time(0); 70113247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 70216760Slepreau if (!guest) { /* anon must hang on */ 70316760Slepreau (void) close(wtmp); 70416760Slepreau wtmp = -1; 70516760Slepreau } 70613247Ssam } 70713247Ssam } 70813247Ssam 70913247Ssam /* 71013247Ssam * Record logout in wtmp file 71113247Ssam * and exit with supplied status. 71213247Ssam */ 71313247Ssam dologout(status) 71413247Ssam int status; 71513247Ssam { 71616339Skarels 717*17580Ssam if (logged_in) { 718*17580Ssam (void) seteuid(0); 719*17580Ssam if (wtmp < 0) 720*17580Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 721*17580Ssam if (wtmp >= 0) { 722*17580Ssam SCPYN(utmp.ut_name, ""); 723*17580Ssam SCPYN(utmp.ut_host, ""); 724*17580Ssam utmp.ut_time = time(0); 725*17580Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 726*17580Ssam (void) close(wtmp); 727*17580Ssam } 72813247Ssam } 72914436Ssam /* beware of flushing buffers after a SIGPIPE */ 73014436Ssam _exit(status); 73113247Ssam } 73213247Ssam 73313247Ssam /* 73410695Ssam * Special version of popen which avoids 73510695Ssam * call to shell. This insures noone may 73610695Ssam * create a pipe to a hidden program as a side 73710695Ssam * effect of a list or dir command. 73810695Ssam */ 73910695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 74010695Ssam #define RDR 0 74110695Ssam #define WTR 1 74210695Ssam static int popen_pid[5]; 74310695Ssam 74410695Ssam static char * 74510695Ssam nextarg(cpp) 74610695Ssam char *cpp; 74710695Ssam { 74810695Ssam register char *cp = cpp; 74910695Ssam 75010695Ssam if (cp == 0) 75110695Ssam return (cp); 75210695Ssam while (*cp && *cp != ' ' && *cp != '\t') 75310695Ssam cp++; 75410695Ssam if (*cp == ' ' || *cp == '\t') { 75510695Ssam *cp++ = '\0'; 75610695Ssam while (*cp == ' ' || *cp == '\t') 75710695Ssam cp++; 75810695Ssam } 75910695Ssam if (cp == cpp) 76010695Ssam return ((char *)0); 76110695Ssam return (cp); 76210695Ssam } 76310695Ssam 76410695Ssam FILE * 76510695Ssam popen(cmd, mode) 76610695Ssam char *cmd, *mode; 76710695Ssam { 76813211Sroot int p[2], ac, gac; 76910695Ssam register myside, hisside, pid; 77013211Sroot char *av[20], *gav[512]; 77110695Ssam register char *cp; 77210695Ssam 77310695Ssam if (pipe(p) < 0) 77410695Ssam return (NULL); 77510695Ssam cp = cmd, ac = 0; 77613211Sroot /* break up string into pieces */ 77710695Ssam do { 77810695Ssam av[ac++] = cp; 77910695Ssam cp = nextarg(cp); 78013211Sroot } while (cp && *cp && ac < 20); 78110695Ssam av[ac] = (char *)0; 78213211Sroot gav[0] = av[0]; 78313211Sroot /* glob each piece */ 78413211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 78513211Sroot char **pop; 78613211Sroot extern char **glob(); 78713211Sroot 78813211Sroot pop = glob(av[ac]); 78913211Sroot if (pop) { 79013211Sroot av[ac] = (char *)pop; /* save to free later */ 79113211Sroot while (*pop && gac < 512) 79213211Sroot gav[gac++] = *pop++; 79313211Sroot } 79411757Ssam } 79513211Sroot gav[gac] = (char *)0; 79610695Ssam myside = tst(p[WTR], p[RDR]); 79710695Ssam hisside = tst(p[RDR], p[WTR]); 79810695Ssam if ((pid = fork()) == 0) { 79910695Ssam /* myside and hisside reverse roles in child */ 80010695Ssam close(myside); 80110695Ssam dup2(hisside, tst(0, 1)); 80210695Ssam close(hisside); 80313211Sroot execv(gav[0], gav); 80410695Ssam _exit(1); 80510695Ssam } 80613211Sroot for (ac = 1; av[ac] != NULL; ac++) 80713211Sroot blkfree((char **)av[ac]); 80810695Ssam if (pid == -1) 80910695Ssam return (NULL); 81010695Ssam popen_pid[myside] = pid; 81110695Ssam close(hisside); 81210695Ssam return (fdopen(myside, mode)); 81310695Ssam } 81410695Ssam 81510695Ssam pclose(ptr) 81610695Ssam FILE *ptr; 81710695Ssam { 81810695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 81910695Ssam int status; 82010695Ssam 82110695Ssam f = fileno(ptr); 82210695Ssam fclose(ptr); 82310695Ssam istat = signal(SIGINT, SIG_IGN); 82410695Ssam qstat = signal(SIGQUIT, SIG_IGN); 82510695Ssam hstat = signal(SIGHUP, SIG_IGN); 82610695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 82710695Ssam ; 82810695Ssam if (r == -1) 82910695Ssam status = -1; 83010695Ssam signal(SIGINT, istat); 83110695Ssam signal(SIGQUIT, qstat); 83210695Ssam signal(SIGHUP, hstat); 83310695Ssam return (status); 83410695Ssam } 83510695Ssam 83610695Ssam /* 83710695Ssam * Check user requesting login priviledges. 83810695Ssam * Disallow anyone mentioned in the file FTPUSERS 83910695Ssam * to allow people such as uucp to be avoided. 84010695Ssam */ 84110695Ssam checkuser(name) 84210695Ssam register char *name; 84310695Ssam { 84410695Ssam char line[BUFSIZ], *index(); 84510695Ssam FILE *fd; 84610695Ssam int found = 0; 84710695Ssam 84810695Ssam fd = fopen(FTPUSERS, "r"); 84910695Ssam if (fd == NULL) 85010695Ssam return (1); 85110695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 85210695Ssam register char *cp = index(line, '\n'); 85310695Ssam 85410695Ssam if (cp) 85510695Ssam *cp = '\0'; 85610695Ssam if (strcmp(line, name) == 0) { 85710695Ssam found++; 85810695Ssam break; 85910695Ssam } 86010695Ssam } 86110695Ssam fclose(fd); 86210695Ssam return (!found); 86310695Ssam } 864