110275Ssam #ifndef lint 2*14089Ssam static char sccsid[] = "@(#)ftpd.c 4.25 (Berkeley) 07/24/83"; 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; 5710275Ssam int type; 5810275Ssam int form; 5910275Ssam int stru; /* avoid C keyword */ 6010275Ssam int mode; 6110321Ssam int usedefault = 1; /* for data transfers */ 6210275Ssam char hostname[32]; 6313247Ssam char remotehost[32]; 6410321Ssam struct servent *sp; 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 { 8510275Ssam int ctrl, s, options = 0; 8610275Ssam char *cp; 8710275Ssam 8810275Ssam sp = getservbyname("ftp", "tcp"); 8910275Ssam if (sp == 0) { 9011220Ssam fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 9110275Ssam exit(1); 9210275Ssam } 9310275Ssam ctrl_addr.sin_port = sp->s_port; 9410275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 9510275Ssam signal(SIGPIPE, lostconn); 9610275Ssam debug = 0; 9710275Ssam argc--, argv++; 9810275Ssam while (argc > 0 && *argv[0] == '-') { 9910275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 10010275Ssam 10111653Ssam case 'v': 10211653Ssam debug = 1; 10311653Ssam break; 10411653Ssam 10510275Ssam case 'd': 10610275Ssam debug = 1; 10710275Ssam options |= SO_DEBUG; 10810275Ssam break; 10910275Ssam 11011757Ssam case 'l': 11111757Ssam logging = 1; 11211757Ssam break; 11311757Ssam 11411653Ssam case 't': 11511653Ssam timeout = atoi(++cp); 11611653Ssam goto nextopt; 11711653Ssam break; 11811653Ssam 11910275Ssam default: 12011653Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", *cp); 12110275Ssam break; 12210275Ssam } 12311653Ssam nextopt: 12410275Ssam argc--, argv++; 12510275Ssam } 12610275Ssam #ifndef DEBUG 12710275Ssam if (fork()) 12810275Ssam exit(0); 12910275Ssam for (s = 0; s < 10; s++) 13011653Ssam if (!logging || (s != 2)) 13111653Ssam (void) close(s); 13213247Ssam (void) open("/", O_RDONLY); 13310275Ssam (void) dup2(0, 1); 13411653Ssam if (!logging) 13511653Ssam (void) dup2(0, 2); 13613247Ssam { int tt = open("/dev/tty", O_RDWR); 13710275Ssam if (tt > 0) { 13810275Ssam ioctl(tt, TIOCNOTTY, 0); 13910275Ssam close(tt); 14010275Ssam } 14110275Ssam } 14210275Ssam #endif 14313247Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 14410275Ssam perror("ftpd: socket"); 14510275Ssam sleep(5); 14610275Ssam } 14710419Ssam if (options & SO_DEBUG) 14810419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 14910419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 15010419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 15110419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 15210275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 15310275Ssam perror("ftpd: bind"); 15410275Ssam sleep(5); 15510275Ssam } 15613034Ssam signal(SIGCHLD, reapchild); 15710303Ssam listen(s, 10); 15810275Ssam for (;;) { 15910275Ssam int hisaddrlen = sizeof (his_addr); 16010275Ssam 16110275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 16210275Ssam if (ctrl < 0) { 16310419Ssam if (errno == EINTR) 16410419Ssam continue; 16510275Ssam perror("ftpd: accept"); 16610275Ssam continue; 16710275Ssam } 16810275Ssam if (fork() == 0) { 16911220Ssam signal (SIGCHLD, SIG_IGN); 17013247Ssam dolog(&his_addr); 17110275Ssam close(s); 17210275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 17310275Ssam /* do telnet option negotiation here */ 17410303Ssam /* 17510303Ssam * Set up default state 17610303Ssam */ 17710275Ssam logged_in = 0; 17810275Ssam data = -1; 17910303Ssam type = TYPE_A; 18010303Ssam form = FORM_N; 18110303Ssam stru = STRU_F; 18210303Ssam mode = MODE_S; 18313152Ssam (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr)); 18410275Ssam gethostname(hostname, sizeof (hostname)); 18510275Ssam reply(220, "%s FTP server (%s) ready.", 18610275Ssam hostname, version); 18710275Ssam for (;;) { 18810275Ssam setjmp(errcatch); 18910275Ssam yyparse(); 19010275Ssam } 19110275Ssam } 19210275Ssam close(ctrl); 19310275Ssam } 19410275Ssam } 19510275Ssam 19610419Ssam reapchild() 19710419Ssam { 19810419Ssam union wait status; 19910419Ssam 20010419Ssam while (wait3(&status, WNOHANG, 0) > 0) 20110419Ssam ; 20210419Ssam } 20310419Ssam 20410275Ssam lostconn() 20510275Ssam { 20610275Ssam 207*14089Ssam if (debug) 208*14089Ssam fprintf(stderr, "Lost connection.\n"); 209*14089Ssam dologout(-1); 21010275Ssam } 21110275Ssam 21210275Ssam pass(passwd) 21310275Ssam char *passwd; 21410275Ssam { 21510303Ssam char *xpasswd, *savestr(); 21610303Ssam static struct passwd save; 21710275Ssam 21810275Ssam if (logged_in || pw == NULL) { 21910275Ssam reply(503, "Login with USER first."); 22010275Ssam return; 22110275Ssam } 22210275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 22310275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22410275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 22510275Ssam reply(530, "Login incorrect."); 22610275Ssam pw = NULL; 22710275Ssam return; 22810275Ssam } 22910275Ssam } 23010303Ssam setegid(pw->pw_gid); 23110275Ssam initgroups(pw->pw_name, pw->pw_gid); 23210275Ssam if (chdir(pw->pw_dir)) { 23310275Ssam reply(550, "User %s: can't change directory to $s.", 23410275Ssam pw->pw_name, pw->pw_dir); 23510303Ssam goto bad; 23610275Ssam } 23710303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23810275Ssam reply(550, "Can't set guest privileges."); 23910303Ssam goto bad; 24010275Ssam } 24110275Ssam if (!guest) 24210275Ssam reply(230, "User %s logged in.", pw->pw_name); 24310275Ssam else 24410275Ssam reply(230, "Guest login ok, access restrictions apply."); 24510275Ssam logged_in = 1; 24613247Ssam dologin(pw); 24710303Ssam seteuid(pw->pw_uid); 24810303Ssam /* 24910303Ssam * Save everything so globbing doesn't 25010303Ssam * clobber the fields. 25110303Ssam */ 25210303Ssam save = *pw; 25310303Ssam save.pw_name = savestr(pw->pw_name); 25410303Ssam save.pw_passwd = savestr(pw->pw_passwd); 25510303Ssam save.pw_comment = savestr(pw->pw_comment); 25610303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 25710303Ssam save.pw_dir = savestr(pw->pw_dir); 25810303Ssam save.pw_shell = savestr(pw->pw_shell); 25910303Ssam pw = &save; 26010303Ssam home = pw->pw_dir; /* home dir for globbing */ 26110303Ssam return; 26210303Ssam bad: 26310303Ssam seteuid(0); 26410303Ssam pw = NULL; 26510275Ssam } 26610275Ssam 26710303Ssam char * 26810303Ssam savestr(s) 26910303Ssam char *s; 27010303Ssam { 27110303Ssam char *malloc(); 27210303Ssam char *new = malloc(strlen(s) + 1); 27310303Ssam 27410303Ssam if (new != NULL) 27510303Ssam strcpy(new, s); 27611347Ssam return (new); 27710303Ssam } 27810303Ssam 27910275Ssam retrieve(cmd, name) 28010275Ssam char *cmd, *name; 28110275Ssam { 28210275Ssam FILE *fin, *dout; 28310275Ssam struct stat st; 28410275Ssam int (*closefunc)(); 28510275Ssam 28610275Ssam if (cmd == 0) { 28710317Ssam #ifdef notdef 28810317Ssam /* no remote command execution -- it's a security hole */ 28911653Ssam if (*name == '|') 29010275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 29110275Ssam else 29210317Ssam #endif 29310275Ssam fin = fopen(name, "r"), closefunc = fclose; 29410275Ssam } else { 29510275Ssam char line[BUFSIZ]; 29610275Ssam 29710422Ssam sprintf(line, cmd, name), name = line; 29810275Ssam fin = popen(line, "r"), closefunc = pclose; 29910275Ssam } 30010275Ssam if (fin == NULL) { 30113152Ssam if (errno != 0) 30213152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30310275Ssam return; 30410275Ssam } 30510275Ssam st.st_size = 0; 30610275Ssam if (cmd == 0 && 30710275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 30810275Ssam reply(550, "%s: not a plain file.", name); 30910275Ssam goto done; 31010275Ssam } 31110275Ssam dout = dataconn(name, st.st_size, "w"); 31210275Ssam if (dout == NULL) 31310275Ssam goto done; 31410303Ssam if (send_data(fin, dout) || ferror(dout)) 31510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31610275Ssam else 31710275Ssam reply(226, "Transfer complete."); 31810303Ssam fclose(dout), data = -1; 31910275Ssam done: 32010275Ssam (*closefunc)(fin); 32110275Ssam } 32210275Ssam 32310275Ssam store(name, mode) 32410275Ssam char *name, *mode; 32510275Ssam { 32610275Ssam FILE *fout, *din; 32710303Ssam int (*closefunc)(), dochown = 0; 32810275Ssam 32910317Ssam #ifdef notdef 33010317Ssam /* no remote command execution -- it's a security hole */ 33111653Ssam if (name[0] == '|') 33210275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33310317Ssam else 33410317Ssam #endif 33510317Ssam { 33610303Ssam struct stat st; 33710303Ssam 33810303Ssam if (stat(name, &st) < 0) 33910303Ssam dochown++; 34010275Ssam fout = fopen(name, mode), closefunc = fclose; 34110303Ssam } 34210275Ssam if (fout == NULL) { 34310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34410275Ssam return; 34510275Ssam } 34611653Ssam din = dataconn(name, (off_t)-1, "r"); 34710275Ssam if (din == NULL) 34810275Ssam goto done; 34910303Ssam if (receive_data(din, fout) || ferror(fout)) 35010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 35110275Ssam else 35210275Ssam reply(226, "Transfer complete."); 35310275Ssam fclose(din), data = -1; 35410275Ssam done: 35510303Ssam if (dochown) 35610303Ssam (void) chown(name, pw->pw_uid, -1); 35710275Ssam (*closefunc)(fout); 35810275Ssam } 35910275Ssam 36010275Ssam FILE * 36110275Ssam getdatasock(mode) 36210275Ssam char *mode; 36310275Ssam { 36413247Ssam int s; 36510275Ssam 36610275Ssam if (data >= 0) 36710275Ssam return (fdopen(data, mode)); 36813247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 36910602Ssam if (s < 0) 37010275Ssam return (NULL); 37110275Ssam seteuid(0); 37210602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 37310602Ssam goto bad; 37413152Ssam /* anchor socket to avoid multi-homing problems */ 37513152Ssam data_source.sin_family = AF_INET; 37613152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 37710602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 37810602Ssam goto bad; 37910311Ssam seteuid(pw->pw_uid); 38010275Ssam return (fdopen(s, mode)); 38110602Ssam bad: 38210602Ssam seteuid(pw->pw_uid); 38310602Ssam close(s); 38410602Ssam return (NULL); 38510275Ssam } 38610275Ssam 38710275Ssam FILE * 38810275Ssam dataconn(name, size, mode) 38910275Ssam char *name; 39011653Ssam off_t size; 39110275Ssam char *mode; 39210275Ssam { 39310275Ssam char sizebuf[32]; 39410275Ssam FILE *file; 39511653Ssam int retry = 0; 39610275Ssam 39710275Ssam if (size >= 0) 39811653Ssam sprintf (sizebuf, " (%ld bytes)", size); 39910275Ssam else 40010275Ssam (void) strcpy(sizebuf, ""); 40110275Ssam if (data >= 0) { 40210275Ssam reply(125, "Using existing data connection for %s%s.", 40310275Ssam name, sizebuf); 40410321Ssam usedefault = 1; 40510275Ssam return (fdopen(data, mode)); 40610275Ssam } 40710566Ssam if (usedefault) 40810422Ssam data_dest = his_addr; 40910422Ssam usedefault = 1; 41010275Ssam file = getdatasock(mode); 41110275Ssam if (file == NULL) { 41210275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 41313247Ssam inet_ntoa(data_source.sin_addr), 41410275Ssam ntohs(data_source.sin_port), 41510275Ssam sys_errlist[errno]); 41610275Ssam return (NULL); 41710275Ssam } 41810602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 41913247Ssam name, inet_ntoa(data_dest.sin_addr.s_addr), 42010602Ssam ntohs(data_dest.sin_port), sizebuf); 42110275Ssam data = fileno(file); 42211653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 42311653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 42411653Ssam sleep(swaitint); 42511653Ssam retry += swaitint; 42611653Ssam continue; 42711653Ssam } 42810275Ssam reply(425, "Can't build data connection: %s.", 42910275Ssam sys_errlist[errno]); 43010275Ssam (void) fclose(file); 43110275Ssam data = -1; 43210275Ssam return (NULL); 43310275Ssam } 43410275Ssam return (file); 43510275Ssam } 43610275Ssam 43710275Ssam /* 43810275Ssam * Tranfer the contents of "instr" to 43910275Ssam * "outstr" peer using the appropriate 44010275Ssam * encapulation of the date subject 44110275Ssam * to Mode, Structure, and Type. 44210275Ssam * 44310275Ssam * NB: Form isn't handled. 44410275Ssam */ 44510275Ssam send_data(instr, outstr) 44610275Ssam FILE *instr, *outstr; 44710275Ssam { 44810275Ssam register int c; 44910275Ssam int netfd, filefd, cnt; 45010275Ssam char buf[BUFSIZ]; 45110275Ssam 45210275Ssam switch (type) { 45310275Ssam 45410275Ssam case TYPE_A: 45510275Ssam while ((c = getc(instr)) != EOF) { 45611220Ssam if (c == '\n') { 45711220Ssam if (ferror (outstr)) 45811220Ssam return (1); 45910275Ssam putc('\r', outstr); 46011220Ssam } 46111220Ssam putc(c, outstr); 46211220Ssam if (c == '\r') 46311220Ssam putc ('\0', outstr); 46410275Ssam } 46511220Ssam if (ferror (instr) || ferror (outstr)) 46611220Ssam return (1); 46710275Ssam return (0); 46810275Ssam 46910275Ssam case TYPE_I: 47010275Ssam case TYPE_L: 47110275Ssam netfd = fileno(outstr); 47210275Ssam filefd = fileno(instr); 47310275Ssam 47410303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 47510275Ssam if (write(netfd, buf, cnt) < 0) 47610275Ssam return (1); 47710275Ssam return (cnt < 0); 47810275Ssam } 47910275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 48010275Ssam return (1); 48110275Ssam } 48210275Ssam 48310275Ssam /* 48410275Ssam * Transfer data from peer to 48510275Ssam * "outstr" using the appropriate 48610275Ssam * encapulation of the data subject 48710275Ssam * to Mode, Structure, and Type. 48810275Ssam * 48910275Ssam * N.B.: Form isn't handled. 49010275Ssam */ 49110275Ssam receive_data(instr, outstr) 49210275Ssam FILE *instr, *outstr; 49310275Ssam { 49410275Ssam register int c; 49511220Ssam int cnt; 49610275Ssam char buf[BUFSIZ]; 49710275Ssam 49810275Ssam 49910275Ssam switch (type) { 50010275Ssam 50110275Ssam case TYPE_I: 50210275Ssam case TYPE_L: 50310616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 50410616Ssam if (write(fileno(outstr), buf, cnt) < 0) 50510275Ssam return (1); 50610275Ssam return (cnt < 0); 50710275Ssam 50810275Ssam case TYPE_E: 50910275Ssam reply(504, "TYPE E not implemented."); 51010275Ssam return (1); 51110275Ssam 51210275Ssam case TYPE_A: 51310275Ssam while ((c = getc(instr)) != EOF) { 51410275Ssam if (c == '\r') { 51511220Ssam if (ferror (outstr)) 51611220Ssam return (1); 51711220Ssam if ((c = getc(instr)) != '\n') 51811220Ssam putc ('\r', outstr); 51911220Ssam if (c == '\0') 52011220Ssam continue; 52110275Ssam } 52211220Ssam putc (c, outstr); 52310275Ssam } 52411220Ssam if (ferror (instr) || ferror (outstr)) 52511220Ssam return (1); 52610275Ssam return (0); 52710275Ssam } 52810275Ssam fatal("Unknown type in receive_data."); 52910275Ssam /*NOTREACHED*/ 53010275Ssam } 53110275Ssam 53210275Ssam fatal(s) 53310275Ssam char *s; 53410275Ssam { 53510275Ssam reply(451, "Error in server: %s\n", s); 53610275Ssam reply(221, "Closing connection due to server error."); 53713247Ssam dologout(0); 53810275Ssam } 53910275Ssam 54010275Ssam reply(n, s, args) 54110275Ssam int n; 54210275Ssam char *s; 54310275Ssam { 54410275Ssam 54510275Ssam printf("%d ", n); 54610275Ssam _doprnt(s, &args, stdout); 54710275Ssam printf("\r\n"); 54810275Ssam fflush(stdout); 54910275Ssam if (debug) { 55010275Ssam fprintf(stderr, "<--- %d ", n); 55110275Ssam _doprnt(s, &args, stderr); 55210275Ssam fprintf(stderr, "\n"); 55310275Ssam fflush(stderr); 55410275Ssam } 55510275Ssam } 55610275Ssam 55710275Ssam lreply(n, s, args) 55810275Ssam int n; 55910275Ssam char *s; 56010275Ssam { 56110275Ssam printf("%d-", n); 56210275Ssam _doprnt(s, &args, stdout); 56310275Ssam printf("\r\n"); 56410275Ssam fflush(stdout); 56510275Ssam if (debug) { 56610275Ssam fprintf(stderr, "<--- %d-", n); 56710275Ssam _doprnt(s, &args, stderr); 56810275Ssam fprintf(stderr, "\n"); 56910275Ssam } 57010275Ssam } 57110275Ssam 57210275Ssam replystr(s) 57310275Ssam char *s; 57410275Ssam { 57510275Ssam printf("%s\r\n", s); 57610275Ssam fflush(stdout); 57710275Ssam if (debug) 57810275Ssam fprintf(stderr, "<--- %s\n", s); 57910275Ssam } 58010275Ssam 58110275Ssam ack(s) 58210275Ssam char *s; 58310275Ssam { 58410275Ssam reply(200, "%s command okay.", s); 58510275Ssam } 58610275Ssam 58710275Ssam nack(s) 58810275Ssam char *s; 58910275Ssam { 59010275Ssam reply(502, "%s command not implemented.", s); 59110275Ssam } 59210275Ssam 59310275Ssam yyerror() 59410275Ssam { 59510275Ssam reply(500, "Command not understood."); 59610275Ssam } 59710275Ssam 59810275Ssam delete(name) 59910275Ssam char *name; 60010275Ssam { 60110275Ssam struct stat st; 60210275Ssam 60310275Ssam if (stat(name, &st) < 0) { 60410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60510275Ssam return; 60610275Ssam } 60710275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 60810275Ssam if (rmdir(name) < 0) { 60910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61010275Ssam return; 61110275Ssam } 61210275Ssam goto done; 61310275Ssam } 61410275Ssam if (unlink(name) < 0) { 61510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61610275Ssam return; 61710275Ssam } 61810275Ssam done: 61910275Ssam ack("DELE"); 62010275Ssam } 62110275Ssam 62210275Ssam cwd(path) 62310275Ssam char *path; 62410275Ssam { 62510275Ssam 62610275Ssam if (chdir(path) < 0) { 62710275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 62810275Ssam return; 62910275Ssam } 63010275Ssam ack("CWD"); 63110275Ssam } 63210275Ssam 63310303Ssam makedir(name) 63410275Ssam char *name; 63510275Ssam { 63610303Ssam struct stat st; 63710303Ssam int dochown = stat(name, &st) < 0; 63810275Ssam 63910275Ssam if (mkdir(name, 0777) < 0) { 64010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64110275Ssam return; 64210275Ssam } 64310303Ssam if (dochown) 64410303Ssam (void) chown(name, pw->pw_uid, -1); 64510275Ssam ack("MKDIR"); 64610275Ssam } 64710275Ssam 64810303Ssam removedir(name) 64910275Ssam char *name; 65010275Ssam { 65110275Ssam 65210275Ssam if (rmdir(name) < 0) { 65310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 65410275Ssam return; 65510275Ssam } 65610275Ssam ack("RMDIR"); 65710275Ssam } 65810275Ssam 65910303Ssam pwd() 66010275Ssam { 66110303Ssam char path[MAXPATHLEN + 1]; 66210275Ssam 66310275Ssam if (getwd(path) == NULL) { 66410275Ssam reply(451, "%s.", path); 66510275Ssam return; 66610275Ssam } 66710275Ssam reply(251, "\"%s\" is current directory.", path); 66810275Ssam } 66910275Ssam 67010275Ssam char * 67110275Ssam renamefrom(name) 67210275Ssam char *name; 67310275Ssam { 67410275Ssam struct stat st; 67510275Ssam 67610275Ssam if (stat(name, &st) < 0) { 67710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67810275Ssam return ((char *)0); 67910275Ssam } 68010303Ssam reply(350, "File exists, ready for destination name"); 68110275Ssam return (name); 68210275Ssam } 68310275Ssam 68410275Ssam renamecmd(from, to) 68510275Ssam char *from, *to; 68610275Ssam { 68710275Ssam 68810275Ssam if (rename(from, to) < 0) { 68910275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 69010275Ssam return; 69110275Ssam } 69210275Ssam ack("RNTO"); 69310275Ssam } 69410275Ssam 69510275Ssam dolog(sin) 69610275Ssam struct sockaddr_in *sin; 69710275Ssam { 69810275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 69910275Ssam sizeof (struct in_addr), AF_INET); 70010275Ssam time_t t; 70110275Ssam 70213247Ssam if (hp) { 70313247Ssam strncpy(remotehost, hp->h_name, sizeof (remotehost)); 70413247Ssam endhostent(); 70513247Ssam } else 70613247Ssam strncpy(remotehost, inet_ntoa(sin->sin_addr), 70713247Ssam sizeof (remotehost)); 70813247Ssam if (!logging) 70913247Ssam return; 71010275Ssam t = time(0); 71111757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 71210275Ssam fflush(stderr); 71310275Ssam } 71410695Ssam 71513247Ssam #include <utmp.h> 71613247Ssam 71713247Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 71813247Ssam struct utmp utmp; 71913247Ssam 72010695Ssam /* 72113247Ssam * Record login in wtmp file. 72213247Ssam */ 72313247Ssam dologin(pw) 72413247Ssam struct passwd *pw; 72513247Ssam { 72613247Ssam int wtmp; 72713247Ssam char line[32]; 72813247Ssam 72913247Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 73013247Ssam if (wtmp >= 0) { 73113247Ssam /* hack, but must be unique and no tty line */ 73213247Ssam sprintf(line, "ftp%d", getpid()); 73313247Ssam SCPYN(utmp.ut_line, line); 73413247Ssam SCPYN(utmp.ut_name, pw->pw_name); 73513247Ssam SCPYN(utmp.ut_host, remotehost); 73613247Ssam utmp.ut_time = time(0); 73713247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 73813247Ssam (void) close(wtmp); 73913247Ssam } 74013247Ssam } 74113247Ssam 74213247Ssam /* 74313247Ssam * Record logout in wtmp file 74413247Ssam * and exit with supplied status. 74513247Ssam */ 74613247Ssam dologout(status) 74713247Ssam int status; 74813247Ssam { 74913247Ssam int wtmp; 75013247Ssam 75113247Ssam if (!logged_in) 75213247Ssam return; 75313247Ssam seteuid(0); 75413247Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 75513247Ssam if (wtmp >= 0) { 75613247Ssam SCPYN(utmp.ut_name, ""); 75713247Ssam SCPYN(utmp.ut_host, ""); 75813247Ssam utmp.ut_time = time(0); 75913247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 76013247Ssam (void) close(wtmp); 76113247Ssam } 76213247Ssam exit(status); 76313247Ssam } 76413247Ssam 76513247Ssam /* 76610695Ssam * Special version of popen which avoids 76710695Ssam * call to shell. This insures noone may 76810695Ssam * create a pipe to a hidden program as a side 76910695Ssam * effect of a list or dir command. 77010695Ssam */ 77110695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 77210695Ssam #define RDR 0 77310695Ssam #define WTR 1 77410695Ssam static int popen_pid[5]; 77510695Ssam 77610695Ssam static char * 77710695Ssam nextarg(cpp) 77810695Ssam char *cpp; 77910695Ssam { 78010695Ssam register char *cp = cpp; 78110695Ssam 78210695Ssam if (cp == 0) 78310695Ssam return (cp); 78410695Ssam while (*cp && *cp != ' ' && *cp != '\t') 78510695Ssam cp++; 78610695Ssam if (*cp == ' ' || *cp == '\t') { 78710695Ssam *cp++ = '\0'; 78810695Ssam while (*cp == ' ' || *cp == '\t') 78910695Ssam cp++; 79010695Ssam } 79110695Ssam if (cp == cpp) 79210695Ssam return ((char *)0); 79310695Ssam return (cp); 79410695Ssam } 79510695Ssam 79610695Ssam FILE * 79710695Ssam popen(cmd, mode) 79810695Ssam char *cmd, *mode; 79910695Ssam { 80013211Sroot int p[2], ac, gac; 80110695Ssam register myside, hisside, pid; 80213211Sroot char *av[20], *gav[512]; 80310695Ssam register char *cp; 80410695Ssam 80510695Ssam if (pipe(p) < 0) 80610695Ssam return (NULL); 80710695Ssam cp = cmd, ac = 0; 80813211Sroot /* break up string into pieces */ 80910695Ssam do { 81010695Ssam av[ac++] = cp; 81110695Ssam cp = nextarg(cp); 81213211Sroot } while (cp && *cp && ac < 20); 81310695Ssam av[ac] = (char *)0; 81413211Sroot gav[0] = av[0]; 81513211Sroot /* glob each piece */ 81613211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 81713211Sroot char **pop; 81813211Sroot extern char **glob(); 81913211Sroot 82013211Sroot pop = glob(av[ac]); 82113211Sroot if (pop) { 82213211Sroot av[ac] = (char *)pop; /* save to free later */ 82313211Sroot while (*pop && gac < 512) 82413211Sroot gav[gac++] = *pop++; 82513211Sroot } 82611757Ssam } 82713211Sroot gav[gac] = (char *)0; 82810695Ssam myside = tst(p[WTR], p[RDR]); 82910695Ssam hisside = tst(p[RDR], p[WTR]); 83010695Ssam if ((pid = fork()) == 0) { 83110695Ssam /* myside and hisside reverse roles in child */ 83210695Ssam close(myside); 83310695Ssam dup2(hisside, tst(0, 1)); 83410695Ssam close(hisside); 83513211Sroot execv(gav[0], gav); 83610695Ssam _exit(1); 83710695Ssam } 83813211Sroot for (ac = 1; av[ac] != NULL; ac++) 83913211Sroot blkfree((char **)av[ac]); 84010695Ssam if (pid == -1) 84110695Ssam return (NULL); 84210695Ssam popen_pid[myside] = pid; 84310695Ssam close(hisside); 84410695Ssam return (fdopen(myside, mode)); 84510695Ssam } 84610695Ssam 84710695Ssam pclose(ptr) 84810695Ssam FILE *ptr; 84910695Ssam { 85010695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 85110695Ssam int status; 85210695Ssam 85310695Ssam f = fileno(ptr); 85410695Ssam fclose(ptr); 85510695Ssam istat = signal(SIGINT, SIG_IGN); 85610695Ssam qstat = signal(SIGQUIT, SIG_IGN); 85710695Ssam hstat = signal(SIGHUP, SIG_IGN); 85810695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 85910695Ssam ; 86010695Ssam if (r == -1) 86110695Ssam status = -1; 86210695Ssam signal(SIGINT, istat); 86310695Ssam signal(SIGQUIT, qstat); 86410695Ssam signal(SIGHUP, hstat); 86510695Ssam return (status); 86610695Ssam } 86710695Ssam 86810695Ssam /* 86910695Ssam * Check user requesting login priviledges. 87010695Ssam * Disallow anyone mentioned in the file FTPUSERS 87110695Ssam * to allow people such as uucp to be avoided. 87210695Ssam */ 87310695Ssam checkuser(name) 87410695Ssam register char *name; 87510695Ssam { 87610695Ssam char line[BUFSIZ], *index(); 87710695Ssam FILE *fd; 87810695Ssam int found = 0; 87910695Ssam 88010695Ssam fd = fopen(FTPUSERS, "r"); 88110695Ssam if (fd == NULL) 88210695Ssam return (1); 88310695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 88410695Ssam register char *cp = index(line, '\n'); 88510695Ssam 88610695Ssam if (cp) 88710695Ssam *cp = '\0'; 88810695Ssam if (strcmp(line, name) == 0) { 88910695Ssam found++; 89010695Ssam break; 89110695Ssam } 89210695Ssam } 89310695Ssam fclose(fd); 89410695Ssam return (!found); 89510695Ssam } 896