110275Ssam #ifndef lint 2*13152Ssam static char sccsid[] = "@(#)ftpd.c 4.21 (Berkeley) 06/15/83"; 310275Ssam #endif 410275Ssam 510275Ssam /* 610275Ssam * FTP server. 710275Ssam */ 810303Ssam #include <sys/param.h> 910275Ssam #include <sys/stat.h> 1010275Ssam #include <sys/ioctl.h> 1110275Ssam #include <sys/socket.h> 1210275Ssam 1310275Ssam #include <netinet/in.h> 1410275Ssam 1513034Ssam #include <arpa/ftp.h> 1613034Ssam 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 } 15713034Ssam 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; 185*13152Ssam (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr)); 18610275Ssam gethostname(hostname, sizeof (hostname)); 18710275Ssam reply(220, "%s FTP server (%s) ready.", 18810275Ssam hostname, version); 18910275Ssam for (;;) { 19010275Ssam setjmp(errcatch); 19110275Ssam yyparse(); 19210275Ssam } 19310275Ssam } 19410275Ssam close(ctrl); 19510275Ssam } 19610275Ssam } 19710275Ssam 19810419Ssam reapchild() 19910419Ssam { 20010419Ssam union wait status; 20110419Ssam 20210419Ssam while (wait3(&status, WNOHANG, 0) > 0) 20310419Ssam ; 20410419Ssam } 20510419Ssam 20610275Ssam lostconn() 20710275Ssam { 20810275Ssam 20910275Ssam fatal("Connection closed."); 21010275Ssam } 21110275Ssam 21210275Ssam pass(passwd) 21310275Ssam char *passwd; 21410275Ssam { 21510303Ssam char *xpasswd, *savestr(); 21610303Ssam static struct passwd save; 21710275Ssam 21810275Ssam if (logged_in || pw == NULL) { 21910275Ssam reply(503, "Login with USER first."); 22010275Ssam return; 22110275Ssam } 22210275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 22310275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22410275Ssam if (strcmp(xpasswd, pw->pw_passwd) != 0) { 22510275Ssam reply(530, "Login incorrect."); 22610275Ssam pw = NULL; 22710275Ssam return; 22810275Ssam } 22910275Ssam } 23010303Ssam setegid(pw->pw_gid); 23110275Ssam initgroups(pw->pw_name, pw->pw_gid); 23210275Ssam if (chdir(pw->pw_dir)) { 23310275Ssam reply(550, "User %s: can't change directory to $s.", 23410275Ssam pw->pw_name, pw->pw_dir); 23510303Ssam goto bad; 23610275Ssam } 23710303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23810275Ssam reply(550, "Can't set guest privileges."); 23910303Ssam goto bad; 24010275Ssam } 24110275Ssam if (!guest) 24210275Ssam reply(230, "User %s logged in.", pw->pw_name); 24310275Ssam else 24410275Ssam reply(230, "Guest login ok, access restrictions apply."); 24510275Ssam logged_in = 1; 24610303Ssam seteuid(pw->pw_uid); 24710303Ssam /* 24810303Ssam * Save everything so globbing doesn't 24910303Ssam * clobber the fields. 25010303Ssam */ 25110303Ssam save = *pw; 25210303Ssam save.pw_name = savestr(pw->pw_name); 25310303Ssam save.pw_passwd = savestr(pw->pw_passwd); 25410303Ssam save.pw_comment = savestr(pw->pw_comment); 25510303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 25610303Ssam save.pw_dir = savestr(pw->pw_dir); 25710303Ssam save.pw_shell = savestr(pw->pw_shell); 25810303Ssam pw = &save; 25910303Ssam home = pw->pw_dir; /* home dir for globbing */ 26010303Ssam return; 26110303Ssam bad: 26210303Ssam seteuid(0); 26310303Ssam pw = NULL; 26410275Ssam } 26510275Ssam 26610303Ssam char * 26710303Ssam savestr(s) 26810303Ssam char *s; 26910303Ssam { 27010303Ssam char *malloc(); 27110303Ssam char *new = malloc(strlen(s) + 1); 27210303Ssam 27310303Ssam if (new != NULL) 27410303Ssam strcpy(new, s); 27511347Ssam return (new); 27610303Ssam } 27710303Ssam 27810275Ssam retrieve(cmd, name) 27910275Ssam char *cmd, *name; 28010275Ssam { 28110275Ssam FILE *fin, *dout; 28210275Ssam struct stat st; 28310275Ssam int (*closefunc)(); 28410275Ssam 28510275Ssam if (cmd == 0) { 28610317Ssam #ifdef notdef 28710317Ssam /* no remote command execution -- it's a security hole */ 28811653Ssam if (*name == '|') 28910275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 29010275Ssam else 29110317Ssam #endif 29210275Ssam fin = fopen(name, "r"), closefunc = fclose; 29310275Ssam } else { 29410275Ssam char line[BUFSIZ]; 29510275Ssam 29610422Ssam sprintf(line, cmd, name), name = line; 29710275Ssam fin = popen(line, "r"), closefunc = pclose; 29810275Ssam } 29910275Ssam if (fin == NULL) { 300*13152Ssam if (errno != 0) 301*13152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30210275Ssam return; 30310275Ssam } 30410275Ssam st.st_size = 0; 30510275Ssam if (cmd == 0 && 30610275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 30710275Ssam reply(550, "%s: not a plain file.", name); 30810275Ssam goto done; 30910275Ssam } 31010275Ssam dout = dataconn(name, st.st_size, "w"); 31110275Ssam if (dout == NULL) 31210275Ssam goto done; 31310303Ssam if (send_data(fin, dout) || ferror(dout)) 31410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31510275Ssam else 31610275Ssam reply(226, "Transfer complete."); 31710303Ssam fclose(dout), data = -1; 31810275Ssam done: 31910275Ssam (*closefunc)(fin); 32010275Ssam } 32110275Ssam 32210275Ssam store(name, mode) 32310275Ssam char *name, *mode; 32410275Ssam { 32510275Ssam FILE *fout, *din; 32610303Ssam int (*closefunc)(), dochown = 0; 32710275Ssam 32810317Ssam #ifdef notdef 32910317Ssam /* no remote command execution -- it's a security hole */ 33011653Ssam if (name[0] == '|') 33110275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33210317Ssam else 33310317Ssam #endif 33410317Ssam { 33510303Ssam struct stat st; 33610303Ssam 33710303Ssam if (stat(name, &st) < 0) 33810303Ssam dochown++; 33910275Ssam fout = fopen(name, mode), closefunc = fclose; 34010303Ssam } 34110275Ssam if (fout == NULL) { 34210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34310275Ssam return; 34410275Ssam } 34511653Ssam din = dataconn(name, (off_t)-1, "r"); 34610275Ssam if (din == NULL) 34710275Ssam goto done; 34810303Ssam if (receive_data(din, fout) || ferror(fout)) 34910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 35010275Ssam else 35110275Ssam reply(226, "Transfer complete."); 35210275Ssam fclose(din), data = -1; 35310275Ssam done: 35410303Ssam if (dochown) 35510303Ssam (void) chown(name, pw->pw_uid, -1); 35610275Ssam (*closefunc)(fout); 35710275Ssam } 35810275Ssam 35910275Ssam FILE * 36010275Ssam getdatasock(mode) 36110275Ssam char *mode; 36210275Ssam { 36313034Ssam int s, linger; 36410275Ssam 36510275Ssam if (data >= 0) 36610275Ssam return (fdopen(data, mode)); 36710602Ssam s = socket(AF_INET, SOCK_STREAM, 0, 0); 36810602Ssam if (s < 0) 36910275Ssam return (NULL); 37010275Ssam seteuid(0); 37110602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 37210602Ssam goto bad; 373*13152Ssam /* anchor socket to avoid multi-homing problems */ 374*13152Ssam data_source.sin_family = AF_INET; 375*13152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 37610602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 37710602Ssam goto bad; 37813034Ssam linger = 60; /* value ignored by system */ 37913034Ssam (void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)); 38010311Ssam seteuid(pw->pw_uid); 38110275Ssam return (fdopen(s, mode)); 38210602Ssam bad: 38310602Ssam seteuid(pw->pw_uid); 38410602Ssam close(s); 38510602Ssam return (NULL); 38610275Ssam } 38710275Ssam 38810275Ssam FILE * 38910275Ssam dataconn(name, size, mode) 39010275Ssam char *name; 39111653Ssam off_t size; 39210275Ssam char *mode; 39310275Ssam { 39410275Ssam char sizebuf[32]; 39510275Ssam FILE *file; 39611653Ssam int retry = 0; 39710275Ssam 39810275Ssam if (size >= 0) 39911653Ssam sprintf (sizebuf, " (%ld bytes)", size); 40010275Ssam else 40110275Ssam (void) strcpy(sizebuf, ""); 40210275Ssam if (data >= 0) { 40310275Ssam reply(125, "Using existing data connection for %s%s.", 40410275Ssam name, sizebuf); 40510321Ssam usedefault = 1; 40610275Ssam return (fdopen(data, mode)); 40710275Ssam } 40810566Ssam if (usedefault) 40910422Ssam data_dest = his_addr; 41010422Ssam usedefault = 1; 41110275Ssam file = getdatasock(mode); 41210275Ssam if (file == NULL) { 41310275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 41410275Ssam ntoa(data_source.sin_addr), 41510275Ssam ntohs(data_source.sin_port), 41610275Ssam sys_errlist[errno]); 41710275Ssam return (NULL); 41810275Ssam } 41910602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 42010602Ssam name, ntoa(data_dest.sin_addr.s_addr), 42110602Ssam ntohs(data_dest.sin_port), sizebuf); 42210275Ssam data = fileno(file); 42311653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 42411653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 42511653Ssam sleep(swaitint); 42611653Ssam retry += swaitint; 42711653Ssam continue; 42811653Ssam } 42910275Ssam reply(425, "Can't build data connection: %s.", 43010275Ssam sys_errlist[errno]); 43110275Ssam (void) fclose(file); 43210275Ssam data = -1; 43310275Ssam return (NULL); 43410275Ssam } 43510275Ssam return (file); 43610275Ssam } 43710275Ssam 43810275Ssam /* 43910275Ssam * Tranfer the contents of "instr" to 44010275Ssam * "outstr" peer using the appropriate 44110275Ssam * encapulation of the date subject 44210275Ssam * to Mode, Structure, and Type. 44310275Ssam * 44410275Ssam * NB: Form isn't handled. 44510275Ssam */ 44610275Ssam send_data(instr, outstr) 44710275Ssam FILE *instr, *outstr; 44810275Ssam { 44910275Ssam register int c; 45010275Ssam int netfd, filefd, cnt; 45110275Ssam char buf[BUFSIZ]; 45210275Ssam 45310275Ssam switch (type) { 45410275Ssam 45510275Ssam case TYPE_A: 45610275Ssam while ((c = getc(instr)) != EOF) { 45711220Ssam if (c == '\n') { 45811220Ssam if (ferror (outstr)) 45911220Ssam return (1); 46010275Ssam putc('\r', outstr); 46111220Ssam } 46211220Ssam putc(c, outstr); 46311220Ssam if (c == '\r') 46411220Ssam putc ('\0', outstr); 46510275Ssam } 46611220Ssam if (ferror (instr) || ferror (outstr)) 46711220Ssam return (1); 46810275Ssam return (0); 46910275Ssam 47010275Ssam case TYPE_I: 47110275Ssam case TYPE_L: 47210275Ssam netfd = fileno(outstr); 47310275Ssam filefd = fileno(instr); 47410275Ssam 47510303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 47610275Ssam if (write(netfd, buf, cnt) < 0) 47710275Ssam return (1); 47810275Ssam return (cnt < 0); 47910275Ssam } 48010275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 48110275Ssam return (1); 48210275Ssam } 48310275Ssam 48410275Ssam /* 48510275Ssam * Transfer data from peer to 48610275Ssam * "outstr" using the appropriate 48710275Ssam * encapulation of the data subject 48810275Ssam * to Mode, Structure, and Type. 48910275Ssam * 49010275Ssam * N.B.: Form isn't handled. 49110275Ssam */ 49210275Ssam receive_data(instr, outstr) 49310275Ssam FILE *instr, *outstr; 49410275Ssam { 49510275Ssam register int c; 49611220Ssam int cnt; 49710275Ssam char buf[BUFSIZ]; 49810275Ssam 49910275Ssam 50010275Ssam switch (type) { 50110275Ssam 50210275Ssam case TYPE_I: 50310275Ssam case TYPE_L: 50410616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 50510616Ssam if (write(fileno(outstr), buf, cnt) < 0) 50610275Ssam return (1); 50710275Ssam return (cnt < 0); 50810275Ssam 50910275Ssam case TYPE_E: 51010275Ssam reply(504, "TYPE E not implemented."); 51110275Ssam return (1); 51210275Ssam 51310275Ssam case TYPE_A: 51410275Ssam while ((c = getc(instr)) != EOF) { 51510275Ssam if (c == '\r') { 51611220Ssam if (ferror (outstr)) 51711220Ssam return (1); 51811220Ssam if ((c = getc(instr)) != '\n') 51911220Ssam putc ('\r', outstr); 52011220Ssam if (c == '\0') 52111220Ssam continue; 52210275Ssam } 52311220Ssam putc (c, outstr); 52410275Ssam } 52511220Ssam if (ferror (instr) || ferror (outstr)) 52611220Ssam return (1); 52710275Ssam return (0); 52810275Ssam } 52910275Ssam fatal("Unknown type in receive_data."); 53010275Ssam /*NOTREACHED*/ 53110275Ssam } 53210275Ssam 53310275Ssam fatal(s) 53410275Ssam char *s; 53510275Ssam { 53610275Ssam reply(451, "Error in server: %s\n", s); 53710275Ssam reply(221, "Closing connection due to server error."); 53810275Ssam exit(0); 53910275Ssam } 54010275Ssam 54110275Ssam reply(n, s, args) 54210275Ssam int n; 54310275Ssam char *s; 54410275Ssam { 54510275Ssam 54610275Ssam printf("%d ", n); 54710275Ssam _doprnt(s, &args, stdout); 54810275Ssam printf("\r\n"); 54910275Ssam fflush(stdout); 55010275Ssam if (debug) { 55110275Ssam fprintf(stderr, "<--- %d ", n); 55210275Ssam _doprnt(s, &args, stderr); 55310275Ssam fprintf(stderr, "\n"); 55410275Ssam fflush(stderr); 55510275Ssam } 55610275Ssam } 55710275Ssam 55810275Ssam lreply(n, s, args) 55910275Ssam int n; 56010275Ssam char *s; 56110275Ssam { 56210275Ssam printf("%d-", n); 56310275Ssam _doprnt(s, &args, stdout); 56410275Ssam printf("\r\n"); 56510275Ssam fflush(stdout); 56610275Ssam if (debug) { 56710275Ssam fprintf(stderr, "<--- %d-", n); 56810275Ssam _doprnt(s, &args, stderr); 56910275Ssam fprintf(stderr, "\n"); 57010275Ssam } 57110275Ssam } 57210275Ssam 57310275Ssam replystr(s) 57410275Ssam char *s; 57510275Ssam { 57610275Ssam printf("%s\r\n", s); 57710275Ssam fflush(stdout); 57810275Ssam if (debug) 57910275Ssam fprintf(stderr, "<--- %s\n", s); 58010275Ssam } 58110275Ssam 58210275Ssam ack(s) 58310275Ssam char *s; 58410275Ssam { 58510275Ssam reply(200, "%s command okay.", s); 58610275Ssam } 58710275Ssam 58810275Ssam nack(s) 58910275Ssam char *s; 59010275Ssam { 59110275Ssam reply(502, "%s command not implemented.", s); 59210275Ssam } 59310275Ssam 59410275Ssam yyerror() 59510275Ssam { 59610275Ssam reply(500, "Command not understood."); 59710275Ssam } 59810275Ssam 59910275Ssam delete(name) 60010275Ssam char *name; 60110275Ssam { 60210275Ssam struct stat st; 60310275Ssam 60410275Ssam if (stat(name, &st) < 0) { 60510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60610275Ssam return; 60710275Ssam } 60810275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 60910275Ssam if (rmdir(name) < 0) { 61010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61110275Ssam return; 61210275Ssam } 61310275Ssam goto done; 61410275Ssam } 61510275Ssam if (unlink(name) < 0) { 61610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61710275Ssam return; 61810275Ssam } 61910275Ssam done: 62010275Ssam ack("DELE"); 62110275Ssam } 62210275Ssam 62310275Ssam cwd(path) 62410275Ssam char *path; 62510275Ssam { 62610275Ssam 62710275Ssam if (chdir(path) < 0) { 62810275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 62910275Ssam return; 63010275Ssam } 63110275Ssam ack("CWD"); 63210275Ssam } 63310275Ssam 63410303Ssam makedir(name) 63510275Ssam char *name; 63610275Ssam { 63710303Ssam struct stat st; 63810303Ssam int dochown = stat(name, &st) < 0; 63910275Ssam 64010275Ssam if (mkdir(name, 0777) < 0) { 64110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 64210275Ssam return; 64310275Ssam } 64410303Ssam if (dochown) 64510303Ssam (void) chown(name, pw->pw_uid, -1); 64610275Ssam ack("MKDIR"); 64710275Ssam } 64810275Ssam 64910303Ssam removedir(name) 65010275Ssam char *name; 65110275Ssam { 65210275Ssam 65310275Ssam if (rmdir(name) < 0) { 65410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 65510275Ssam return; 65610275Ssam } 65710275Ssam ack("RMDIR"); 65810275Ssam } 65910275Ssam 66010303Ssam pwd() 66110275Ssam { 66210303Ssam char path[MAXPATHLEN + 1]; 66310275Ssam 66410275Ssam if (getwd(path) == NULL) { 66510275Ssam reply(451, "%s.", path); 66610275Ssam return; 66710275Ssam } 66810275Ssam reply(251, "\"%s\" is current directory.", path); 66910275Ssam } 67010275Ssam 67110275Ssam char * 67210275Ssam renamefrom(name) 67310275Ssam char *name; 67410275Ssam { 67510275Ssam struct stat st; 67610275Ssam 67710275Ssam if (stat(name, &st) < 0) { 67810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67910275Ssam return ((char *)0); 68010275Ssam } 68110303Ssam reply(350, "File exists, ready for destination name"); 68210275Ssam return (name); 68310275Ssam } 68410275Ssam 68510275Ssam renamecmd(from, to) 68610275Ssam char *from, *to; 68710275Ssam { 68810275Ssam 68910275Ssam if (rename(from, to) < 0) { 69010275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 69110275Ssam return; 69210275Ssam } 69310275Ssam ack("RNTO"); 69410275Ssam } 69510275Ssam 69610275Ssam /* 69710275Ssam * Convert network-format internet address 69810275Ssam * to base 256 d.d.d.d representation. 69910275Ssam */ 70010275Ssam char * 70110275Ssam ntoa(in) 70210275Ssam struct in_addr in; 70310275Ssam { 70410275Ssam static char b[18]; 70510275Ssam register char *p; 70610275Ssam 70710275Ssam p = (char *)∈ 70810275Ssam #define UC(b) (((int)b)&0xff) 70910275Ssam sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); 71010275Ssam return (b); 71110275Ssam } 71210275Ssam 71310275Ssam dolog(sin) 71410275Ssam struct sockaddr_in *sin; 71510275Ssam { 71610275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 71710275Ssam sizeof (struct in_addr), AF_INET); 71810275Ssam char *remotehost; 71910275Ssam time_t t; 72010275Ssam 72110275Ssam if (hp) 72210275Ssam remotehost = hp->h_name; 72310275Ssam else 724*13152Ssam remotehost = inet_ntoa(sin->sin_addr); 72510275Ssam t = time(0); 72611757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 72710275Ssam fflush(stderr); 72810275Ssam } 72910695Ssam 73010695Ssam /* 73110695Ssam * Special version of popen which avoids 73210695Ssam * call to shell. This insures noone may 73310695Ssam * create a pipe to a hidden program as a side 73410695Ssam * effect of a list or dir command. 73510695Ssam */ 73610695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 73710695Ssam #define RDR 0 73810695Ssam #define WTR 1 73910695Ssam static int popen_pid[5]; 74010695Ssam 74110695Ssam static char * 74210695Ssam nextarg(cpp) 74310695Ssam char *cpp; 74410695Ssam { 74510695Ssam register char *cp = cpp; 74610695Ssam 74710695Ssam if (cp == 0) 74810695Ssam return (cp); 74910695Ssam while (*cp && *cp != ' ' && *cp != '\t') 75010695Ssam cp++; 75110695Ssam if (*cp == ' ' || *cp == '\t') { 75210695Ssam *cp++ = '\0'; 75310695Ssam while (*cp == ' ' || *cp == '\t') 75410695Ssam cp++; 75510695Ssam } 75610695Ssam if (cp == cpp) 75710695Ssam return ((char *)0); 75810695Ssam return (cp); 75910695Ssam } 76010695Ssam 76110695Ssam FILE * 76210695Ssam popen(cmd, mode) 76310695Ssam char *cmd, *mode; 76410695Ssam { 76510695Ssam int p[2], ac; 76610695Ssam register myside, hisside, pid; 76711757Ssam char *av[512]; 76811757Ssam char **pop, **popargs = NULL; 76911757Ssam extern char **glob(); 77010695Ssam register char *cp; 77110695Ssam 77210695Ssam if (pipe(p) < 0) 77310695Ssam return (NULL); 77410695Ssam cp = cmd, ac = 0; 77510695Ssam do { 77610695Ssam av[ac++] = cp; 77710695Ssam cp = nextarg(cp); 77810695Ssam } while (cp && *cp); 77910695Ssam av[ac] = (char *)0; 78011757Ssam if (ac > 1) { 78111757Ssam popargs = glob(&av[1]); 78211757Ssam if (popargs == NULL) 78311757Ssam return (NULL); 78411757Ssam for (ac = 1, pop = popargs; *pop;) 78511757Ssam av[ac++] = *pop++; 78611757Ssam } 78711757Ssam av[ac] = (char *)0; 78810695Ssam myside = tst(p[WTR], p[RDR]); 78910695Ssam hisside = tst(p[RDR], p[WTR]); 79010695Ssam if ((pid = fork()) == 0) { 79110695Ssam /* myside and hisside reverse roles in child */ 79210695Ssam close(myside); 79310695Ssam dup2(hisside, tst(0, 1)); 79410695Ssam close(hisside); 79510695Ssam execv(av[0], av); 79610695Ssam _exit(1); 79710695Ssam } 79811757Ssam if (popargs != NULL) 79911757Ssam blkfree(popargs); 80010695Ssam if (pid == -1) 80110695Ssam return (NULL); 80210695Ssam popen_pid[myside] = pid; 80310695Ssam close(hisside); 80410695Ssam return (fdopen(myside, mode)); 80510695Ssam } 80610695Ssam 80710695Ssam pclose(ptr) 80810695Ssam FILE *ptr; 80910695Ssam { 81010695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 81110695Ssam int status; 81210695Ssam 81310695Ssam f = fileno(ptr); 81410695Ssam fclose(ptr); 81510695Ssam istat = signal(SIGINT, SIG_IGN); 81610695Ssam qstat = signal(SIGQUIT, SIG_IGN); 81710695Ssam hstat = signal(SIGHUP, SIG_IGN); 81810695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 81910695Ssam ; 82010695Ssam if (r == -1) 82110695Ssam status = -1; 82210695Ssam signal(SIGINT, istat); 82310695Ssam signal(SIGQUIT, qstat); 82410695Ssam signal(SIGHUP, hstat); 82510695Ssam return (status); 82610695Ssam } 82710695Ssam 82810695Ssam /* 82910695Ssam * Check user requesting login priviledges. 83010695Ssam * Disallow anyone mentioned in the file FTPUSERS 83110695Ssam * to allow people such as uucp to be avoided. 83210695Ssam */ 83310695Ssam checkuser(name) 83410695Ssam register char *name; 83510695Ssam { 83610695Ssam char line[BUFSIZ], *index(); 83710695Ssam FILE *fd; 83810695Ssam int found = 0; 83910695Ssam 84010695Ssam fd = fopen(FTPUSERS, "r"); 84110695Ssam if (fd == NULL) 84210695Ssam return (1); 84310695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 84410695Ssam register char *cp = index(line, '\n'); 84510695Ssam 84610695Ssam if (cp) 84710695Ssam *cp = '\0'; 84810695Ssam if (strcmp(line, name) == 0) { 84910695Ssam found++; 85010695Ssam break; 85110695Ssam } 85210695Ssam } 85310695Ssam fclose(fd); 85410695Ssam return (!found); 85510695Ssam } 856