1*22499Sdist /* 2*22499Sdist * Copyright (c) 1983 Regents of the University of California. 3*22499Sdist * All rights reserved. The Berkeley software License Agreement 4*22499Sdist * specifies the terms and conditions for redistribution. 5*22499Sdist */ 6*22499Sdist 710275Ssam #ifndef lint 8*22499Sdist char copyright[] = 9*22499Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10*22499Sdist All rights reserved.\n"; 11*22499Sdist #endif not lint 1210275Ssam 13*22499Sdist #ifndef lint 14*22499Sdist static char sccsid[] = "@(#)ftpd.c 5.1 (Berkeley) 06/06/85"; 15*22499Sdist #endif not lint 16*22499Sdist 1710275Ssam /* 1810275Ssam * FTP server. 1910275Ssam */ 2010303Ssam #include <sys/param.h> 2110275Ssam #include <sys/stat.h> 2210275Ssam #include <sys/ioctl.h> 2310275Ssam #include <sys/socket.h> 2413247Ssam #include <sys/file.h> 2513595Ssam #include <sys/wait.h> 2610275Ssam 2710275Ssam #include <netinet/in.h> 2810275Ssam 2913034Ssam #include <arpa/ftp.h> 3013211Sroot #include <arpa/inet.h> 3113034Ssam 3210275Ssam #include <stdio.h> 3310275Ssam #include <signal.h> 3410275Ssam #include <pwd.h> 3510275Ssam #include <setjmp.h> 3610275Ssam #include <netdb.h> 3710423Ssam #include <errno.h> 3810275Ssam 3910695Ssam /* 4010695Ssam * File containing login names 4110695Ssam * NOT to be used on this machine. 4210695Ssam * Commonly used to disallow uucp. 4310695Ssam */ 4410695Ssam #define FTPUSERS "/etc/ftpusers" 4510695Ssam 4610275Ssam extern int errno; 4710275Ssam extern char *sys_errlist[]; 4810275Ssam extern char *crypt(); 4910275Ssam extern char version[]; 5010275Ssam extern char *home; /* pointer to home directory for glob */ 5110275Ssam extern FILE *popen(), *fopen(); 5210275Ssam extern int pclose(), fclose(); 5310275Ssam 5410275Ssam struct sockaddr_in ctrl_addr; 5510275Ssam struct sockaddr_in data_source; 5610275Ssam struct sockaddr_in data_dest; 5710275Ssam struct sockaddr_in his_addr; 5810275Ssam 5910275Ssam struct hostent *hp; 6010275Ssam 6110275Ssam int data; 6210275Ssam jmp_buf errcatch; 6310275Ssam int logged_in; 6410275Ssam struct passwd *pw; 6510275Ssam int debug; 6611653Ssam int timeout; 6711757Ssam int logging; 6810275Ssam int guest; 6916033Sralph int wtmp; 7010275Ssam int type; 7110275Ssam int form; 7210275Ssam int stru; /* avoid C keyword */ 7310275Ssam int mode; 7410321Ssam int usedefault = 1; /* for data transfers */ 7510275Ssam char hostname[32]; 7613247Ssam char remotehost[32]; 7710275Ssam 7811653Ssam /* 7911653Ssam * Timeout intervals for retrying connections 8011653Ssam * to hosts that don't accept PORT cmds. This 8111653Ssam * is a kludge, but given the problems with TCP... 8211653Ssam */ 8311653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 8411653Ssam #define SWAITINT 5 /* interval between retries */ 8511653Ssam 8611653Ssam int swaitmax = SWAITMAX; 8711653Ssam int swaitint = SWAITINT; 8811653Ssam 8910275Ssam int lostconn(); 9010419Ssam int reapchild(); 9110275Ssam FILE *getdatasock(), *dataconn(); 9210275Ssam 9310275Ssam main(argc, argv) 9410275Ssam int argc; 9510275Ssam char *argv[]; 9610275Ssam { 9716339Skarels int options = 0, addrlen; 9810275Ssam char *cp; 9910275Ssam 10016339Skarels addrlen = sizeof (his_addr); 10116339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 10216339Skarels fprintf(stderr, "%s: ", argv[0]); 10316339Skarels perror("getpeername"); 10410275Ssam exit(1); 10510275Ssam } 10616339Skarels addrlen = sizeof (ctrl_addr); 10716339Skarels if (getsockname(0, &ctrl_addr, &addrlen) < 0) { 10816339Skarels fprintf(stderr, "%s: ", argv[0]); 10916339Skarels perror("getsockname"); 11016339Skarels exit(1); 11116339Skarels } 11216339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 11310275Ssam debug = 0; 11410275Ssam argc--, argv++; 11510275Ssam while (argc > 0 && *argv[0] == '-') { 11610275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 11710275Ssam 11811653Ssam case 'v': 11911653Ssam debug = 1; 12011653Ssam break; 12111653Ssam 12210275Ssam case 'd': 12310275Ssam debug = 1; 12410275Ssam options |= SO_DEBUG; 12510275Ssam break; 12610275Ssam 12711757Ssam case 'l': 12811757Ssam logging = 1; 12911757Ssam break; 13011757Ssam 13111653Ssam case 't': 13211653Ssam timeout = atoi(++cp); 13311653Ssam goto nextopt; 13411653Ssam break; 13511653Ssam 13610275Ssam default: 13716339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 13816339Skarels *cp); 13910275Ssam break; 14010275Ssam } 14111653Ssam nextopt: 14210275Ssam argc--, argv++; 14310275Ssam } 14416339Skarels signal(SIGPIPE, lostconn); 14516339Skarels signal(SIGCHLD, SIG_IGN); 14616760Slepreau dolog(&his_addr); 14716339Skarels /* do telnet option negotiation here */ 14816339Skarels /* 14916339Skarels * Set up default state 15016339Skarels */ 15116339Skarels logged_in = 0; 15216339Skarels data = -1; 15316339Skarels type = TYPE_A; 15416339Skarels form = FORM_N; 15516339Skarels stru = STRU_F; 15616339Skarels mode = MODE_S; 15716339Skarels gethostname(hostname, sizeof (hostname)); 15816339Skarels reply(220, "%s FTP server (%s) ready.", 15916339Skarels hostname, version); 16010275Ssam for (;;) { 16116339Skarels setjmp(errcatch); 16216339Skarels yyparse(); 16310275Ssam } 16410275Ssam } 16510275Ssam 16610419Ssam reapchild() 16710419Ssam { 16810419Ssam union wait status; 16910419Ssam 17010419Ssam while (wait3(&status, WNOHANG, 0) > 0) 17110419Ssam ; 17210419Ssam } 17310419Ssam 17410275Ssam lostconn() 17510275Ssam { 17610275Ssam 17714089Ssam if (debug) 17814089Ssam fprintf(stderr, "Lost connection.\n"); 17914089Ssam dologout(-1); 18010275Ssam } 18110275Ssam 18210275Ssam pass(passwd) 18310275Ssam char *passwd; 18410275Ssam { 18510303Ssam char *xpasswd, *savestr(); 18610303Ssam static struct passwd save; 18710275Ssam 18810275Ssam if (logged_in || pw == NULL) { 18910275Ssam reply(503, "Login with USER first."); 19010275Ssam return; 19110275Ssam } 19210275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 19310275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 19416760Slepreau /* The strcmp does not catch null passwords! */ 19516760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 19610275Ssam reply(530, "Login incorrect."); 19710275Ssam pw = NULL; 19810275Ssam return; 19910275Ssam } 20010275Ssam } 20110303Ssam setegid(pw->pw_gid); 20210275Ssam initgroups(pw->pw_name, pw->pw_gid); 20310275Ssam if (chdir(pw->pw_dir)) { 20418107Sbloom reply(550, "User %s: can't change directory to %s.", 20510275Ssam pw->pw_name, pw->pw_dir); 20610303Ssam goto bad; 20710275Ssam } 20816033Sralph 20916760Slepreau /* grab wtmp before chroot */ 21016760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 21110303Ssam if (guest && chroot(pw->pw_dir) < 0) { 21210275Ssam reply(550, "Can't set guest privileges."); 21316760Slepreau if (wtmp >= 0) { 21416760Slepreau (void) close(wtmp); 21516760Slepreau wtmp = -1; 21616760Slepreau } 21710303Ssam goto bad; 21810275Ssam } 21910275Ssam if (!guest) 22010275Ssam reply(230, "User %s logged in.", pw->pw_name); 22110275Ssam else 22210275Ssam reply(230, "Guest login ok, access restrictions apply."); 22310275Ssam logged_in = 1; 22413247Ssam dologin(pw); 22510303Ssam seteuid(pw->pw_uid); 22610303Ssam /* 22710303Ssam * Save everything so globbing doesn't 22810303Ssam * clobber the fields. 22910303Ssam */ 23010303Ssam save = *pw; 23110303Ssam save.pw_name = savestr(pw->pw_name); 23210303Ssam save.pw_passwd = savestr(pw->pw_passwd); 23310303Ssam save.pw_comment = savestr(pw->pw_comment); 23410303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 23510303Ssam save.pw_dir = savestr(pw->pw_dir); 23610303Ssam save.pw_shell = savestr(pw->pw_shell); 23710303Ssam pw = &save; 23810303Ssam home = pw->pw_dir; /* home dir for globbing */ 23910303Ssam return; 24010303Ssam bad: 24110303Ssam seteuid(0); 24210303Ssam pw = NULL; 24310275Ssam } 24410275Ssam 24510303Ssam char * 24610303Ssam savestr(s) 24710303Ssam char *s; 24810303Ssam { 24910303Ssam char *malloc(); 25010303Ssam char *new = malloc(strlen(s) + 1); 25110303Ssam 25210303Ssam if (new != NULL) 25310303Ssam strcpy(new, s); 25411347Ssam return (new); 25510303Ssam } 25610303Ssam 25710275Ssam retrieve(cmd, name) 25810275Ssam char *cmd, *name; 25910275Ssam { 26010275Ssam FILE *fin, *dout; 26110275Ssam struct stat st; 26210275Ssam int (*closefunc)(); 26310275Ssam 26410275Ssam if (cmd == 0) { 26510317Ssam #ifdef notdef 26610317Ssam /* no remote command execution -- it's a security hole */ 26711653Ssam if (*name == '|') 26810275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 26910275Ssam else 27010317Ssam #endif 27110275Ssam fin = fopen(name, "r"), closefunc = fclose; 27210275Ssam } else { 27310275Ssam char line[BUFSIZ]; 27410275Ssam 27510422Ssam sprintf(line, cmd, name), name = line; 27610275Ssam fin = popen(line, "r"), closefunc = pclose; 27710275Ssam } 27810275Ssam if (fin == NULL) { 27913152Ssam if (errno != 0) 28013152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 28110275Ssam return; 28210275Ssam } 28310275Ssam st.st_size = 0; 28410275Ssam if (cmd == 0 && 28510275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 28610275Ssam reply(550, "%s: not a plain file.", name); 28710275Ssam goto done; 28810275Ssam } 28910275Ssam dout = dataconn(name, st.st_size, "w"); 29010275Ssam if (dout == NULL) 29110275Ssam goto done; 29210303Ssam if (send_data(fin, dout) || ferror(dout)) 29310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 29410275Ssam else 29510275Ssam reply(226, "Transfer complete."); 29610303Ssam fclose(dout), data = -1; 29710275Ssam done: 29810275Ssam (*closefunc)(fin); 29910275Ssam } 30010275Ssam 30110275Ssam store(name, mode) 30210275Ssam char *name, *mode; 30310275Ssam { 30410275Ssam FILE *fout, *din; 30510303Ssam int (*closefunc)(), dochown = 0; 30610275Ssam 30710317Ssam #ifdef notdef 30810317Ssam /* no remote command execution -- it's a security hole */ 30911653Ssam if (name[0] == '|') 31010275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 31110317Ssam else 31210317Ssam #endif 31310317Ssam { 31410303Ssam struct stat st; 31510303Ssam 31610303Ssam if (stat(name, &st) < 0) 31710303Ssam dochown++; 31810275Ssam fout = fopen(name, mode), closefunc = fclose; 31910303Ssam } 32010275Ssam if (fout == NULL) { 32110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 32210275Ssam return; 32310275Ssam } 32411653Ssam din = dataconn(name, (off_t)-1, "r"); 32510275Ssam if (din == NULL) 32610275Ssam goto done; 32710303Ssam if (receive_data(din, fout) || ferror(fout)) 32810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 32910275Ssam else 33010275Ssam reply(226, "Transfer complete."); 33110275Ssam fclose(din), data = -1; 33210275Ssam done: 33310303Ssam if (dochown) 33410303Ssam (void) chown(name, pw->pw_uid, -1); 33510275Ssam (*closefunc)(fout); 33610275Ssam } 33710275Ssam 33810275Ssam FILE * 33910275Ssam getdatasock(mode) 34010275Ssam char *mode; 34110275Ssam { 34217157Ssam int s, on = 1; 34310275Ssam 34410275Ssam if (data >= 0) 34510275Ssam return (fdopen(data, mode)); 34613247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 34710602Ssam if (s < 0) 34810275Ssam return (NULL); 34910275Ssam seteuid(0); 35017157Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) 35110602Ssam goto bad; 35213152Ssam /* anchor socket to avoid multi-homing problems */ 35313152Ssam data_source.sin_family = AF_INET; 35413152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 35510602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 35610602Ssam goto bad; 35710311Ssam seteuid(pw->pw_uid); 35810275Ssam return (fdopen(s, mode)); 35910602Ssam bad: 36010602Ssam seteuid(pw->pw_uid); 36110602Ssam close(s); 36210602Ssam return (NULL); 36310275Ssam } 36410275Ssam 36510275Ssam FILE * 36610275Ssam dataconn(name, size, mode) 36710275Ssam char *name; 36811653Ssam off_t size; 36910275Ssam char *mode; 37010275Ssam { 37110275Ssam char sizebuf[32]; 37210275Ssam FILE *file; 37311653Ssam int retry = 0; 37410275Ssam 37510275Ssam if (size >= 0) 37611653Ssam sprintf (sizebuf, " (%ld bytes)", size); 37710275Ssam else 37810275Ssam (void) strcpy(sizebuf, ""); 37910275Ssam if (data >= 0) { 38010275Ssam reply(125, "Using existing data connection for %s%s.", 38110275Ssam name, sizebuf); 38210321Ssam usedefault = 1; 38310275Ssam return (fdopen(data, mode)); 38410275Ssam } 38510566Ssam if (usedefault) 38610422Ssam data_dest = his_addr; 38710422Ssam usedefault = 1; 38810275Ssam file = getdatasock(mode); 38910275Ssam if (file == NULL) { 39010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 39113247Ssam inet_ntoa(data_source.sin_addr), 39210275Ssam ntohs(data_source.sin_port), 39310275Ssam sys_errlist[errno]); 39410275Ssam return (NULL); 39510275Ssam } 39610602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 39713247Ssam name, inet_ntoa(data_dest.sin_addr.s_addr), 39810602Ssam ntohs(data_dest.sin_port), sizebuf); 39910275Ssam data = fileno(file); 40011653Ssam while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 40111653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 40211653Ssam sleep(swaitint); 40311653Ssam retry += swaitint; 40411653Ssam continue; 40511653Ssam } 40610275Ssam reply(425, "Can't build data connection: %s.", 40710275Ssam sys_errlist[errno]); 40810275Ssam (void) fclose(file); 40910275Ssam data = -1; 41010275Ssam return (NULL); 41110275Ssam } 41210275Ssam return (file); 41310275Ssam } 41410275Ssam 41510275Ssam /* 41610275Ssam * Tranfer the contents of "instr" to 41710275Ssam * "outstr" peer using the appropriate 41810275Ssam * encapulation of the date subject 41910275Ssam * to Mode, Structure, and Type. 42010275Ssam * 42110275Ssam * NB: Form isn't handled. 42210275Ssam */ 42310275Ssam send_data(instr, outstr) 42410275Ssam FILE *instr, *outstr; 42510275Ssam { 42610275Ssam register int c; 42710275Ssam int netfd, filefd, cnt; 42810275Ssam char buf[BUFSIZ]; 42910275Ssam 43010275Ssam switch (type) { 43110275Ssam 43210275Ssam case TYPE_A: 43310275Ssam while ((c = getc(instr)) != EOF) { 43411220Ssam if (c == '\n') { 43511220Ssam if (ferror (outstr)) 43611220Ssam return (1); 43710275Ssam putc('\r', outstr); 43811220Ssam } 43911220Ssam putc(c, outstr); 44011220Ssam if (c == '\r') 44111220Ssam putc ('\0', outstr); 44210275Ssam } 44311220Ssam if (ferror (instr) || ferror (outstr)) 44411220Ssam return (1); 44510275Ssam return (0); 44610275Ssam 44710275Ssam case TYPE_I: 44810275Ssam case TYPE_L: 44910275Ssam netfd = fileno(outstr); 45010275Ssam filefd = fileno(instr); 45110275Ssam 45210303Ssam while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 45310275Ssam if (write(netfd, buf, cnt) < 0) 45410275Ssam return (1); 45510275Ssam return (cnt < 0); 45610275Ssam } 45710275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 45810275Ssam return (1); 45910275Ssam } 46010275Ssam 46110275Ssam /* 46210275Ssam * Transfer data from peer to 46310275Ssam * "outstr" using the appropriate 46410275Ssam * encapulation of the data subject 46510275Ssam * to Mode, Structure, and Type. 46610275Ssam * 46710275Ssam * N.B.: Form isn't handled. 46810275Ssam */ 46910275Ssam receive_data(instr, outstr) 47010275Ssam FILE *instr, *outstr; 47110275Ssam { 47210275Ssam register int c; 47311220Ssam int cnt; 47410275Ssam char buf[BUFSIZ]; 47510275Ssam 47610275Ssam 47710275Ssam switch (type) { 47810275Ssam 47910275Ssam case TYPE_I: 48010275Ssam case TYPE_L: 48110616Ssam while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 48210616Ssam if (write(fileno(outstr), buf, cnt) < 0) 48310275Ssam return (1); 48410275Ssam return (cnt < 0); 48510275Ssam 48610275Ssam case TYPE_E: 48710275Ssam reply(504, "TYPE E not implemented."); 48810275Ssam return (1); 48910275Ssam 49010275Ssam case TYPE_A: 49110275Ssam while ((c = getc(instr)) != EOF) { 49210275Ssam if (c == '\r') { 49311220Ssam if (ferror (outstr)) 49411220Ssam return (1); 49511220Ssam if ((c = getc(instr)) != '\n') 49611220Ssam putc ('\r', outstr); 49711220Ssam if (c == '\0') 49811220Ssam continue; 49910275Ssam } 50011220Ssam putc (c, outstr); 50110275Ssam } 50211220Ssam if (ferror (instr) || ferror (outstr)) 50311220Ssam return (1); 50410275Ssam return (0); 50510275Ssam } 50610275Ssam fatal("Unknown type in receive_data."); 50710275Ssam /*NOTREACHED*/ 50810275Ssam } 50910275Ssam 51010275Ssam fatal(s) 51110275Ssam char *s; 51210275Ssam { 51310275Ssam reply(451, "Error in server: %s\n", s); 51410275Ssam reply(221, "Closing connection due to server error."); 51513247Ssam dologout(0); 51610275Ssam } 51710275Ssam 51810275Ssam reply(n, s, args) 51910275Ssam int n; 52010275Ssam char *s; 52110275Ssam { 52210275Ssam 52310275Ssam printf("%d ", n); 52410275Ssam _doprnt(s, &args, stdout); 52510275Ssam printf("\r\n"); 52610275Ssam fflush(stdout); 52710275Ssam if (debug) { 52810275Ssam fprintf(stderr, "<--- %d ", n); 52910275Ssam _doprnt(s, &args, stderr); 53010275Ssam fprintf(stderr, "\n"); 53110275Ssam fflush(stderr); 53210275Ssam } 53310275Ssam } 53410275Ssam 53510275Ssam lreply(n, s, args) 53610275Ssam int n; 53710275Ssam char *s; 53810275Ssam { 53910275Ssam printf("%d-", n); 54010275Ssam _doprnt(s, &args, stdout); 54110275Ssam printf("\r\n"); 54210275Ssam fflush(stdout); 54310275Ssam if (debug) { 54410275Ssam fprintf(stderr, "<--- %d-", n); 54510275Ssam _doprnt(s, &args, stderr); 54610275Ssam fprintf(stderr, "\n"); 54710275Ssam } 54810275Ssam } 54910275Ssam 55010275Ssam replystr(s) 55110275Ssam char *s; 55210275Ssam { 55310275Ssam printf("%s\r\n", s); 55410275Ssam fflush(stdout); 55510275Ssam if (debug) 55610275Ssam fprintf(stderr, "<--- %s\n", s); 55710275Ssam } 55810275Ssam 55910275Ssam ack(s) 56010275Ssam char *s; 56110275Ssam { 56210275Ssam reply(200, "%s command okay.", s); 56310275Ssam } 56410275Ssam 56510275Ssam nack(s) 56610275Ssam char *s; 56710275Ssam { 56810275Ssam reply(502, "%s command not implemented.", s); 56910275Ssam } 57010275Ssam 57110275Ssam yyerror() 57210275Ssam { 57310275Ssam reply(500, "Command not understood."); 57410275Ssam } 57510275Ssam 57610275Ssam delete(name) 57710275Ssam char *name; 57810275Ssam { 57910275Ssam struct stat st; 58010275Ssam 58110275Ssam if (stat(name, &st) < 0) { 58210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 58310275Ssam return; 58410275Ssam } 58510275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 58610275Ssam if (rmdir(name) < 0) { 58710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 58810275Ssam return; 58910275Ssam } 59010275Ssam goto done; 59110275Ssam } 59210275Ssam if (unlink(name) < 0) { 59310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 59410275Ssam return; 59510275Ssam } 59610275Ssam done: 59710275Ssam ack("DELE"); 59810275Ssam } 59910275Ssam 60010275Ssam cwd(path) 60110275Ssam char *path; 60210275Ssam { 60310275Ssam 60410275Ssam if (chdir(path) < 0) { 60510275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 60610275Ssam return; 60710275Ssam } 60810275Ssam ack("CWD"); 60910275Ssam } 61010275Ssam 61110303Ssam makedir(name) 61210275Ssam char *name; 61310275Ssam { 61410303Ssam struct stat st; 61510303Ssam int dochown = stat(name, &st) < 0; 61610275Ssam 61710275Ssam if (mkdir(name, 0777) < 0) { 61810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 61910275Ssam return; 62010275Ssam } 62110303Ssam if (dochown) 62210303Ssam (void) chown(name, pw->pw_uid, -1); 62310275Ssam ack("MKDIR"); 62410275Ssam } 62510275Ssam 62610303Ssam removedir(name) 62710275Ssam char *name; 62810275Ssam { 62910275Ssam 63010275Ssam if (rmdir(name) < 0) { 63110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 63210275Ssam return; 63310275Ssam } 63410275Ssam ack("RMDIR"); 63510275Ssam } 63610275Ssam 63710303Ssam pwd() 63810275Ssam { 63910303Ssam char path[MAXPATHLEN + 1]; 64010275Ssam 64110275Ssam if (getwd(path) == NULL) { 64210275Ssam reply(451, "%s.", path); 64310275Ssam return; 64410275Ssam } 64510275Ssam reply(251, "\"%s\" is current directory.", path); 64610275Ssam } 64710275Ssam 64810275Ssam char * 64910275Ssam renamefrom(name) 65010275Ssam char *name; 65110275Ssam { 65210275Ssam struct stat st; 65310275Ssam 65410275Ssam if (stat(name, &st) < 0) { 65510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 65610275Ssam return ((char *)0); 65710275Ssam } 65810303Ssam reply(350, "File exists, ready for destination name"); 65910275Ssam return (name); 66010275Ssam } 66110275Ssam 66210275Ssam renamecmd(from, to) 66310275Ssam char *from, *to; 66410275Ssam { 66510275Ssam 66610275Ssam if (rename(from, to) < 0) { 66710275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 66810275Ssam return; 66910275Ssam } 67010275Ssam ack("RNTO"); 67110275Ssam } 67210275Ssam 67310275Ssam dolog(sin) 67410275Ssam struct sockaddr_in *sin; 67510275Ssam { 67610275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 67710275Ssam sizeof (struct in_addr), AF_INET); 67810275Ssam time_t t; 67910275Ssam 68013247Ssam if (hp) { 68113247Ssam strncpy(remotehost, hp->h_name, sizeof (remotehost)); 68213247Ssam endhostent(); 68313247Ssam } else 68413247Ssam strncpy(remotehost, inet_ntoa(sin->sin_addr), 68513247Ssam sizeof (remotehost)); 68613247Ssam if (!logging) 68713247Ssam return; 68810275Ssam t = time(0); 68911757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 69010275Ssam fflush(stderr); 69110275Ssam } 69210695Ssam 69313247Ssam #include <utmp.h> 69413247Ssam 69513247Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 69613247Ssam struct utmp utmp; 69713247Ssam 69810695Ssam /* 69913247Ssam * Record login in wtmp file. 70013247Ssam */ 70113247Ssam dologin(pw) 70213247Ssam struct passwd *pw; 70313247Ssam { 70413247Ssam char line[32]; 70513247Ssam 70613247Ssam if (wtmp >= 0) { 70713247Ssam /* hack, but must be unique and no tty line */ 70813247Ssam sprintf(line, "ftp%d", getpid()); 70913247Ssam SCPYN(utmp.ut_line, line); 71013247Ssam SCPYN(utmp.ut_name, pw->pw_name); 71113247Ssam SCPYN(utmp.ut_host, remotehost); 71213247Ssam utmp.ut_time = time(0); 71313247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 71416760Slepreau if (!guest) { /* anon must hang on */ 71516760Slepreau (void) close(wtmp); 71616760Slepreau wtmp = -1; 71716760Slepreau } 71813247Ssam } 71913247Ssam } 72013247Ssam 72113247Ssam /* 72213247Ssam * Record logout in wtmp file 72313247Ssam * and exit with supplied status. 72413247Ssam */ 72513247Ssam dologout(status) 72613247Ssam int status; 72713247Ssam { 72816339Skarels 72917580Ssam if (logged_in) { 73017580Ssam (void) seteuid(0); 73117580Ssam if (wtmp < 0) 73217580Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 73317580Ssam if (wtmp >= 0) { 73417580Ssam SCPYN(utmp.ut_name, ""); 73517580Ssam SCPYN(utmp.ut_host, ""); 73617580Ssam utmp.ut_time = time(0); 73717580Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 73817580Ssam (void) close(wtmp); 73917580Ssam } 74013247Ssam } 74114436Ssam /* beware of flushing buffers after a SIGPIPE */ 74214436Ssam _exit(status); 74313247Ssam } 74413247Ssam 74513247Ssam /* 74610695Ssam * Special version of popen which avoids 74710695Ssam * call to shell. This insures noone may 74810695Ssam * create a pipe to a hidden program as a side 74910695Ssam * effect of a list or dir command. 75010695Ssam */ 75110695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 75210695Ssam #define RDR 0 75310695Ssam #define WTR 1 75410695Ssam static int popen_pid[5]; 75510695Ssam 75610695Ssam static char * 75710695Ssam nextarg(cpp) 75810695Ssam char *cpp; 75910695Ssam { 76010695Ssam register char *cp = cpp; 76110695Ssam 76210695Ssam if (cp == 0) 76310695Ssam return (cp); 76410695Ssam while (*cp && *cp != ' ' && *cp != '\t') 76510695Ssam cp++; 76610695Ssam if (*cp == ' ' || *cp == '\t') { 76710695Ssam *cp++ = '\0'; 76810695Ssam while (*cp == ' ' || *cp == '\t') 76910695Ssam cp++; 77010695Ssam } 77110695Ssam if (cp == cpp) 77210695Ssam return ((char *)0); 77310695Ssam return (cp); 77410695Ssam } 77510695Ssam 77610695Ssam FILE * 77710695Ssam popen(cmd, mode) 77810695Ssam char *cmd, *mode; 77910695Ssam { 78013211Sroot int p[2], ac, gac; 78110695Ssam register myside, hisside, pid; 78213211Sroot char *av[20], *gav[512]; 78310695Ssam register char *cp; 78410695Ssam 78510695Ssam if (pipe(p) < 0) 78610695Ssam return (NULL); 78710695Ssam cp = cmd, ac = 0; 78813211Sroot /* break up string into pieces */ 78910695Ssam do { 79010695Ssam av[ac++] = cp; 79110695Ssam cp = nextarg(cp); 79213211Sroot } while (cp && *cp && ac < 20); 79310695Ssam av[ac] = (char *)0; 79413211Sroot gav[0] = av[0]; 79513211Sroot /* glob each piece */ 79613211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 79713211Sroot char **pop; 79822024Ssam extern char **glob(), **copyblk(); 79913211Sroot 80013211Sroot pop = glob(av[ac]); 80122024Ssam if (pop == (char **)NULL) { /* globbing failed */ 80222024Ssam char *vv[2]; 80322024Ssam 80422024Ssam vv[0] = av[ac]; 80522024Ssam vv[1] = 0; 80622024Ssam pop = copyblk(vv); 80713211Sroot } 80822024Ssam av[ac] = (char *)pop; /* save to free later */ 80922024Ssam while (*pop && gac < 512) 81022024Ssam gav[gac++] = *pop++; 81111757Ssam } 81213211Sroot gav[gac] = (char *)0; 81310695Ssam myside = tst(p[WTR], p[RDR]); 81410695Ssam hisside = tst(p[RDR], p[WTR]); 81510695Ssam if ((pid = fork()) == 0) { 81610695Ssam /* myside and hisside reverse roles in child */ 81710695Ssam close(myside); 81810695Ssam dup2(hisside, tst(0, 1)); 81910695Ssam close(hisside); 82013211Sroot execv(gav[0], gav); 82110695Ssam _exit(1); 82210695Ssam } 82313211Sroot for (ac = 1; av[ac] != NULL; ac++) 82413211Sroot blkfree((char **)av[ac]); 82510695Ssam if (pid == -1) 82610695Ssam return (NULL); 82710695Ssam popen_pid[myside] = pid; 82810695Ssam close(hisside); 82910695Ssam return (fdopen(myside, mode)); 83010695Ssam } 83110695Ssam 83210695Ssam pclose(ptr) 83310695Ssam FILE *ptr; 83410695Ssam { 83510695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 83610695Ssam int status; 83710695Ssam 83810695Ssam f = fileno(ptr); 83910695Ssam fclose(ptr); 84010695Ssam istat = signal(SIGINT, SIG_IGN); 84110695Ssam qstat = signal(SIGQUIT, SIG_IGN); 84210695Ssam hstat = signal(SIGHUP, SIG_IGN); 84310695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 84410695Ssam ; 84510695Ssam if (r == -1) 84610695Ssam status = -1; 84710695Ssam signal(SIGINT, istat); 84810695Ssam signal(SIGQUIT, qstat); 84910695Ssam signal(SIGHUP, hstat); 85010695Ssam return (status); 85110695Ssam } 85210695Ssam 85310695Ssam /* 85410695Ssam * Check user requesting login priviledges. 85510695Ssam * Disallow anyone mentioned in the file FTPUSERS 85610695Ssam * to allow people such as uucp to be avoided. 85710695Ssam */ 85810695Ssam checkuser(name) 85910695Ssam register char *name; 86010695Ssam { 86110695Ssam char line[BUFSIZ], *index(); 86210695Ssam FILE *fd; 86310695Ssam int found = 0; 86410695Ssam 86510695Ssam fd = fopen(FTPUSERS, "r"); 86610695Ssam if (fd == NULL) 86710695Ssam return (1); 86810695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 86910695Ssam register char *cp = index(line, '\n'); 87010695Ssam 87110695Ssam if (cp) 87210695Ssam *cp = '\0'; 87310695Ssam if (strcmp(line, name) == 0) { 87410695Ssam found++; 87510695Ssam break; 87610695Ssam } 87710695Ssam } 87810695Ssam fclose(fd); 87910695Ssam return (!found); 88010695Ssam } 881