110275Ssam #ifndef lint 2*11757Ssam static char sccsid[] = "@(#)ftpd.c 4.19 (Berkeley) 03/29/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 1510275Ssam #include <stdio.h> 1610275Ssam #include <signal.h> 1710275Ssam #include <wait.h> 1810275Ssam #include <pwd.h> 1910275Ssam #include <setjmp.h> 2010275Ssam #include <netdb.h> 2110423Ssam #include <errno.h> 2210275Ssam 2310275Ssam #include "ftp.h" 2410275Ssam 2510695Ssam /* 2610695Ssam * File containing login names 2710695Ssam * NOT to be used on this machine. 2810695Ssam * Commonly used to disallow uucp. 2910695Ssam */ 3010695Ssam #define FTPUSERS "/etc/ftpusers" 3110695Ssam 3210275Ssam extern int errno; 3310275Ssam extern char *sys_errlist[]; 3410275Ssam extern char *crypt(); 3510275Ssam extern char version[]; 3610275Ssam extern char *home; /* pointer to home directory for glob */ 3710275Ssam extern FILE *popen(), *fopen(); 3810275Ssam extern int pclose(), fclose(); 3910275Ssam 4010275Ssam struct sockaddr_in ctrl_addr; 4110275Ssam struct sockaddr_in data_source; 4210275Ssam struct sockaddr_in data_dest; 4310275Ssam struct sockaddr_in his_addr; 4410275Ssam 4510275Ssam struct hostent *hp; 4610275Ssam 4710275Ssam int data; 4810275Ssam jmp_buf errcatch; 4910275Ssam int logged_in; 5010275Ssam struct passwd *pw; 5110275Ssam int debug; 5211653Ssam int timeout; 53*11757Ssam int logging; 5410275Ssam int guest; 5510275Ssam int type; 5610275Ssam int form; 5710275Ssam int stru; /* avoid C keyword */ 5810275Ssam int mode; 5910321Ssam int usedefault = 1; /* for data transfers */ 6010275Ssam char hostname[32]; 6110275Ssam char *remotehost; 6210321Ssam struct servent *sp; 6310275Ssam 6411653Ssam /* 6511653Ssam * Timeout intervals for retrying connections 6611653Ssam * to hosts that don't accept PORT cmds. This 6711653Ssam * is a kludge, but given the problems with TCP... 6811653Ssam */ 6911653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 7011653Ssam #define SWAITINT 5 /* interval between retries */ 7111653Ssam 7211653Ssam int swaitmax = SWAITMAX; 7311653Ssam int swaitint = SWAITINT; 7411653Ssam 7510275Ssam int lostconn(); 7610419Ssam int reapchild(); 7710275Ssam FILE *getdatasock(), *dataconn(); 7810275Ssam char *ntoa(); 7910275Ssam 8010275Ssam main(argc, argv) 8110275Ssam int argc; 8210275Ssam char *argv[]; 8310275Ssam { 8410275Ssam int ctrl, s, options = 0; 8510275Ssam char *cp; 8610275Ssam 8710275Ssam sp = getservbyname("ftp", "tcp"); 8810275Ssam if (sp == 0) { 8911220Ssam fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 9010275Ssam exit(1); 9110275Ssam } 9210275Ssam ctrl_addr.sin_port = sp->s_port; 9310275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 9410275Ssam signal(SIGPIPE, lostconn); 9510275Ssam debug = 0; 9610275Ssam argc--, argv++; 9710275Ssam while (argc > 0 && *argv[0] == '-') { 9810275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 9910275Ssam 10011653Ssam case 'v': 10111653Ssam debug = 1; 10211653Ssam break; 10311653Ssam 10410275Ssam case 'd': 10510275Ssam debug = 1; 10610275Ssam options |= SO_DEBUG; 10710275Ssam break; 10810275Ssam 109*11757Ssam case 'l': 110*11757Ssam logging = 1; 111*11757Ssam break; 112*11757Ssam 11311653Ssam case 't': 11411653Ssam timeout = atoi(++cp); 11511653Ssam goto nextopt; 11611653Ssam break; 11711653Ssam 11810275Ssam default: 11911653Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", *cp); 12010275Ssam break; 12110275Ssam } 12211653Ssam nextopt: 12310275Ssam argc--, argv++; 12410275Ssam } 12510275Ssam #ifndef DEBUG 12610275Ssam if (fork()) 12710275Ssam exit(0); 12810275Ssam for (s = 0; s < 10; s++) 12911653Ssam if (!logging || (s != 2)) 13011653Ssam (void) close(s); 13111220Ssam (void) open("/", 0); 13210275Ssam (void) dup2(0, 1); 13311653Ssam if (!logging) 13411653Ssam (void) dup2(0, 2); 13510275Ssam { int tt = open("/dev/tty", 2); 13610275Ssam if (tt > 0) { 13710275Ssam ioctl(tt, TIOCNOTTY, 0); 13810275Ssam close(tt); 13910275Ssam } 14010275Ssam } 14110275Ssam #endif 14210303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 14310275Ssam perror("ftpd: socket"); 14410275Ssam sleep(5); 14510275Ssam } 14610419Ssam if (options & SO_DEBUG) 14710419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 14810419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 14910419Ssam #ifdef notdef 15010419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 15110419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 15210419Ssam #endif 15310275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 15410275Ssam perror("ftpd: bind"); 15510275Ssam sleep(5); 15610275Ssam } 15710588Ssam sigset(SIGCHLD, reapchild); 15810303Ssam listen(s, 10); 15910275Ssam for (;;) { 16010275Ssam int hisaddrlen = sizeof (his_addr); 16110275Ssam 16210275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 16310275Ssam if (ctrl < 0) { 16410419Ssam if (errno == EINTR) 16510419Ssam continue; 16610275Ssam perror("ftpd: accept"); 16710275Ssam continue; 16810275Ssam } 16910275Ssam if (fork() == 0) { 17011220Ssam signal (SIGCHLD, SIG_IGN); 17110275Ssam if (logging) 17210275Ssam dolog(&his_addr); 17310275Ssam close(s); 17410275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 17510275Ssam /* do telnet option negotiation here */ 17610303Ssam /* 17710303Ssam * Set up default state 17810303Ssam */ 17910275Ssam logged_in = 0; 18010275Ssam data = -1; 18110303Ssam type = TYPE_A; 18210303Ssam form = FORM_N; 18310303Ssam stru = STRU_F; 18410303Ssam mode = MODE_S; 18510275Ssam gethostname(hostname, sizeof (hostname)); 18610275Ssam reply(220, "%s FTP server (%s) ready.", 18710275Ssam hostname, version); 18810275Ssam for (;;) { 18910275Ssam setjmp(errcatch); 19010275Ssam yyparse(); 19110275Ssam } 19210275Ssam } 19310275Ssam close(ctrl); 19410275Ssam } 19510275Ssam } 19610275Ssam 19710419Ssam reapchild() 19810419Ssam { 19910419Ssam union wait status; 20010419Ssam 20110419Ssam while (wait3(&status, WNOHANG, 0) > 0) 20210419Ssam ; 20310419Ssam } 20410419Ssam 20510275Ssam lostconn() 20610275Ssam { 20710275Ssam 20810275Ssam fatal("Connection closed."); 20910275Ssam } 21010275Ssam 21110275Ssam pass(passwd) 21210275Ssam char *passwd; 21310275Ssam { 21410303Ssam char *xpasswd, *savestr(); 21510303Ssam static struct passwd save; 21610275Ssam 21710275Ssam if (logged_in || pw == NULL) { 21810275Ssam reply(503, "Login with USER first."); 21910275Ssam return; 22010275Ssam } 22110275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 22210275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22310275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 22410275Ssam reply(530, "Login incorrect."); 22510275Ssam pw = NULL; 22610275Ssam return; 22710275Ssam } 22810275Ssam } 22910303Ssam setegid(pw->pw_gid); 23010275Ssam initgroups(pw->pw_name, pw->pw_gid); 23110275Ssam if (chdir(pw->pw_dir)) { 23210275Ssam reply(550, "User %s: can't change directory to $s.", 23310275Ssam pw->pw_name, pw->pw_dir); 23410303Ssam goto bad; 23510275Ssam } 23610303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23710275Ssam reply(550, "Can't set guest privileges."); 23810303Ssam goto bad; 23910275Ssam } 24010275Ssam if (!guest) 24110275Ssam reply(230, "User %s logged in.", pw->pw_name); 24210275Ssam else 24310275Ssam reply(230, "Guest login ok, access restrictions apply."); 24410275Ssam logged_in = 1; 24510303Ssam seteuid(pw->pw_uid); 24610303Ssam /* 24710303Ssam * Save everything so globbing doesn't 24810303Ssam * clobber the fields. 24910303Ssam */ 25010303Ssam save = *pw; 25110303Ssam save.pw_name = savestr(pw->pw_name); 25210303Ssam save.pw_passwd = savestr(pw->pw_passwd); 25310303Ssam save.pw_comment = savestr(pw->pw_comment); 25410303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 25510303Ssam save.pw_dir = savestr(pw->pw_dir); 25610303Ssam save.pw_shell = savestr(pw->pw_shell); 25710303Ssam pw = &save; 25810303Ssam home = pw->pw_dir; /* home dir for globbing */ 25910303Ssam return; 26010303Ssam bad: 26110303Ssam seteuid(0); 26210303Ssam pw = NULL; 26310275Ssam } 26410275Ssam 26510303Ssam char * 26610303Ssam savestr(s) 26710303Ssam char *s; 26810303Ssam { 26910303Ssam char *malloc(); 27010303Ssam char *new = malloc(strlen(s) + 1); 27110303Ssam 27210303Ssam if (new != NULL) 27310303Ssam strcpy(new, s); 27411347Ssam return (new); 27510303Ssam } 27610303Ssam 27710275Ssam retrieve(cmd, name) 27810275Ssam char *cmd, *name; 27910275Ssam { 28010275Ssam FILE *fin, *dout; 28110275Ssam struct stat st; 28210275Ssam int (*closefunc)(); 28310275Ssam 28410275Ssam if (cmd == 0) { 28510317Ssam #ifdef notdef 28610317Ssam /* no remote command execution -- it's a security hole */ 28711653Ssam if (*name == '|') 28810275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 28910275Ssam else 29010317Ssam #endif 29110275Ssam fin = fopen(name, "r"), closefunc = fclose; 29210275Ssam } else { 29310275Ssam char line[BUFSIZ]; 29410275Ssam 29510422Ssam sprintf(line, cmd, name), name = line; 29610275Ssam fin = popen(line, "r"), closefunc = pclose; 29710275Ssam } 29810275Ssam if (fin == NULL) { 29910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30010275Ssam return; 30110275Ssam } 30210275Ssam st.st_size = 0; 30310275Ssam if (cmd == 0 && 30410275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 30510275Ssam reply(550, "%s: not a plain file.", name); 30610275Ssam goto done; 30710275Ssam } 30810275Ssam dout = dataconn(name, st.st_size, "w"); 30910275Ssam if (dout == NULL) 31010275Ssam goto done; 31110303Ssam if (send_data(fin, dout) || ferror(dout)) 31210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31310275Ssam else 31410275Ssam reply(226, "Transfer complete."); 31510303Ssam fclose(dout), data = -1; 31610275Ssam done: 31710275Ssam (*closefunc)(fin); 31810275Ssam } 31910275Ssam 32010275Ssam store(name, mode) 32110275Ssam char *name, *mode; 32210275Ssam { 32310275Ssam FILE *fout, *din; 32410303Ssam int (*closefunc)(), dochown = 0; 32510275Ssam 32610317Ssam #ifdef notdef 32710317Ssam /* no remote command execution -- it's a security hole */ 32811653Ssam if (name[0] == '|') 32910275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33010317Ssam else 33110317Ssam #endif 33210317Ssam { 33310303Ssam struct stat st; 33410303Ssam 33510303Ssam if (stat(name, &st) < 0) 33610303Ssam dochown++; 33710275Ssam fout = fopen(name, mode), closefunc = fclose; 33810303Ssam } 33910275Ssam if (fout == NULL) { 34010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34110275Ssam return; 34210275Ssam } 34311653Ssam din = dataconn(name, (off_t)-1, "r"); 34410275Ssam if (din == NULL) 34510275Ssam goto done; 34610303Ssam if (receive_data(din, fout) || ferror(fout)) 34710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34810275Ssam else 34910275Ssam reply(226, "Transfer complete."); 35010275Ssam fclose(din), data = -1; 35110275Ssam done: 35210303Ssam if (dochown) 35310303Ssam (void) chown(name, pw->pw_uid, -1); 35410275Ssam (*closefunc)(fout); 35510275Ssam } 35610275Ssam 35710275Ssam FILE * 35810275Ssam getdatasock(mode) 35910275Ssam char *mode; 36010275Ssam { 36110602Ssam int s; 36210275Ssam 36310275Ssam if (data >= 0) 36410275Ssam return (fdopen(data, mode)); 36510602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 36610602Ssam if (s < 0) 36710275Ssam return (NULL); 36810275Ssam seteuid(0); 36910602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 37010602Ssam goto bad; 37110602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 37210602Ssam goto bad; 37310311Ssam seteuid(pw->pw_uid); 37410275Ssam return (fdopen(s, mode)); 37510602Ssam bad: 37610602Ssam seteuid(pw->pw_uid); 37710602Ssam close(s); 37810602Ssam return (NULL); 37910275Ssam } 38010275Ssam 38110275Ssam FILE * 38210275Ssam dataconn(name, size, mode) 38310275Ssam char *name; 38411653Ssam off_t size; 38510275Ssam char *mode; 38610275Ssam { 38710275Ssam char sizebuf[32]; 38810275Ssam FILE *file; 38911653Ssam int retry = 0; 39010275Ssam 39110275Ssam if (size >= 0) 39211653Ssam sprintf (sizebuf, " (%ld bytes)", size); 39310275Ssam else 39410275Ssam (void) strcpy(sizebuf, ""); 39510275Ssam if (data >= 0) { 39610275Ssam reply(125, "Using existing data connection for %s%s.", 39710275Ssam name, sizebuf); 39810321Ssam usedefault = 1; 39910275Ssam return (fdopen(data, mode)); 40010275Ssam } 40110566Ssam if (usedefault) 40210422Ssam data_dest = his_addr; 40310422Ssam usedefault = 1; 40410275Ssam file = getdatasock(mode); 40510275Ssam if (file == NULL) { 40610275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 40710275Ssam ntoa(data_source.sin_addr), 40810275Ssam ntohs(data_source.sin_port), 40910275Ssam sys_errlist[errno]); 41010275Ssam return (NULL); 41110275Ssam } 41210602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 41310602Ssam name, ntoa(data_dest.sin_addr.s_addr), 41410602Ssam ntohs(data_dest.sin_port), sizebuf); 41510275Ssam data = fileno(file); 41611653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 41711653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 41811653Ssam sleep(swaitint); 41911653Ssam retry += swaitint; 42011653Ssam continue; 42111653Ssam } 42210275Ssam reply(425, "Can't build data connection: %s.", 42310275Ssam sys_errlist[errno]); 42410275Ssam (void) fclose(file); 42510275Ssam data = -1; 42610275Ssam return (NULL); 42710275Ssam } 42810275Ssam return (file); 42910275Ssam } 43010275Ssam 43110275Ssam /* 43210275Ssam * Tranfer the contents of "instr" to 43310275Ssam * "outstr" peer using the appropriate 43410275Ssam * encapulation of the date subject 43510275Ssam * to Mode, Structure, and Type. 43610275Ssam * 43710275Ssam * NB: Form isn't handled. 43810275Ssam */ 43910275Ssam send_data(instr, outstr) 44010275Ssam FILE *instr, *outstr; 44110275Ssam { 44210275Ssam register int c; 44310275Ssam int netfd, filefd, cnt; 44410275Ssam char buf[BUFSIZ]; 44510275Ssam 44610275Ssam switch (type) { 44710275Ssam 44810275Ssam case TYPE_A: 44910275Ssam while ((c = getc(instr)) != EOF) { 45011220Ssam if (c == '\n') { 45111220Ssam if (ferror (outstr)) 45211220Ssam return (1); 45310275Ssam putc('\r', outstr); 45411220Ssam } 45511220Ssam putc(c, outstr); 45611220Ssam if (c == '\r') 45711220Ssam putc ('\0', outstr); 45810275Ssam } 45911220Ssam if (ferror (instr) || ferror (outstr)) 46011220Ssam return (1); 46110275Ssam return (0); 46210275Ssam 46310275Ssam case TYPE_I: 46410275Ssam case TYPE_L: 46510275Ssam netfd = fileno(outstr); 46610275Ssam filefd = fileno(instr); 46710275Ssam 46810303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 46910275Ssam if (write(netfd, buf, cnt) < 0) 47010275Ssam return (1); 47110275Ssam return (cnt < 0); 47210275Ssam } 47310275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 47410275Ssam return (1); 47510275Ssam } 47610275Ssam 47710275Ssam /* 47810275Ssam * Transfer data from peer to 47910275Ssam * "outstr" using the appropriate 48010275Ssam * encapulation of the data subject 48110275Ssam * to Mode, Structure, and Type. 48210275Ssam * 48310275Ssam * N.B.: Form isn't handled. 48410275Ssam */ 48510275Ssam receive_data(instr, outstr) 48610275Ssam FILE *instr, *outstr; 48710275Ssam { 48810275Ssam register int c; 48911220Ssam int cnt; 49010275Ssam char buf[BUFSIZ]; 49110275Ssam 49210275Ssam 49310275Ssam switch (type) { 49410275Ssam 49510275Ssam case TYPE_I: 49610275Ssam case TYPE_L: 49710616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 49810616Ssam if (write(fileno(outstr), buf, cnt) < 0) 49910275Ssam return (1); 50010275Ssam return (cnt < 0); 50110275Ssam 50210275Ssam case TYPE_E: 50310275Ssam reply(504, "TYPE E not implemented."); 50410275Ssam return (1); 50510275Ssam 50610275Ssam case TYPE_A: 50710275Ssam while ((c = getc(instr)) != EOF) { 50810275Ssam if (c == '\r') { 50911220Ssam if (ferror (outstr)) 51011220Ssam return (1); 51111220Ssam if ((c = getc(instr)) != '\n') 51211220Ssam putc ('\r', outstr); 51311220Ssam if (c == '\0') 51411220Ssam continue; 51510275Ssam } 51611220Ssam putc (c, outstr); 51710275Ssam } 51811220Ssam if (ferror (instr) || ferror (outstr)) 51911220Ssam return (1); 52010275Ssam return (0); 52110275Ssam } 52210275Ssam fatal("Unknown type in receive_data."); 52310275Ssam /*NOTREACHED*/ 52410275Ssam } 52510275Ssam 52610275Ssam fatal(s) 52710275Ssam char *s; 52810275Ssam { 52910275Ssam reply(451, "Error in server: %s\n", s); 53010275Ssam reply(221, "Closing connection due to server error."); 53110275Ssam exit(0); 53210275Ssam } 53310275Ssam 53410275Ssam reply(n, s, args) 53510275Ssam int n; 53610275Ssam char *s; 53710275Ssam { 53810275Ssam 53910275Ssam printf("%d ", n); 54010275Ssam _doprnt(s, &args, stdout); 54110275Ssam printf("\r\n"); 54210275Ssam fflush(stdout); 54310275Ssam if (debug) { 54410275Ssam fprintf(stderr, "<--- %d ", n); 54510275Ssam _doprnt(s, &args, stderr); 54610275Ssam fprintf(stderr, "\n"); 54710275Ssam fflush(stderr); 54810275Ssam } 54910275Ssam } 55010275Ssam 55110275Ssam lreply(n, s, args) 55210275Ssam int n; 55310275Ssam char *s; 55410275Ssam { 55510275Ssam printf("%d-", n); 55610275Ssam _doprnt(s, &args, stdout); 55710275Ssam printf("\r\n"); 55810275Ssam fflush(stdout); 55910275Ssam if (debug) { 56010275Ssam fprintf(stderr, "<--- %d-", n); 56110275Ssam _doprnt(s, &args, stderr); 56210275Ssam fprintf(stderr, "\n"); 56310275Ssam } 56410275Ssam } 56510275Ssam 56610275Ssam replystr(s) 56710275Ssam char *s; 56810275Ssam { 56910275Ssam printf("%s\r\n", s); 57010275Ssam fflush(stdout); 57110275Ssam if (debug) 57210275Ssam fprintf(stderr, "<--- %s\n", s); 57310275Ssam } 57410275Ssam 57510275Ssam ack(s) 57610275Ssam char *s; 57710275Ssam { 57810275Ssam reply(200, "%s command okay.", s); 57910275Ssam } 58010275Ssam 58110275Ssam nack(s) 58210275Ssam char *s; 58310275Ssam { 58410275Ssam reply(502, "%s command not implemented.", s); 58510275Ssam } 58610275Ssam 58710275Ssam yyerror() 58810275Ssam { 58910275Ssam reply(500, "Command not understood."); 59010275Ssam } 59110275Ssam 59210275Ssam delete(name) 59310275Ssam char *name; 59410275Ssam { 59510275Ssam struct stat st; 59610275Ssam 59710275Ssam if (stat(name, &st) < 0) { 59810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 59910275Ssam return; 60010275Ssam } 60110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 60210275Ssam if (rmdir(name) < 0) { 60310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60410275Ssam return; 60510275Ssam } 60610275Ssam goto done; 60710275Ssam } 60810275Ssam if (unlink(name) < 0) { 60910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61010275Ssam return; 61110275Ssam } 61210275Ssam done: 61310275Ssam ack("DELE"); 61410275Ssam } 61510275Ssam 61610275Ssam cwd(path) 61710275Ssam char *path; 61810275Ssam { 61910275Ssam 62010275Ssam if (chdir(path) < 0) { 62110275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 62210275Ssam return; 62310275Ssam } 62410275Ssam ack("CWD"); 62510275Ssam } 62610275Ssam 62710303Ssam makedir(name) 62810275Ssam char *name; 62910275Ssam { 63010303Ssam struct stat st; 63110303Ssam int dochown = stat(name, &st) < 0; 63210275Ssam 63310275Ssam if (mkdir(name, 0777) < 0) { 63410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 63510275Ssam return; 63610275Ssam } 63710303Ssam if (dochown) 63810303Ssam (void) chown(name, pw->pw_uid, -1); 63910275Ssam ack("MKDIR"); 64010275Ssam } 64110275Ssam 64210303Ssam removedir(name) 64310275Ssam char *name; 64410275Ssam { 64510275Ssam 64610275Ssam if (rmdir(name) < 0) { 64710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64810275Ssam return; 64910275Ssam } 65010275Ssam ack("RMDIR"); 65110275Ssam } 65210275Ssam 65310303Ssam pwd() 65410275Ssam { 65510303Ssam char path[MAXPATHLEN + 1]; 65610275Ssam 65710275Ssam if (getwd(path) == NULL) { 65810275Ssam reply(451, "%s.", path); 65910275Ssam return; 66010275Ssam } 66110275Ssam reply(251, "\"%s\" is current directory.", path); 66210275Ssam } 66310275Ssam 66410275Ssam char * 66510275Ssam renamefrom(name) 66610275Ssam char *name; 66710275Ssam { 66810275Ssam struct stat st; 66910275Ssam 67010275Ssam if (stat(name, &st) < 0) { 67110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67210275Ssam return ((char *)0); 67310275Ssam } 67410303Ssam reply(350, "File exists, ready for destination name"); 67510275Ssam return (name); 67610275Ssam } 67710275Ssam 67810275Ssam renamecmd(from, to) 67910275Ssam char *from, *to; 68010275Ssam { 68110275Ssam 68210275Ssam if (rename(from, to) < 0) { 68310275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 68410275Ssam return; 68510275Ssam } 68610275Ssam ack("RNTO"); 68710275Ssam } 68810275Ssam 68910275Ssam /* 69010275Ssam * Convert network-format internet address 69110275Ssam * to base 256 d.d.d.d representation. 69210275Ssam */ 69310275Ssam char * 69410275Ssam ntoa(in) 69510275Ssam struct in_addr in; 69610275Ssam { 69710275Ssam static char b[18]; 69810275Ssam register char *p; 69910275Ssam 70010275Ssam p = (char *)∈ 70110275Ssam #define UC(b) (((int)b)&0xff) 70210275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 70310275Ssam return (b); 70410275Ssam } 70510275Ssam 70610275Ssam dolog(sin) 70710275Ssam struct sockaddr_in *sin; 70810275Ssam { 70910275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 71010275Ssam sizeof (struct in_addr), AF_INET); 71110275Ssam char *remotehost; 71210275Ssam time_t t; 71310275Ssam 71410275Ssam if (hp) 71510275Ssam remotehost = hp->h_name; 71610275Ssam else 71710275Ssam remotehost = "UNKNOWNHOST"; 71810275Ssam t = time(0); 719*11757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 72010275Ssam fflush(stderr); 72110275Ssam } 72210695Ssam 72310695Ssam /* 72410695Ssam * Special version of popen which avoids 72510695Ssam * call to shell. This insures noone may 72610695Ssam * create a pipe to a hidden program as a side 72710695Ssam * effect of a list or dir command. 72810695Ssam */ 72910695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 73010695Ssam #define RDR 0 73110695Ssam #define WTR 1 73210695Ssam static int popen_pid[5]; 73310695Ssam 73410695Ssam static char * 73510695Ssam nextarg(cpp) 73610695Ssam char *cpp; 73710695Ssam { 73810695Ssam register char *cp = cpp; 73910695Ssam 74010695Ssam if (cp == 0) 74110695Ssam return (cp); 74210695Ssam while (*cp && *cp != ' ' && *cp != '\t') 74310695Ssam cp++; 74410695Ssam if (*cp == ' ' || *cp == '\t') { 74510695Ssam *cp++ = '\0'; 74610695Ssam while (*cp == ' ' || *cp == '\t') 74710695Ssam cp++; 74810695Ssam } 74910695Ssam if (cp == cpp) 75010695Ssam return ((char *)0); 75110695Ssam return (cp); 75210695Ssam } 75310695Ssam 75410695Ssam FILE * 75510695Ssam popen(cmd, mode) 75610695Ssam char *cmd, *mode; 75710695Ssam { 75810695Ssam int p[2], ac; 75910695Ssam register myside, hisside, pid; 760*11757Ssam char *av[512]; 761*11757Ssam char **pop, **popargs = NULL; 762*11757Ssam extern char **glob(); 76310695Ssam register char *cp; 76410695Ssam 76510695Ssam if (pipe(p) < 0) 76610695Ssam return (NULL); 76710695Ssam cp = cmd, ac = 0; 76810695Ssam do { 76910695Ssam av[ac++] = cp; 77010695Ssam cp = nextarg(cp); 77110695Ssam } while (cp && *cp); 77210695Ssam av[ac] = (char *)0; 773*11757Ssam if (ac > 1) { 774*11757Ssam popargs = glob(&av[1]); 775*11757Ssam if (popargs == NULL) 776*11757Ssam return (NULL); 777*11757Ssam for (ac = 1, pop = popargs; *pop;) 778*11757Ssam av[ac++] = *pop++; 779*11757Ssam } 780*11757Ssam av[ac] = (char *)0; 78110695Ssam myside = tst(p[WTR], p[RDR]); 78210695Ssam hisside = tst(p[RDR], p[WTR]); 78310695Ssam if ((pid = fork()) == 0) { 78410695Ssam /* myside and hisside reverse roles in child */ 78510695Ssam close(myside); 78610695Ssam dup2(hisside, tst(0, 1)); 78710695Ssam close(hisside); 78810695Ssam execv(av[0], av); 78910695Ssam _exit(1); 79010695Ssam } 791*11757Ssam if (popargs != NULL) 792*11757Ssam blkfree(popargs); 79310695Ssam if (pid == -1) 79410695Ssam return (NULL); 79510695Ssam popen_pid[myside] = pid; 79610695Ssam close(hisside); 79710695Ssam return (fdopen(myside, mode)); 79810695Ssam } 79910695Ssam 80010695Ssam pclose(ptr) 80110695Ssam FILE *ptr; 80210695Ssam { 80310695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 80410695Ssam int status; 80510695Ssam 80610695Ssam f = fileno(ptr); 80710695Ssam fclose(ptr); 80810695Ssam istat = signal(SIGINT, SIG_IGN); 80910695Ssam qstat = signal(SIGQUIT, SIG_IGN); 81010695Ssam hstat = signal(SIGHUP, SIG_IGN); 81110695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 81210695Ssam ; 81310695Ssam if (r == -1) 81410695Ssam status = -1; 81510695Ssam signal(SIGINT, istat); 81610695Ssam signal(SIGQUIT, qstat); 81710695Ssam signal(SIGHUP, hstat); 81810695Ssam return (status); 81910695Ssam } 82010695Ssam 82110695Ssam /* 82210695Ssam * Check user requesting login priviledges. 82310695Ssam * Disallow anyone mentioned in the file FTPUSERS 82410695Ssam * to allow people such as uucp to be avoided. 82510695Ssam */ 82610695Ssam checkuser(name) 82710695Ssam register char *name; 82810695Ssam { 82910695Ssam char line[BUFSIZ], *index(); 83010695Ssam FILE *fd; 83110695Ssam int found = 0; 83210695Ssam 83310695Ssam fd = fopen(FTPUSERS, "r"); 83410695Ssam if (fd == NULL) 83510695Ssam return (1); 83610695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 83710695Ssam register char *cp = index(line, '\n'); 83810695Ssam 83910695Ssam if (cp) 84010695Ssam *cp = '\0'; 84110695Ssam if (strcmp(line, name) == 0) { 84210695Ssam found++; 84310695Ssam break; 84410695Ssam } 84510695Ssam } 84610695Ssam fclose(fd); 84710695Ssam return (!found); 84810695Ssam } 849