110275Ssam #ifndef lint 2*13034Ssam static char sccsid[] = "@(#)ftpd.c 4.20 (Berkeley) 06/12/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 15*13034Ssam #include <arpa/ftp.h> 16*13034Ssam 1710275Ssam #include <stdio.h> 1810275Ssam #include <signal.h> 1910275Ssam #include <wait.h> 2010275Ssam #include <pwd.h> 2110275Ssam #include <setjmp.h> 2210275Ssam #include <netdb.h> 2310423Ssam #include <errno.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; 5311757Ssam 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 10911757Ssam case 'l': 11011757Ssam logging = 1; 11111757Ssam break; 11211757Ssam 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 } 157*13034Ssam signal(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); 188*13034Ssam /* 189*13034Ssam * Anchor data source address to that 190*13034Ssam * of the control port so hosts with 191*13034Ssam * multiple address won't have data 192*13034Ssam * connections bound to an address different 193*13034Ssam * than the control port. 194*13034Ssam */ 195*13034Ssam if (getsockname(0, &ctrl_addr, sizeof (ctrl_addr)) >= 0) 196*13034Ssam data_source.sin_addr = ctrl_addr.sin_addr; 19710275Ssam for (;;) { 19810275Ssam setjmp(errcatch); 19910275Ssam yyparse(); 20010275Ssam } 20110275Ssam } 20210275Ssam close(ctrl); 20310275Ssam } 20410275Ssam } 20510275Ssam 20610419Ssam reapchild() 20710419Ssam { 20810419Ssam union wait status; 20910419Ssam 21010419Ssam while (wait3(&status, WNOHANG, 0) > 0) 21110419Ssam ; 21210419Ssam } 21310419Ssam 21410275Ssam lostconn() 21510275Ssam { 21610275Ssam 21710275Ssam fatal("Connection closed."); 21810275Ssam } 21910275Ssam 22010275Ssam pass(passwd) 22110275Ssam char *passwd; 22210275Ssam { 22310303Ssam char *xpasswd, *savestr(); 22410303Ssam static struct passwd save; 22510275Ssam 22610275Ssam if (logged_in || pw == NULL) { 22710275Ssam reply(503, "Login with USER first."); 22810275Ssam return; 22910275Ssam } 23010275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 23110275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 23210275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 23310275Ssam reply(530, "Login incorrect."); 23410275Ssam pw = NULL; 23510275Ssam return; 23610275Ssam } 23710275Ssam } 23810303Ssam setegid(pw->pw_gid); 23910275Ssam initgroups(pw->pw_name, pw->pw_gid); 24010275Ssam if (chdir(pw->pw_dir)) { 24110275Ssam reply(550, "User %s: can't change directory to $s.", 24210275Ssam pw->pw_name, pw->pw_dir); 24310303Ssam goto bad; 24410275Ssam } 24510303Ssam if (guest && chroot(pw->pw_dir) < 0) { 24610275Ssam reply(550, "Can't set guest privileges."); 24710303Ssam goto bad; 24810275Ssam } 24910275Ssam if (!guest) 25010275Ssam reply(230, "User %s logged in.", pw->pw_name); 25110275Ssam else 25210275Ssam reply(230, "Guest login ok, access restrictions apply."); 25310275Ssam logged_in = 1; 25410303Ssam seteuid(pw->pw_uid); 25510303Ssam /* 25610303Ssam * Save everything so globbing doesn't 25710303Ssam * clobber the fields. 25810303Ssam */ 25910303Ssam save = *pw; 26010303Ssam save.pw_name = savestr(pw->pw_name); 26110303Ssam save.pw_passwd = savestr(pw->pw_passwd); 26210303Ssam save.pw_comment = savestr(pw->pw_comment); 26310303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 26410303Ssam save.pw_dir = savestr(pw->pw_dir); 26510303Ssam save.pw_shell = savestr(pw->pw_shell); 26610303Ssam pw = &save; 26710303Ssam home = pw->pw_dir; /* home dir for globbing */ 26810303Ssam return; 26910303Ssam bad: 27010303Ssam seteuid(0); 27110303Ssam pw = NULL; 27210275Ssam } 27310275Ssam 27410303Ssam char * 27510303Ssam savestr(s) 27610303Ssam char *s; 27710303Ssam { 27810303Ssam char *malloc(); 27910303Ssam char *new = malloc(strlen(s) + 1); 28010303Ssam 28110303Ssam if (new != NULL) 28210303Ssam strcpy(new, s); 28311347Ssam return (new); 28410303Ssam } 28510303Ssam 28610275Ssam retrieve(cmd, name) 28710275Ssam char *cmd, *name; 28810275Ssam { 28910275Ssam FILE *fin, *dout; 29010275Ssam struct stat st; 29110275Ssam int (*closefunc)(); 29210275Ssam 29310275Ssam if (cmd == 0) { 29410317Ssam #ifdef notdef 29510317Ssam /* no remote command execution -- it's a security hole */ 29611653Ssam if (*name == '|') 29710275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 29810275Ssam else 29910317Ssam #endif 30010275Ssam fin = fopen(name, "r"), closefunc = fclose; 30110275Ssam } else { 30210275Ssam char line[BUFSIZ]; 30310275Ssam 30410422Ssam sprintf(line, cmd, name), name = line; 30510275Ssam fin = popen(line, "r"), closefunc = pclose; 30610275Ssam } 30710275Ssam if (fin == NULL) { 30810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30910275Ssam return; 31010275Ssam } 31110275Ssam st.st_size = 0; 31210275Ssam if (cmd == 0 && 31310275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 31410275Ssam reply(550, "%s: not a plain file.", name); 31510275Ssam goto done; 31610275Ssam } 31710275Ssam dout = dataconn(name, st.st_size, "w"); 31810275Ssam if (dout == NULL) 31910275Ssam goto done; 32010303Ssam if (send_data(fin, dout) || ferror(dout)) 32110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 32210275Ssam else 32310275Ssam reply(226, "Transfer complete."); 32410303Ssam fclose(dout), data = -1; 32510275Ssam done: 32610275Ssam (*closefunc)(fin); 32710275Ssam } 32810275Ssam 32910275Ssam store(name, mode) 33010275Ssam char *name, *mode; 33110275Ssam { 33210275Ssam FILE *fout, *din; 33310303Ssam int (*closefunc)(), dochown = 0; 33410275Ssam 33510317Ssam #ifdef notdef 33610317Ssam /* no remote command execution -- it's a security hole */ 33711653Ssam if (name[0] == '|') 33810275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33910317Ssam else 34010317Ssam #endif 34110317Ssam { 34210303Ssam struct stat st; 34310303Ssam 34410303Ssam if (stat(name, &st) < 0) 34510303Ssam dochown++; 34610275Ssam fout = fopen(name, mode), closefunc = fclose; 34710303Ssam } 34810275Ssam if (fout == NULL) { 34910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 35010275Ssam return; 35110275Ssam } 35211653Ssam din = dataconn(name, (off_t)-1, "r"); 35310275Ssam if (din == NULL) 35410275Ssam goto done; 35510303Ssam if (receive_data(din, fout) || ferror(fout)) 35610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 35710275Ssam else 35810275Ssam reply(226, "Transfer complete."); 35910275Ssam fclose(din), data = -1; 36010275Ssam done: 36110303Ssam if (dochown) 36210303Ssam (void) chown(name, pw->pw_uid, -1); 36310275Ssam (*closefunc)(fout); 36410275Ssam } 36510275Ssam 36610275Ssam FILE * 36710275Ssam getdatasock(mode) 36810275Ssam char *mode; 36910275Ssam { 370*13034Ssam int s, linger; 37110275Ssam 37210275Ssam if (data >= 0) 37310275Ssam return (fdopen(data, mode)); 37410602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 37510602Ssam if (s < 0) 37610275Ssam return (NULL); 37710275Ssam seteuid(0); 37810602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 37910602Ssam goto bad; 38010602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 38110602Ssam goto bad; 382*13034Ssam linger = 60; /* value ignored by system */ 383*13034Ssam (void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)); 38410311Ssam seteuid(pw->pw_uid); 38510275Ssam return (fdopen(s, mode)); 38610602Ssam bad: 38710602Ssam seteuid(pw->pw_uid); 38810602Ssam close(s); 38910602Ssam return (NULL); 39010275Ssam } 39110275Ssam 39210275Ssam FILE * 39310275Ssam dataconn(name, size, mode) 39410275Ssam char *name; 39511653Ssam off_t size; 39610275Ssam char *mode; 39710275Ssam { 39810275Ssam char sizebuf[32]; 39910275Ssam FILE *file; 40011653Ssam int retry = 0; 40110275Ssam 40210275Ssam if (size >= 0) 40311653Ssam sprintf (sizebuf, " (%ld bytes)", size); 40410275Ssam else 40510275Ssam (void) strcpy(sizebuf, ""); 40610275Ssam if (data >= 0) { 40710275Ssam reply(125, "Using existing data connection for %s%s.", 40810275Ssam name, sizebuf); 40910321Ssam usedefault = 1; 41010275Ssam return (fdopen(data, mode)); 41110275Ssam } 41210566Ssam if (usedefault) 41310422Ssam data_dest = his_addr; 41410422Ssam usedefault = 1; 41510275Ssam file = getdatasock(mode); 41610275Ssam if (file == NULL) { 41710275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 41810275Ssam ntoa(data_source.sin_addr), 41910275Ssam ntohs(data_source.sin_port), 42010275Ssam sys_errlist[errno]); 42110275Ssam return (NULL); 42210275Ssam } 42310602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 42410602Ssam name, ntoa(data_dest.sin_addr.s_addr), 42510602Ssam ntohs(data_dest.sin_port), sizebuf); 42610275Ssam data = fileno(file); 42711653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 42811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 42911653Ssam sleep(swaitint); 43011653Ssam retry += swaitint; 43111653Ssam continue; 43211653Ssam } 43310275Ssam reply(425, "Can't build data connection: %s.", 43410275Ssam sys_errlist[errno]); 43510275Ssam (void) fclose(file); 43610275Ssam data = -1; 43710275Ssam return (NULL); 43810275Ssam } 43910275Ssam return (file); 44010275Ssam } 44110275Ssam 44210275Ssam /* 44310275Ssam * Tranfer the contents of "instr" to 44410275Ssam * "outstr" peer using the appropriate 44510275Ssam * encapulation of the date subject 44610275Ssam * to Mode, Structure, and Type. 44710275Ssam * 44810275Ssam * NB: Form isn't handled. 44910275Ssam */ 45010275Ssam send_data(instr, outstr) 45110275Ssam FILE *instr, *outstr; 45210275Ssam { 45310275Ssam register int c; 45410275Ssam int netfd, filefd, cnt; 45510275Ssam char buf[BUFSIZ]; 45610275Ssam 45710275Ssam switch (type) { 45810275Ssam 45910275Ssam case TYPE_A: 46010275Ssam while ((c = getc(instr)) != EOF) { 46111220Ssam if (c == '\n') { 46211220Ssam if (ferror (outstr)) 46311220Ssam return (1); 46410275Ssam putc('\r', outstr); 46511220Ssam } 46611220Ssam putc(c, outstr); 46711220Ssam if (c == '\r') 46811220Ssam putc ('\0', outstr); 46910275Ssam } 47011220Ssam if (ferror (instr) || ferror (outstr)) 47111220Ssam return (1); 47210275Ssam return (0); 47310275Ssam 47410275Ssam case TYPE_I: 47510275Ssam case TYPE_L: 47610275Ssam netfd = fileno(outstr); 47710275Ssam filefd = fileno(instr); 47810275Ssam 47910303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 48010275Ssam if (write(netfd, buf, cnt) < 0) 48110275Ssam return (1); 48210275Ssam return (cnt < 0); 48310275Ssam } 48410275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 48510275Ssam return (1); 48610275Ssam } 48710275Ssam 48810275Ssam /* 48910275Ssam * Transfer data from peer to 49010275Ssam * "outstr" using the appropriate 49110275Ssam * encapulation of the data subject 49210275Ssam * to Mode, Structure, and Type. 49310275Ssam * 49410275Ssam * N.B.: Form isn't handled. 49510275Ssam */ 49610275Ssam receive_data(instr, outstr) 49710275Ssam FILE *instr, *outstr; 49810275Ssam { 49910275Ssam register int c; 50011220Ssam int cnt; 50110275Ssam char buf[BUFSIZ]; 50210275Ssam 50310275Ssam 50410275Ssam switch (type) { 50510275Ssam 50610275Ssam case TYPE_I: 50710275Ssam case TYPE_L: 50810616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 50910616Ssam if (write(fileno(outstr), buf, cnt) < 0) 51010275Ssam return (1); 51110275Ssam return (cnt < 0); 51210275Ssam 51310275Ssam case TYPE_E: 51410275Ssam reply(504, "TYPE E not implemented."); 51510275Ssam return (1); 51610275Ssam 51710275Ssam case TYPE_A: 51810275Ssam while ((c = getc(instr)) != EOF) { 51910275Ssam if (c == '\r') { 52011220Ssam if (ferror (outstr)) 52111220Ssam return (1); 52211220Ssam if ((c = getc(instr)) != '\n') 52311220Ssam putc ('\r', outstr); 52411220Ssam if (c == '\0') 52511220Ssam continue; 52610275Ssam } 52711220Ssam putc (c, outstr); 52810275Ssam } 52911220Ssam if (ferror (instr) || ferror (outstr)) 53011220Ssam return (1); 53110275Ssam return (0); 53210275Ssam } 53310275Ssam fatal("Unknown type in receive_data."); 53410275Ssam /*NOTREACHED*/ 53510275Ssam } 53610275Ssam 53710275Ssam fatal(s) 53810275Ssam char *s; 53910275Ssam { 54010275Ssam reply(451, "Error in server: %s\n", s); 54110275Ssam reply(221, "Closing connection due to server error."); 54210275Ssam exit(0); 54310275Ssam } 54410275Ssam 54510275Ssam reply(n, s, args) 54610275Ssam int n; 54710275Ssam char *s; 54810275Ssam { 54910275Ssam 55010275Ssam printf("%d ", n); 55110275Ssam _doprnt(s, &args, stdout); 55210275Ssam printf("\r\n"); 55310275Ssam fflush(stdout); 55410275Ssam if (debug) { 55510275Ssam fprintf(stderr, "<--- %d ", n); 55610275Ssam _doprnt(s, &args, stderr); 55710275Ssam fprintf(stderr, "\n"); 55810275Ssam fflush(stderr); 55910275Ssam } 56010275Ssam } 56110275Ssam 56210275Ssam lreply(n, s, args) 56310275Ssam int n; 56410275Ssam char *s; 56510275Ssam { 56610275Ssam printf("%d-", n); 56710275Ssam _doprnt(s, &args, stdout); 56810275Ssam printf("\r\n"); 56910275Ssam fflush(stdout); 57010275Ssam if (debug) { 57110275Ssam fprintf(stderr, "<--- %d-", n); 57210275Ssam _doprnt(s, &args, stderr); 57310275Ssam fprintf(stderr, "\n"); 57410275Ssam } 57510275Ssam } 57610275Ssam 57710275Ssam replystr(s) 57810275Ssam char *s; 57910275Ssam { 58010275Ssam printf("%s\r\n", s); 58110275Ssam fflush(stdout); 58210275Ssam if (debug) 58310275Ssam fprintf(stderr, "<--- %s\n", s); 58410275Ssam } 58510275Ssam 58610275Ssam ack(s) 58710275Ssam char *s; 58810275Ssam { 58910275Ssam reply(200, "%s command okay.", s); 59010275Ssam } 59110275Ssam 59210275Ssam nack(s) 59310275Ssam char *s; 59410275Ssam { 59510275Ssam reply(502, "%s command not implemented.", s); 59610275Ssam } 59710275Ssam 59810275Ssam yyerror() 59910275Ssam { 60010275Ssam reply(500, "Command not understood."); 60110275Ssam } 60210275Ssam 60310275Ssam delete(name) 60410275Ssam char *name; 60510275Ssam { 60610275Ssam struct stat st; 60710275Ssam 60810275Ssam if (stat(name, &st) < 0) { 60910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61010275Ssam return; 61110275Ssam } 61210275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 61310275Ssam if (rmdir(name) < 0) { 61410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61510275Ssam return; 61610275Ssam } 61710275Ssam goto done; 61810275Ssam } 61910275Ssam if (unlink(name) < 0) { 62010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 62110275Ssam return; 62210275Ssam } 62310275Ssam done: 62410275Ssam ack("DELE"); 62510275Ssam } 62610275Ssam 62710275Ssam cwd(path) 62810275Ssam char *path; 62910275Ssam { 63010275Ssam 63110275Ssam if (chdir(path) < 0) { 63210275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 63310275Ssam return; 63410275Ssam } 63510275Ssam ack("CWD"); 63610275Ssam } 63710275Ssam 63810303Ssam makedir(name) 63910275Ssam char *name; 64010275Ssam { 64110303Ssam struct stat st; 64210303Ssam int dochown = stat(name, &st) < 0; 64310275Ssam 64410275Ssam if (mkdir(name, 0777) < 0) { 64510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64610275Ssam return; 64710275Ssam } 64810303Ssam if (dochown) 64910303Ssam (void) chown(name, pw->pw_uid, -1); 65010275Ssam ack("MKDIR"); 65110275Ssam } 65210275Ssam 65310303Ssam removedir(name) 65410275Ssam char *name; 65510275Ssam { 65610275Ssam 65710275Ssam if (rmdir(name) < 0) { 65810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 65910275Ssam return; 66010275Ssam } 66110275Ssam ack("RMDIR"); 66210275Ssam } 66310275Ssam 66410303Ssam pwd() 66510275Ssam { 66610303Ssam char path[MAXPATHLEN + 1]; 66710275Ssam 66810275Ssam if (getwd(path) == NULL) { 66910275Ssam reply(451, "%s.", path); 67010275Ssam return; 67110275Ssam } 67210275Ssam reply(251, "\"%s\" is current directory.", path); 67310275Ssam } 67410275Ssam 67510275Ssam char * 67610275Ssam renamefrom(name) 67710275Ssam char *name; 67810275Ssam { 67910275Ssam struct stat st; 68010275Ssam 68110275Ssam if (stat(name, &st) < 0) { 68210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 68310275Ssam return ((char *)0); 68410275Ssam } 68510303Ssam reply(350, "File exists, ready for destination name"); 68610275Ssam return (name); 68710275Ssam } 68810275Ssam 68910275Ssam renamecmd(from, to) 69010275Ssam char *from, *to; 69110275Ssam { 69210275Ssam 69310275Ssam if (rename(from, to) < 0) { 69410275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 69510275Ssam return; 69610275Ssam } 69710275Ssam ack("RNTO"); 69810275Ssam } 69910275Ssam 70010275Ssam /* 70110275Ssam * Convert network-format internet address 70210275Ssam * to base 256 d.d.d.d representation. 70310275Ssam */ 70410275Ssam char * 70510275Ssam ntoa(in) 70610275Ssam struct in_addr in; 70710275Ssam { 70810275Ssam static char b[18]; 70910275Ssam register char *p; 71010275Ssam 71110275Ssam p = (char *)∈ 71210275Ssam #define UC(b) (((int)b)&0xff) 71310275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 71410275Ssam return (b); 71510275Ssam } 71610275Ssam 71710275Ssam dolog(sin) 71810275Ssam struct sockaddr_in *sin; 71910275Ssam { 72010275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 72110275Ssam sizeof (struct in_addr), AF_INET); 72210275Ssam char *remotehost; 72310275Ssam time_t t; 72410275Ssam 72510275Ssam if (hp) 72610275Ssam remotehost = hp->h_name; 72710275Ssam else 72810275Ssam remotehost = "UNKNOWNHOST"; 72910275Ssam t = time(0); 73011757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 73110275Ssam fflush(stderr); 73210275Ssam } 73310695Ssam 73410695Ssam /* 73510695Ssam * Special version of popen which avoids 73610695Ssam * call to shell. This insures noone may 73710695Ssam * create a pipe to a hidden program as a side 73810695Ssam * effect of a list or dir command. 73910695Ssam */ 74010695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 74110695Ssam #define RDR 0 74210695Ssam #define WTR 1 74310695Ssam static int popen_pid[5]; 74410695Ssam 74510695Ssam static char * 74610695Ssam nextarg(cpp) 74710695Ssam char *cpp; 74810695Ssam { 74910695Ssam register char *cp = cpp; 75010695Ssam 75110695Ssam if (cp == 0) 75210695Ssam return (cp); 75310695Ssam while (*cp && *cp != ' ' && *cp != '\t') 75410695Ssam cp++; 75510695Ssam if (*cp == ' ' || *cp == '\t') { 75610695Ssam *cp++ = '\0'; 75710695Ssam while (*cp == ' ' || *cp == '\t') 75810695Ssam cp++; 75910695Ssam } 76010695Ssam if (cp == cpp) 76110695Ssam return ((char *)0); 76210695Ssam return (cp); 76310695Ssam } 76410695Ssam 76510695Ssam FILE * 76610695Ssam popen(cmd, mode) 76710695Ssam char *cmd, *mode; 76810695Ssam { 76910695Ssam int p[2], ac; 77010695Ssam register myside, hisside, pid; 77111757Ssam char *av[512]; 77211757Ssam char **pop, **popargs = NULL; 77311757Ssam extern char **glob(); 77410695Ssam register char *cp; 77510695Ssam 77610695Ssam if (pipe(p) < 0) 77710695Ssam return (NULL); 77810695Ssam cp = cmd, ac = 0; 77910695Ssam do { 78010695Ssam av[ac++] = cp; 78110695Ssam cp = nextarg(cp); 78210695Ssam } while (cp && *cp); 78310695Ssam av[ac] = (char *)0; 78411757Ssam if (ac > 1) { 78511757Ssam popargs = glob(&av[1]); 78611757Ssam if (popargs == NULL) 78711757Ssam return (NULL); 78811757Ssam for (ac = 1, pop = popargs; *pop;) 78911757Ssam av[ac++] = *pop++; 79011757Ssam } 79111757Ssam av[ac] = (char *)0; 79210695Ssam myside = tst(p[WTR], p[RDR]); 79310695Ssam hisside = tst(p[RDR], p[WTR]); 79410695Ssam if ((pid = fork()) == 0) { 79510695Ssam /* myside and hisside reverse roles in child */ 79610695Ssam close(myside); 79710695Ssam dup2(hisside, tst(0, 1)); 79810695Ssam close(hisside); 79910695Ssam execv(av[0], av); 80010695Ssam _exit(1); 80110695Ssam } 80211757Ssam if (popargs != NULL) 80311757Ssam blkfree(popargs); 80410695Ssam if (pid == -1) 80510695Ssam return (NULL); 80610695Ssam popen_pid[myside] = pid; 80710695Ssam close(hisside); 80810695Ssam return (fdopen(myside, mode)); 80910695Ssam } 81010695Ssam 81110695Ssam pclose(ptr) 81210695Ssam FILE *ptr; 81310695Ssam { 81410695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 81510695Ssam int status; 81610695Ssam 81710695Ssam f = fileno(ptr); 81810695Ssam fclose(ptr); 81910695Ssam istat = signal(SIGINT, SIG_IGN); 82010695Ssam qstat = signal(SIGQUIT, SIG_IGN); 82110695Ssam hstat = signal(SIGHUP, SIG_IGN); 82210695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 82310695Ssam ; 82410695Ssam if (r == -1) 82510695Ssam status = -1; 82610695Ssam signal(SIGINT, istat); 82710695Ssam signal(SIGQUIT, qstat); 82810695Ssam signal(SIGHUP, hstat); 82910695Ssam return (status); 83010695Ssam } 83110695Ssam 83210695Ssam /* 83310695Ssam * Check user requesting login priviledges. 83410695Ssam * Disallow anyone mentioned in the file FTPUSERS 83510695Ssam * to allow people such as uucp to be avoided. 83610695Ssam */ 83710695Ssam checkuser(name) 83810695Ssam register char *name; 83910695Ssam { 84010695Ssam char line[BUFSIZ], *index(); 84110695Ssam FILE *fd; 84210695Ssam int found = 0; 84310695Ssam 84410695Ssam fd = fopen(FTPUSERS, "r"); 84510695Ssam if (fd == NULL) 84610695Ssam return (1); 84710695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 84810695Ssam register char *cp = index(line, '\n'); 84910695Ssam 85010695Ssam if (cp) 85110695Ssam *cp = '\0'; 85210695Ssam if (strcmp(line, name) == 0) { 85310695Ssam found++; 85410695Ssam break; 85510695Ssam } 85610695Ssam } 85710695Ssam fclose(fd); 85810695Ssam return (!found); 85910695Ssam } 860