110275Ssam #ifndef lint 2*11220Ssam static char sccsid[] = "@(#)ftpd.c 4.16 (Berkeley) 02/21/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; 5210275Ssam int logging = 1; 5310275Ssam int guest; 5410275Ssam int type; 5510275Ssam int form; 5610275Ssam int stru; /* avoid C keyword */ 5710275Ssam int mode; 5810321Ssam int usedefault = 1; /* for data transfers */ 5910275Ssam char hostname[32]; 6010275Ssam char *remotehost; 6110321Ssam struct servent *sp; 6210275Ssam 6310275Ssam int lostconn(); 6410419Ssam int reapchild(); 6510275Ssam FILE *getdatasock(), *dataconn(); 6610275Ssam char *ntoa(); 6710275Ssam 6810275Ssam main(argc, argv) 6910275Ssam int argc; 7010275Ssam char *argv[]; 7110275Ssam { 7210275Ssam int ctrl, s, options = 0; 7310275Ssam char *cp; 7410275Ssam 7510275Ssam sp = getservbyname("ftp", "tcp"); 7610275Ssam if (sp == 0) { 77*11220Ssam fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 7810275Ssam exit(1); 7910275Ssam } 8010275Ssam ctrl_addr.sin_port = sp->s_port; 8110275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 8210275Ssam signal(SIGPIPE, lostconn); 8310275Ssam debug = 0; 8410275Ssam argc--, argv++; 8510275Ssam while (argc > 0 && *argv[0] == '-') { 8610275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 8710275Ssam 8810275Ssam case 'd': 8910275Ssam debug = 1; 9010275Ssam options |= SO_DEBUG; 9110275Ssam break; 9210275Ssam 9310275Ssam default: 9410275Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", cp); 9510275Ssam break; 9610275Ssam } 9710275Ssam argc--, argv++; 9810275Ssam } 9910275Ssam #ifndef DEBUG 10010275Ssam if (fork()) 10110275Ssam exit(0); 10210275Ssam for (s = 0; s < 10; s++) 10310317Ssam (void) close(s); 104*11220Ssam (void) open("/", 0); 10510275Ssam (void) dup2(0, 1); 106*11220Ssam (void) dup2(0, 2); 10710275Ssam { int tt = open("/dev/tty", 2); 10810275Ssam if (tt > 0) { 10910275Ssam ioctl(tt, TIOCNOTTY, 0); 11010275Ssam close(tt); 11110275Ssam } 11210275Ssam } 11310275Ssam #endif 11410303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 11510275Ssam perror("ftpd: socket"); 11610275Ssam sleep(5); 11710275Ssam } 11810419Ssam if (options & SO_DEBUG) 11910419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 12010419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 12110419Ssam #ifdef notdef 12210419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 12310419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 12410419Ssam #endif 12510275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 12610275Ssam perror("ftpd: bind"); 12710275Ssam sleep(5); 12810275Ssam } 12910588Ssam sigset(SIGCHLD, reapchild); 13010303Ssam listen(s, 10); 13110275Ssam for (;;) { 13210275Ssam int hisaddrlen = sizeof (his_addr); 13310275Ssam 13410275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 13510275Ssam if (ctrl < 0) { 13610419Ssam if (errno == EINTR) 13710419Ssam continue; 13810275Ssam perror("ftpd: accept"); 13910275Ssam continue; 14010275Ssam } 14110275Ssam if (fork() == 0) { 142*11220Ssam signal (SIGCHLD, SIG_IGN); 14310275Ssam if (logging) 14410275Ssam dolog(&his_addr); 14510275Ssam close(s); 14610275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 14710275Ssam /* do telnet option negotiation here */ 14810303Ssam /* 14910303Ssam * Set up default state 15010303Ssam */ 15110275Ssam logged_in = 0; 15210275Ssam data = -1; 15310303Ssam type = TYPE_A; 15410303Ssam form = FORM_N; 15510303Ssam stru = STRU_F; 15610303Ssam mode = MODE_S; 15710275Ssam gethostname(hostname, sizeof (hostname)); 15810275Ssam reply(220, "%s FTP server (%s) ready.", 15910275Ssam hostname, version); 16010275Ssam for (;;) { 16110275Ssam setjmp(errcatch); 16210275Ssam yyparse(); 16310275Ssam } 16410275Ssam } 16510275Ssam close(ctrl); 16610275Ssam } 16710275Ssam } 16810275Ssam 16910419Ssam reapchild() 17010419Ssam { 17110419Ssam union wait status; 17210419Ssam 17310419Ssam while (wait3(&status, WNOHANG, 0) > 0) 17410419Ssam ; 17510419Ssam } 17610419Ssam 17710275Ssam lostconn() 17810275Ssam { 17910275Ssam 18010275Ssam fatal("Connection closed."); 18110275Ssam } 18210275Ssam 18310275Ssam pass(passwd) 18410275Ssam char *passwd; 18510275Ssam { 18610303Ssam char *xpasswd, *savestr(); 18710303Ssam static struct passwd save; 18810275Ssam 18910275Ssam if (logged_in || pw == NULL) { 19010275Ssam reply(503, "Login with USER first."); 19110275Ssam return; 19210275Ssam } 19310275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 19410275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 19510275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 19610275Ssam reply(530, "Login incorrect."); 19710275Ssam pw = NULL; 19810275Ssam return; 19910275Ssam } 20010275Ssam } 20110303Ssam setegid(pw->pw_gid); 20210275Ssam initgroups(pw->pw_name, pw->pw_gid); 20310275Ssam if (chdir(pw->pw_dir)) { 20410275Ssam reply(550, "User %s: can't change directory to $s.", 20510275Ssam pw->pw_name, pw->pw_dir); 20610303Ssam goto bad; 20710275Ssam } 20810303Ssam if (guest && chroot(pw->pw_dir) < 0) { 20910275Ssam reply(550, "Can't set guest privileges."); 21010303Ssam goto bad; 21110275Ssam } 21210275Ssam if (!guest) 21310275Ssam reply(230, "User %s logged in.", pw->pw_name); 21410275Ssam else 21510275Ssam reply(230, "Guest login ok, access restrictions apply."); 21610275Ssam logged_in = 1; 21710303Ssam seteuid(pw->pw_uid); 21810303Ssam /* 21910303Ssam * Save everything so globbing doesn't 22010303Ssam * clobber the fields. 22110303Ssam */ 22210303Ssam save = *pw; 22310303Ssam save.pw_name = savestr(pw->pw_name); 22410303Ssam save.pw_passwd = savestr(pw->pw_passwd); 22510303Ssam save.pw_comment = savestr(pw->pw_comment); 22610303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 22710303Ssam save.pw_dir = savestr(pw->pw_dir); 22810303Ssam save.pw_shell = savestr(pw->pw_shell); 22910303Ssam pw = &save; 23010303Ssam home = pw->pw_dir; /* home dir for globbing */ 23110303Ssam return; 23210303Ssam bad: 23310303Ssam seteuid(0); 23410303Ssam pw = NULL; 23510275Ssam } 23610275Ssam 23710303Ssam char * 23810303Ssam savestr(s) 23910303Ssam char *s; 24010303Ssam { 24110303Ssam char *malloc(); 24210303Ssam char *new = malloc(strlen(s) + 1); 24310303Ssam 24410303Ssam if (new != NULL) 24510303Ssam strcpy(new, s); 24610303Ssam return(new); 24710303Ssam } 24810303Ssam 24910275Ssam retrieve(cmd, name) 25010275Ssam char *cmd, *name; 25110275Ssam { 25210275Ssam FILE *fin, *dout; 25310275Ssam struct stat st; 25410275Ssam int (*closefunc)(); 25510275Ssam 25610275Ssam if (cmd == 0) { 25710317Ssam #ifdef notdef 25810317Ssam /* no remote command execution -- it's a security hole */ 25910275Ssam if (*name == '!') 26010275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 26110275Ssam else 26210317Ssam #endif 26310275Ssam fin = fopen(name, "r"), closefunc = fclose; 26410275Ssam } else { 26510275Ssam char line[BUFSIZ]; 26610275Ssam 26710422Ssam sprintf(line, cmd, name), name = line; 26810275Ssam fin = popen(line, "r"), closefunc = pclose; 26910275Ssam } 27010275Ssam if (fin == NULL) { 27110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 27210275Ssam return; 27310275Ssam } 27410275Ssam st.st_size = 0; 27510275Ssam if (cmd == 0 && 27610275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 27710275Ssam reply(550, "%s: not a plain file.", name); 27810275Ssam goto done; 27910275Ssam } 28010275Ssam dout = dataconn(name, st.st_size, "w"); 28110275Ssam if (dout == NULL) 28210275Ssam goto done; 28310303Ssam if (send_data(fin, dout) || ferror(dout)) 28410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 28510275Ssam else 28610275Ssam reply(226, "Transfer complete."); 28710303Ssam fclose(dout), data = -1; 28810275Ssam done: 28910275Ssam (*closefunc)(fin); 29010275Ssam } 29110275Ssam 29210275Ssam store(name, mode) 29310275Ssam char *name, *mode; 29410275Ssam { 29510275Ssam FILE *fout, *din; 29610303Ssam int (*closefunc)(), dochown = 0; 29710275Ssam 29810317Ssam #ifdef notdef 29910317Ssam /* no remote command execution -- it's a security hole */ 30010275Ssam if (name[0] == '!') 30110275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 30210317Ssam else 30310317Ssam #endif 30410317Ssam { 30510303Ssam struct stat st; 30610303Ssam 30710303Ssam if (stat(name, &st) < 0) 30810303Ssam dochown++; 30910275Ssam fout = fopen(name, mode), closefunc = fclose; 31010303Ssam } 31110275Ssam if (fout == NULL) { 31210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31310275Ssam return; 31410275Ssam } 31510275Ssam din = dataconn(name, -1, "r"); 31610275Ssam if (din == NULL) 31710275Ssam goto done; 31810303Ssam if (receive_data(din, fout) || ferror(fout)) 31910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 32010275Ssam else 32110275Ssam reply(226, "Transfer complete."); 32210275Ssam fclose(din), data = -1; 32310275Ssam done: 32410303Ssam if (dochown) 32510303Ssam (void) chown(name, pw->pw_uid, -1); 32610275Ssam (*closefunc)(fout); 32710275Ssam } 32810275Ssam 32910275Ssam FILE * 33010275Ssam getdatasock(mode) 33110275Ssam char *mode; 33210275Ssam { 33310602Ssam int s; 33410275Ssam 33510275Ssam if (data >= 0) 33610275Ssam return (fdopen(data, mode)); 33710602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 33810602Ssam if (s < 0) 33910275Ssam return (NULL); 34010275Ssam seteuid(0); 34110602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 34210602Ssam goto bad; 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; 35610275Ssam int size; 35710275Ssam char *mode; 35810275Ssam { 35910275Ssam char sizebuf[32]; 36010275Ssam FILE *file; 36110275Ssam 36210275Ssam if (size >= 0) 36310275Ssam sprintf(sizebuf, " (%d bytes)", size); 36410275Ssam else 36510275Ssam (void) strcpy(sizebuf, ""); 36610275Ssam if (data >= 0) { 36710275Ssam reply(125, "Using existing data connection for %s%s.", 36810275Ssam name, sizebuf); 36910321Ssam usedefault = 1; 37010275Ssam return (fdopen(data, mode)); 37110275Ssam } 37210566Ssam if (usedefault) 37310422Ssam data_dest = his_addr; 37410422Ssam usedefault = 1; 37510275Ssam file = getdatasock(mode); 37610275Ssam if (file == NULL) { 37710275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 37810275Ssam ntoa(data_source.sin_addr), 37910275Ssam ntohs(data_source.sin_port), 38010275Ssam sys_errlist[errno]); 38110275Ssam return (NULL); 38210275Ssam } 38310602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 38410602Ssam name, ntoa(data_dest.sin_addr.s_addr), 38510602Ssam ntohs(data_dest.sin_port), sizebuf); 38610275Ssam data = fileno(file); 38710275Ssam if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 38810275Ssam reply(425, "Can't build data connection: %s.", 38910275Ssam sys_errlist[errno]); 39010275Ssam (void) fclose(file); 39110275Ssam data = -1; 39210275Ssam return (NULL); 39310275Ssam } 39410275Ssam return (file); 39510275Ssam } 39610275Ssam 39710275Ssam /* 39810275Ssam * Tranfer the contents of "instr" to 39910275Ssam * "outstr" peer using the appropriate 40010275Ssam * encapulation of the date subject 40110275Ssam * to Mode, Structure, and Type. 40210275Ssam * 40310275Ssam * NB: Form isn't handled. 40410275Ssam */ 40510275Ssam send_data(instr, outstr) 40610275Ssam FILE *instr, *outstr; 40710275Ssam { 40810275Ssam register int c; 40910275Ssam int netfd, filefd, cnt; 41010275Ssam char buf[BUFSIZ]; 41110275Ssam 41210275Ssam switch (type) { 41310275Ssam 41410275Ssam case TYPE_A: 41510275Ssam while ((c = getc(instr)) != EOF) { 416*11220Ssam if (c == '\n') { 417*11220Ssam if (ferror (outstr)) 418*11220Ssam return (1); 41910275Ssam putc('\r', outstr); 420*11220Ssam } 421*11220Ssam putc(c, outstr); 422*11220Ssam if (c == '\r') 423*11220Ssam putc ('\0', outstr); 42410275Ssam } 425*11220Ssam if (ferror (instr) || ferror (outstr)) 426*11220Ssam return (1); 42710275Ssam return (0); 42810275Ssam 42910275Ssam case TYPE_I: 43010275Ssam case TYPE_L: 43110275Ssam netfd = fileno(outstr); 43210275Ssam filefd = fileno(instr); 43310275Ssam 43410303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 43510275Ssam if (write(netfd, buf, cnt) < 0) 43610275Ssam return (1); 43710275Ssam return (cnt < 0); 43810275Ssam } 43910275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 44010275Ssam return (1); 44110275Ssam } 44210275Ssam 44310275Ssam /* 44410275Ssam * Transfer data from peer to 44510275Ssam * "outstr" using the appropriate 44610275Ssam * encapulation of the data subject 44710275Ssam * to Mode, Structure, and Type. 44810275Ssam * 44910275Ssam * N.B.: Form isn't handled. 45010275Ssam */ 45110275Ssam receive_data(instr, outstr) 45210275Ssam FILE *instr, *outstr; 45310275Ssam { 45410275Ssam register int c; 455*11220Ssam int cnt; 45610275Ssam char buf[BUFSIZ]; 45710275Ssam 45810275Ssam 45910275Ssam switch (type) { 46010275Ssam 46110275Ssam case TYPE_I: 46210275Ssam case TYPE_L: 46310616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 46410616Ssam if (write(fileno(outstr), buf, cnt) < 0) 46510275Ssam return (1); 46610275Ssam return (cnt < 0); 46710275Ssam 46810275Ssam case TYPE_E: 46910275Ssam reply(504, "TYPE E not implemented."); 47010275Ssam return (1); 47110275Ssam 47210275Ssam case TYPE_A: 47310275Ssam while ((c = getc(instr)) != EOF) { 47410275Ssam if (c == '\r') { 475*11220Ssam if (ferror (outstr)) 476*11220Ssam return (1); 477*11220Ssam if ((c = getc(instr)) != '\n') 478*11220Ssam putc ('\r', outstr); 479*11220Ssam if (c == '\0') 480*11220Ssam continue; 48110275Ssam } 482*11220Ssam putc (c, outstr); 48310275Ssam } 484*11220Ssam if (ferror (instr) || ferror (outstr)) 485*11220Ssam return (1); 48610275Ssam return (0); 48710275Ssam } 48810275Ssam fatal("Unknown type in receive_data."); 48910275Ssam /*NOTREACHED*/ 49010275Ssam } 49110275Ssam 49210275Ssam fatal(s) 49310275Ssam char *s; 49410275Ssam { 49510275Ssam reply(451, "Error in server: %s\n", s); 49610275Ssam reply(221, "Closing connection due to server error."); 49710275Ssam exit(0); 49810275Ssam } 49910275Ssam 50010275Ssam reply(n, s, args) 50110275Ssam int n; 50210275Ssam char *s; 50310275Ssam { 50410275Ssam 50510275Ssam printf("%d ", n); 50610275Ssam _doprnt(s, &args, stdout); 50710275Ssam printf("\r\n"); 50810275Ssam fflush(stdout); 50910275Ssam if (debug) { 51010275Ssam fprintf(stderr, "<--- %d ", n); 51110275Ssam _doprnt(s, &args, stderr); 51210275Ssam fprintf(stderr, "\n"); 51310275Ssam fflush(stderr); 51410275Ssam } 51510275Ssam } 51610275Ssam 51710275Ssam lreply(n, s, args) 51810275Ssam int n; 51910275Ssam char *s; 52010275Ssam { 52110275Ssam printf("%d-", n); 52210275Ssam _doprnt(s, &args, stdout); 52310275Ssam printf("\r\n"); 52410275Ssam fflush(stdout); 52510275Ssam if (debug) { 52610275Ssam fprintf(stderr, "<--- %d-", n); 52710275Ssam _doprnt(s, &args, stderr); 52810275Ssam fprintf(stderr, "\n"); 52910275Ssam } 53010275Ssam } 53110275Ssam 53210275Ssam replystr(s) 53310275Ssam char *s; 53410275Ssam { 53510275Ssam printf("%s\r\n", s); 53610275Ssam fflush(stdout); 53710275Ssam if (debug) 53810275Ssam fprintf(stderr, "<--- %s\n", s); 53910275Ssam } 54010275Ssam 54110275Ssam ack(s) 54210275Ssam char *s; 54310275Ssam { 54410275Ssam reply(200, "%s command okay.", s); 54510275Ssam } 54610275Ssam 54710275Ssam nack(s) 54810275Ssam char *s; 54910275Ssam { 55010275Ssam reply(502, "%s command not implemented.", s); 55110275Ssam } 55210275Ssam 55310275Ssam yyerror() 55410275Ssam { 55510275Ssam reply(500, "Command not understood."); 55610275Ssam } 55710275Ssam 55810275Ssam delete(name) 55910275Ssam char *name; 56010275Ssam { 56110275Ssam struct stat st; 56210275Ssam 56310275Ssam if (stat(name, &st) < 0) { 56410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 56510275Ssam return; 56610275Ssam } 56710275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 56810275Ssam if (rmdir(name) < 0) { 56910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57010275Ssam return; 57110275Ssam } 57210275Ssam goto done; 57310275Ssam } 57410275Ssam if (unlink(name) < 0) { 57510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57610275Ssam return; 57710275Ssam } 57810275Ssam done: 57910275Ssam ack("DELE"); 58010275Ssam } 58110275Ssam 58210275Ssam cwd(path) 58310275Ssam char *path; 58410275Ssam { 58510275Ssam 58610275Ssam if (chdir(path) < 0) { 58710275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 58810275Ssam return; 58910275Ssam } 59010275Ssam ack("CWD"); 59110275Ssam } 59210275Ssam 59310303Ssam makedir(name) 59410275Ssam char *name; 59510275Ssam { 59610303Ssam struct stat st; 59710303Ssam int dochown = stat(name, &st) < 0; 59810275Ssam 59910275Ssam if (mkdir(name, 0777) < 0) { 60010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60110275Ssam return; 60210275Ssam } 60310303Ssam if (dochown) 60410303Ssam (void) chown(name, pw->pw_uid, -1); 60510275Ssam ack("MKDIR"); 60610275Ssam } 60710275Ssam 60810303Ssam removedir(name) 60910275Ssam char *name; 61010275Ssam { 61110275Ssam 61210275Ssam if (rmdir(name) < 0) { 61310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61410275Ssam return; 61510275Ssam } 61610275Ssam ack("RMDIR"); 61710275Ssam } 61810275Ssam 61910303Ssam pwd() 62010275Ssam { 62110303Ssam char path[MAXPATHLEN + 1]; 62210275Ssam char *p; 62310275Ssam 62410275Ssam if (getwd(path) == NULL) { 62510275Ssam reply(451, "%s.", path); 62610275Ssam return; 62710275Ssam } 62810275Ssam reply(251, "\"%s\" is current directory.", path); 62910275Ssam } 63010275Ssam 63110275Ssam char * 63210275Ssam renamefrom(name) 63310275Ssam char *name; 63410275Ssam { 63510275Ssam struct stat st; 63610275Ssam 63710275Ssam if (stat(name, &st) < 0) { 63810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 63910275Ssam return ((char *)0); 64010275Ssam } 64110303Ssam reply(350, "File exists, ready for destination name"); 64210275Ssam return (name); 64310275Ssam } 64410275Ssam 64510275Ssam renamecmd(from, to) 64610275Ssam char *from, *to; 64710275Ssam { 64810275Ssam 64910275Ssam if (rename(from, to) < 0) { 65010275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 65110275Ssam return; 65210275Ssam } 65310275Ssam ack("RNTO"); 65410275Ssam } 65510275Ssam 65610275Ssam int guest; 65710275Ssam /* 65810275Ssam * Test pathname for guest-user safety. 65910275Ssam */ 66010275Ssam inappropriate_request(name) 66110275Ssam char *name; 66210275Ssam { 66310275Ssam int bogus = 0, depth = 0, length = strlen(name); 66410275Ssam char *p, *s; 66510275Ssam 66610275Ssam if (!guest) 66710275Ssam return (0); 66810275Ssam if (name[0] == '/' || name[0] == '|') 66910275Ssam bogus = 1; 67010275Ssam for (p = name; p < name+length;) { 67110275Ssam s = p; /* start of token */ 67210275Ssam while ( *p && *p!= '/') 67310275Ssam p++; 67410275Ssam *p = 0; 67510275Ssam if (strcmp(s, "..") == 0) 67610275Ssam depth -= 1; /* backing up */ 67710275Ssam else if (strcmp(s, ".") == 0) 67810275Ssam depth += 0; /* no change */ 67910275Ssam else 68010275Ssam depth += 1; /* descending */ 68110275Ssam if (depth < 0) { 68210275Ssam bogus = 1; 68310275Ssam break; 68410275Ssam } 68510275Ssam } 68610275Ssam if (bogus) 68710275Ssam reply(553, "%s: pathname disallowed guest users", name); 68810275Ssam return (bogus); 68910275Ssam } 69010275Ssam 69110275Ssam /* 69210275Ssam * Convert network-format internet address 69310275Ssam * to base 256 d.d.d.d representation. 69410275Ssam */ 69510275Ssam char * 69610275Ssam ntoa(in) 69710275Ssam struct in_addr in; 69810275Ssam { 69910275Ssam static char b[18]; 70010275Ssam register char *p; 70110275Ssam 70210275Ssam p = (char *)∈ 70310275Ssam #define UC(b) (((int)b)&0xff) 70410275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 70510275Ssam return (b); 70610275Ssam } 70710275Ssam 70810275Ssam dolog(sin) 70910275Ssam struct sockaddr_in *sin; 71010275Ssam { 71110275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 71210275Ssam sizeof (struct in_addr), AF_INET); 71310275Ssam char *remotehost; 71410275Ssam time_t t; 71510275Ssam 71610275Ssam if (hp) 71710275Ssam remotehost = hp->h_name; 71810275Ssam else 71910275Ssam remotehost = "UNKNOWNHOST"; 72010275Ssam t = time(0); 72110303Ssam fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 72210275Ssam fflush(stderr); 72310275Ssam } 72410695Ssam 72510695Ssam /* 72610695Ssam * Special version of popen which avoids 72710695Ssam * call to shell. This insures noone may 72810695Ssam * create a pipe to a hidden program as a side 72910695Ssam * effect of a list or dir command. 73010695Ssam */ 73110695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 73210695Ssam #define RDR 0 73310695Ssam #define WTR 1 73410695Ssam static int popen_pid[5]; 73510695Ssam 73610695Ssam static char * 73710695Ssam nextarg(cpp) 73810695Ssam char *cpp; 73910695Ssam { 74010695Ssam register char *cp = cpp; 74110695Ssam 74210695Ssam if (cp == 0) 74310695Ssam return (cp); 74410695Ssam while (*cp && *cp != ' ' && *cp != '\t') 74510695Ssam cp++; 74610695Ssam if (*cp == ' ' || *cp == '\t') { 74710695Ssam *cp++ = '\0'; 74810695Ssam while (*cp == ' ' || *cp == '\t') 74910695Ssam cp++; 75010695Ssam } 75110695Ssam if (cp == cpp) 75210695Ssam return ((char *)0); 75310695Ssam return (cp); 75410695Ssam } 75510695Ssam 75610695Ssam FILE * 75710695Ssam popen(cmd, mode) 75810695Ssam char *cmd, *mode; 75910695Ssam { 76010695Ssam int p[2], ac; 76110695Ssam register myside, hisside, pid; 76210695Ssam char *av[10]; 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; 77310695Ssam myside = tst(p[WTR], p[RDR]); 77410695Ssam hisside = tst(p[RDR], p[WTR]); 77510695Ssam if ((pid = fork()) == 0) { 77610695Ssam /* myside and hisside reverse roles in child */ 77710695Ssam close(myside); 77810695Ssam dup2(hisside, tst(0, 1)); 77910695Ssam close(hisside); 78010695Ssam execv(av[0], av); 78110695Ssam _exit(1); 78210695Ssam } 78310695Ssam if (pid == -1) 78410695Ssam return (NULL); 78510695Ssam popen_pid[myside] = pid; 78610695Ssam close(hisside); 78710695Ssam return (fdopen(myside, mode)); 78810695Ssam } 78910695Ssam 79010695Ssam pclose(ptr) 79110695Ssam FILE *ptr; 79210695Ssam { 79310695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 79410695Ssam int status; 79510695Ssam 79610695Ssam f = fileno(ptr); 79710695Ssam fclose(ptr); 79810695Ssam istat = signal(SIGINT, SIG_IGN); 79910695Ssam qstat = signal(SIGQUIT, SIG_IGN); 80010695Ssam hstat = signal(SIGHUP, SIG_IGN); 80110695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 80210695Ssam ; 80310695Ssam if (r == -1) 80410695Ssam status = -1; 80510695Ssam signal(SIGINT, istat); 80610695Ssam signal(SIGQUIT, qstat); 80710695Ssam signal(SIGHUP, hstat); 80810695Ssam return (status); 80910695Ssam } 81010695Ssam 81110695Ssam /* 81210695Ssam * Check user requesting login priviledges. 81310695Ssam * Disallow anyone mentioned in the file FTPUSERS 81410695Ssam * to allow people such as uucp to be avoided. 81510695Ssam */ 81610695Ssam checkuser(name) 81710695Ssam register char *name; 81810695Ssam { 81910695Ssam char line[BUFSIZ], *index(); 82010695Ssam FILE *fd; 82110695Ssam int found = 0; 82210695Ssam 82310695Ssam fd = fopen(FTPUSERS, "r"); 82410695Ssam if (fd == NULL) 82510695Ssam return (1); 82610695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 82710695Ssam register char *cp = index(line, '\n'); 82810695Ssam 82910695Ssam if (cp) 83010695Ssam *cp = '\0'; 83110695Ssam if (strcmp(line, name) == 0) { 83210695Ssam found++; 83310695Ssam break; 83410695Ssam } 83510695Ssam } 83610695Ssam fclose(fd); 83710695Ssam return (!found); 83810695Ssam } 839