110275Ssam #ifndef lint 2*10695Ssam static char sccsid[] = "@(#)ftpd.c 4.15 (Berkeley) 02/02/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 25*10695Ssam /* 26*10695Ssam * File containing login names 27*10695Ssam * NOT to be used on this machine. 28*10695Ssam * Commonly used to disallow uucp. 29*10695Ssam */ 30*10695Ssam #define FTPUSERS "/etc/ftpusers" 31*10695Ssam 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) { 7710275Ssam fprintf(stderr, "ftpd: fpt/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); 10410275Ssam (void) open("/dev/null", 0); 10510275Ssam (void) dup2(0, 1); 10610275Ssam { int tt = open("/dev/tty", 2); 10710275Ssam if (tt > 0) { 10810275Ssam ioctl(tt, TIOCNOTTY, 0); 10910275Ssam close(tt); 11010275Ssam } 11110275Ssam } 11210275Ssam #endif 11310303Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) { 11410275Ssam perror("ftpd: socket"); 11510275Ssam sleep(5); 11610275Ssam } 11710419Ssam if (options & SO_DEBUG) 11810419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 11910419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 12010419Ssam #ifdef notdef 12110419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 12210419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 12310419Ssam #endif 12410275Ssam while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 12510275Ssam perror("ftpd: bind"); 12610275Ssam sleep(5); 12710275Ssam } 12810588Ssam sigset(SIGCHLD, reapchild); 12910303Ssam listen(s, 10); 13010275Ssam for (;;) { 13110275Ssam int hisaddrlen = sizeof (his_addr); 13210275Ssam 13310275Ssam ctrl = accept(s, &his_addr, &hisaddrlen, 0); 13410275Ssam if (ctrl < 0) { 13510419Ssam if (errno == EINTR) 13610419Ssam continue; 13710275Ssam perror("ftpd: accept"); 13810275Ssam continue; 13910275Ssam } 14010275Ssam if (fork() == 0) { 14110275Ssam if (logging) 14210275Ssam dolog(&his_addr); 14310275Ssam close(s); 14410275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 14510275Ssam /* do telnet option negotiation here */ 14610303Ssam /* 14710303Ssam * Set up default state 14810303Ssam */ 14910275Ssam logged_in = 0; 15010275Ssam data = -1; 15110303Ssam type = TYPE_A; 15210303Ssam form = FORM_N; 15310303Ssam stru = STRU_F; 15410303Ssam mode = MODE_S; 15510275Ssam gethostname(hostname, sizeof (hostname)); 15610275Ssam reply(220, "%s FTP server (%s) ready.", 15710275Ssam hostname, version); 15810275Ssam for (;;) { 15910275Ssam setjmp(errcatch); 16010275Ssam yyparse(); 16110275Ssam } 16210275Ssam } 16310275Ssam close(ctrl); 16410275Ssam } 16510275Ssam } 16610275Ssam 16710419Ssam reapchild() 16810419Ssam { 16910419Ssam union wait status; 17010419Ssam 17110419Ssam while (wait3(&status, WNOHANG, 0) > 0) 17210419Ssam ; 17310419Ssam } 17410419Ssam 17510275Ssam lostconn() 17610275Ssam { 17710275Ssam 17810275Ssam fatal("Connection closed."); 17910275Ssam } 18010275Ssam 18110275Ssam pass(passwd) 18210275Ssam char *passwd; 18310275Ssam { 18410303Ssam char *xpasswd, *savestr(); 18510303Ssam static struct passwd save; 18610275Ssam 18710275Ssam if (logged_in || pw == NULL) { 18810275Ssam reply(503, "Login with USER first."); 18910275Ssam return; 19010275Ssam } 19110275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 19210275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 19310275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 19410275Ssam reply(530, "Login incorrect."); 19510275Ssam pw = NULL; 19610275Ssam return; 19710275Ssam } 19810275Ssam } 19910303Ssam setegid(pw->pw_gid); 20010275Ssam initgroups(pw->pw_name, pw->pw_gid); 20110275Ssam if (chdir(pw->pw_dir)) { 20210275Ssam reply(550, "User %s: can't change directory to $s.", 20310275Ssam pw->pw_name, pw->pw_dir); 20410303Ssam goto bad; 20510275Ssam } 20610303Ssam if (guest && chroot(pw->pw_dir) < 0) { 20710275Ssam reply(550, "Can't set guest privileges."); 20810303Ssam goto bad; 20910275Ssam } 21010275Ssam if (!guest) 21110275Ssam reply(230, "User %s logged in.", pw->pw_name); 21210275Ssam else 21310275Ssam reply(230, "Guest login ok, access restrictions apply."); 21410275Ssam logged_in = 1; 21510303Ssam seteuid(pw->pw_uid); 21610303Ssam /* 21710303Ssam * Save everything so globbing doesn't 21810303Ssam * clobber the fields. 21910303Ssam */ 22010303Ssam save = *pw; 22110303Ssam save.pw_name = savestr(pw->pw_name); 22210303Ssam save.pw_passwd = savestr(pw->pw_passwd); 22310303Ssam save.pw_comment = savestr(pw->pw_comment); 22410303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 22510303Ssam save.pw_dir = savestr(pw->pw_dir); 22610303Ssam save.pw_shell = savestr(pw->pw_shell); 22710303Ssam pw = &save; 22810303Ssam home = pw->pw_dir; /* home dir for globbing */ 22910303Ssam return; 23010303Ssam bad: 23110303Ssam seteuid(0); 23210303Ssam pw = NULL; 23310275Ssam } 23410275Ssam 23510303Ssam char * 23610303Ssam savestr(s) 23710303Ssam char *s; 23810303Ssam { 23910303Ssam char *malloc(); 24010303Ssam char *new = malloc(strlen(s) + 1); 24110303Ssam 24210303Ssam if (new != NULL) 24310303Ssam strcpy(new, s); 24410303Ssam return(new); 24510303Ssam } 24610303Ssam 24710275Ssam retrieve(cmd, name) 24810275Ssam char *cmd, *name; 24910275Ssam { 25010275Ssam FILE *fin, *dout; 25110275Ssam struct stat st; 25210275Ssam int (*closefunc)(); 25310275Ssam 25410275Ssam if (cmd == 0) { 25510317Ssam #ifdef notdef 25610317Ssam /* no remote command execution -- it's a security hole */ 25710275Ssam if (*name == '!') 25810275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 25910275Ssam else 26010317Ssam #endif 26110275Ssam fin = fopen(name, "r"), closefunc = fclose; 26210275Ssam } else { 26310275Ssam char line[BUFSIZ]; 26410275Ssam 26510422Ssam sprintf(line, cmd, name), name = line; 26610275Ssam fin = popen(line, "r"), closefunc = pclose; 26710275Ssam } 26810275Ssam if (fin == NULL) { 26910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 27010275Ssam return; 27110275Ssam } 27210275Ssam st.st_size = 0; 27310275Ssam if (cmd == 0 && 27410275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 27510275Ssam reply(550, "%s: not a plain file.", name); 27610275Ssam goto done; 27710275Ssam } 27810275Ssam dout = dataconn(name, st.st_size, "w"); 27910275Ssam if (dout == NULL) 28010275Ssam goto done; 28110303Ssam if (send_data(fin, dout) || ferror(dout)) 28210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 28310275Ssam else 28410275Ssam reply(226, "Transfer complete."); 28510303Ssam fclose(dout), data = -1; 28610275Ssam done: 28710275Ssam (*closefunc)(fin); 28810275Ssam } 28910275Ssam 29010275Ssam store(name, mode) 29110275Ssam char *name, *mode; 29210275Ssam { 29310275Ssam FILE *fout, *din; 29410303Ssam int (*closefunc)(), dochown = 0; 29510275Ssam 29610317Ssam #ifdef notdef 29710317Ssam /* no remote command execution -- it's a security hole */ 29810275Ssam if (name[0] == '!') 29910275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 30010317Ssam else 30110317Ssam #endif 30210317Ssam { 30310303Ssam struct stat st; 30410303Ssam 30510303Ssam if (stat(name, &st) < 0) 30610303Ssam dochown++; 30710275Ssam fout = fopen(name, mode), closefunc = fclose; 30810303Ssam } 30910275Ssam if (fout == NULL) { 31010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31110275Ssam return; 31210275Ssam } 31310275Ssam din = dataconn(name, -1, "r"); 31410275Ssam if (din == NULL) 31510275Ssam goto done; 31610303Ssam if (receive_data(din, fout) || ferror(fout)) 31710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31810275Ssam else 31910275Ssam reply(226, "Transfer complete."); 32010275Ssam fclose(din), data = -1; 32110275Ssam done: 32210303Ssam if (dochown) 32310303Ssam (void) chown(name, pw->pw_uid, -1); 32410275Ssam (*closefunc)(fout); 32510275Ssam } 32610275Ssam 32710275Ssam FILE * 32810275Ssam getdatasock(mode) 32910275Ssam char *mode; 33010275Ssam { 33110602Ssam int s; 33210275Ssam 33310275Ssam if (data >= 0) 33410275Ssam return (fdopen(data, mode)); 33510602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 33610602Ssam if (s < 0) 33710275Ssam return (NULL); 33810275Ssam seteuid(0); 33910602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 34010602Ssam goto bad; 34110602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 34210602Ssam goto bad; 34310311Ssam seteuid(pw->pw_uid); 34410275Ssam return (fdopen(s, mode)); 34510602Ssam bad: 34610602Ssam seteuid(pw->pw_uid); 34710602Ssam close(s); 34810602Ssam return (NULL); 34910275Ssam } 35010275Ssam 35110275Ssam FILE * 35210275Ssam dataconn(name, size, mode) 35310275Ssam char *name; 35410275Ssam int size; 35510275Ssam char *mode; 35610275Ssam { 35710275Ssam char sizebuf[32]; 35810275Ssam FILE *file; 35910275Ssam 36010275Ssam if (size >= 0) 36110275Ssam sprintf(sizebuf, " (%d bytes)", size); 36210275Ssam else 36310275Ssam (void) strcpy(sizebuf, ""); 36410275Ssam if (data >= 0) { 36510275Ssam reply(125, "Using existing data connection for %s%s.", 36610275Ssam name, sizebuf); 36710321Ssam usedefault = 1; 36810275Ssam return (fdopen(data, mode)); 36910275Ssam } 37010566Ssam if (usedefault) 37110422Ssam data_dest = his_addr; 37210422Ssam usedefault = 1; 37310275Ssam file = getdatasock(mode); 37410275Ssam if (file == NULL) { 37510275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 37610275Ssam ntoa(data_source.sin_addr), 37710275Ssam ntohs(data_source.sin_port), 37810275Ssam sys_errlist[errno]); 37910275Ssam return (NULL); 38010275Ssam } 38110602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 38210602Ssam name, ntoa(data_dest.sin_addr.s_addr), 38310602Ssam ntohs(data_dest.sin_port), sizebuf); 38410275Ssam data = fileno(file); 38510275Ssam if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 38610275Ssam reply(425, "Can't build data connection: %s.", 38710275Ssam sys_errlist[errno]); 38810275Ssam (void) fclose(file); 38910275Ssam data = -1; 39010275Ssam return (NULL); 39110275Ssam } 39210275Ssam return (file); 39310275Ssam } 39410275Ssam 39510275Ssam /* 39610275Ssam * Tranfer the contents of "instr" to 39710275Ssam * "outstr" peer using the appropriate 39810275Ssam * encapulation of the date subject 39910275Ssam * to Mode, Structure, and Type. 40010275Ssam * 40110275Ssam * NB: Form isn't handled. 40210275Ssam */ 40310275Ssam send_data(instr, outstr) 40410275Ssam FILE *instr, *outstr; 40510275Ssam { 40610275Ssam register int c; 40710275Ssam int netfd, filefd, cnt; 40810275Ssam char buf[BUFSIZ]; 40910275Ssam 41010275Ssam switch (type) { 41110275Ssam 41210275Ssam case TYPE_A: 41310275Ssam while ((c = getc(instr)) != EOF) { 41410275Ssam if (c == '\n') 41510275Ssam putc('\r', outstr); 41610275Ssam if (putc(c, outstr) == EOF) 41710275Ssam return (1); 41810275Ssam } 41910275Ssam return (0); 42010275Ssam 42110275Ssam case TYPE_I: 42210275Ssam case TYPE_L: 42310275Ssam netfd = fileno(outstr); 42410275Ssam filefd = fileno(instr); 42510275Ssam 42610303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 42710275Ssam if (write(netfd, buf, cnt) < 0) 42810275Ssam return (1); 42910275Ssam return (cnt < 0); 43010275Ssam } 43110275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 43210275Ssam return (1); 43310275Ssam } 43410275Ssam 43510275Ssam /* 43610275Ssam * Transfer data from peer to 43710275Ssam * "outstr" using the appropriate 43810275Ssam * encapulation of the data subject 43910275Ssam * to Mode, Structure, and Type. 44010275Ssam * 44110275Ssam * N.B.: Form isn't handled. 44210275Ssam */ 44310275Ssam receive_data(instr, outstr) 44410275Ssam FILE *instr, *outstr; 44510275Ssam { 44610275Ssam register int c; 44710616Ssam int cr, escape, eof, cnt; 44810275Ssam char buf[BUFSIZ]; 44910275Ssam 45010275Ssam 45110275Ssam switch (type) { 45210275Ssam 45310275Ssam case TYPE_I: 45410275Ssam case TYPE_L: 45510616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 45610616Ssam if (write(fileno(outstr), buf, cnt) < 0) 45710275Ssam return (1); 45810275Ssam return (cnt < 0); 45910275Ssam 46010275Ssam case TYPE_E: 46110275Ssam reply(504, "TYPE E not implemented."); 46210275Ssam return (1); 46310275Ssam 46410275Ssam case TYPE_A: 46510275Ssam cr = 0; 46610275Ssam while ((c = getc(instr)) != EOF) { 46710275Ssam if (cr) { 46810275Ssam if (c != '\r' && c != '\n') 46910275Ssam putc('\r', outstr); 47010275Ssam putc(c, outstr); 47110275Ssam cr = c == '\r'; 47210275Ssam continue; 47310275Ssam } 47410275Ssam if (c == '\r') { 47510275Ssam cr = 1; 47610275Ssam continue; 47710275Ssam } 47810275Ssam putc(c, outstr); 47910275Ssam } 48010275Ssam if (cr) 48110275Ssam putc('\r', outstr); 48210275Ssam return (0); 48310275Ssam } 48410275Ssam fatal("Unknown type in receive_data."); 48510275Ssam /*NOTREACHED*/ 48610275Ssam } 48710275Ssam 48810275Ssam fatal(s) 48910275Ssam char *s; 49010275Ssam { 49110275Ssam reply(451, "Error in server: %s\n", s); 49210275Ssam reply(221, "Closing connection due to server error."); 49310275Ssam exit(0); 49410275Ssam } 49510275Ssam 49610275Ssam reply(n, s, args) 49710275Ssam int n; 49810275Ssam char *s; 49910275Ssam { 50010275Ssam 50110275Ssam printf("%d ", n); 50210275Ssam _doprnt(s, &args, stdout); 50310275Ssam printf("\r\n"); 50410275Ssam fflush(stdout); 50510275Ssam if (debug) { 50610275Ssam fprintf(stderr, "<--- %d ", n); 50710275Ssam _doprnt(s, &args, stderr); 50810275Ssam fprintf(stderr, "\n"); 50910275Ssam fflush(stderr); 51010275Ssam } 51110275Ssam } 51210275Ssam 51310275Ssam lreply(n, s, args) 51410275Ssam int n; 51510275Ssam char *s; 51610275Ssam { 51710275Ssam printf("%d-", n); 51810275Ssam _doprnt(s, &args, stdout); 51910275Ssam printf("\r\n"); 52010275Ssam fflush(stdout); 52110275Ssam if (debug) { 52210275Ssam fprintf(stderr, "<--- %d-", n); 52310275Ssam _doprnt(s, &args, stderr); 52410275Ssam fprintf(stderr, "\n"); 52510275Ssam } 52610275Ssam } 52710275Ssam 52810275Ssam replystr(s) 52910275Ssam char *s; 53010275Ssam { 53110275Ssam printf("%s\r\n", s); 53210275Ssam fflush(stdout); 53310275Ssam if (debug) 53410275Ssam fprintf(stderr, "<--- %s\n", s); 53510275Ssam } 53610275Ssam 53710275Ssam ack(s) 53810275Ssam char *s; 53910275Ssam { 54010275Ssam reply(200, "%s command okay.", s); 54110275Ssam } 54210275Ssam 54310275Ssam nack(s) 54410275Ssam char *s; 54510275Ssam { 54610275Ssam reply(502, "%s command not implemented.", s); 54710275Ssam } 54810275Ssam 54910275Ssam yyerror() 55010275Ssam { 55110275Ssam reply(500, "Command not understood."); 55210275Ssam } 55310275Ssam 55410275Ssam delete(name) 55510275Ssam char *name; 55610275Ssam { 55710275Ssam struct stat st; 55810275Ssam 55910275Ssam if (stat(name, &st) < 0) { 56010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 56110275Ssam return; 56210275Ssam } 56310275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 56410275Ssam if (rmdir(name) < 0) { 56510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 56610275Ssam return; 56710275Ssam } 56810275Ssam goto done; 56910275Ssam } 57010275Ssam if (unlink(name) < 0) { 57110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57210275Ssam return; 57310275Ssam } 57410275Ssam done: 57510275Ssam ack("DELE"); 57610275Ssam } 57710275Ssam 57810275Ssam cwd(path) 57910275Ssam char *path; 58010275Ssam { 58110275Ssam 58210275Ssam if (chdir(path) < 0) { 58310275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 58410275Ssam return; 58510275Ssam } 58610275Ssam ack("CWD"); 58710275Ssam } 58810275Ssam 58910303Ssam makedir(name) 59010275Ssam char *name; 59110275Ssam { 59210303Ssam struct stat st; 59310303Ssam int dochown = stat(name, &st) < 0; 59410275Ssam 59510275Ssam if (mkdir(name, 0777) < 0) { 59610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 59710275Ssam return; 59810275Ssam } 59910303Ssam if (dochown) 60010303Ssam (void) chown(name, pw->pw_uid, -1); 60110275Ssam ack("MKDIR"); 60210275Ssam } 60310275Ssam 60410303Ssam removedir(name) 60510275Ssam char *name; 60610275Ssam { 60710275Ssam 60810275Ssam if (rmdir(name) < 0) { 60910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61010275Ssam return; 61110275Ssam } 61210275Ssam ack("RMDIR"); 61310275Ssam } 61410275Ssam 61510303Ssam pwd() 61610275Ssam { 61710303Ssam char path[MAXPATHLEN + 1]; 61810275Ssam char *p; 61910275Ssam 62010275Ssam if (getwd(path) == NULL) { 62110275Ssam reply(451, "%s.", path); 62210275Ssam return; 62310275Ssam } 62410275Ssam reply(251, "\"%s\" is current directory.", path); 62510275Ssam } 62610275Ssam 62710275Ssam char * 62810275Ssam renamefrom(name) 62910275Ssam char *name; 63010275Ssam { 63110275Ssam struct stat st; 63210275Ssam 63310275Ssam if (stat(name, &st) < 0) { 63410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 63510275Ssam return ((char *)0); 63610275Ssam } 63710303Ssam reply(350, "File exists, ready for destination name"); 63810275Ssam return (name); 63910275Ssam } 64010275Ssam 64110275Ssam renamecmd(from, to) 64210275Ssam char *from, *to; 64310275Ssam { 64410275Ssam 64510275Ssam if (rename(from, to) < 0) { 64610275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 64710275Ssam return; 64810275Ssam } 64910275Ssam ack("RNTO"); 65010275Ssam } 65110275Ssam 65210275Ssam int guest; 65310275Ssam /* 65410275Ssam * Test pathname for guest-user safety. 65510275Ssam */ 65610275Ssam inappropriate_request(name) 65710275Ssam char *name; 65810275Ssam { 65910275Ssam int bogus = 0, depth = 0, length = strlen(name); 66010275Ssam char *p, *s; 66110275Ssam 66210275Ssam if (!guest) 66310275Ssam return (0); 66410275Ssam if (name[0] == '/' || name[0] == '|') 66510275Ssam bogus = 1; 66610275Ssam for (p = name; p < name+length;) { 66710275Ssam s = p; /* start of token */ 66810275Ssam while ( *p && *p!= '/') 66910275Ssam p++; 67010275Ssam *p = 0; 67110275Ssam if (strcmp(s, "..") == 0) 67210275Ssam depth -= 1; /* backing up */ 67310275Ssam else if (strcmp(s, ".") == 0) 67410275Ssam depth += 0; /* no change */ 67510275Ssam else 67610275Ssam depth += 1; /* descending */ 67710275Ssam if (depth < 0) { 67810275Ssam bogus = 1; 67910275Ssam break; 68010275Ssam } 68110275Ssam } 68210275Ssam if (bogus) 68310275Ssam reply(553, "%s: pathname disallowed guest users", name); 68410275Ssam return (bogus); 68510275Ssam } 68610275Ssam 68710275Ssam /* 68810275Ssam * Convert network-format internet address 68910275Ssam * to base 256 d.d.d.d representation. 69010275Ssam */ 69110275Ssam char * 69210275Ssam ntoa(in) 69310275Ssam struct in_addr in; 69410275Ssam { 69510275Ssam static char b[18]; 69610275Ssam register char *p; 69710275Ssam 69810275Ssam in.s_addr = ntohl(in.s_addr); 69910275Ssam p = (char *)∈ 70010275Ssam #define UC(b) (((int)b)&0xff) 70110275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 70210275Ssam return (b); 70310275Ssam } 70410275Ssam 70510275Ssam dolog(sin) 70610275Ssam struct sockaddr_in *sin; 70710275Ssam { 70810275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 70910275Ssam sizeof (struct in_addr), AF_INET); 71010275Ssam char *remotehost; 71110275Ssam time_t t; 71210275Ssam 71310275Ssam if (hp) 71410275Ssam remotehost = hp->h_name; 71510275Ssam else 71610275Ssam remotehost = "UNKNOWNHOST"; 71710275Ssam t = time(0); 71810303Ssam fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t)); 71910275Ssam fflush(stderr); 72010275Ssam } 721*10695Ssam 722*10695Ssam /* 723*10695Ssam * Special version of popen which avoids 724*10695Ssam * call to shell. This insures noone may 725*10695Ssam * create a pipe to a hidden program as a side 726*10695Ssam * effect of a list or dir command. 727*10695Ssam */ 728*10695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 729*10695Ssam #define RDR 0 730*10695Ssam #define WTR 1 731*10695Ssam static int popen_pid[5]; 732*10695Ssam 733*10695Ssam static char * 734*10695Ssam nextarg(cpp) 735*10695Ssam char *cpp; 736*10695Ssam { 737*10695Ssam register char *cp = cpp; 738*10695Ssam 739*10695Ssam if (cp == 0) 740*10695Ssam return (cp); 741*10695Ssam while (*cp && *cp != ' ' && *cp != '\t') 742*10695Ssam cp++; 743*10695Ssam if (*cp == ' ' || *cp == '\t') { 744*10695Ssam *cp++ = '\0'; 745*10695Ssam while (*cp == ' ' || *cp == '\t') 746*10695Ssam cp++; 747*10695Ssam } 748*10695Ssam if (cp == cpp) 749*10695Ssam return ((char *)0); 750*10695Ssam return (cp); 751*10695Ssam } 752*10695Ssam 753*10695Ssam FILE * 754*10695Ssam popen(cmd, mode) 755*10695Ssam char *cmd, *mode; 756*10695Ssam { 757*10695Ssam int p[2], ac; 758*10695Ssam register myside, hisside, pid; 759*10695Ssam char *av[10]; 760*10695Ssam register char *cp; 761*10695Ssam 762*10695Ssam if (pipe(p) < 0) 763*10695Ssam return (NULL); 764*10695Ssam cp = cmd, ac = 0; 765*10695Ssam do { 766*10695Ssam av[ac++] = cp; 767*10695Ssam cp = nextarg(cp); 768*10695Ssam } while (cp && *cp); 769*10695Ssam av[ac] = (char *)0; 770*10695Ssam myside = tst(p[WTR], p[RDR]); 771*10695Ssam hisside = tst(p[RDR], p[WTR]); 772*10695Ssam if ((pid = fork()) == 0) { 773*10695Ssam /* myside and hisside reverse roles in child */ 774*10695Ssam close(myside); 775*10695Ssam dup2(hisside, tst(0, 1)); 776*10695Ssam close(hisside); 777*10695Ssam execv(av[0], av); 778*10695Ssam _exit(1); 779*10695Ssam } 780*10695Ssam if (pid == -1) 781*10695Ssam return (NULL); 782*10695Ssam popen_pid[myside] = pid; 783*10695Ssam close(hisside); 784*10695Ssam return (fdopen(myside, mode)); 785*10695Ssam } 786*10695Ssam 787*10695Ssam pclose(ptr) 788*10695Ssam FILE *ptr; 789*10695Ssam { 790*10695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 791*10695Ssam int status; 792*10695Ssam 793*10695Ssam f = fileno(ptr); 794*10695Ssam fclose(ptr); 795*10695Ssam istat = signal(SIGINT, SIG_IGN); 796*10695Ssam qstat = signal(SIGQUIT, SIG_IGN); 797*10695Ssam hstat = signal(SIGHUP, SIG_IGN); 798*10695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 799*10695Ssam ; 800*10695Ssam if (r == -1) 801*10695Ssam status = -1; 802*10695Ssam signal(SIGINT, istat); 803*10695Ssam signal(SIGQUIT, qstat); 804*10695Ssam signal(SIGHUP, hstat); 805*10695Ssam return (status); 806*10695Ssam } 807*10695Ssam 808*10695Ssam /* 809*10695Ssam * Check user requesting login priviledges. 810*10695Ssam * Disallow anyone mentioned in the file FTPUSERS 811*10695Ssam * to allow people such as uucp to be avoided. 812*10695Ssam */ 813*10695Ssam checkuser(name) 814*10695Ssam register char *name; 815*10695Ssam { 816*10695Ssam char line[BUFSIZ], *index(); 817*10695Ssam FILE *fd; 818*10695Ssam int found = 0; 819*10695Ssam 820*10695Ssam fd = fopen(FTPUSERS, "r"); 821*10695Ssam if (fd == NULL) 822*10695Ssam return (1); 823*10695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 824*10695Ssam register char *cp = index(line, '\n'); 825*10695Ssam 826*10695Ssam if (cp) 827*10695Ssam *cp = '\0'; 828*10695Ssam if (strcmp(line, name) == 0) { 829*10695Ssam found++; 830*10695Ssam break; 831*10695Ssam } 832*10695Ssam } 833*10695Ssam fclose(fd); 834*10695Ssam return (!found); 835*10695Ssam } 836