122499Sdist /* 226044Sminshall * Copyright (c) 1985 Regents of the University of California. 333738Sbostic * All rights reserved. 433738Sbostic * 533738Sbostic * Redistribution and use in source and binary forms are permitted 6*34769Sbostic * provided that the above copyright notice and this paragraph are 7*34769Sbostic * duplicated in all such forms and that any documentation, 8*34769Sbostic * advertising materials, and other materials related to such 9*34769Sbostic * distribution and use acknowledge that the software was developed 10*34769Sbostic * by the University of California, Berkeley. The name of the 11*34769Sbostic * University may not be used to endorse or promote products derived 12*34769Sbostic * from this software without specific prior written permission. 13*34769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34769Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622499Sdist */ 1722499Sdist 1810275Ssam #ifndef lint 1922499Sdist char copyright[] = 2026044Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 2122499Sdist All rights reserved.\n"; 2233738Sbostic #endif /* not lint */ 2310275Ssam 2422499Sdist #ifndef lint 25*34769Sbostic static char sccsid[] = "@(#)ftpd.c 5.12 (Berkeley) 06/18/88"; 2633738Sbostic #endif /* not lint */ 2722499Sdist 2810275Ssam /* 2910275Ssam * FTP server. 3010275Ssam */ 3110303Ssam #include <sys/param.h> 3210275Ssam #include <sys/stat.h> 3310275Ssam #include <sys/ioctl.h> 3410275Ssam #include <sys/socket.h> 3513247Ssam #include <sys/file.h> 3613595Ssam #include <sys/wait.h> 3710275Ssam 3810275Ssam #include <netinet/in.h> 3910275Ssam 4013034Ssam #include <arpa/ftp.h> 4113211Sroot #include <arpa/inet.h> 4226044Sminshall #include <arpa/telnet.h> 4313034Ssam 4410275Ssam #include <stdio.h> 4510275Ssam #include <signal.h> 4610275Ssam #include <pwd.h> 4710275Ssam #include <setjmp.h> 4810275Ssam #include <netdb.h> 4910423Ssam #include <errno.h> 5026044Sminshall #include <strings.h> 5126493Sminshall #include <syslog.h> 5210275Ssam 5310695Ssam /* 5410695Ssam * File containing login names 5510695Ssam * NOT to be used on this machine. 5610695Ssam * Commonly used to disallow uucp. 5710695Ssam */ 5810695Ssam #define FTPUSERS "/etc/ftpusers" 5910695Ssam 6010275Ssam extern int errno; 6110275Ssam extern char *sys_errlist[]; 6210275Ssam extern char *crypt(); 6310275Ssam extern char version[]; 6410275Ssam extern char *home; /* pointer to home directory for glob */ 6526044Sminshall extern FILE *popen(), *fopen(), *freopen(); 6626493Sminshall extern int pclose(), fclose(); 6726044Sminshall extern char *getline(); 6826044Sminshall extern char cbuf[]; 6910275Ssam 7010275Ssam struct sockaddr_in ctrl_addr; 7110275Ssam struct sockaddr_in data_source; 7210275Ssam struct sockaddr_in data_dest; 7310275Ssam struct sockaddr_in his_addr; 7410275Ssam 7510275Ssam int data; 7626044Sminshall jmp_buf errcatch, urgcatch; 7710275Ssam int logged_in; 7810275Ssam struct passwd *pw; 7910275Ssam int debug; 8026493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8111757Ssam int logging; 8210275Ssam int guest; 8316033Sralph int wtmp; 8410275Ssam int type; 8510275Ssam int form; 8610275Ssam int stru; /* avoid C keyword */ 8710275Ssam int mode; 8810321Ssam int usedefault = 1; /* for data transfers */ 8926044Sminshall int pdata; /* for passive mode */ 9026044Sminshall int unique; 9126044Sminshall int transflag; 9226044Sminshall char tmpline[7]; 9310275Ssam char hostname[32]; 9413247Ssam char remotehost[32]; 9510275Ssam 9611653Ssam /* 9711653Ssam * Timeout intervals for retrying connections 9811653Ssam * to hosts that don't accept PORT cmds. This 9911653Ssam * is a kludge, but given the problems with TCP... 10011653Ssam */ 10111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10211653Ssam #define SWAITINT 5 /* interval between retries */ 10311653Ssam 10411653Ssam int swaitmax = SWAITMAX; 10511653Ssam int swaitint = SWAITINT; 10611653Ssam 10710275Ssam int lostconn(); 10826044Sminshall int myoob(); 10910275Ssam FILE *getdatasock(), *dataconn(); 11010275Ssam 11110275Ssam main(argc, argv) 11210275Ssam int argc; 11310275Ssam char *argv[]; 11410275Ssam { 11527750Sminshall int addrlen, on = 1; 11626044Sminshall long pgid; 11710275Ssam char *cp; 11810275Ssam 11916339Skarels addrlen = sizeof (his_addr); 12016339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 12126493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12210275Ssam exit(1); 12310275Ssam } 12416339Skarels addrlen = sizeof (ctrl_addr); 12526493Sminshall if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) { 12626493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 12716339Skarels exit(1); 12816339Skarels } 12916339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 13010275Ssam debug = 0; 13126493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 13210275Ssam argc--, argv++; 13310275Ssam while (argc > 0 && *argv[0] == '-') { 13410275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 13510275Ssam 13611653Ssam case 'v': 13711653Ssam debug = 1; 13811653Ssam break; 13911653Ssam 14010275Ssam case 'd': 14110275Ssam debug = 1; 14210275Ssam break; 14310275Ssam 14411757Ssam case 'l': 14511757Ssam logging = 1; 14611757Ssam break; 14711757Ssam 14811653Ssam case 't': 14911653Ssam timeout = atoi(++cp); 15011653Ssam goto nextopt; 15111653Ssam break; 15211653Ssam 15310275Ssam default: 15416339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 15516339Skarels *cp); 15610275Ssam break; 15710275Ssam } 15811653Ssam nextopt: 15910275Ssam argc--, argv++; 16010275Ssam } 16130944Scsvsj (void) freopen("/dev/null", "w", stderr); 16226493Sminshall (void) signal(SIGPIPE, lostconn); 16326493Sminshall (void) signal(SIGCHLD, SIG_IGN); 16426044Sminshall if (signal(SIGURG, myoob) < 0) { 16526493Sminshall syslog(LOG_ERR, "signal: %m"); 16626044Sminshall } 16727750Sminshall /* handle urgent data inline */ 16827750Sminshall #ifdef SO_OOBINLINE 16927750Sminshall if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { 17027750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 17127750Sminshall } 17227750Sminshall #endif SO_OOBINLINE 17326044Sminshall pgid = getpid(); 17427750Sminshall if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) { 17526493Sminshall syslog(LOG_ERR, "ioctl: %m"); 17626044Sminshall } 17716760Slepreau dolog(&his_addr); 17816339Skarels /* do telnet option negotiation here */ 17916339Skarels /* 18016339Skarels * Set up default state 18116339Skarels */ 18216339Skarels logged_in = 0; 18316339Skarels data = -1; 18416339Skarels type = TYPE_A; 18516339Skarels form = FORM_N; 18616339Skarels stru = STRU_F; 18716339Skarels mode = MODE_S; 18826044Sminshall tmpline[0] = '\0'; 18926493Sminshall (void) gethostname(hostname, sizeof (hostname)); 19016339Skarels reply(220, "%s FTP server (%s) ready.", 19116339Skarels hostname, version); 19210275Ssam for (;;) { 19326493Sminshall (void) setjmp(errcatch); 19426493Sminshall (void) yyparse(); 19510275Ssam } 19610275Ssam } 19710419Ssam 19810275Ssam lostconn() 19910275Ssam { 20010275Ssam 20114089Ssam if (debug) 20226493Sminshall syslog(LOG_DEBUG, "lost connection"); 20314089Ssam dologout(-1); 20410275Ssam } 20510275Ssam 20610275Ssam pass(passwd) 20710275Ssam char *passwd; 20810275Ssam { 20910303Ssam char *xpasswd, *savestr(); 21010303Ssam static struct passwd save; 21110275Ssam 21210275Ssam if (logged_in || pw == NULL) { 21310275Ssam reply(503, "Login with USER first."); 21410275Ssam return; 21510275Ssam } 21610275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 21710275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 21816760Slepreau /* The strcmp does not catch null passwords! */ 21916760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 22010275Ssam reply(530, "Login incorrect."); 22110275Ssam pw = NULL; 22210275Ssam return; 22310275Ssam } 22410275Ssam } 22510303Ssam setegid(pw->pw_gid); 22610275Ssam initgroups(pw->pw_name, pw->pw_gid); 22710275Ssam if (chdir(pw->pw_dir)) { 22827106Smckusick reply(530, "User %s: can't change directory to %s.", 22910275Ssam pw->pw_name, pw->pw_dir); 23010303Ssam goto bad; 23110275Ssam } 23216033Sralph 23316760Slepreau /* grab wtmp before chroot */ 23416760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 23510303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23610275Ssam reply(550, "Can't set guest privileges."); 23716760Slepreau if (wtmp >= 0) { 23816760Slepreau (void) close(wtmp); 23916760Slepreau wtmp = -1; 24016760Slepreau } 24110303Ssam goto bad; 24210275Ssam } 24310275Ssam if (!guest) 24410275Ssam reply(230, "User %s logged in.", pw->pw_name); 24510275Ssam else 24610275Ssam reply(230, "Guest login ok, access restrictions apply."); 24710275Ssam logged_in = 1; 24813247Ssam dologin(pw); 24910303Ssam seteuid(pw->pw_uid); 25010303Ssam /* 25110303Ssam * Save everything so globbing doesn't 25210303Ssam * clobber the fields. 25310303Ssam */ 25410303Ssam save = *pw; 25510303Ssam save.pw_name = savestr(pw->pw_name); 25610303Ssam save.pw_passwd = savestr(pw->pw_passwd); 25710303Ssam save.pw_comment = savestr(pw->pw_comment); 25826493Sminshall save.pw_gecos = savestr(pw->pw_gecos); 25910303Ssam save.pw_dir = savestr(pw->pw_dir); 26010303Ssam save.pw_shell = savestr(pw->pw_shell); 26110303Ssam pw = &save; 26210303Ssam home = pw->pw_dir; /* home dir for globbing */ 26310303Ssam return; 26410303Ssam bad: 26510303Ssam seteuid(0); 26610303Ssam pw = NULL; 26710275Ssam } 26810275Ssam 26910303Ssam char * 27010303Ssam savestr(s) 27110303Ssam char *s; 27210303Ssam { 27310303Ssam char *malloc(); 27426493Sminshall char *new = malloc((unsigned) strlen(s) + 1); 27510303Ssam 27610303Ssam if (new != NULL) 27726493Sminshall (void) strcpy(new, s); 27811347Ssam return (new); 27910303Ssam } 28010303Ssam 28110275Ssam retrieve(cmd, name) 28210275Ssam char *cmd, *name; 28310275Ssam { 28410275Ssam FILE *fin, *dout; 28510275Ssam struct stat st; 28626044Sminshall int (*closefunc)(), tmp; 28710275Ssam 28810275Ssam if (cmd == 0) { 28910317Ssam #ifdef notdef 29010317Ssam /* no remote command execution -- it's a security hole */ 29111653Ssam if (*name == '|') 29210275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 29310275Ssam else 29410317Ssam #endif 29510275Ssam fin = fopen(name, "r"), closefunc = fclose; 29610275Ssam } else { 29710275Ssam char line[BUFSIZ]; 29810275Ssam 29926493Sminshall (void) sprintf(line, cmd, name), name = line; 30010275Ssam fin = popen(line, "r"), closefunc = pclose; 30110275Ssam } 30210275Ssam if (fin == NULL) { 30313152Ssam if (errno != 0) 30413152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30510275Ssam return; 30610275Ssam } 30710275Ssam st.st_size = 0; 30810275Ssam if (cmd == 0 && 30910275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 31010275Ssam reply(550, "%s: not a plain file.", name); 31110275Ssam goto done; 31210275Ssam } 31310275Ssam dout = dataconn(name, st.st_size, "w"); 31410275Ssam if (dout == NULL) 31510275Ssam goto done; 31626044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 31710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 31826044Sminshall } 31926044Sminshall else if (tmp == 0) { 32010275Ssam reply(226, "Transfer complete."); 32126044Sminshall } 32226493Sminshall (void) fclose(dout); 32326044Sminshall data = -1; 32426044Sminshall pdata = -1; 32510275Ssam done: 32610275Ssam (*closefunc)(fin); 32710275Ssam } 32810275Ssam 32910275Ssam store(name, mode) 33010275Ssam char *name, *mode; 33110275Ssam { 33210275Ssam FILE *fout, *din; 33326044Sminshall int (*closefunc)(), dochown = 0, tmp; 33426044Sminshall char *gunique(), *local; 33510275Ssam 33610317Ssam #ifdef notdef 33710317Ssam /* no remote command execution -- it's a security hole */ 33811653Ssam if (name[0] == '|') 33910275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 34010317Ssam else 34110317Ssam #endif 34210317Ssam { 34310303Ssam struct stat st; 34410303Ssam 34526044Sminshall local = name; 34626044Sminshall if (stat(name, &st) < 0) { 34710303Ssam dochown++; 34826044Sminshall } 34926044Sminshall else if (unique) { 35026044Sminshall if ((local = gunique(name)) == NULL) { 35126044Sminshall return; 35226044Sminshall } 35326044Sminshall dochown++; 35426044Sminshall } 35526044Sminshall fout = fopen(local, mode), closefunc = fclose; 35610303Ssam } 35710275Ssam if (fout == NULL) { 35827106Smckusick reply(553, "%s: %s.", local, sys_errlist[errno]); 35910275Ssam return; 36010275Ssam } 36126044Sminshall din = dataconn(local, (off_t)-1, "r"); 36210275Ssam if (din == NULL) 36310275Ssam goto done; 36426044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 36527106Smckusick reply(552, "%s: %s.", local, sys_errlist[errno]); 36626044Sminshall } 36726044Sminshall else if (tmp == 0 && !unique) { 36810275Ssam reply(226, "Transfer complete."); 36926044Sminshall } 37026044Sminshall else if (tmp == 0 && unique) { 37126044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 37226044Sminshall } 37326493Sminshall (void) fclose(din); 37426044Sminshall data = -1; 37526044Sminshall pdata = -1; 37610275Ssam done: 37710303Ssam if (dochown) 37826044Sminshall (void) chown(local, pw->pw_uid, -1); 37910275Ssam (*closefunc)(fout); 38010275Ssam } 38110275Ssam 38210275Ssam FILE * 38310275Ssam getdatasock(mode) 38410275Ssam char *mode; 38510275Ssam { 38617157Ssam int s, on = 1; 38710275Ssam 38810275Ssam if (data >= 0) 38910275Ssam return (fdopen(data, mode)); 39013247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 39110602Ssam if (s < 0) 39210275Ssam return (NULL); 39310275Ssam seteuid(0); 39426493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 39510602Ssam goto bad; 39613152Ssam /* anchor socket to avoid multi-homing problems */ 39713152Ssam data_source.sin_family = AF_INET; 39813152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 39926493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 40010602Ssam goto bad; 40110311Ssam seteuid(pw->pw_uid); 40210275Ssam return (fdopen(s, mode)); 40310602Ssam bad: 40410602Ssam seteuid(pw->pw_uid); 40526493Sminshall (void) close(s); 40610602Ssam return (NULL); 40710275Ssam } 40810275Ssam 40910275Ssam FILE * 41010275Ssam dataconn(name, size, mode) 41110275Ssam char *name; 41211653Ssam off_t size; 41310275Ssam char *mode; 41410275Ssam { 41510275Ssam char sizebuf[32]; 41610275Ssam FILE *file; 41711653Ssam int retry = 0; 41810275Ssam 41910275Ssam if (size >= 0) 42026493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 42110275Ssam else 42210275Ssam (void) strcpy(sizebuf, ""); 42326044Sminshall if (pdata > 0) { 42426044Sminshall struct sockaddr_in from; 42526044Sminshall int s, fromlen = sizeof(from); 42626044Sminshall 42726493Sminshall s = accept(pdata, &from, &fromlen); 42826044Sminshall if (s < 0) { 42926044Sminshall reply(425, "Can't open data connection."); 43026044Sminshall (void) close(pdata); 43126044Sminshall pdata = -1; 43226044Sminshall return(NULL); 43326044Sminshall } 43426044Sminshall (void) close(pdata); 43526044Sminshall pdata = s; 43626044Sminshall reply(150, "Openning data connection for %s (%s,%d)%s.", 43726493Sminshall name, inet_ntoa(from.sin_addr), 43826044Sminshall ntohs(from.sin_port), sizebuf); 43926044Sminshall return(fdopen(pdata, mode)); 44026044Sminshall } 44110275Ssam if (data >= 0) { 44210275Ssam reply(125, "Using existing data connection for %s%s.", 44310275Ssam name, sizebuf); 44410321Ssam usedefault = 1; 44510275Ssam return (fdopen(data, mode)); 44610275Ssam } 44710566Ssam if (usedefault) 44810422Ssam data_dest = his_addr; 44910422Ssam usedefault = 1; 45010275Ssam file = getdatasock(mode); 45110275Ssam if (file == NULL) { 45210275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 45313247Ssam inet_ntoa(data_source.sin_addr), 45410275Ssam ntohs(data_source.sin_port), 45510275Ssam sys_errlist[errno]); 45610275Ssam return (NULL); 45710275Ssam } 45810275Ssam data = fileno(file); 45926044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 46011653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 46126493Sminshall sleep((unsigned) swaitint); 46211653Ssam retry += swaitint; 46311653Ssam continue; 46411653Ssam } 46510275Ssam reply(425, "Can't build data connection: %s.", 46610275Ssam sys_errlist[errno]); 46710275Ssam (void) fclose(file); 46810275Ssam data = -1; 46910275Ssam return (NULL); 47010275Ssam } 47127750Sminshall reply(150, "Opening data connection for %s (%s,%d)%s.", 47227750Sminshall name, inet_ntoa(data_dest.sin_addr), 47327750Sminshall ntohs(data_dest.sin_port), sizebuf); 47410275Ssam return (file); 47510275Ssam } 47610275Ssam 47710275Ssam /* 47810275Ssam * Tranfer the contents of "instr" to 47910275Ssam * "outstr" peer using the appropriate 48010275Ssam * encapulation of the date subject 48110275Ssam * to Mode, Structure, and Type. 48210275Ssam * 48310275Ssam * NB: Form isn't handled. 48410275Ssam */ 48510275Ssam send_data(instr, outstr) 48610275Ssam FILE *instr, *outstr; 48710275Ssam { 48810275Ssam register int c; 48910275Ssam int netfd, filefd, cnt; 49010275Ssam char buf[BUFSIZ]; 49110275Ssam 49226044Sminshall transflag++; 49326044Sminshall if (setjmp(urgcatch)) { 49426044Sminshall transflag = 0; 49526044Sminshall return(-1); 49626044Sminshall } 49710275Ssam switch (type) { 49810275Ssam 49910275Ssam case TYPE_A: 50010275Ssam while ((c = getc(instr)) != EOF) { 50111220Ssam if (c == '\n') { 50226044Sminshall if (ferror (outstr)) { 50326044Sminshall transflag = 0; 50411220Ssam return (1); 50526044Sminshall } 50627750Sminshall (void) putc('\r', outstr); 50711220Ssam } 50827750Sminshall (void) putc(c, outstr); 50926044Sminshall /* if (c == '\r') */ 51026044Sminshall /* putc ('\0', outstr); */ 51110275Ssam } 51226044Sminshall transflag = 0; 51326044Sminshall if (ferror (instr) || ferror (outstr)) { 51411220Ssam return (1); 51526044Sminshall } 51610275Ssam return (0); 51710275Ssam 51810275Ssam case TYPE_I: 51910275Ssam case TYPE_L: 52010275Ssam netfd = fileno(outstr); 52110275Ssam filefd = fileno(instr); 52210275Ssam 52326044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 52426044Sminshall if (write(netfd, buf, cnt) < 0) { 52526044Sminshall transflag = 0; 52610275Ssam return (1); 52726044Sminshall } 52826044Sminshall } 52926044Sminshall transflag = 0; 53010275Ssam return (cnt < 0); 53110275Ssam } 53227106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 53326044Sminshall transflag = 0; 53427106Smckusick return (-1); 53510275Ssam } 53610275Ssam 53710275Ssam /* 53810275Ssam * Transfer data from peer to 53910275Ssam * "outstr" using the appropriate 54010275Ssam * encapulation of the data subject 54110275Ssam * to Mode, Structure, and Type. 54210275Ssam * 54310275Ssam * N.B.: Form isn't handled. 54410275Ssam */ 54510275Ssam receive_data(instr, outstr) 54610275Ssam FILE *instr, *outstr; 54710275Ssam { 54810275Ssam register int c; 54911220Ssam int cnt; 55010275Ssam char buf[BUFSIZ]; 55110275Ssam 55210275Ssam 55326044Sminshall transflag++; 55426044Sminshall if (setjmp(urgcatch)) { 55526044Sminshall transflag = 0; 55626044Sminshall return(-1); 55726044Sminshall } 55810275Ssam switch (type) { 55910275Ssam 56010275Ssam case TYPE_I: 56110275Ssam case TYPE_L: 56226044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 56326044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 56426044Sminshall transflag = 0; 56510275Ssam return (1); 56626044Sminshall } 56726044Sminshall } 56826044Sminshall transflag = 0; 56910275Ssam return (cnt < 0); 57010275Ssam 57110275Ssam case TYPE_E: 57227106Smckusick reply(553, "TYPE E not implemented."); 57326044Sminshall transflag = 0; 57427106Smckusick return (-1); 57510275Ssam 57610275Ssam case TYPE_A: 57710275Ssam while ((c = getc(instr)) != EOF) { 57827750Sminshall while (c == '\r') { 57926044Sminshall if (ferror (outstr)) { 58026044Sminshall transflag = 0; 58111220Ssam return (1); 58226044Sminshall } 58311220Ssam if ((c = getc(instr)) != '\n') 58427750Sminshall (void) putc ('\r', outstr); 58526044Sminshall /* if (c == '\0') */ 58626044Sminshall /* continue; */ 58710275Ssam } 58827750Sminshall (void) putc (c, outstr); 58910275Ssam } 59026044Sminshall transflag = 0; 59111220Ssam if (ferror (instr) || ferror (outstr)) 59211220Ssam return (1); 59310275Ssam return (0); 59410275Ssam } 59526044Sminshall transflag = 0; 59610275Ssam fatal("Unknown type in receive_data."); 59710275Ssam /*NOTREACHED*/ 59810275Ssam } 59910275Ssam 60010275Ssam fatal(s) 60110275Ssam char *s; 60210275Ssam { 60310275Ssam reply(451, "Error in server: %s\n", s); 60410275Ssam reply(221, "Closing connection due to server error."); 60513247Ssam dologout(0); 60610275Ssam } 60710275Ssam 60832110Smckusick reply(n, s, p0, p1, p2, p3, p4) 60910275Ssam int n; 61010275Ssam char *s; 61110275Ssam { 61210275Ssam 61310275Ssam printf("%d ", n); 61432110Smckusick printf(s, p0, p1, p2, p3, p4); 61510275Ssam printf("\r\n"); 61626493Sminshall (void) fflush(stdout); 61710275Ssam if (debug) { 61826493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 61932110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 62010275Ssam } 62110275Ssam } 62210275Ssam 62332110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 62410275Ssam int n; 62510275Ssam char *s; 62610275Ssam { 62710275Ssam printf("%d-", n); 62832110Smckusick printf(s, p0, p1, p2, p3, p4); 62910275Ssam printf("\r\n"); 63026493Sminshall (void) fflush(stdout); 63110275Ssam if (debug) { 63226493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 63332110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 63410275Ssam } 63510275Ssam } 63610275Ssam 63710275Ssam ack(s) 63810275Ssam char *s; 63910275Ssam { 64027106Smckusick reply(250, "%s command successful.", s); 64110275Ssam } 64210275Ssam 64310275Ssam nack(s) 64410275Ssam char *s; 64510275Ssam { 64610275Ssam reply(502, "%s command not implemented.", s); 64710275Ssam } 64810275Ssam 64926493Sminshall yyerror(s) 65026493Sminshall char *s; 65110275Ssam { 65226044Sminshall char *cp; 65326044Sminshall 65426044Sminshall cp = index(cbuf,'\n'); 65526044Sminshall *cp = '\0'; 65626044Sminshall reply(500, "'%s': command not understood.",cbuf); 65710275Ssam } 65810275Ssam 65910275Ssam delete(name) 66010275Ssam char *name; 66110275Ssam { 66210275Ssam struct stat st; 66310275Ssam 66410275Ssam if (stat(name, &st) < 0) { 66510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 66610275Ssam return; 66710275Ssam } 66810275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 66910275Ssam if (rmdir(name) < 0) { 67010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67110275Ssam return; 67210275Ssam } 67310275Ssam goto done; 67410275Ssam } 67510275Ssam if (unlink(name) < 0) { 67610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67710275Ssam return; 67810275Ssam } 67910275Ssam done: 68010275Ssam ack("DELE"); 68110275Ssam } 68210275Ssam 68310275Ssam cwd(path) 68410275Ssam char *path; 68510275Ssam { 68610275Ssam 68710275Ssam if (chdir(path) < 0) { 68810275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 68910275Ssam return; 69010275Ssam } 69110275Ssam ack("CWD"); 69210275Ssam } 69310275Ssam 69410303Ssam makedir(name) 69510275Ssam char *name; 69610275Ssam { 69710303Ssam struct stat st; 69810303Ssam int dochown = stat(name, &st) < 0; 69910275Ssam 70010275Ssam if (mkdir(name, 0777) < 0) { 70110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70210275Ssam return; 70310275Ssam } 70410303Ssam if (dochown) 70510303Ssam (void) chown(name, pw->pw_uid, -1); 70627106Smckusick reply(257, "MKD command successful."); 70710275Ssam } 70810275Ssam 70910303Ssam removedir(name) 71010275Ssam char *name; 71110275Ssam { 71210275Ssam 71310275Ssam if (rmdir(name) < 0) { 71410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 71510275Ssam return; 71610275Ssam } 71727106Smckusick ack("RMD"); 71810275Ssam } 71910275Ssam 72010303Ssam pwd() 72110275Ssam { 72210303Ssam char path[MAXPATHLEN + 1]; 72310275Ssam 72410275Ssam if (getwd(path) == NULL) { 72527106Smckusick reply(550, "%s.", path); 72610275Ssam return; 72710275Ssam } 72827106Smckusick reply(257, "\"%s\" is current directory.", path); 72910275Ssam } 73010275Ssam 73110275Ssam char * 73210275Ssam renamefrom(name) 73310275Ssam char *name; 73410275Ssam { 73510275Ssam struct stat st; 73610275Ssam 73710275Ssam if (stat(name, &st) < 0) { 73810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 73910275Ssam return ((char *)0); 74010275Ssam } 74110303Ssam reply(350, "File exists, ready for destination name"); 74210275Ssam return (name); 74310275Ssam } 74410275Ssam 74510275Ssam renamecmd(from, to) 74610275Ssam char *from, *to; 74710275Ssam { 74810275Ssam 74910275Ssam if (rename(from, to) < 0) { 75010275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 75110275Ssam return; 75210275Ssam } 75310275Ssam ack("RNTO"); 75410275Ssam } 75510275Ssam 75610275Ssam dolog(sin) 75710275Ssam struct sockaddr_in *sin; 75810275Ssam { 75910275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 76010275Ssam sizeof (struct in_addr), AF_INET); 76110275Ssam time_t t; 76226493Sminshall extern char *ctime(); 76310275Ssam 76413247Ssam if (hp) { 76526493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 76613247Ssam endhostent(); 76713247Ssam } else 76826493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 76913247Ssam sizeof (remotehost)); 77013247Ssam if (!logging) 77113247Ssam return; 77226493Sminshall t = time((time_t *) 0); 77326493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 77410275Ssam } 77510695Ssam 77613247Ssam #include <utmp.h> 77713247Ssam 77826493Sminshall #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a)) 77913247Ssam struct utmp utmp; 78013247Ssam 78110695Ssam /* 78213247Ssam * Record login in wtmp file. 78313247Ssam */ 78413247Ssam dologin(pw) 78513247Ssam struct passwd *pw; 78613247Ssam { 78713247Ssam char line[32]; 78813247Ssam 78913247Ssam if (wtmp >= 0) { 79013247Ssam /* hack, but must be unique and no tty line */ 79126493Sminshall (void) sprintf(line, "ftp%d", getpid()); 79213247Ssam SCPYN(utmp.ut_line, line); 79313247Ssam SCPYN(utmp.ut_name, pw->pw_name); 79413247Ssam SCPYN(utmp.ut_host, remotehost); 79526493Sminshall utmp.ut_time = (long) time((time_t *) 0); 79613247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 79716760Slepreau if (!guest) { /* anon must hang on */ 79816760Slepreau (void) close(wtmp); 79916760Slepreau wtmp = -1; 80016760Slepreau } 80113247Ssam } 80213247Ssam } 80313247Ssam 80413247Ssam /* 80513247Ssam * Record logout in wtmp file 80613247Ssam * and exit with supplied status. 80713247Ssam */ 80813247Ssam dologout(status) 80913247Ssam int status; 81013247Ssam { 81116339Skarels 81217580Ssam if (logged_in) { 81317580Ssam (void) seteuid(0); 81417580Ssam if (wtmp < 0) 81517580Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 81617580Ssam if (wtmp >= 0) { 81717580Ssam SCPYN(utmp.ut_name, ""); 81817580Ssam SCPYN(utmp.ut_host, ""); 81926493Sminshall utmp.ut_time = (long) time((time_t *) 0); 82017580Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 82117580Ssam (void) close(wtmp); 82217580Ssam } 82313247Ssam } 82414436Ssam /* beware of flushing buffers after a SIGPIPE */ 82514436Ssam _exit(status); 82613247Ssam } 82713247Ssam 82813247Ssam /* 82910695Ssam * Special version of popen which avoids 83010695Ssam * call to shell. This insures noone may 83110695Ssam * create a pipe to a hidden program as a side 83210695Ssam * effect of a list or dir command. 83310695Ssam */ 83410695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 83510695Ssam #define RDR 0 83610695Ssam #define WTR 1 83710695Ssam static int popen_pid[5]; 83810695Ssam 83910695Ssam static char * 84010695Ssam nextarg(cpp) 84110695Ssam char *cpp; 84210695Ssam { 84310695Ssam register char *cp = cpp; 84410695Ssam 84510695Ssam if (cp == 0) 84610695Ssam return (cp); 84710695Ssam while (*cp && *cp != ' ' && *cp != '\t') 84810695Ssam cp++; 84910695Ssam if (*cp == ' ' || *cp == '\t') { 85010695Ssam *cp++ = '\0'; 85110695Ssam while (*cp == ' ' || *cp == '\t') 85210695Ssam cp++; 85310695Ssam } 85410695Ssam if (cp == cpp) 85510695Ssam return ((char *)0); 85610695Ssam return (cp); 85710695Ssam } 85810695Ssam 85910695Ssam FILE * 86010695Ssam popen(cmd, mode) 86110695Ssam char *cmd, *mode; 86210695Ssam { 86313211Sroot int p[2], ac, gac; 86410695Ssam register myside, hisside, pid; 86513211Sroot char *av[20], *gav[512]; 86610695Ssam register char *cp; 86710695Ssam 86810695Ssam if (pipe(p) < 0) 86910695Ssam return (NULL); 87010695Ssam cp = cmd, ac = 0; 87113211Sroot /* break up string into pieces */ 87210695Ssam do { 87310695Ssam av[ac++] = cp; 87410695Ssam cp = nextarg(cp); 87513211Sroot } while (cp && *cp && ac < 20); 87610695Ssam av[ac] = (char *)0; 87713211Sroot gav[0] = av[0]; 87813211Sroot /* glob each piece */ 87913211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 88013211Sroot char **pop; 88122024Ssam extern char **glob(), **copyblk(); 88213211Sroot 88313211Sroot pop = glob(av[ac]); 88422024Ssam if (pop == (char **)NULL) { /* globbing failed */ 88522024Ssam char *vv[2]; 88622024Ssam 88722024Ssam vv[0] = av[ac]; 88822024Ssam vv[1] = 0; 88922024Ssam pop = copyblk(vv); 89013211Sroot } 89122024Ssam av[ac] = (char *)pop; /* save to free later */ 89222024Ssam while (*pop && gac < 512) 89322024Ssam gav[gac++] = *pop++; 89411757Ssam } 89513211Sroot gav[gac] = (char *)0; 89610695Ssam myside = tst(p[WTR], p[RDR]); 89710695Ssam hisside = tst(p[RDR], p[WTR]); 89810695Ssam if ((pid = fork()) == 0) { 89910695Ssam /* myside and hisside reverse roles in child */ 90026493Sminshall (void) close(myside); 90126493Sminshall (void) dup2(hisside, tst(0, 1)); 90226493Sminshall (void) close(hisside); 90313211Sroot execv(gav[0], gav); 90410695Ssam _exit(1); 90510695Ssam } 90613211Sroot for (ac = 1; av[ac] != NULL; ac++) 90713211Sroot blkfree((char **)av[ac]); 90810695Ssam if (pid == -1) 90910695Ssam return (NULL); 91010695Ssam popen_pid[myside] = pid; 91126493Sminshall (void) close(hisside); 91210695Ssam return (fdopen(myside, mode)); 91310695Ssam } 91410695Ssam 91510695Ssam pclose(ptr) 91610695Ssam FILE *ptr; 91710695Ssam { 91810695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 91910695Ssam int status; 92010695Ssam 92110695Ssam f = fileno(ptr); 92226493Sminshall (void) fclose(ptr); 92310695Ssam istat = signal(SIGINT, SIG_IGN); 92410695Ssam qstat = signal(SIGQUIT, SIG_IGN); 92510695Ssam hstat = signal(SIGHUP, SIG_IGN); 92610695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 92710695Ssam ; 92810695Ssam if (r == -1) 92910695Ssam status = -1; 93026493Sminshall (void) signal(SIGINT, istat); 93126493Sminshall (void) signal(SIGQUIT, qstat); 93226493Sminshall (void) signal(SIGHUP, hstat); 93310695Ssam return (status); 93410695Ssam } 93510695Ssam 93610695Ssam /* 93710695Ssam * Check user requesting login priviledges. 93828864Smckusick * Disallow anyone who does not have a standard 93928864Smckusick * shell returned by getusershell() (/etc/shells). 94010695Ssam * Disallow anyone mentioned in the file FTPUSERS 94110695Ssam * to allow people such as uucp to be avoided. 94210695Ssam */ 94310695Ssam checkuser(name) 94410695Ssam register char *name; 94510695Ssam { 94628864Smckusick register char *cp; 94728864Smckusick char line[BUFSIZ], *index(), *getusershell(); 94810695Ssam FILE *fd; 94928864Smckusick struct passwd *pw; 95010695Ssam int found = 0; 95110695Ssam 95228864Smckusick pw = getpwnam(name); 95328864Smckusick if (pw == NULL) 95428864Smckusick return (0); 95530102Sbostic if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL) 95630102Sbostic pw->pw_shell = "/bin/sh"; 95728864Smckusick while ((cp = getusershell()) != NULL) 95828864Smckusick if (strcmp(cp, pw->pw_shell) == 0) 95928864Smckusick break; 96028864Smckusick endusershell(); 96128864Smckusick if (cp == NULL) 96228864Smckusick return (0); 96310695Ssam fd = fopen(FTPUSERS, "r"); 96410695Ssam if (fd == NULL) 96510695Ssam return (1); 96610695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 96728864Smckusick cp = index(line, '\n'); 96810695Ssam if (cp) 96910695Ssam *cp = '\0'; 97010695Ssam if (strcmp(line, name) == 0) { 97110695Ssam found++; 97210695Ssam break; 97310695Ssam } 97410695Ssam } 97526493Sminshall (void) fclose(fd); 97610695Ssam return (!found); 97710695Ssam } 97826044Sminshall 97926044Sminshall myoob() 98026044Sminshall { 98127750Sminshall char *cp; 98226044Sminshall 98327750Sminshall /* only process if transfer occurring */ 98426044Sminshall if (!transflag) { 98526044Sminshall return; 98626044Sminshall } 98727750Sminshall cp = tmpline; 98827750Sminshall if (getline(cp, 7, stdin) == NULL) { 98927750Sminshall reply(221, "You could at least say goodby."); 99027750Sminshall dologout(0); 99126044Sminshall } 99226044Sminshall upper(cp); 99326227Ssam if (strcmp(cp, "ABOR\r\n")) 99426044Sminshall return; 99526044Sminshall tmpline[0] = '\0'; 99626044Sminshall reply(426,"Transfer aborted. Data connection closed."); 99726044Sminshall reply(226,"Abort successful"); 99826044Sminshall longjmp(urgcatch, 1); 99926044Sminshall } 100026044Sminshall 100127106Smckusick /* 100227106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 100327106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 100427106Smckusick */ 100526044Sminshall passive() 100626044Sminshall { 100726044Sminshall int len; 100826044Sminshall struct sockaddr_in tmp; 100926044Sminshall register char *p, *a; 101026044Sminshall 101126044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 101226044Sminshall if (pdata < 0) { 101327106Smckusick reply(530, "Can't open passive connection"); 101426044Sminshall return; 101526044Sminshall } 101626044Sminshall tmp = ctrl_addr; 101726044Sminshall tmp.sin_port = 0; 101826044Sminshall seteuid(0); 101926493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 102026044Sminshall seteuid(pw->pw_uid); 102126044Sminshall (void) close(pdata); 102226044Sminshall pdata = -1; 102327106Smckusick reply(530, "Can't open passive connection"); 102426044Sminshall return; 102526044Sminshall } 102626044Sminshall seteuid(pw->pw_uid); 102726044Sminshall len = sizeof(tmp); 102826044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 102926044Sminshall (void) close(pdata); 103026044Sminshall pdata = -1; 103127106Smckusick reply(530, "Can't open passive connection"); 103226044Sminshall return; 103326044Sminshall } 103426044Sminshall if (listen(pdata, 1) < 0) { 103526044Sminshall (void) close(pdata); 103626044Sminshall pdata = -1; 103727106Smckusick reply(530, "Can't open passive connection"); 103826044Sminshall return; 103926044Sminshall } 104026044Sminshall a = (char *) &tmp.sin_addr; 104126044Sminshall p = (char *) &tmp.sin_port; 104226044Sminshall 104326044Sminshall #define UC(b) (((int) b) & 0xff) 104426044Sminshall 104526044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 104626044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 104726044Sminshall } 104826044Sminshall 104926044Sminshall char * 105026044Sminshall gunique(local) 105126044Sminshall char *local; 105226044Sminshall { 105326044Sminshall static char new[MAXPATHLEN]; 105426044Sminshall char *cp = rindex(local, '/'); 105526044Sminshall int d, count=0; 105626044Sminshall char ext = '1'; 105726044Sminshall 105826044Sminshall if (cp) { 105926044Sminshall *cp = '\0'; 106026044Sminshall } 106126044Sminshall d = access(cp ? local : ".", 2); 106226044Sminshall if (cp) { 106326044Sminshall *cp = '/'; 106426044Sminshall } 106526044Sminshall if (d < 0) { 106626493Sminshall syslog(LOG_ERR, "%s: %m", local); 106726044Sminshall return((char *) 0); 106826044Sminshall } 106926044Sminshall (void) strcpy(new, local); 107026044Sminshall cp = new + strlen(new); 107126044Sminshall *cp++ = '.'; 107226044Sminshall while (!d) { 107326044Sminshall if (++count == 100) { 107427106Smckusick reply(452, "Unique file name not cannot be created."); 107526044Sminshall return((char *) 0); 107626044Sminshall } 107726044Sminshall *cp++ = ext; 107826044Sminshall *cp = '\0'; 107926044Sminshall if (ext == '9') { 108026044Sminshall ext = '0'; 108126044Sminshall } 108226044Sminshall else { 108326044Sminshall ext++; 108426044Sminshall } 108526044Sminshall if ((d = access(new, 0)) < 0) { 108626044Sminshall break; 108726044Sminshall } 108826044Sminshall if (ext != '0') { 108926044Sminshall cp--; 109026044Sminshall } 109126044Sminshall else if (*(cp - 2) == '.') { 109226044Sminshall *(cp - 1) = '1'; 109326044Sminshall } 109426044Sminshall else { 109526044Sminshall *(cp - 2) = *(cp - 2) + 1; 109626044Sminshall cp--; 109726044Sminshall } 109826044Sminshall } 109926044Sminshall return(new); 110026044Sminshall } 1101