110275Ssam #ifndef lint 2*13211Sroot static char sccsid[] = "@(#)ftpd.c 4.22 (Berkeley) 06/19/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> 1210275Ssam 1310275Ssam #include <netinet/in.h> 1410275Ssam 1513034Ssam #include <arpa/ftp.h> 16*13211Sroot #include <arpa/inet.h> 1713034Ssam 1810275Ssam #include <stdio.h> 1910275Ssam #include <signal.h> 2010275Ssam #include <wait.h> 2110275Ssam #include <pwd.h> 2210275Ssam #include <setjmp.h> 2310275Ssam #include <netdb.h> 2410423Ssam #include <errno.h> 2510275Ssam 2610695Ssam /* 2710695Ssam * File containing login names 2810695Ssam * NOT to be used on this machine. 2910695Ssam * Commonly used to disallow uucp. 3010695Ssam */ 3110695Ssam #define FTPUSERS "/etc/ftpusers" 3210695Ssam 3310275Ssam extern int errno; 3410275Ssam extern char *sys_errlist[]; 3510275Ssam extern char *crypt(); 3610275Ssam extern char version[]; 3710275Ssam extern char *home; /* pointer to home directory for glob */ 3810275Ssam extern FILE *popen(), *fopen(); 3910275Ssam extern int pclose(), fclose(); 4010275Ssam 4110275Ssam struct sockaddr_in ctrl_addr; 4210275Ssam struct sockaddr_in data_source; 4310275Ssam struct sockaddr_in data_dest; 4410275Ssam struct sockaddr_in his_addr; 4510275Ssam 4610275Ssam struct hostent *hp; 4710275Ssam 4810275Ssam int data; 4910275Ssam jmp_buf errcatch; 5010275Ssam int logged_in; 5110275Ssam struct passwd *pw; 5210275Ssam int debug; 5311653Ssam int timeout; 5411757Ssam int logging; 5510275Ssam int guest; 5610275Ssam int type; 5710275Ssam int form; 5810275Ssam int stru; /* avoid C keyword */ 5910275Ssam int mode; 6010321Ssam int usedefault = 1; /* for data transfers */ 6110275Ssam char hostname[32]; 6210275Ssam char *remotehost; 6310321Ssam struct servent *sp; 6410275Ssam 6511653Ssam /* 6611653Ssam * Timeout intervals for retrying connections 6711653Ssam * to hosts that don't accept PORT cmds. This 6811653Ssam * is a kludge, but given the problems with TCP... 6911653Ssam */ 7011653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 7111653Ssam #define SWAITINT 5 /* interval between retries */ 7211653Ssam 7311653Ssam int swaitmax = SWAITMAX; 7411653Ssam int swaitint = SWAITINT; 7511653Ssam 7610275Ssam int lostconn(); 7710419Ssam int reapchild(); 7810275Ssam FILE *getdatasock(), *dataconn(); 7910275Ssam char *ntoa(); 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); 13211220Ssam (void) open("/", 0); 13310275Ssam (void) dup2(0, 1); 13411653Ssam if (!logging) 13511653Ssam (void) dup2(0, 2); 13610275Ssam { int tt = open("/dev/tty", 2); 13710275Ssam if (tt > 0) { 13810275Ssam ioctl(tt, TIOCNOTTY, 0); 13910275Ssam close(tt); 14010275Ssam } 14110275Ssam } 14210275Ssam #endif 14310303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 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 #ifdef notdef 15110419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 15210419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 15310419Ssam #endif 15410275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 15510275Ssam perror("ftpd: bind"); 15610275Ssam sleep(5); 15710275Ssam } 15813034Ssam signal(SIGCHLD, reapchild); 15910303Ssam listen(s, 10); 16010275Ssam for (;;) { 16110275Ssam int hisaddrlen = sizeof (his_addr); 16210275Ssam 16310275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 16410275Ssam if (ctrl < 0) { 16510419Ssam if (errno == EINTR) 16610419Ssam continue; 16710275Ssam perror("ftpd: accept"); 16810275Ssam continue; 16910275Ssam } 17010275Ssam if (fork() == 0) { 17111220Ssam signal (SIGCHLD, SIG_IGN); 17210275Ssam if (logging) 17310275Ssam dolog(&his_addr); 17410275Ssam close(s); 17510275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 17610275Ssam /* do telnet option negotiation here */ 17710303Ssam /* 17810303Ssam * Set up default state 17910303Ssam */ 18010275Ssam logged_in = 0; 18110275Ssam data = -1; 18210303Ssam type = TYPE_A; 18310303Ssam form = FORM_N; 18410303Ssam stru = STRU_F; 18510303Ssam mode = MODE_S; 18613152Ssam (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr)); 18710275Ssam gethostname(hostname, sizeof (hostname)); 18810275Ssam reply(220, "%s FTP server (%s) ready.", 18910275Ssam hostname, version); 19010275Ssam for (;;) { 19110275Ssam setjmp(errcatch); 19210275Ssam yyparse(); 19310275Ssam } 19410275Ssam } 19510275Ssam close(ctrl); 19610275Ssam } 19710275Ssam } 19810275Ssam 19910419Ssam reapchild() 20010419Ssam { 20110419Ssam union wait status; 20210419Ssam 20310419Ssam while (wait3(&status, WNOHANG, 0) > 0) 20410419Ssam ; 20510419Ssam } 20610419Ssam 20710275Ssam lostconn() 20810275Ssam { 20910275Ssam 21010275Ssam fatal("Connection closed."); 21110275Ssam } 21210275Ssam 21310275Ssam pass(passwd) 21410275Ssam char *passwd; 21510275Ssam { 21610303Ssam char *xpasswd, *savestr(); 21710303Ssam static struct passwd save; 21810275Ssam 21910275Ssam if (logged_in || pw == NULL) { 22010275Ssam reply(503, "Login with USER first."); 22110275Ssam return; 22210275Ssam } 22310275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 22410275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22510275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 22610275Ssam reply(530, "Login incorrect."); 22710275Ssam pw = NULL; 22810275Ssam return; 22910275Ssam } 23010275Ssam } 23110303Ssam setegid(pw->pw_gid); 23210275Ssam initgroups(pw->pw_name, pw->pw_gid); 23310275Ssam if (chdir(pw->pw_dir)) { 23410275Ssam reply(550, "User %s: can't change directory to $s.", 23510275Ssam pw->pw_name, pw->pw_dir); 23610303Ssam goto bad; 23710275Ssam } 23810303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23910275Ssam reply(550, "Can't set guest privileges."); 24010303Ssam goto bad; 24110275Ssam } 24210275Ssam if (!guest) 24310275Ssam reply(230, "User %s logged in.", pw->pw_name); 24410275Ssam else 24510275Ssam reply(230, "Guest login ok, access restrictions apply."); 24610275Ssam logged_in = 1; 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 { 36413034Ssam int s, linger; 36510275Ssam 36610275Ssam if (data >= 0) 36710275Ssam return (fdopen(data, mode)); 36810602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 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; 37913034Ssam linger = 60; /* value ignored by system */ 38013034Ssam (void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)); 38110311Ssam seteuid(pw->pw_uid); 38210275Ssam return (fdopen(s, mode)); 38310602Ssam bad: 38410602Ssam seteuid(pw->pw_uid); 38510602Ssam close(s); 38610602Ssam return (NULL); 38710275Ssam } 38810275Ssam 38910275Ssam FILE * 39010275Ssam dataconn(name, size, mode) 39110275Ssam char *name; 39211653Ssam off_t size; 39310275Ssam char *mode; 39410275Ssam { 39510275Ssam char sizebuf[32]; 39610275Ssam FILE *file; 39711653Ssam int retry = 0; 39810275Ssam 39910275Ssam if (size >= 0) 40011653Ssam sprintf (sizebuf, " (%ld bytes)", size); 40110275Ssam else 40210275Ssam (void) strcpy(sizebuf, ""); 40310275Ssam if (data >= 0) { 40410275Ssam reply(125, "Using existing data connection for %s%s.", 40510275Ssam name, sizebuf); 40610321Ssam usedefault = 1; 40710275Ssam return (fdopen(data, mode)); 40810275Ssam } 40910566Ssam if (usedefault) 41010422Ssam data_dest = his_addr; 41110422Ssam usedefault = 1; 41210275Ssam file = getdatasock(mode); 41310275Ssam if (file == NULL) { 41410275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 41510275Ssam ntoa(data_source.sin_addr), 41610275Ssam ntohs(data_source.sin_port), 41710275Ssam sys_errlist[errno]); 41810275Ssam return (NULL); 41910275Ssam } 42010602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 42110602Ssam name, ntoa(data_dest.sin_addr.s_addr), 42210602Ssam ntohs(data_dest.sin_port), sizebuf); 42310275Ssam data = fileno(file); 42411653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 42511653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 42611653Ssam sleep(swaitint); 42711653Ssam retry += swaitint; 42811653Ssam continue; 42911653Ssam } 43010275Ssam reply(425, "Can't build data connection: %s.", 43110275Ssam sys_errlist[errno]); 43210275Ssam (void) fclose(file); 43310275Ssam data = -1; 43410275Ssam return (NULL); 43510275Ssam } 43610275Ssam return (file); 43710275Ssam } 43810275Ssam 43910275Ssam /* 44010275Ssam * Tranfer the contents of "instr" to 44110275Ssam * "outstr" peer using the appropriate 44210275Ssam * encapulation of the date subject 44310275Ssam * to Mode, Structure, and Type. 44410275Ssam * 44510275Ssam * NB: Form isn't handled. 44610275Ssam */ 44710275Ssam send_data(instr, outstr) 44810275Ssam FILE *instr, *outstr; 44910275Ssam { 45010275Ssam register int c; 45110275Ssam int netfd, filefd, cnt; 45210275Ssam char buf[BUFSIZ]; 45310275Ssam 45410275Ssam switch (type) { 45510275Ssam 45610275Ssam case TYPE_A: 45710275Ssam while ((c = getc(instr)) != EOF) { 45811220Ssam if (c == '\n') { 45911220Ssam if (ferror (outstr)) 46011220Ssam return (1); 46110275Ssam putc('\r', outstr); 46211220Ssam } 46311220Ssam putc(c, outstr); 46411220Ssam if (c == '\r') 46511220Ssam putc ('\0', outstr); 46610275Ssam } 46711220Ssam if (ferror (instr) || ferror (outstr)) 46811220Ssam return (1); 46910275Ssam return (0); 47010275Ssam 47110275Ssam case TYPE_I: 47210275Ssam case TYPE_L: 47310275Ssam netfd = fileno(outstr); 47410275Ssam filefd = fileno(instr); 47510275Ssam 47610303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 47710275Ssam if (write(netfd, buf, cnt) < 0) 47810275Ssam return (1); 47910275Ssam return (cnt < 0); 48010275Ssam } 48110275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 48210275Ssam return (1); 48310275Ssam } 48410275Ssam 48510275Ssam /* 48610275Ssam * Transfer data from peer to 48710275Ssam * "outstr" using the appropriate 48810275Ssam * encapulation of the data subject 48910275Ssam * to Mode, Structure, and Type. 49010275Ssam * 49110275Ssam * N.B.: Form isn't handled. 49210275Ssam */ 49310275Ssam receive_data(instr, outstr) 49410275Ssam FILE *instr, *outstr; 49510275Ssam { 49610275Ssam register int c; 49711220Ssam int cnt; 49810275Ssam char buf[BUFSIZ]; 49910275Ssam 50010275Ssam 50110275Ssam switch (type) { 50210275Ssam 50310275Ssam case TYPE_I: 50410275Ssam case TYPE_L: 50510616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 50610616Ssam if (write(fileno(outstr), buf, cnt) < 0) 50710275Ssam return (1); 50810275Ssam return (cnt < 0); 50910275Ssam 51010275Ssam case TYPE_E: 51110275Ssam reply(504, "TYPE E not implemented."); 51210275Ssam return (1); 51310275Ssam 51410275Ssam case TYPE_A: 51510275Ssam while ((c = getc(instr)) != EOF) { 51610275Ssam if (c == '\r') { 51711220Ssam if (ferror (outstr)) 51811220Ssam return (1); 51911220Ssam if ((c = getc(instr)) != '\n') 52011220Ssam putc ('\r', outstr); 52111220Ssam if (c == '\0') 52211220Ssam continue; 52310275Ssam } 52411220Ssam putc (c, outstr); 52510275Ssam } 52611220Ssam if (ferror (instr) || ferror (outstr)) 52711220Ssam return (1); 52810275Ssam return (0); 52910275Ssam } 53010275Ssam fatal("Unknown type in receive_data."); 53110275Ssam /*NOTREACHED*/ 53210275Ssam } 53310275Ssam 53410275Ssam fatal(s) 53510275Ssam char *s; 53610275Ssam { 53710275Ssam reply(451, "Error in server: %s\n", s); 53810275Ssam reply(221, "Closing connection due to server error."); 53910275Ssam exit(0); 54010275Ssam } 54110275Ssam 54210275Ssam reply(n, s, args) 54310275Ssam int n; 54410275Ssam char *s; 54510275Ssam { 54610275Ssam 54710275Ssam printf("%d ", n); 54810275Ssam _doprnt(s, &args, stdout); 54910275Ssam printf("\r\n"); 55010275Ssam fflush(stdout); 55110275Ssam if (debug) { 55210275Ssam fprintf(stderr, "<--- %d ", n); 55310275Ssam _doprnt(s, &args, stderr); 55410275Ssam fprintf(stderr, "\n"); 55510275Ssam fflush(stderr); 55610275Ssam } 55710275Ssam } 55810275Ssam 55910275Ssam lreply(n, s, args) 56010275Ssam int n; 56110275Ssam char *s; 56210275Ssam { 56310275Ssam printf("%d-", n); 56410275Ssam _doprnt(s, &args, stdout); 56510275Ssam printf("\r\n"); 56610275Ssam fflush(stdout); 56710275Ssam if (debug) { 56810275Ssam fprintf(stderr, "<--- %d-", n); 56910275Ssam _doprnt(s, &args, stderr); 57010275Ssam fprintf(stderr, "\n"); 57110275Ssam } 57210275Ssam } 57310275Ssam 57410275Ssam replystr(s) 57510275Ssam char *s; 57610275Ssam { 57710275Ssam printf("%s\r\n", s); 57810275Ssam fflush(stdout); 57910275Ssam if (debug) 58010275Ssam fprintf(stderr, "<--- %s\n", s); 58110275Ssam } 58210275Ssam 58310275Ssam ack(s) 58410275Ssam char *s; 58510275Ssam { 58610275Ssam reply(200, "%s command okay.", s); 58710275Ssam } 58810275Ssam 58910275Ssam nack(s) 59010275Ssam char *s; 59110275Ssam { 59210275Ssam reply(502, "%s command not implemented.", s); 59310275Ssam } 59410275Ssam 59510275Ssam yyerror() 59610275Ssam { 59710275Ssam reply(500, "Command not understood."); 59810275Ssam } 59910275Ssam 60010275Ssam delete(name) 60110275Ssam char *name; 60210275Ssam { 60310275Ssam struct stat st; 60410275Ssam 60510275Ssam if (stat(name, &st) < 0) { 60610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60710275Ssam return; 60810275Ssam } 60910275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 61010275Ssam if (rmdir(name) < 0) { 61110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61210275Ssam return; 61310275Ssam } 61410275Ssam goto done; 61510275Ssam } 61610275Ssam if (unlink(name) < 0) { 61710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61810275Ssam return; 61910275Ssam } 62010275Ssam done: 62110275Ssam ack("DELE"); 62210275Ssam } 62310275Ssam 62410275Ssam cwd(path) 62510275Ssam char *path; 62610275Ssam { 62710275Ssam 62810275Ssam if (chdir(path) < 0) { 62910275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 63010275Ssam return; 63110275Ssam } 63210275Ssam ack("CWD"); 63310275Ssam } 63410275Ssam 63510303Ssam makedir(name) 63610275Ssam char *name; 63710275Ssam { 63810303Ssam struct stat st; 63910303Ssam int dochown = stat(name, &st) < 0; 64010275Ssam 64110275Ssam if (mkdir(name, 0777) < 0) { 64210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64310275Ssam return; 64410275Ssam } 64510303Ssam if (dochown) 64610303Ssam (void) chown(name, pw->pw_uid, -1); 64710275Ssam ack("MKDIR"); 64810275Ssam } 64910275Ssam 65010303Ssam removedir(name) 65110275Ssam char *name; 65210275Ssam { 65310275Ssam 65410275Ssam if (rmdir(name) < 0) { 65510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 65610275Ssam return; 65710275Ssam } 65810275Ssam ack("RMDIR"); 65910275Ssam } 66010275Ssam 66110303Ssam pwd() 66210275Ssam { 66310303Ssam char path[MAXPATHLEN + 1]; 66410275Ssam 66510275Ssam if (getwd(path) == NULL) { 66610275Ssam reply(451, "%s.", path); 66710275Ssam return; 66810275Ssam } 66910275Ssam reply(251, "\"%s\" is current directory.", path); 67010275Ssam } 67110275Ssam 67210275Ssam char * 67310275Ssam renamefrom(name) 67410275Ssam char *name; 67510275Ssam { 67610275Ssam struct stat st; 67710275Ssam 67810275Ssam if (stat(name, &st) < 0) { 67910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 68010275Ssam return ((char *)0); 68110275Ssam } 68210303Ssam reply(350, "File exists, ready for destination name"); 68310275Ssam return (name); 68410275Ssam } 68510275Ssam 68610275Ssam renamecmd(from, to) 68710275Ssam char *from, *to; 68810275Ssam { 68910275Ssam 69010275Ssam if (rename(from, to) < 0) { 69110275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 69210275Ssam return; 69310275Ssam } 69410275Ssam ack("RNTO"); 69510275Ssam } 69610275Ssam 69710275Ssam /* 69810275Ssam * Convert network-format internet address 69910275Ssam * to base 256 d.d.d.d representation. 70010275Ssam */ 70110275Ssam char * 70210275Ssam ntoa(in) 70310275Ssam struct in_addr in; 70410275Ssam { 70510275Ssam static char b[18]; 70610275Ssam register char *p; 70710275Ssam 70810275Ssam p = (char *)∈ 70910275Ssam #define UC(b) (((int)b)&0xff) 71010275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 71110275Ssam return (b); 71210275Ssam } 71310275Ssam 71410275Ssam dolog(sin) 71510275Ssam struct sockaddr_in *sin; 71610275Ssam { 71710275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 71810275Ssam sizeof (struct in_addr), AF_INET); 71910275Ssam char *remotehost; 72010275Ssam time_t t; 72110275Ssam 72210275Ssam if (hp) 72310275Ssam remotehost = hp->h_name; 72410275Ssam else 72513152Ssam remotehost = inet_ntoa(sin->sin_addr); 72610275Ssam t = time(0); 72711757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 72810275Ssam fflush(stderr); 72910275Ssam } 73010695Ssam 73110695Ssam /* 73210695Ssam * Special version of popen which avoids 73310695Ssam * call to shell. This insures noone may 73410695Ssam * create a pipe to a hidden program as a side 73510695Ssam * effect of a list or dir command. 73610695Ssam */ 73710695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 73810695Ssam #define RDR 0 73910695Ssam #define WTR 1 74010695Ssam static int popen_pid[5]; 74110695Ssam 74210695Ssam static char * 74310695Ssam nextarg(cpp) 74410695Ssam char *cpp; 74510695Ssam { 74610695Ssam register char *cp = cpp; 74710695Ssam 74810695Ssam if (cp == 0) 74910695Ssam return (cp); 75010695Ssam while (*cp && *cp != ' ' && *cp != '\t') 75110695Ssam cp++; 75210695Ssam if (*cp == ' ' || *cp == '\t') { 75310695Ssam *cp++ = '\0'; 75410695Ssam while (*cp == ' ' || *cp == '\t') 75510695Ssam cp++; 75610695Ssam } 75710695Ssam if (cp == cpp) 75810695Ssam return ((char *)0); 75910695Ssam return (cp); 76010695Ssam } 76110695Ssam 76210695Ssam FILE * 76310695Ssam popen(cmd, mode) 76410695Ssam char *cmd, *mode; 76510695Ssam { 766*13211Sroot int p[2], ac, gac; 76710695Ssam register myside, hisside, pid; 768*13211Sroot char *av[20], *gav[512]; 76910695Ssam register char *cp; 77010695Ssam 77110695Ssam if (pipe(p) < 0) 77210695Ssam return (NULL); 77310695Ssam cp = cmd, ac = 0; 774*13211Sroot /* break up string into pieces */ 77510695Ssam do { 77610695Ssam av[ac++] = cp; 77710695Ssam cp = nextarg(cp); 778*13211Sroot } while (cp && *cp && ac < 20); 77910695Ssam av[ac] = (char *)0; 780*13211Sroot gav[0] = av[0]; 781*13211Sroot /* glob each piece */ 782*13211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 783*13211Sroot char **pop; 784*13211Sroot extern char **glob(); 785*13211Sroot 786*13211Sroot pop = glob(av[ac]); 787*13211Sroot if (pop) { 788*13211Sroot av[ac] = (char *)pop; /* save to free later */ 789*13211Sroot while (*pop && gac < 512) 790*13211Sroot gav[gac++] = *pop++; 791*13211Sroot } 79211757Ssam } 793*13211Sroot gav[gac] = (char *)0; 79410695Ssam myside = tst(p[WTR], p[RDR]); 79510695Ssam hisside = tst(p[RDR], p[WTR]); 79610695Ssam if ((pid = fork()) == 0) { 79710695Ssam /* myside and hisside reverse roles in child */ 79810695Ssam close(myside); 79910695Ssam dup2(hisside, tst(0, 1)); 80010695Ssam close(hisside); 801*13211Sroot execv(gav[0], gav); 80210695Ssam _exit(1); 80310695Ssam } 804*13211Sroot for (ac = 1; av[ac] != NULL; ac++) 805*13211Sroot blkfree((char **)av[ac]); 80610695Ssam if (pid == -1) 80710695Ssam return (NULL); 80810695Ssam popen_pid[myside] = pid; 80910695Ssam close(hisside); 81010695Ssam return (fdopen(myside, mode)); 81110695Ssam } 81210695Ssam 81310695Ssam pclose(ptr) 81410695Ssam FILE *ptr; 81510695Ssam { 81610695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 81710695Ssam int status; 81810695Ssam 81910695Ssam f = fileno(ptr); 82010695Ssam fclose(ptr); 82110695Ssam istat = signal(SIGINT, SIG_IGN); 82210695Ssam qstat = signal(SIGQUIT, SIG_IGN); 82310695Ssam hstat = signal(SIGHUP, SIG_IGN); 82410695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 82510695Ssam ; 82610695Ssam if (r == -1) 82710695Ssam status = -1; 82810695Ssam signal(SIGINT, istat); 82910695Ssam signal(SIGQUIT, qstat); 83010695Ssam signal(SIGHUP, hstat); 83110695Ssam return (status); 83210695Ssam } 83310695Ssam 83410695Ssam /* 83510695Ssam * Check user requesting login priviledges. 83610695Ssam * Disallow anyone mentioned in the file FTPUSERS 83710695Ssam * to allow people such as uucp to be avoided. 83810695Ssam */ 83910695Ssam checkuser(name) 84010695Ssam register char *name; 84110695Ssam { 84210695Ssam char line[BUFSIZ], *index(); 84310695Ssam FILE *fd; 84410695Ssam int found = 0; 84510695Ssam 84610695Ssam fd = fopen(FTPUSERS, "r"); 84710695Ssam if (fd == NULL) 84810695Ssam return (1); 84910695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 85010695Ssam register char *cp = index(line, '\n'); 85110695Ssam 85210695Ssam if (cp) 85310695Ssam *cp = '\0'; 85410695Ssam if (strcmp(line, name) == 0) { 85510695Ssam found++; 85610695Ssam break; 85710695Ssam } 85810695Ssam } 85910695Ssam fclose(fd); 86010695Ssam return (!found); 86110695Ssam } 862