110275Ssam #ifndef lint 2*16033Sralph static char sccsid[] = "@(#)ftpd.c 4.29 (Berkeley) 02/10/84"; 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> 1213247Ssam #include <sys/file.h> 1313595Ssam #include <sys/wait.h> 1410275Ssam 1510275Ssam #include <netinet/in.h> 1610275Ssam 1713034Ssam #include <arpa/ftp.h> 1813211Sroot #include <arpa/inet.h> 1913034Ssam 2010275Ssam #include <stdio.h> 2110275Ssam #include <signal.h> 2210275Ssam #include <pwd.h> 2310275Ssam #include <setjmp.h> 2410275Ssam #include <netdb.h> 2510423Ssam #include <errno.h> 2610275Ssam 2710695Ssam /* 2810695Ssam * File containing login names 2910695Ssam * NOT to be used on this machine. 3010695Ssam * Commonly used to disallow uucp. 3110695Ssam */ 3210695Ssam #define FTPUSERS "/etc/ftpusers" 3310695Ssam 3410275Ssam extern int errno; 3510275Ssam extern char *sys_errlist[]; 3610275Ssam extern char *crypt(); 3710275Ssam extern char version[]; 3810275Ssam extern char *home; /* pointer to home directory for glob */ 3910275Ssam extern FILE *popen(), *fopen(); 4010275Ssam extern int pclose(), fclose(); 4110275Ssam 4210275Ssam struct sockaddr_in ctrl_addr; 4310275Ssam struct sockaddr_in data_source; 4410275Ssam struct sockaddr_in data_dest; 4510275Ssam struct sockaddr_in his_addr; 4610275Ssam 4710275Ssam struct hostent *hp; 4810275Ssam 4910275Ssam int data; 5010275Ssam jmp_buf errcatch; 5110275Ssam int logged_in; 5210275Ssam struct passwd *pw; 5310275Ssam int debug; 5411653Ssam int timeout; 5511757Ssam int logging; 5610275Ssam int guest; 57*16033Sralph int wtmp; 5810275Ssam int type; 5910275Ssam int form; 6010275Ssam int stru; /* avoid C keyword */ 6110275Ssam int mode; 6210321Ssam int usedefault = 1; /* for data transfers */ 6310275Ssam char hostname[32]; 6413247Ssam char remotehost[32]; 6510321Ssam struct servent *sp; 6610275Ssam 6711653Ssam /* 6811653Ssam * Timeout intervals for retrying connections 6911653Ssam * to hosts that don't accept PORT cmds. This 7011653Ssam * is a kludge, but given the problems with TCP... 7111653Ssam */ 7211653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 7311653Ssam #define SWAITINT 5 /* interval between retries */ 7411653Ssam 7511653Ssam int swaitmax = SWAITMAX; 7611653Ssam int swaitint = SWAITINT; 7711653Ssam 7810275Ssam int lostconn(); 7910419Ssam int reapchild(); 8010275Ssam FILE *getdatasock(), *dataconn(); 8110275Ssam 8210275Ssam main(argc, argv) 8310275Ssam int argc; 8410275Ssam char *argv[]; 8510275Ssam { 8610275Ssam int ctrl, s, options = 0; 8710275Ssam char *cp; 8810275Ssam 8910275Ssam sp = getservbyname("ftp", "tcp"); 9010275Ssam if (sp == 0) { 9111220Ssam fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 9210275Ssam exit(1); 9310275Ssam } 9410275Ssam ctrl_addr.sin_port = sp->s_port; 9510275Ssam data_source.sin_port = htons(ntohs(sp->s_port) - 1); 9610275Ssam signal(SIGPIPE, lostconn); 9710275Ssam debug = 0; 9810275Ssam argc--, argv++; 9910275Ssam while (argc > 0 && *argv[0] == '-') { 10010275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 10110275Ssam 10211653Ssam case 'v': 10311653Ssam debug = 1; 10411653Ssam break; 10511653Ssam 10610275Ssam case 'd': 10710275Ssam debug = 1; 10810275Ssam options |= SO_DEBUG; 10910275Ssam break; 11010275Ssam 11111757Ssam case 'l': 11211757Ssam logging = 1; 11311757Ssam break; 11411757Ssam 11511653Ssam case 't': 11611653Ssam timeout = atoi(++cp); 11711653Ssam goto nextopt; 11811653Ssam break; 11911653Ssam 12010275Ssam default: 12111653Ssam fprintf(stderr, "Unknown flag -%c ignored.\n", *cp); 12210275Ssam break; 12310275Ssam } 12411653Ssam nextopt: 12510275Ssam argc--, argv++; 12610275Ssam } 12710275Ssam #ifndef DEBUG 12810275Ssam if (fork()) 12910275Ssam exit(0); 13010275Ssam for (s = 0; s < 10; s++) 13111653Ssam if (!logging || (s != 2)) 13211653Ssam (void) close(s); 13313247Ssam (void) open("/", O_RDONLY); 13410275Ssam (void) dup2(0, 1); 13511653Ssam if (!logging) 13611653Ssam (void) dup2(0, 2); 13713247Ssam { int tt = open("/dev/tty", O_RDWR); 13810275Ssam if (tt > 0) { 13910275Ssam ioctl(tt, TIOCNOTTY, 0); 14010275Ssam close(tt); 14110275Ssam } 14210275Ssam } 14310275Ssam #endif 14413247Ssam while ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 14510275Ssam perror("ftpd: socket"); 14610275Ssam sleep(5); 14710275Ssam } 14810419Ssam if (options & SO_DEBUG) 14910419Ssam if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 15010419Ssam perror("ftpd: setsockopt (SO_DEBUG)"); 15110419Ssam if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 15210419Ssam perror("ftpd: setsockopt (SO_KEEPALIVE)"); 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); 17113247Ssam dolog(&his_addr); 17210275Ssam close(s); 17310275Ssam dup2(ctrl, 0), close(ctrl), dup2(0, 1); 17410275Ssam /* do telnet option negotiation here */ 17510303Ssam /* 17610303Ssam * Set up default state 17710303Ssam */ 17810275Ssam logged_in = 0; 17910275Ssam data = -1; 18010303Ssam type = TYPE_A; 18110303Ssam form = FORM_N; 18210303Ssam stru = STRU_F; 18310303Ssam mode = MODE_S; 18413152Ssam (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr)); 18510275Ssam gethostname(hostname, sizeof (hostname)); 18610275Ssam reply(220, "%s FTP server (%s) ready.", 18710275Ssam hostname, version); 18810275Ssam for (;;) { 18910275Ssam setjmp(errcatch); 19010275Ssam yyparse(); 19110275Ssam } 19210275Ssam } 19310275Ssam close(ctrl); 19410275Ssam } 19510275Ssam } 19610275Ssam 19710419Ssam reapchild() 19810419Ssam { 19910419Ssam union wait status; 20010419Ssam 20110419Ssam while (wait3(&status, WNOHANG, 0) > 0) 20210419Ssam ; 20310419Ssam } 20410419Ssam 20510275Ssam lostconn() 20610275Ssam { 20710275Ssam 20814089Ssam if (debug) 20914089Ssam fprintf(stderr, "Lost connection.\n"); 21014089Ssam dologout(-1); 21110275Ssam } 21210275Ssam 21310275Ssam pass(passwd) 21410275Ssam char *passwd; 21510275Ssam { 21610303Ssam char *xpasswd, *savestr(); 21710303Ssam static struct passwd save; 21810275Ssam 21910275Ssam if (logged_in || pw == NULL) { 22010275Ssam reply(503, "Login with USER first."); 22110275Ssam return; 22210275Ssam } 22310275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 22410275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22515050Ssam if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 22610275Ssam reply(530, "Login incorrect."); 22710275Ssam pw = NULL; 22810275Ssam return; 22910275Ssam } 23010275Ssam } 23110303Ssam setegid(pw->pw_gid); 23210275Ssam initgroups(pw->pw_name, pw->pw_gid); 23310275Ssam if (chdir(pw->pw_dir)) { 23410275Ssam reply(550, "User %s: can't change directory to $s.", 23510275Ssam pw->pw_name, pw->pw_dir); 23610303Ssam goto bad; 23710275Ssam } 238*16033Sralph 239*16033Sralph if (guest) /* grab wtmp before chroot */ 240*16033Sralph wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 241*16033Sralph 24210303Ssam if (guest && chroot(pw->pw_dir) < 0) { 24310275Ssam reply(550, "Can't set guest privileges."); 24410303Ssam goto bad; 24510275Ssam } 24610275Ssam if (!guest) 24710275Ssam reply(230, "User %s logged in.", pw->pw_name); 24810275Ssam else 24910275Ssam reply(230, "Guest login ok, access restrictions apply."); 25010275Ssam logged_in = 1; 25113247Ssam dologin(pw); 25210303Ssam seteuid(pw->pw_uid); 25310303Ssam /* 25410303Ssam * Save everything so globbing doesn't 25510303Ssam * clobber the fields. 25610303Ssam */ 25710303Ssam save = *pw; 25810303Ssam save.pw_name = savestr(pw->pw_name); 25910303Ssam save.pw_passwd = savestr(pw->pw_passwd); 26010303Ssam save.pw_comment = savestr(pw->pw_comment); 26110303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 26210303Ssam save.pw_dir = savestr(pw->pw_dir); 26310303Ssam save.pw_shell = savestr(pw->pw_shell); 26410303Ssam pw = &save; 26510303Ssam home = pw->pw_dir; /* home dir for globbing */ 26610303Ssam return; 26710303Ssam bad: 26810303Ssam seteuid(0); 26910303Ssam pw = NULL; 27010275Ssam } 27110275Ssam 27210303Ssam char * 27310303Ssam savestr(s) 27410303Ssam char *s; 27510303Ssam { 27610303Ssam char *malloc(); 27710303Ssam char *new = malloc(strlen(s) + 1); 27810303Ssam 27910303Ssam if (new != NULL) 28010303Ssam strcpy(new, s); 28111347Ssam return (new); 28210303Ssam } 28310303Ssam 28410275Ssam retrieve(cmd, name) 28510275Ssam char *cmd, *name; 28610275Ssam { 28710275Ssam FILE *fin, *dout; 28810275Ssam struct stat st; 28910275Ssam int (*closefunc)(); 29010275Ssam 29110275Ssam if (cmd == 0) { 29210317Ssam #ifdef notdef 29310317Ssam /* no remote command execution -- it's a security hole */ 29411653Ssam if (*name == '|') 29510275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 29610275Ssam else 29710317Ssam #endif 29810275Ssam fin = fopen(name, "r"), closefunc = fclose; 29910275Ssam } else { 30010275Ssam char line[BUFSIZ]; 30110275Ssam 30210422Ssam sprintf(line, cmd, name), name = line; 30310275Ssam fin = popen(line, "r"), closefunc = pclose; 30410275Ssam } 30510275Ssam if (fin == NULL) { 30613152Ssam if (errno != 0) 30713152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30810275Ssam return; 30910275Ssam } 31010275Ssam st.st_size = 0; 31110275Ssam if (cmd == 0 && 31210275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 31310275Ssam reply(550, "%s: not a plain file.", name); 31410275Ssam goto done; 31510275Ssam } 31610275Ssam dout = dataconn(name, st.st_size, "w"); 31710275Ssam if (dout == NULL) 31810275Ssam goto done; 31910303Ssam if (send_data(fin, dout) || ferror(dout)) 32010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 32110275Ssam else 32210275Ssam reply(226, "Transfer complete."); 32310303Ssam fclose(dout), data = -1; 32410275Ssam done: 32510275Ssam (*closefunc)(fin); 32610275Ssam } 32710275Ssam 32810275Ssam store(name, mode) 32910275Ssam char *name, *mode; 33010275Ssam { 33110275Ssam FILE *fout, *din; 33210303Ssam int (*closefunc)(), dochown = 0; 33310275Ssam 33410317Ssam #ifdef notdef 33510317Ssam /* no remote command execution -- it's a security hole */ 33611653Ssam if (name[0] == '|') 33710275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33810317Ssam else 33910317Ssam #endif 34010317Ssam { 34110303Ssam struct stat st; 34210303Ssam 34310303Ssam if (stat(name, &st) < 0) 34410303Ssam dochown++; 34510275Ssam fout = fopen(name, mode), closefunc = fclose; 34610303Ssam } 34710275Ssam if (fout == NULL) { 34810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34910275Ssam return; 35010275Ssam } 35111653Ssam din = dataconn(name, (off_t)-1, "r"); 35210275Ssam if (din == NULL) 35310275Ssam goto done; 35410303Ssam if (receive_data(din, fout) || ferror(fout)) 35510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 35610275Ssam else 35710275Ssam reply(226, "Transfer complete."); 35810275Ssam fclose(din), data = -1; 35910275Ssam done: 36010303Ssam if (dochown) 36110303Ssam (void) chown(name, pw->pw_uid, -1); 36210275Ssam (*closefunc)(fout); 36310275Ssam } 36410275Ssam 36510275Ssam FILE * 36610275Ssam getdatasock(mode) 36710275Ssam char *mode; 36810275Ssam { 36913247Ssam int s; 37010275Ssam 37110275Ssam if (data >= 0) 37210275Ssam return (fdopen(data, mode)); 37313247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 37410602Ssam if (s < 0) 37510275Ssam return (NULL); 37610275Ssam seteuid(0); 37710602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 37810602Ssam goto bad; 37913152Ssam /* anchor socket to avoid multi-homing problems */ 38013152Ssam data_source.sin_family = AF_INET; 38113152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 38210602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 38310602Ssam goto bad; 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.", 41813247Ssam inet_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.", 42413247Ssam name, inet_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."); 54213247Ssam dologout(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 dolog(sin) 70110275Ssam struct sockaddr_in *sin; 70210275Ssam { 70310275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 70410275Ssam sizeof (struct in_addr), AF_INET); 70510275Ssam time_t t; 70610275Ssam 70713247Ssam if (hp) { 70813247Ssam strncpy(remotehost, hp->h_name, sizeof (remotehost)); 70913247Ssam endhostent(); 71013247Ssam } else 71113247Ssam strncpy(remotehost, inet_ntoa(sin->sin_addr), 71213247Ssam sizeof (remotehost)); 71313247Ssam if (!logging) 71413247Ssam return; 71510275Ssam t = time(0); 71611757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 71710275Ssam fflush(stderr); 71810275Ssam } 71910695Ssam 72013247Ssam #include <utmp.h> 72113247Ssam 72213247Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 72313247Ssam struct utmp utmp; 72413247Ssam 72510695Ssam /* 72613247Ssam * Record login in wtmp file. 72713247Ssam */ 72813247Ssam dologin(pw) 72913247Ssam struct passwd *pw; 73013247Ssam { 73113247Ssam char line[32]; 73213247Ssam 733*16033Sralph if (guest && (wtmp >= 0)) 734*16033Sralph lseek(wtmp, 0, L_XTND); 735*16033Sralph else 736*16033Sralph wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 73713247Ssam if (wtmp >= 0) { 73813247Ssam /* hack, but must be unique and no tty line */ 73913247Ssam sprintf(line, "ftp%d", getpid()); 74013247Ssam SCPYN(utmp.ut_line, line); 74113247Ssam SCPYN(utmp.ut_name, pw->pw_name); 74213247Ssam SCPYN(utmp.ut_host, remotehost); 74313247Ssam utmp.ut_time = time(0); 74413247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 745*16033Sralph if (!guest) 746*16033Sralph (void) close(wtmp); 74713247Ssam } 74813247Ssam } 74913247Ssam 75013247Ssam /* 75113247Ssam * Record logout in wtmp file 75213247Ssam * and exit with supplied status. 75313247Ssam */ 75413247Ssam dologout(status) 75513247Ssam int status; 75613247Ssam { 75713247Ssam if (!logged_in) 75814811Skarels _exit(status); 75913247Ssam seteuid(0); 760*16033Sralph if (guest && (wtmp >= 0)) 761*16033Sralph lseek(wtmp, 0, L_XTND); 762*16033Sralph else 763*16033Sralph wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 76413247Ssam if (wtmp >= 0) { 76513247Ssam SCPYN(utmp.ut_name, ""); 76613247Ssam SCPYN(utmp.ut_host, ""); 76713247Ssam utmp.ut_time = time(0); 76813247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 76913247Ssam (void) close(wtmp); 77013247Ssam } 77114436Ssam /* beware of flushing buffers after a SIGPIPE */ 77214436Ssam _exit(status); 77313247Ssam } 77413247Ssam 77513247Ssam /* 77610695Ssam * Special version of popen which avoids 77710695Ssam * call to shell. This insures noone may 77810695Ssam * create a pipe to a hidden program as a side 77910695Ssam * effect of a list or dir command. 78010695Ssam */ 78110695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 78210695Ssam #define RDR 0 78310695Ssam #define WTR 1 78410695Ssam static int popen_pid[5]; 78510695Ssam 78610695Ssam static char * 78710695Ssam nextarg(cpp) 78810695Ssam char *cpp; 78910695Ssam { 79010695Ssam register char *cp = cpp; 79110695Ssam 79210695Ssam if (cp == 0) 79310695Ssam return (cp); 79410695Ssam while (*cp && *cp != ' ' && *cp != '\t') 79510695Ssam cp++; 79610695Ssam if (*cp == ' ' || *cp == '\t') { 79710695Ssam *cp++ = '\0'; 79810695Ssam while (*cp == ' ' || *cp == '\t') 79910695Ssam cp++; 80010695Ssam } 80110695Ssam if (cp == cpp) 80210695Ssam return ((char *)0); 80310695Ssam return (cp); 80410695Ssam } 80510695Ssam 80610695Ssam FILE * 80710695Ssam popen(cmd, mode) 80810695Ssam char *cmd, *mode; 80910695Ssam { 81013211Sroot int p[2], ac, gac; 81110695Ssam register myside, hisside, pid; 81213211Sroot char *av[20], *gav[512]; 81310695Ssam register char *cp; 81410695Ssam 81510695Ssam if (pipe(p) < 0) 81610695Ssam return (NULL); 81710695Ssam cp = cmd, ac = 0; 81813211Sroot /* break up string into pieces */ 81910695Ssam do { 82010695Ssam av[ac++] = cp; 82110695Ssam cp = nextarg(cp); 82213211Sroot } while (cp && *cp && ac < 20); 82310695Ssam av[ac] = (char *)0; 82413211Sroot gav[0] = av[0]; 82513211Sroot /* glob each piece */ 82613211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 82713211Sroot char **pop; 82813211Sroot extern char **glob(); 82913211Sroot 83013211Sroot pop = glob(av[ac]); 83113211Sroot if (pop) { 83213211Sroot av[ac] = (char *)pop; /* save to free later */ 83313211Sroot while (*pop && gac < 512) 83413211Sroot gav[gac++] = *pop++; 83513211Sroot } 83611757Ssam } 83713211Sroot gav[gac] = (char *)0; 83810695Ssam myside = tst(p[WTR], p[RDR]); 83910695Ssam hisside = tst(p[RDR], p[WTR]); 84010695Ssam if ((pid = fork()) == 0) { 84110695Ssam /* myside and hisside reverse roles in child */ 84210695Ssam close(myside); 84310695Ssam dup2(hisside, tst(0, 1)); 84410695Ssam close(hisside); 84513211Sroot execv(gav[0], gav); 84610695Ssam _exit(1); 84710695Ssam } 84813211Sroot for (ac = 1; av[ac] != NULL; ac++) 84913211Sroot blkfree((char **)av[ac]); 85010695Ssam if (pid == -1) 85110695Ssam return (NULL); 85210695Ssam popen_pid[myside] = pid; 85310695Ssam close(hisside); 85410695Ssam return (fdopen(myside, mode)); 85510695Ssam } 85610695Ssam 85710695Ssam pclose(ptr) 85810695Ssam FILE *ptr; 85910695Ssam { 86010695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 86110695Ssam int status; 86210695Ssam 86310695Ssam f = fileno(ptr); 86410695Ssam fclose(ptr); 86510695Ssam istat = signal(SIGINT, SIG_IGN); 86610695Ssam qstat = signal(SIGQUIT, SIG_IGN); 86710695Ssam hstat = signal(SIGHUP, SIG_IGN); 86810695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 86910695Ssam ; 87010695Ssam if (r == -1) 87110695Ssam status = -1; 87210695Ssam signal(SIGINT, istat); 87310695Ssam signal(SIGQUIT, qstat); 87410695Ssam signal(SIGHUP, hstat); 87510695Ssam return (status); 87610695Ssam } 87710695Ssam 87810695Ssam /* 87910695Ssam * Check user requesting login priviledges. 88010695Ssam * Disallow anyone mentioned in the file FTPUSERS 88110695Ssam * to allow people such as uucp to be avoided. 88210695Ssam */ 88310695Ssam checkuser(name) 88410695Ssam register char *name; 88510695Ssam { 88610695Ssam char line[BUFSIZ], *index(); 88710695Ssam FILE *fd; 88810695Ssam int found = 0; 88910695Ssam 89010695Ssam fd = fopen(FTPUSERS, "r"); 89110695Ssam if (fd == NULL) 89210695Ssam return (1); 89310695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 89410695Ssam register char *cp = index(line, '\n'); 89510695Ssam 89610695Ssam if (cp) 89710695Ssam *cp = '\0'; 89810695Ssam if (strcmp(line, name) == 0) { 89910695Ssam found++; 90010695Ssam break; 90110695Ssam } 90210695Ssam } 90310695Ssam fclose(fd); 90410695Ssam return (!found); 90510695Ssam } 906