110275Ssam #ifndef lint 2*16339Skarels static char sccsid[] = "@(#)ftpd.c 4.30 (Berkeley) 04/11/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; 5716033Sralph 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]; 6510275Ssam 6611653Ssam /* 6711653Ssam * Timeout intervals for retrying connections 6811653Ssam * to hosts that don't accept PORT cmds. This 6911653Ssam * is a kludge, but given the problems with TCP... 7011653Ssam */ 7111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 7211653Ssam #define SWAITINT 5 /* interval between retries */ 7311653Ssam 7411653Ssam int swaitmax = SWAITMAX; 7511653Ssam int swaitint = SWAITINT; 7611653Ssam 7710275Ssam int lostconn(); 7810419Ssam int reapchild(); 7910275Ssam FILE *getdatasock(), *dataconn(); 8010275Ssam 8110275Ssam main(argc, argv) 8210275Ssam int argc; 8310275Ssam char *argv[]; 8410275Ssam { 85*16339Skarels int options = 0, addrlen; 8610275Ssam char *cp; 8710275Ssam 88*16339Skarels addrlen = sizeof (his_addr); 89*16339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 90*16339Skarels fprintf(stderr, "%s: ", argv[0]); 91*16339Skarels perror("getpeername"); 9210275Ssam exit(1); 9310275Ssam } 94*16339Skarels addrlen = sizeof (ctrl_addr); 95*16339Skarels if (getsockname(0, &ctrl_addr, &addrlen) < 0) { 96*16339Skarels fprintf(stderr, "%s: ", argv[0]); 97*16339Skarels perror("getsockname"); 98*16339Skarels exit(1); 99*16339Skarels } 100*16339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 10110275Ssam debug = 0; 10210275Ssam argc--, argv++; 10310275Ssam while (argc > 0 && *argv[0] == '-') { 10410275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 10510275Ssam 10611653Ssam case 'v': 10711653Ssam debug = 1; 10811653Ssam break; 10911653Ssam 11010275Ssam case 'd': 11110275Ssam debug = 1; 11210275Ssam options |= SO_DEBUG; 11310275Ssam break; 11410275Ssam 11511757Ssam case 'l': 11611757Ssam logging = 1; 11711757Ssam break; 11811757Ssam 11911653Ssam case 't': 12011653Ssam timeout = atoi(++cp); 12111653Ssam goto nextopt; 12211653Ssam break; 12311653Ssam 12410275Ssam default: 125*16339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 126*16339Skarels *cp); 12710275Ssam break; 12810275Ssam } 12911653Ssam nextopt: 13010275Ssam argc--, argv++; 13110275Ssam } 132*16339Skarels signal(SIGPIPE, lostconn); 133*16339Skarels signal(SIGCHLD, SIG_IGN); 134*16339Skarels /* do telnet option negotiation here */ 135*16339Skarels /* 136*16339Skarels * Set up default state 137*16339Skarels */ 138*16339Skarels logged_in = 0; 139*16339Skarels data = -1; 140*16339Skarels type = TYPE_A; 141*16339Skarels form = FORM_N; 142*16339Skarels stru = STRU_F; 143*16339Skarels mode = MODE_S; 144*16339Skarels gethostname(hostname, sizeof (hostname)); 145*16339Skarels reply(220, "%s FTP server (%s) ready.", 146*16339Skarels hostname, version); 14710275Ssam for (;;) { 148*16339Skarels setjmp(errcatch); 149*16339Skarels yyparse(); 15010275Ssam } 15110275Ssam } 15210275Ssam 15310419Ssam reapchild() 15410419Ssam { 15510419Ssam union wait status; 15610419Ssam 15710419Ssam while (wait3(&status, WNOHANG, 0) > 0) 15810419Ssam ; 15910419Ssam } 16010419Ssam 16110275Ssam lostconn() 16210275Ssam { 16310275Ssam 16414089Ssam if (debug) 16514089Ssam fprintf(stderr, "Lost connection.\n"); 16614089Ssam dologout(-1); 16710275Ssam } 16810275Ssam 16910275Ssam pass(passwd) 17010275Ssam char *passwd; 17110275Ssam { 17210303Ssam char *xpasswd, *savestr(); 17310303Ssam static struct passwd save; 17410275Ssam 17510275Ssam if (logged_in || pw == NULL) { 17610275Ssam reply(503, "Login with USER first."); 17710275Ssam return; 17810275Ssam } 17910275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 18010275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 181*16339Skarels if (strcmp(xpasswd, pw->pw_passwd) != 0) { 18210275Ssam reply(530, "Login incorrect."); 18310275Ssam pw = NULL; 18410275Ssam return; 18510275Ssam } 18610275Ssam } 18710303Ssam setegid(pw->pw_gid); 18810275Ssam initgroups(pw->pw_name, pw->pw_gid); 18910275Ssam if (chdir(pw->pw_dir)) { 19010275Ssam reply(550, "User %s: can't change directory to $s.", 19110275Ssam pw->pw_name, pw->pw_dir); 19210303Ssam goto bad; 19310275Ssam } 19416033Sralph 19516033Sralph if (guest) /* grab wtmp before chroot */ 19616033Sralph wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 19710303Ssam if (guest && chroot(pw->pw_dir) < 0) { 19810275Ssam reply(550, "Can't set guest privileges."); 19910303Ssam goto bad; 20010275Ssam } 20110275Ssam if (!guest) 20210275Ssam reply(230, "User %s logged in.", pw->pw_name); 20310275Ssam else 20410275Ssam reply(230, "Guest login ok, access restrictions apply."); 20510275Ssam logged_in = 1; 20613247Ssam dologin(pw); 20710303Ssam seteuid(pw->pw_uid); 20810303Ssam /* 20910303Ssam * Save everything so globbing doesn't 21010303Ssam * clobber the fields. 21110303Ssam */ 21210303Ssam save = *pw; 21310303Ssam save.pw_name = savestr(pw->pw_name); 21410303Ssam save.pw_passwd = savestr(pw->pw_passwd); 21510303Ssam save.pw_comment = savestr(pw->pw_comment); 21610303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 21710303Ssam save.pw_dir = savestr(pw->pw_dir); 21810303Ssam save.pw_shell = savestr(pw->pw_shell); 21910303Ssam pw = &save; 22010303Ssam home = pw->pw_dir; /* home dir for globbing */ 22110303Ssam return; 22210303Ssam bad: 22310303Ssam seteuid(0); 22410303Ssam pw = NULL; 22510275Ssam } 22610275Ssam 22710303Ssam char * 22810303Ssam savestr(s) 22910303Ssam char *s; 23010303Ssam { 23110303Ssam char *malloc(); 23210303Ssam char *new = malloc(strlen(s) + 1); 23310303Ssam 23410303Ssam if (new != NULL) 23510303Ssam strcpy(new, s); 23611347Ssam return (new); 23710303Ssam } 23810303Ssam 23910275Ssam retrieve(cmd, name) 24010275Ssam char *cmd, *name; 24110275Ssam { 24210275Ssam FILE *fin, *dout; 24310275Ssam struct stat st; 24410275Ssam int (*closefunc)(); 24510275Ssam 24610275Ssam if (cmd == 0) { 24710317Ssam #ifdef notdef 24810317Ssam /* no remote command execution -- it's a security hole */ 24911653Ssam if (*name == '|') 25010275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 25110275Ssam else 25210317Ssam #endif 25310275Ssam fin = fopen(name, "r"), closefunc = fclose; 25410275Ssam } else { 25510275Ssam char line[BUFSIZ]; 25610275Ssam 25710422Ssam sprintf(line, cmd, name), name = line; 25810275Ssam fin = popen(line, "r"), closefunc = pclose; 25910275Ssam } 26010275Ssam if (fin == NULL) { 26113152Ssam if (errno != 0) 26213152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 26310275Ssam return; 26410275Ssam } 26510275Ssam st.st_size = 0; 26610275Ssam if (cmd == 0 && 26710275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 26810275Ssam reply(550, "%s: not a plain file.", name); 26910275Ssam goto done; 27010275Ssam } 27110275Ssam dout = dataconn(name, st.st_size, "w"); 27210275Ssam if (dout == NULL) 27310275Ssam goto done; 27410303Ssam if (send_data(fin, dout) || ferror(dout)) 27510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 27610275Ssam else 27710275Ssam reply(226, "Transfer complete."); 27810303Ssam fclose(dout), data = -1; 27910275Ssam done: 28010275Ssam (*closefunc)(fin); 28110275Ssam } 28210275Ssam 28310275Ssam store(name, mode) 28410275Ssam char *name, *mode; 28510275Ssam { 28610275Ssam FILE *fout, *din; 28710303Ssam int (*closefunc)(), dochown = 0; 28810275Ssam 28910317Ssam #ifdef notdef 29010317Ssam /* no remote command execution -- it's a security hole */ 29111653Ssam if (name[0] == '|') 29210275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 29310317Ssam else 29410317Ssam #endif 29510317Ssam { 29610303Ssam struct stat st; 29710303Ssam 29810303Ssam if (stat(name, &st) < 0) 29910303Ssam dochown++; 30010275Ssam fout = fopen(name, mode), closefunc = fclose; 30110303Ssam } 30210275Ssam if (fout == NULL) { 30310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30410275Ssam return; 30510275Ssam } 30611653Ssam din = dataconn(name, (off_t)-1, "r"); 30710275Ssam if (din == NULL) 30810275Ssam goto done; 30910303Ssam if (receive_data(din, fout) || ferror(fout)) 31010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31110275Ssam else 31210275Ssam reply(226, "Transfer complete."); 31310275Ssam fclose(din), data = -1; 31410275Ssam done: 31510303Ssam if (dochown) 31610303Ssam (void) chown(name, pw->pw_uid, -1); 31710275Ssam (*closefunc)(fout); 31810275Ssam } 31910275Ssam 32010275Ssam FILE * 32110275Ssam getdatasock(mode) 32210275Ssam char *mode; 32310275Ssam { 32413247Ssam int s; 32510275Ssam 32610275Ssam if (data >= 0) 32710275Ssam return (fdopen(data, mode)); 32813247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 32910602Ssam if (s < 0) 33010275Ssam return (NULL); 33110275Ssam seteuid(0); 33210602Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 33310602Ssam goto bad; 33413152Ssam /* anchor socket to avoid multi-homing problems */ 33513152Ssam data_source.sin_family = AF_INET; 33613152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 33710602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 33810602Ssam goto bad; 33910311Ssam seteuid(pw->pw_uid); 34010275Ssam return (fdopen(s, mode)); 34110602Ssam bad: 34210602Ssam seteuid(pw->pw_uid); 34310602Ssam close(s); 34410602Ssam return (NULL); 34510275Ssam } 34610275Ssam 34710275Ssam FILE * 34810275Ssam dataconn(name, size, mode) 34910275Ssam char *name; 35011653Ssam off_t size; 35110275Ssam char *mode; 35210275Ssam { 35310275Ssam char sizebuf[32]; 35410275Ssam FILE *file; 35511653Ssam int retry = 0; 35610275Ssam 35710275Ssam if (size >= 0) 35811653Ssam sprintf (sizebuf, " (%ld bytes)", size); 35910275Ssam else 36010275Ssam (void) strcpy(sizebuf, ""); 36110275Ssam if (data >= 0) { 36210275Ssam reply(125, "Using existing data connection for %s%s.", 36310275Ssam name, sizebuf); 36410321Ssam usedefault = 1; 36510275Ssam return (fdopen(data, mode)); 36610275Ssam } 36710566Ssam if (usedefault) 36810422Ssam data_dest = his_addr; 36910422Ssam usedefault = 1; 37010275Ssam file = getdatasock(mode); 37110275Ssam if (file == NULL) { 37210275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 37313247Ssam inet_ntoa(data_source.sin_addr), 37410275Ssam ntohs(data_source.sin_port), 37510275Ssam sys_errlist[errno]); 37610275Ssam return (NULL); 37710275Ssam } 37810602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 37913247Ssam name, inet_ntoa(data_dest.sin_addr.s_addr), 38010602Ssam ntohs(data_dest.sin_port), sizebuf); 38110275Ssam data = fileno(file); 38211653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 38311653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 38411653Ssam sleep(swaitint); 38511653Ssam retry += swaitint; 38611653Ssam continue; 38711653Ssam } 38810275Ssam reply(425, "Can't build data connection: %s.", 38910275Ssam sys_errlist[errno]); 39010275Ssam (void) fclose(file); 39110275Ssam data = -1; 39210275Ssam return (NULL); 39310275Ssam } 39410275Ssam return (file); 39510275Ssam } 39610275Ssam 39710275Ssam /* 39810275Ssam * Tranfer the contents of "instr" to 39910275Ssam * "outstr" peer using the appropriate 40010275Ssam * encapulation of the date subject 40110275Ssam * to Mode, Structure, and Type. 40210275Ssam * 40310275Ssam * NB: Form isn't handled. 40410275Ssam */ 40510275Ssam send_data(instr, outstr) 40610275Ssam FILE *instr, *outstr; 40710275Ssam { 40810275Ssam register int c; 40910275Ssam int netfd, filefd, cnt; 41010275Ssam char buf[BUFSIZ]; 41110275Ssam 41210275Ssam switch (type) { 41310275Ssam 41410275Ssam case TYPE_A: 41510275Ssam while ((c = getc(instr)) != EOF) { 41611220Ssam if (c == '\n') { 41711220Ssam if (ferror (outstr)) 41811220Ssam return (1); 41910275Ssam putc('\r', outstr); 42011220Ssam } 42111220Ssam putc(c, outstr); 42211220Ssam if (c == '\r') 42311220Ssam putc ('\0', outstr); 42410275Ssam } 42511220Ssam if (ferror (instr) || ferror (outstr)) 42611220Ssam return (1); 42710275Ssam return (0); 42810275Ssam 42910275Ssam case TYPE_I: 43010275Ssam case TYPE_L: 43110275Ssam netfd = fileno(outstr); 43210275Ssam filefd = fileno(instr); 43310275Ssam 43410303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 43510275Ssam if (write(netfd, buf, cnt) < 0) 43610275Ssam return (1); 43710275Ssam return (cnt < 0); 43810275Ssam } 43910275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 44010275Ssam return (1); 44110275Ssam } 44210275Ssam 44310275Ssam /* 44410275Ssam * Transfer data from peer to 44510275Ssam * "outstr" using the appropriate 44610275Ssam * encapulation of the data subject 44710275Ssam * to Mode, Structure, and Type. 44810275Ssam * 44910275Ssam * N.B.: Form isn't handled. 45010275Ssam */ 45110275Ssam receive_data(instr, outstr) 45210275Ssam FILE *instr, *outstr; 45310275Ssam { 45410275Ssam register int c; 45511220Ssam int cnt; 45610275Ssam char buf[BUFSIZ]; 45710275Ssam 45810275Ssam 45910275Ssam switch (type) { 46010275Ssam 46110275Ssam case TYPE_I: 46210275Ssam case TYPE_L: 46310616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 46410616Ssam if (write(fileno(outstr), buf, cnt) < 0) 46510275Ssam return (1); 46610275Ssam return (cnt < 0); 46710275Ssam 46810275Ssam case TYPE_E: 46910275Ssam reply(504, "TYPE E not implemented."); 47010275Ssam return (1); 47110275Ssam 47210275Ssam case TYPE_A: 47310275Ssam while ((c = getc(instr)) != EOF) { 47410275Ssam if (c == '\r') { 47511220Ssam if (ferror (outstr)) 47611220Ssam return (1); 47711220Ssam if ((c = getc(instr)) != '\n') 47811220Ssam putc ('\r', outstr); 47911220Ssam if (c == '\0') 48011220Ssam continue; 48110275Ssam } 48211220Ssam putc (c, outstr); 48310275Ssam } 48411220Ssam if (ferror (instr) || ferror (outstr)) 48511220Ssam return (1); 48610275Ssam return (0); 48710275Ssam } 48810275Ssam fatal("Unknown type in receive_data."); 48910275Ssam /*NOTREACHED*/ 49010275Ssam } 49110275Ssam 49210275Ssam fatal(s) 49310275Ssam char *s; 49410275Ssam { 49510275Ssam reply(451, "Error in server: %s\n", s); 49610275Ssam reply(221, "Closing connection due to server error."); 49713247Ssam dologout(0); 49810275Ssam } 49910275Ssam 50010275Ssam reply(n, s, args) 50110275Ssam int n; 50210275Ssam char *s; 50310275Ssam { 50410275Ssam 50510275Ssam printf("%d ", n); 50610275Ssam _doprnt(s, &args, stdout); 50710275Ssam printf("\r\n"); 50810275Ssam fflush(stdout); 50910275Ssam if (debug) { 51010275Ssam fprintf(stderr, "<--- %d ", n); 51110275Ssam _doprnt(s, &args, stderr); 51210275Ssam fprintf(stderr, "\n"); 51310275Ssam fflush(stderr); 51410275Ssam } 51510275Ssam } 51610275Ssam 51710275Ssam lreply(n, s, args) 51810275Ssam int n; 51910275Ssam char *s; 52010275Ssam { 52110275Ssam printf("%d-", n); 52210275Ssam _doprnt(s, &args, stdout); 52310275Ssam printf("\r\n"); 52410275Ssam fflush(stdout); 52510275Ssam if (debug) { 52610275Ssam fprintf(stderr, "<--- %d-", n); 52710275Ssam _doprnt(s, &args, stderr); 52810275Ssam fprintf(stderr, "\n"); 52910275Ssam } 53010275Ssam } 53110275Ssam 53210275Ssam replystr(s) 53310275Ssam char *s; 53410275Ssam { 53510275Ssam printf("%s\r\n", s); 53610275Ssam fflush(stdout); 53710275Ssam if (debug) 53810275Ssam fprintf(stderr, "<--- %s\n", s); 53910275Ssam } 54010275Ssam 54110275Ssam ack(s) 54210275Ssam char *s; 54310275Ssam { 54410275Ssam reply(200, "%s command okay.", s); 54510275Ssam } 54610275Ssam 54710275Ssam nack(s) 54810275Ssam char *s; 54910275Ssam { 55010275Ssam reply(502, "%s command not implemented.", s); 55110275Ssam } 55210275Ssam 55310275Ssam yyerror() 55410275Ssam { 55510275Ssam reply(500, "Command not understood."); 55610275Ssam } 55710275Ssam 55810275Ssam delete(name) 55910275Ssam char *name; 56010275Ssam { 56110275Ssam struct stat st; 56210275Ssam 56310275Ssam if (stat(name, &st) < 0) { 56410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 56510275Ssam return; 56610275Ssam } 56710275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 56810275Ssam if (rmdir(name) < 0) { 56910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57010275Ssam return; 57110275Ssam } 57210275Ssam goto done; 57310275Ssam } 57410275Ssam if (unlink(name) < 0) { 57510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 57610275Ssam return; 57710275Ssam } 57810275Ssam done: 57910275Ssam ack("DELE"); 58010275Ssam } 58110275Ssam 58210275Ssam cwd(path) 58310275Ssam char *path; 58410275Ssam { 58510275Ssam 58610275Ssam if (chdir(path) < 0) { 58710275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 58810275Ssam return; 58910275Ssam } 59010275Ssam ack("CWD"); 59110275Ssam } 59210275Ssam 59310303Ssam makedir(name) 59410275Ssam char *name; 59510275Ssam { 59610303Ssam struct stat st; 59710303Ssam int dochown = stat(name, &st) < 0; 59810275Ssam 59910275Ssam if (mkdir(name, 0777) < 0) { 60010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 60110275Ssam return; 60210275Ssam } 60310303Ssam if (dochown) 60410303Ssam (void) chown(name, pw->pw_uid, -1); 60510275Ssam ack("MKDIR"); 60610275Ssam } 60710275Ssam 60810303Ssam removedir(name) 60910275Ssam char *name; 61010275Ssam { 61110275Ssam 61210275Ssam if (rmdir(name) < 0) { 61310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61410275Ssam return; 61510275Ssam } 61610275Ssam ack("RMDIR"); 61710275Ssam } 61810275Ssam 61910303Ssam pwd() 62010275Ssam { 62110303Ssam char path[MAXPATHLEN + 1]; 62210275Ssam 62310275Ssam if (getwd(path) == NULL) { 62410275Ssam reply(451, "%s.", path); 62510275Ssam return; 62610275Ssam } 62710275Ssam reply(251, "\"%s\" is current directory.", path); 62810275Ssam } 62910275Ssam 63010275Ssam char * 63110275Ssam renamefrom(name) 63210275Ssam char *name; 63310275Ssam { 63410275Ssam struct stat st; 63510275Ssam 63610275Ssam if (stat(name, &st) < 0) { 63710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 63810275Ssam return ((char *)0); 63910275Ssam } 64010303Ssam reply(350, "File exists, ready for destination name"); 64110275Ssam return (name); 64210275Ssam } 64310275Ssam 64410275Ssam renamecmd(from, to) 64510275Ssam char *from, *to; 64610275Ssam { 64710275Ssam 64810275Ssam if (rename(from, to) < 0) { 64910275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 65010275Ssam return; 65110275Ssam } 65210275Ssam ack("RNTO"); 65310275Ssam } 65410275Ssam 65510275Ssam dolog(sin) 65610275Ssam struct sockaddr_in *sin; 65710275Ssam { 65810275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 65910275Ssam sizeof (struct in_addr), AF_INET); 66010275Ssam time_t t; 66110275Ssam 66213247Ssam if (hp) { 66313247Ssam strncpy(remotehost, hp->h_name, sizeof (remotehost)); 66413247Ssam endhostent(); 66513247Ssam } else 66613247Ssam strncpy(remotehost, inet_ntoa(sin->sin_addr), 66713247Ssam sizeof (remotehost)); 66813247Ssam if (!logging) 66913247Ssam return; 67010275Ssam t = time(0); 67111757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 67210275Ssam fflush(stderr); 67310275Ssam } 67410695Ssam 67513247Ssam #include <utmp.h> 67613247Ssam 67713247Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 67813247Ssam struct utmp utmp; 67913247Ssam 68010695Ssam /* 68113247Ssam * Record login in wtmp file. 68213247Ssam */ 68313247Ssam dologin(pw) 68413247Ssam struct passwd *pw; 68513247Ssam { 68613247Ssam char line[32]; 68713247Ssam 68816033Sralph if (guest && (wtmp >= 0)) 68916033Sralph lseek(wtmp, 0, L_XTND); 69016033Sralph else 69116033Sralph wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 69213247Ssam if (wtmp >= 0) { 69313247Ssam /* hack, but must be unique and no tty line */ 69413247Ssam sprintf(line, "ftp%d", getpid()); 69513247Ssam SCPYN(utmp.ut_line, line); 69613247Ssam SCPYN(utmp.ut_name, pw->pw_name); 69713247Ssam SCPYN(utmp.ut_host, remotehost); 69813247Ssam utmp.ut_time = time(0); 69913247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 700*16339Skarels (void) close(wtmp); 70113247Ssam } 70213247Ssam } 70313247Ssam 70413247Ssam /* 70513247Ssam * Record logout in wtmp file 70613247Ssam * and exit with supplied status. 70713247Ssam */ 70813247Ssam dologout(status) 70913247Ssam int status; 71013247Ssam { 711*16339Skarels 71213247Ssam if (!logged_in) 713*16339Skarels return; 71413247Ssam seteuid(0); 71516033Sralph if (guest && (wtmp >= 0)) 71616033Sralph lseek(wtmp, 0, L_XTND); 71716033Sralph else 71816033Sralph wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 71913247Ssam if (wtmp >= 0) { 72013247Ssam SCPYN(utmp.ut_name, ""); 72113247Ssam SCPYN(utmp.ut_host, ""); 72213247Ssam utmp.ut_time = time(0); 72313247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 72413247Ssam (void) close(wtmp); 72513247Ssam } 72614436Ssam /* beware of flushing buffers after a SIGPIPE */ 72714436Ssam _exit(status); 72813247Ssam } 72913247Ssam 73013247Ssam /* 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 { 76513211Sroot int p[2], ac, gac; 76610695Ssam register myside, hisside, pid; 76713211Sroot char *av[20], *gav[512]; 76810695Ssam register char *cp; 76910695Ssam 77010695Ssam if (pipe(p) < 0) 77110695Ssam return (NULL); 77210695Ssam cp = cmd, ac = 0; 77313211Sroot /* break up string into pieces */ 77410695Ssam do { 77510695Ssam av[ac++] = cp; 77610695Ssam cp = nextarg(cp); 77713211Sroot } while (cp && *cp && ac < 20); 77810695Ssam av[ac] = (char *)0; 77913211Sroot gav[0] = av[0]; 78013211Sroot /* glob each piece */ 78113211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 78213211Sroot char **pop; 78313211Sroot extern char **glob(); 78413211Sroot 78513211Sroot pop = glob(av[ac]); 78613211Sroot if (pop) { 78713211Sroot av[ac] = (char *)pop; /* save to free later */ 78813211Sroot while (*pop && gac < 512) 78913211Sroot gav[gac++] = *pop++; 79013211Sroot } 79111757Ssam } 79213211Sroot gav[gac] = (char *)0; 79310695Ssam myside = tst(p[WTR], p[RDR]); 79410695Ssam hisside = tst(p[RDR], p[WTR]); 79510695Ssam if ((pid = fork()) == 0) { 79610695Ssam /* myside and hisside reverse roles in child */ 79710695Ssam close(myside); 79810695Ssam dup2(hisside, tst(0, 1)); 79910695Ssam close(hisside); 80013211Sroot execv(gav[0], gav); 80110695Ssam _exit(1); 80210695Ssam } 80313211Sroot for (ac = 1; av[ac] != NULL; ac++) 80413211Sroot blkfree((char **)av[ac]); 80510695Ssam if (pid == -1) 80610695Ssam return (NULL); 80710695Ssam popen_pid[myside] = pid; 80810695Ssam close(hisside); 80910695Ssam return (fdopen(myside, mode)); 81010695Ssam } 81110695Ssam 81210695Ssam pclose(ptr) 81310695Ssam FILE *ptr; 81410695Ssam { 81510695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 81610695Ssam int status; 81710695Ssam 81810695Ssam f = fileno(ptr); 81910695Ssam fclose(ptr); 82010695Ssam istat = signal(SIGINT, SIG_IGN); 82110695Ssam qstat = signal(SIGQUIT, SIG_IGN); 82210695Ssam hstat = signal(SIGHUP, SIG_IGN); 82310695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 82410695Ssam ; 82510695Ssam if (r == -1) 82610695Ssam status = -1; 82710695Ssam signal(SIGINT, istat); 82810695Ssam signal(SIGQUIT, qstat); 82910695Ssam signal(SIGHUP, hstat); 83010695Ssam return (status); 83110695Ssam } 83210695Ssam 83310695Ssam /* 83410695Ssam * Check user requesting login priviledges. 83510695Ssam * Disallow anyone mentioned in the file FTPUSERS 83610695Ssam * to allow people such as uucp to be avoided. 83710695Ssam */ 83810695Ssam checkuser(name) 83910695Ssam register char *name; 84010695Ssam { 84110695Ssam char line[BUFSIZ], *index(); 84210695Ssam FILE *fd; 84310695Ssam int found = 0; 84410695Ssam 84510695Ssam fd = fopen(FTPUSERS, "r"); 84610695Ssam if (fd == NULL) 84710695Ssam return (1); 84810695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 84910695Ssam register char *cp = index(line, '\n'); 85010695Ssam 85110695Ssam if (cp) 85210695Ssam *cp = '\0'; 85310695Ssam if (strcmp(line, name) == 0) { 85410695Ssam found++; 85510695Ssam break; 85610695Ssam } 85710695Ssam } 85810695Ssam fclose(fd); 85910695Ssam return (!found); 86010695Ssam } 861