122499Sdist /* 226044Sminshall * Copyright (c) 1985 Regents of the University of California. 322499Sdist * All rights reserved. The Berkeley software License Agreement 422499Sdist * specifies the terms and conditions for redistribution. 522499Sdist */ 622499Sdist 710275Ssam #ifndef lint 822499Sdist char copyright[] = 926044Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 1022499Sdist All rights reserved.\n"; 1122499Sdist #endif not lint 1210275Ssam 1322499Sdist #ifndef lint 14*32110Smckusick static char sccsid[] = "@(#)ftpd.c 5.10 (Berkeley) 09/04/87"; 1522499Sdist #endif not lint 1622499Sdist 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> 3126044Sminshall #include <arpa/telnet.h> 3213034Ssam 3310275Ssam #include <stdio.h> 3410275Ssam #include <signal.h> 3510275Ssam #include <pwd.h> 3610275Ssam #include <setjmp.h> 3710275Ssam #include <netdb.h> 3810423Ssam #include <errno.h> 3926044Sminshall #include <strings.h> 4026493Sminshall #include <syslog.h> 4110275Ssam 4210695Ssam /* 4310695Ssam * File containing login names 4410695Ssam * NOT to be used on this machine. 4510695Ssam * Commonly used to disallow uucp. 4610695Ssam */ 4710695Ssam #define FTPUSERS "/etc/ftpusers" 4810695Ssam 4910275Ssam extern int errno; 5010275Ssam extern char *sys_errlist[]; 5110275Ssam extern char *crypt(); 5210275Ssam extern char version[]; 5310275Ssam extern char *home; /* pointer to home directory for glob */ 5426044Sminshall extern FILE *popen(), *fopen(), *freopen(); 5526493Sminshall extern int pclose(), fclose(); 5626044Sminshall extern char *getline(); 5726044Sminshall extern char cbuf[]; 5810275Ssam 5910275Ssam struct sockaddr_in ctrl_addr; 6010275Ssam struct sockaddr_in data_source; 6110275Ssam struct sockaddr_in data_dest; 6210275Ssam struct sockaddr_in his_addr; 6310275Ssam 6410275Ssam int data; 6526044Sminshall jmp_buf errcatch, urgcatch; 6610275Ssam int logged_in; 6710275Ssam struct passwd *pw; 6810275Ssam int debug; 6926493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 7011757Ssam int logging; 7110275Ssam int guest; 7216033Sralph int wtmp; 7310275Ssam int type; 7410275Ssam int form; 7510275Ssam int stru; /* avoid C keyword */ 7610275Ssam int mode; 7710321Ssam int usedefault = 1; /* for data transfers */ 7826044Sminshall int pdata; /* for passive mode */ 7926044Sminshall int unique; 8026044Sminshall int transflag; 8126044Sminshall char tmpline[7]; 8210275Ssam char hostname[32]; 8313247Ssam char remotehost[32]; 8410275Ssam 8511653Ssam /* 8611653Ssam * Timeout intervals for retrying connections 8711653Ssam * to hosts that don't accept PORT cmds. This 8811653Ssam * is a kludge, but given the problems with TCP... 8911653Ssam */ 9011653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 9111653Ssam #define SWAITINT 5 /* interval between retries */ 9211653Ssam 9311653Ssam int swaitmax = SWAITMAX; 9411653Ssam int swaitint = SWAITINT; 9511653Ssam 9610275Ssam int lostconn(); 9726044Sminshall int myoob(); 9810275Ssam FILE *getdatasock(), *dataconn(); 9910275Ssam 10010275Ssam main(argc, argv) 10110275Ssam int argc; 10210275Ssam char *argv[]; 10310275Ssam { 10427750Sminshall int addrlen, on = 1; 10526044Sminshall long pgid; 10610275Ssam char *cp; 10710275Ssam 10816339Skarels addrlen = sizeof (his_addr); 10916339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 11026493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 11110275Ssam exit(1); 11210275Ssam } 11316339Skarels addrlen = sizeof (ctrl_addr); 11426493Sminshall if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) { 11526493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 11616339Skarels exit(1); 11716339Skarels } 11816339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 11910275Ssam debug = 0; 12026493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 12110275Ssam argc--, argv++; 12210275Ssam while (argc > 0 && *argv[0] == '-') { 12310275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 12410275Ssam 12511653Ssam case 'v': 12611653Ssam debug = 1; 12711653Ssam break; 12811653Ssam 12910275Ssam case 'd': 13010275Ssam debug = 1; 13110275Ssam break; 13210275Ssam 13311757Ssam case 'l': 13411757Ssam logging = 1; 13511757Ssam break; 13611757Ssam 13711653Ssam case 't': 13811653Ssam timeout = atoi(++cp); 13911653Ssam goto nextopt; 14011653Ssam break; 14111653Ssam 14210275Ssam default: 14316339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 14416339Skarels *cp); 14510275Ssam break; 14610275Ssam } 14711653Ssam nextopt: 14810275Ssam argc--, argv++; 14910275Ssam } 15030944Scsvsj (void) freopen("/dev/null", "w", stderr); 15126493Sminshall (void) signal(SIGPIPE, lostconn); 15226493Sminshall (void) signal(SIGCHLD, SIG_IGN); 15326044Sminshall if (signal(SIGURG, myoob) < 0) { 15426493Sminshall syslog(LOG_ERR, "signal: %m"); 15526044Sminshall } 15627750Sminshall /* handle urgent data inline */ 15727750Sminshall #ifdef SO_OOBINLINE 15827750Sminshall if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { 15927750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 16027750Sminshall } 16127750Sminshall #endif SO_OOBINLINE 16226044Sminshall pgid = getpid(); 16327750Sminshall if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) { 16426493Sminshall syslog(LOG_ERR, "ioctl: %m"); 16526044Sminshall } 16616760Slepreau dolog(&his_addr); 16716339Skarels /* do telnet option negotiation here */ 16816339Skarels /* 16916339Skarels * Set up default state 17016339Skarels */ 17116339Skarels logged_in = 0; 17216339Skarels data = -1; 17316339Skarels type = TYPE_A; 17416339Skarels form = FORM_N; 17516339Skarels stru = STRU_F; 17616339Skarels mode = MODE_S; 17726044Sminshall tmpline[0] = '\0'; 17826493Sminshall (void) gethostname(hostname, sizeof (hostname)); 17916339Skarels reply(220, "%s FTP server (%s) ready.", 18016339Skarels hostname, version); 18110275Ssam for (;;) { 18226493Sminshall (void) setjmp(errcatch); 18326493Sminshall (void) yyparse(); 18410275Ssam } 18510275Ssam } 18610419Ssam 18710275Ssam lostconn() 18810275Ssam { 18910275Ssam 19014089Ssam if (debug) 19126493Sminshall syslog(LOG_DEBUG, "lost connection"); 19214089Ssam dologout(-1); 19310275Ssam } 19410275Ssam 19510275Ssam pass(passwd) 19610275Ssam char *passwd; 19710275Ssam { 19810303Ssam char *xpasswd, *savestr(); 19910303Ssam static struct passwd save; 20010275Ssam 20110275Ssam if (logged_in || pw == NULL) { 20210275Ssam reply(503, "Login with USER first."); 20310275Ssam return; 20410275Ssam } 20510275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 20610275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 20716760Slepreau /* The strcmp does not catch null passwords! */ 20816760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 20910275Ssam reply(530, "Login incorrect."); 21010275Ssam pw = NULL; 21110275Ssam return; 21210275Ssam } 21310275Ssam } 21410303Ssam setegid(pw->pw_gid); 21510275Ssam initgroups(pw->pw_name, pw->pw_gid); 21610275Ssam if (chdir(pw->pw_dir)) { 21727106Smckusick reply(530, "User %s: can't change directory to %s.", 21810275Ssam pw->pw_name, pw->pw_dir); 21910303Ssam goto bad; 22010275Ssam } 22116033Sralph 22216760Slepreau /* grab wtmp before chroot */ 22316760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 22410303Ssam if (guest && chroot(pw->pw_dir) < 0) { 22510275Ssam reply(550, "Can't set guest privileges."); 22616760Slepreau if (wtmp >= 0) { 22716760Slepreau (void) close(wtmp); 22816760Slepreau wtmp = -1; 22916760Slepreau } 23010303Ssam goto bad; 23110275Ssam } 23210275Ssam if (!guest) 23310275Ssam reply(230, "User %s logged in.", pw->pw_name); 23410275Ssam else 23510275Ssam reply(230, "Guest login ok, access restrictions apply."); 23610275Ssam logged_in = 1; 23713247Ssam dologin(pw); 23810303Ssam seteuid(pw->pw_uid); 23910303Ssam /* 24010303Ssam * Save everything so globbing doesn't 24110303Ssam * clobber the fields. 24210303Ssam */ 24310303Ssam save = *pw; 24410303Ssam save.pw_name = savestr(pw->pw_name); 24510303Ssam save.pw_passwd = savestr(pw->pw_passwd); 24610303Ssam save.pw_comment = savestr(pw->pw_comment); 24726493Sminshall save.pw_gecos = savestr(pw->pw_gecos); 24810303Ssam save.pw_dir = savestr(pw->pw_dir); 24910303Ssam save.pw_shell = savestr(pw->pw_shell); 25010303Ssam pw = &save; 25110303Ssam home = pw->pw_dir; /* home dir for globbing */ 25210303Ssam return; 25310303Ssam bad: 25410303Ssam seteuid(0); 25510303Ssam pw = NULL; 25610275Ssam } 25710275Ssam 25810303Ssam char * 25910303Ssam savestr(s) 26010303Ssam char *s; 26110303Ssam { 26210303Ssam char *malloc(); 26326493Sminshall char *new = malloc((unsigned) strlen(s) + 1); 26410303Ssam 26510303Ssam if (new != NULL) 26626493Sminshall (void) strcpy(new, s); 26711347Ssam return (new); 26810303Ssam } 26910303Ssam 27010275Ssam retrieve(cmd, name) 27110275Ssam char *cmd, *name; 27210275Ssam { 27310275Ssam FILE *fin, *dout; 27410275Ssam struct stat st; 27526044Sminshall int (*closefunc)(), tmp; 27610275Ssam 27710275Ssam if (cmd == 0) { 27810317Ssam #ifdef notdef 27910317Ssam /* no remote command execution -- it's a security hole */ 28011653Ssam if (*name == '|') 28110275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 28210275Ssam else 28310317Ssam #endif 28410275Ssam fin = fopen(name, "r"), closefunc = fclose; 28510275Ssam } else { 28610275Ssam char line[BUFSIZ]; 28710275Ssam 28826493Sminshall (void) sprintf(line, cmd, name), name = line; 28910275Ssam fin = popen(line, "r"), closefunc = pclose; 29010275Ssam } 29110275Ssam if (fin == NULL) { 29213152Ssam if (errno != 0) 29313152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 29410275Ssam return; 29510275Ssam } 29610275Ssam st.st_size = 0; 29710275Ssam if (cmd == 0 && 29810275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 29910275Ssam reply(550, "%s: not a plain file.", name); 30010275Ssam goto done; 30110275Ssam } 30210275Ssam dout = dataconn(name, st.st_size, "w"); 30310275Ssam if (dout == NULL) 30410275Ssam goto done; 30526044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 30610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30726044Sminshall } 30826044Sminshall else if (tmp == 0) { 30910275Ssam reply(226, "Transfer complete."); 31026044Sminshall } 31126493Sminshall (void) fclose(dout); 31226044Sminshall data = -1; 31326044Sminshall pdata = -1; 31410275Ssam done: 31510275Ssam (*closefunc)(fin); 31610275Ssam } 31710275Ssam 31810275Ssam store(name, mode) 31910275Ssam char *name, *mode; 32010275Ssam { 32110275Ssam FILE *fout, *din; 32226044Sminshall int (*closefunc)(), dochown = 0, tmp; 32326044Sminshall char *gunique(), *local; 32410275Ssam 32510317Ssam #ifdef notdef 32610317Ssam /* no remote command execution -- it's a security hole */ 32711653Ssam if (name[0] == '|') 32810275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 32910317Ssam else 33010317Ssam #endif 33110317Ssam { 33210303Ssam struct stat st; 33310303Ssam 33426044Sminshall local = name; 33526044Sminshall if (stat(name, &st) < 0) { 33610303Ssam dochown++; 33726044Sminshall } 33826044Sminshall else if (unique) { 33926044Sminshall if ((local = gunique(name)) == NULL) { 34026044Sminshall return; 34126044Sminshall } 34226044Sminshall dochown++; 34326044Sminshall } 34426044Sminshall fout = fopen(local, mode), closefunc = fclose; 34510303Ssam } 34610275Ssam if (fout == NULL) { 34727106Smckusick reply(553, "%s: %s.", local, sys_errlist[errno]); 34810275Ssam return; 34910275Ssam } 35026044Sminshall din = dataconn(local, (off_t)-1, "r"); 35110275Ssam if (din == NULL) 35210275Ssam goto done; 35326044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 35427106Smckusick reply(552, "%s: %s.", local, sys_errlist[errno]); 35526044Sminshall } 35626044Sminshall else if (tmp == 0 && !unique) { 35710275Ssam reply(226, "Transfer complete."); 35826044Sminshall } 35926044Sminshall else if (tmp == 0 && unique) { 36026044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 36126044Sminshall } 36226493Sminshall (void) fclose(din); 36326044Sminshall data = -1; 36426044Sminshall pdata = -1; 36510275Ssam done: 36610303Ssam if (dochown) 36726044Sminshall (void) chown(local, pw->pw_uid, -1); 36810275Ssam (*closefunc)(fout); 36910275Ssam } 37010275Ssam 37110275Ssam FILE * 37210275Ssam getdatasock(mode) 37310275Ssam char *mode; 37410275Ssam { 37517157Ssam int s, on = 1; 37610275Ssam 37710275Ssam if (data >= 0) 37810275Ssam return (fdopen(data, mode)); 37913247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 38010602Ssam if (s < 0) 38110275Ssam return (NULL); 38210275Ssam seteuid(0); 38326493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 38410602Ssam goto bad; 38513152Ssam /* anchor socket to avoid multi-homing problems */ 38613152Ssam data_source.sin_family = AF_INET; 38713152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 38826493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 38910602Ssam goto bad; 39010311Ssam seteuid(pw->pw_uid); 39110275Ssam return (fdopen(s, mode)); 39210602Ssam bad: 39310602Ssam seteuid(pw->pw_uid); 39426493Sminshall (void) close(s); 39510602Ssam return (NULL); 39610275Ssam } 39710275Ssam 39810275Ssam FILE * 39910275Ssam dataconn(name, size, mode) 40010275Ssam char *name; 40111653Ssam off_t size; 40210275Ssam char *mode; 40310275Ssam { 40410275Ssam char sizebuf[32]; 40510275Ssam FILE *file; 40611653Ssam int retry = 0; 40710275Ssam 40810275Ssam if (size >= 0) 40926493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 41010275Ssam else 41110275Ssam (void) strcpy(sizebuf, ""); 41226044Sminshall if (pdata > 0) { 41326044Sminshall struct sockaddr_in from; 41426044Sminshall int s, fromlen = sizeof(from); 41526044Sminshall 41626493Sminshall s = accept(pdata, &from, &fromlen); 41726044Sminshall if (s < 0) { 41826044Sminshall reply(425, "Can't open data connection."); 41926044Sminshall (void) close(pdata); 42026044Sminshall pdata = -1; 42126044Sminshall return(NULL); 42226044Sminshall } 42326044Sminshall (void) close(pdata); 42426044Sminshall pdata = s; 42526044Sminshall reply(150, "Openning data connection for %s (%s,%d)%s.", 42626493Sminshall name, inet_ntoa(from.sin_addr), 42726044Sminshall ntohs(from.sin_port), sizebuf); 42826044Sminshall return(fdopen(pdata, mode)); 42926044Sminshall } 43010275Ssam if (data >= 0) { 43110275Ssam reply(125, "Using existing data connection for %s%s.", 43210275Ssam name, sizebuf); 43310321Ssam usedefault = 1; 43410275Ssam return (fdopen(data, mode)); 43510275Ssam } 43610566Ssam if (usedefault) 43710422Ssam data_dest = his_addr; 43810422Ssam usedefault = 1; 43910275Ssam file = getdatasock(mode); 44010275Ssam if (file == NULL) { 44110275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 44213247Ssam inet_ntoa(data_source.sin_addr), 44310275Ssam ntohs(data_source.sin_port), 44410275Ssam sys_errlist[errno]); 44510275Ssam return (NULL); 44610275Ssam } 44710275Ssam data = fileno(file); 44826044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 44911653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 45026493Sminshall sleep((unsigned) swaitint); 45111653Ssam retry += swaitint; 45211653Ssam continue; 45311653Ssam } 45410275Ssam reply(425, "Can't build data connection: %s.", 45510275Ssam sys_errlist[errno]); 45610275Ssam (void) fclose(file); 45710275Ssam data = -1; 45810275Ssam return (NULL); 45910275Ssam } 46027750Sminshall reply(150, "Opening data connection for %s (%s,%d)%s.", 46127750Sminshall name, inet_ntoa(data_dest.sin_addr), 46227750Sminshall ntohs(data_dest.sin_port), sizebuf); 46310275Ssam return (file); 46410275Ssam } 46510275Ssam 46610275Ssam /* 46710275Ssam * Tranfer the contents of "instr" to 46810275Ssam * "outstr" peer using the appropriate 46910275Ssam * encapulation of the date subject 47010275Ssam * to Mode, Structure, and Type. 47110275Ssam * 47210275Ssam * NB: Form isn't handled. 47310275Ssam */ 47410275Ssam send_data(instr, outstr) 47510275Ssam FILE *instr, *outstr; 47610275Ssam { 47710275Ssam register int c; 47810275Ssam int netfd, filefd, cnt; 47910275Ssam char buf[BUFSIZ]; 48010275Ssam 48126044Sminshall transflag++; 48226044Sminshall if (setjmp(urgcatch)) { 48326044Sminshall transflag = 0; 48426044Sminshall return(-1); 48526044Sminshall } 48610275Ssam switch (type) { 48710275Ssam 48810275Ssam case TYPE_A: 48910275Ssam while ((c = getc(instr)) != EOF) { 49011220Ssam if (c == '\n') { 49126044Sminshall if (ferror (outstr)) { 49226044Sminshall transflag = 0; 49311220Ssam return (1); 49426044Sminshall } 49527750Sminshall (void) putc('\r', outstr); 49611220Ssam } 49727750Sminshall (void) putc(c, outstr); 49826044Sminshall /* if (c == '\r') */ 49926044Sminshall /* putc ('\0', outstr); */ 50010275Ssam } 50126044Sminshall transflag = 0; 50226044Sminshall if (ferror (instr) || ferror (outstr)) { 50311220Ssam return (1); 50426044Sminshall } 50510275Ssam return (0); 50610275Ssam 50710275Ssam case TYPE_I: 50810275Ssam case TYPE_L: 50910275Ssam netfd = fileno(outstr); 51010275Ssam filefd = fileno(instr); 51110275Ssam 51226044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 51326044Sminshall if (write(netfd, buf, cnt) < 0) { 51426044Sminshall transflag = 0; 51510275Ssam return (1); 51626044Sminshall } 51726044Sminshall } 51826044Sminshall transflag = 0; 51910275Ssam return (cnt < 0); 52010275Ssam } 52127106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 52226044Sminshall transflag = 0; 52327106Smckusick return (-1); 52410275Ssam } 52510275Ssam 52610275Ssam /* 52710275Ssam * Transfer data from peer to 52810275Ssam * "outstr" using the appropriate 52910275Ssam * encapulation of the data subject 53010275Ssam * to Mode, Structure, and Type. 53110275Ssam * 53210275Ssam * N.B.: Form isn't handled. 53310275Ssam */ 53410275Ssam receive_data(instr, outstr) 53510275Ssam FILE *instr, *outstr; 53610275Ssam { 53710275Ssam register int c; 53811220Ssam int cnt; 53910275Ssam char buf[BUFSIZ]; 54010275Ssam 54110275Ssam 54226044Sminshall transflag++; 54326044Sminshall if (setjmp(urgcatch)) { 54426044Sminshall transflag = 0; 54526044Sminshall return(-1); 54626044Sminshall } 54710275Ssam switch (type) { 54810275Ssam 54910275Ssam case TYPE_I: 55010275Ssam case TYPE_L: 55126044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 55226044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 55326044Sminshall transflag = 0; 55410275Ssam return (1); 55526044Sminshall } 55626044Sminshall } 55726044Sminshall transflag = 0; 55810275Ssam return (cnt < 0); 55910275Ssam 56010275Ssam case TYPE_E: 56127106Smckusick reply(553, "TYPE E not implemented."); 56226044Sminshall transflag = 0; 56327106Smckusick return (-1); 56410275Ssam 56510275Ssam case TYPE_A: 56610275Ssam while ((c = getc(instr)) != EOF) { 56727750Sminshall while (c == '\r') { 56826044Sminshall if (ferror (outstr)) { 56926044Sminshall transflag = 0; 57011220Ssam return (1); 57126044Sminshall } 57211220Ssam if ((c = getc(instr)) != '\n') 57327750Sminshall (void) putc ('\r', outstr); 57426044Sminshall /* if (c == '\0') */ 57526044Sminshall /* continue; */ 57610275Ssam } 57727750Sminshall (void) putc (c, outstr); 57810275Ssam } 57926044Sminshall transflag = 0; 58011220Ssam if (ferror (instr) || ferror (outstr)) 58111220Ssam return (1); 58210275Ssam return (0); 58310275Ssam } 58426044Sminshall transflag = 0; 58510275Ssam fatal("Unknown type in receive_data."); 58610275Ssam /*NOTREACHED*/ 58710275Ssam } 58810275Ssam 58910275Ssam fatal(s) 59010275Ssam char *s; 59110275Ssam { 59210275Ssam reply(451, "Error in server: %s\n", s); 59310275Ssam reply(221, "Closing connection due to server error."); 59413247Ssam dologout(0); 59510275Ssam } 59610275Ssam 597*32110Smckusick reply(n, s, p0, p1, p2, p3, p4) 59810275Ssam int n; 59910275Ssam char *s; 60010275Ssam { 60110275Ssam 60210275Ssam printf("%d ", n); 603*32110Smckusick printf(s, p0, p1, p2, p3, p4); 60410275Ssam printf("\r\n"); 60526493Sminshall (void) fflush(stdout); 60610275Ssam if (debug) { 60726493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 608*32110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 60910275Ssam } 61010275Ssam } 61110275Ssam 612*32110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 61310275Ssam int n; 61410275Ssam char *s; 61510275Ssam { 61610275Ssam printf("%d-", n); 617*32110Smckusick printf(s, p0, p1, p2, p3, p4); 61810275Ssam printf("\r\n"); 61926493Sminshall (void) fflush(stdout); 62010275Ssam if (debug) { 62126493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 622*32110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 62310275Ssam } 62410275Ssam } 62510275Ssam 62610275Ssam ack(s) 62710275Ssam char *s; 62810275Ssam { 62927106Smckusick reply(250, "%s command successful.", s); 63010275Ssam } 63110275Ssam 63210275Ssam nack(s) 63310275Ssam char *s; 63410275Ssam { 63510275Ssam reply(502, "%s command not implemented.", s); 63610275Ssam } 63710275Ssam 63826493Sminshall yyerror(s) 63926493Sminshall char *s; 64010275Ssam { 64126044Sminshall char *cp; 64226044Sminshall 64326044Sminshall cp = index(cbuf,'\n'); 64426044Sminshall *cp = '\0'; 64526044Sminshall reply(500, "'%s': command not understood.",cbuf); 64610275Ssam } 64710275Ssam 64810275Ssam delete(name) 64910275Ssam char *name; 65010275Ssam { 65110275Ssam struct stat st; 65210275Ssam 65310275Ssam if (stat(name, &st) < 0) { 65410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 65510275Ssam return; 65610275Ssam } 65710275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 65810275Ssam if (rmdir(name) < 0) { 65910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 66010275Ssam return; 66110275Ssam } 66210275Ssam goto done; 66310275Ssam } 66410275Ssam if (unlink(name) < 0) { 66510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 66610275Ssam return; 66710275Ssam } 66810275Ssam done: 66910275Ssam ack("DELE"); 67010275Ssam } 67110275Ssam 67210275Ssam cwd(path) 67310275Ssam char *path; 67410275Ssam { 67510275Ssam 67610275Ssam if (chdir(path) < 0) { 67710275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 67810275Ssam return; 67910275Ssam } 68010275Ssam ack("CWD"); 68110275Ssam } 68210275Ssam 68310303Ssam makedir(name) 68410275Ssam char *name; 68510275Ssam { 68610303Ssam struct stat st; 68710303Ssam int dochown = stat(name, &st) < 0; 68810275Ssam 68910275Ssam if (mkdir(name, 0777) < 0) { 69010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 69110275Ssam return; 69210275Ssam } 69310303Ssam if (dochown) 69410303Ssam (void) chown(name, pw->pw_uid, -1); 69527106Smckusick reply(257, "MKD command successful."); 69610275Ssam } 69710275Ssam 69810303Ssam removedir(name) 69910275Ssam char *name; 70010275Ssam { 70110275Ssam 70210275Ssam if (rmdir(name) < 0) { 70310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70410275Ssam return; 70510275Ssam } 70627106Smckusick ack("RMD"); 70710275Ssam } 70810275Ssam 70910303Ssam pwd() 71010275Ssam { 71110303Ssam char path[MAXPATHLEN + 1]; 71210275Ssam 71310275Ssam if (getwd(path) == NULL) { 71427106Smckusick reply(550, "%s.", path); 71510275Ssam return; 71610275Ssam } 71727106Smckusick reply(257, "\"%s\" is current directory.", path); 71810275Ssam } 71910275Ssam 72010275Ssam char * 72110275Ssam renamefrom(name) 72210275Ssam char *name; 72310275Ssam { 72410275Ssam struct stat st; 72510275Ssam 72610275Ssam if (stat(name, &st) < 0) { 72710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 72810275Ssam return ((char *)0); 72910275Ssam } 73010303Ssam reply(350, "File exists, ready for destination name"); 73110275Ssam return (name); 73210275Ssam } 73310275Ssam 73410275Ssam renamecmd(from, to) 73510275Ssam char *from, *to; 73610275Ssam { 73710275Ssam 73810275Ssam if (rename(from, to) < 0) { 73910275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 74010275Ssam return; 74110275Ssam } 74210275Ssam ack("RNTO"); 74310275Ssam } 74410275Ssam 74510275Ssam dolog(sin) 74610275Ssam struct sockaddr_in *sin; 74710275Ssam { 74810275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 74910275Ssam sizeof (struct in_addr), AF_INET); 75010275Ssam time_t t; 75126493Sminshall extern char *ctime(); 75210275Ssam 75313247Ssam if (hp) { 75426493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 75513247Ssam endhostent(); 75613247Ssam } else 75726493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 75813247Ssam sizeof (remotehost)); 75913247Ssam if (!logging) 76013247Ssam return; 76126493Sminshall t = time((time_t *) 0); 76226493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 76310275Ssam } 76410695Ssam 76513247Ssam #include <utmp.h> 76613247Ssam 76726493Sminshall #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a)) 76813247Ssam struct utmp utmp; 76913247Ssam 77010695Ssam /* 77113247Ssam * Record login in wtmp file. 77213247Ssam */ 77313247Ssam dologin(pw) 77413247Ssam struct passwd *pw; 77513247Ssam { 77613247Ssam char line[32]; 77713247Ssam 77813247Ssam if (wtmp >= 0) { 77913247Ssam /* hack, but must be unique and no tty line */ 78026493Sminshall (void) sprintf(line, "ftp%d", getpid()); 78113247Ssam SCPYN(utmp.ut_line, line); 78213247Ssam SCPYN(utmp.ut_name, pw->pw_name); 78313247Ssam SCPYN(utmp.ut_host, remotehost); 78426493Sminshall utmp.ut_time = (long) time((time_t *) 0); 78513247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 78616760Slepreau if (!guest) { /* anon must hang on */ 78716760Slepreau (void) close(wtmp); 78816760Slepreau wtmp = -1; 78916760Slepreau } 79013247Ssam } 79113247Ssam } 79213247Ssam 79313247Ssam /* 79413247Ssam * Record logout in wtmp file 79513247Ssam * and exit with supplied status. 79613247Ssam */ 79713247Ssam dologout(status) 79813247Ssam int status; 79913247Ssam { 80016339Skarels 80117580Ssam if (logged_in) { 80217580Ssam (void) seteuid(0); 80317580Ssam if (wtmp < 0) 80417580Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 80517580Ssam if (wtmp >= 0) { 80617580Ssam SCPYN(utmp.ut_name, ""); 80717580Ssam SCPYN(utmp.ut_host, ""); 80826493Sminshall utmp.ut_time = (long) time((time_t *) 0); 80917580Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 81017580Ssam (void) close(wtmp); 81117580Ssam } 81213247Ssam } 81314436Ssam /* beware of flushing buffers after a SIGPIPE */ 81414436Ssam _exit(status); 81513247Ssam } 81613247Ssam 81713247Ssam /* 81810695Ssam * Special version of popen which avoids 81910695Ssam * call to shell. This insures noone may 82010695Ssam * create a pipe to a hidden program as a side 82110695Ssam * effect of a list or dir command. 82210695Ssam */ 82310695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 82410695Ssam #define RDR 0 82510695Ssam #define WTR 1 82610695Ssam static int popen_pid[5]; 82710695Ssam 82810695Ssam static char * 82910695Ssam nextarg(cpp) 83010695Ssam char *cpp; 83110695Ssam { 83210695Ssam register char *cp = cpp; 83310695Ssam 83410695Ssam if (cp == 0) 83510695Ssam return (cp); 83610695Ssam while (*cp && *cp != ' ' && *cp != '\t') 83710695Ssam cp++; 83810695Ssam if (*cp == ' ' || *cp == '\t') { 83910695Ssam *cp++ = '\0'; 84010695Ssam while (*cp == ' ' || *cp == '\t') 84110695Ssam cp++; 84210695Ssam } 84310695Ssam if (cp == cpp) 84410695Ssam return ((char *)0); 84510695Ssam return (cp); 84610695Ssam } 84710695Ssam 84810695Ssam FILE * 84910695Ssam popen(cmd, mode) 85010695Ssam char *cmd, *mode; 85110695Ssam { 85213211Sroot int p[2], ac, gac; 85310695Ssam register myside, hisside, pid; 85413211Sroot char *av[20], *gav[512]; 85510695Ssam register char *cp; 85610695Ssam 85710695Ssam if (pipe(p) < 0) 85810695Ssam return (NULL); 85910695Ssam cp = cmd, ac = 0; 86013211Sroot /* break up string into pieces */ 86110695Ssam do { 86210695Ssam av[ac++] = cp; 86310695Ssam cp = nextarg(cp); 86413211Sroot } while (cp && *cp && ac < 20); 86510695Ssam av[ac] = (char *)0; 86613211Sroot gav[0] = av[0]; 86713211Sroot /* glob each piece */ 86813211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 86913211Sroot char **pop; 87022024Ssam extern char **glob(), **copyblk(); 87113211Sroot 87213211Sroot pop = glob(av[ac]); 87322024Ssam if (pop == (char **)NULL) { /* globbing failed */ 87422024Ssam char *vv[2]; 87522024Ssam 87622024Ssam vv[0] = av[ac]; 87722024Ssam vv[1] = 0; 87822024Ssam pop = copyblk(vv); 87913211Sroot } 88022024Ssam av[ac] = (char *)pop; /* save to free later */ 88122024Ssam while (*pop && gac < 512) 88222024Ssam gav[gac++] = *pop++; 88311757Ssam } 88413211Sroot gav[gac] = (char *)0; 88510695Ssam myside = tst(p[WTR], p[RDR]); 88610695Ssam hisside = tst(p[RDR], p[WTR]); 88710695Ssam if ((pid = fork()) == 0) { 88810695Ssam /* myside and hisside reverse roles in child */ 88926493Sminshall (void) close(myside); 89026493Sminshall (void) dup2(hisside, tst(0, 1)); 89126493Sminshall (void) close(hisside); 89213211Sroot execv(gav[0], gav); 89310695Ssam _exit(1); 89410695Ssam } 89513211Sroot for (ac = 1; av[ac] != NULL; ac++) 89613211Sroot blkfree((char **)av[ac]); 89710695Ssam if (pid == -1) 89810695Ssam return (NULL); 89910695Ssam popen_pid[myside] = pid; 90026493Sminshall (void) close(hisside); 90110695Ssam return (fdopen(myside, mode)); 90210695Ssam } 90310695Ssam 90410695Ssam pclose(ptr) 90510695Ssam FILE *ptr; 90610695Ssam { 90710695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 90810695Ssam int status; 90910695Ssam 91010695Ssam f = fileno(ptr); 91126493Sminshall (void) fclose(ptr); 91210695Ssam istat = signal(SIGINT, SIG_IGN); 91310695Ssam qstat = signal(SIGQUIT, SIG_IGN); 91410695Ssam hstat = signal(SIGHUP, SIG_IGN); 91510695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 91610695Ssam ; 91710695Ssam if (r == -1) 91810695Ssam status = -1; 91926493Sminshall (void) signal(SIGINT, istat); 92026493Sminshall (void) signal(SIGQUIT, qstat); 92126493Sminshall (void) signal(SIGHUP, hstat); 92210695Ssam return (status); 92310695Ssam } 92410695Ssam 92510695Ssam /* 92610695Ssam * Check user requesting login priviledges. 92728864Smckusick * Disallow anyone who does not have a standard 92828864Smckusick * shell returned by getusershell() (/etc/shells). 92910695Ssam * Disallow anyone mentioned in the file FTPUSERS 93010695Ssam * to allow people such as uucp to be avoided. 93110695Ssam */ 93210695Ssam checkuser(name) 93310695Ssam register char *name; 93410695Ssam { 93528864Smckusick register char *cp; 93628864Smckusick char line[BUFSIZ], *index(), *getusershell(); 93710695Ssam FILE *fd; 93828864Smckusick struct passwd *pw; 93910695Ssam int found = 0; 94010695Ssam 94128864Smckusick pw = getpwnam(name); 94228864Smckusick if (pw == NULL) 94328864Smckusick return (0); 94430102Sbostic if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL) 94530102Sbostic pw->pw_shell = "/bin/sh"; 94628864Smckusick while ((cp = getusershell()) != NULL) 94728864Smckusick if (strcmp(cp, pw->pw_shell) == 0) 94828864Smckusick break; 94928864Smckusick endusershell(); 95028864Smckusick if (cp == NULL) 95128864Smckusick return (0); 95210695Ssam fd = fopen(FTPUSERS, "r"); 95310695Ssam if (fd == NULL) 95410695Ssam return (1); 95510695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 95628864Smckusick cp = index(line, '\n'); 95710695Ssam if (cp) 95810695Ssam *cp = '\0'; 95910695Ssam if (strcmp(line, name) == 0) { 96010695Ssam found++; 96110695Ssam break; 96210695Ssam } 96310695Ssam } 96426493Sminshall (void) fclose(fd); 96510695Ssam return (!found); 96610695Ssam } 96726044Sminshall 96826044Sminshall myoob() 96926044Sminshall { 97027750Sminshall char *cp; 97126044Sminshall 97227750Sminshall /* only process if transfer occurring */ 97326044Sminshall if (!transflag) { 97426044Sminshall return; 97526044Sminshall } 97627750Sminshall cp = tmpline; 97727750Sminshall if (getline(cp, 7, stdin) == NULL) { 97827750Sminshall reply(221, "You could at least say goodby."); 97927750Sminshall dologout(0); 98026044Sminshall } 98126044Sminshall upper(cp); 98226227Ssam if (strcmp(cp, "ABOR\r\n")) 98326044Sminshall return; 98426044Sminshall tmpline[0] = '\0'; 98526044Sminshall reply(426,"Transfer aborted. Data connection closed."); 98626044Sminshall reply(226,"Abort successful"); 98726044Sminshall longjmp(urgcatch, 1); 98826044Sminshall } 98926044Sminshall 99027106Smckusick /* 99127106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 99227106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 99327106Smckusick */ 99426044Sminshall passive() 99526044Sminshall { 99626044Sminshall int len; 99726044Sminshall struct sockaddr_in tmp; 99826044Sminshall register char *p, *a; 99926044Sminshall 100026044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 100126044Sminshall if (pdata < 0) { 100227106Smckusick reply(530, "Can't open passive connection"); 100326044Sminshall return; 100426044Sminshall } 100526044Sminshall tmp = ctrl_addr; 100626044Sminshall tmp.sin_port = 0; 100726044Sminshall seteuid(0); 100826493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 100926044Sminshall seteuid(pw->pw_uid); 101026044Sminshall (void) close(pdata); 101126044Sminshall pdata = -1; 101227106Smckusick reply(530, "Can't open passive connection"); 101326044Sminshall return; 101426044Sminshall } 101526044Sminshall seteuid(pw->pw_uid); 101626044Sminshall len = sizeof(tmp); 101726044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 101826044Sminshall (void) close(pdata); 101926044Sminshall pdata = -1; 102027106Smckusick reply(530, "Can't open passive connection"); 102126044Sminshall return; 102226044Sminshall } 102326044Sminshall if (listen(pdata, 1) < 0) { 102426044Sminshall (void) close(pdata); 102526044Sminshall pdata = -1; 102627106Smckusick reply(530, "Can't open passive connection"); 102726044Sminshall return; 102826044Sminshall } 102926044Sminshall a = (char *) &tmp.sin_addr; 103026044Sminshall p = (char *) &tmp.sin_port; 103126044Sminshall 103226044Sminshall #define UC(b) (((int) b) & 0xff) 103326044Sminshall 103426044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 103526044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 103626044Sminshall } 103726044Sminshall 103826044Sminshall char * 103926044Sminshall gunique(local) 104026044Sminshall char *local; 104126044Sminshall { 104226044Sminshall static char new[MAXPATHLEN]; 104326044Sminshall char *cp = rindex(local, '/'); 104426044Sminshall int d, count=0; 104526044Sminshall char ext = '1'; 104626044Sminshall 104726044Sminshall if (cp) { 104826044Sminshall *cp = '\0'; 104926044Sminshall } 105026044Sminshall d = access(cp ? local : ".", 2); 105126044Sminshall if (cp) { 105226044Sminshall *cp = '/'; 105326044Sminshall } 105426044Sminshall if (d < 0) { 105526493Sminshall syslog(LOG_ERR, "%s: %m", local); 105626044Sminshall return((char *) 0); 105726044Sminshall } 105826044Sminshall (void) strcpy(new, local); 105926044Sminshall cp = new + strlen(new); 106026044Sminshall *cp++ = '.'; 106126044Sminshall while (!d) { 106226044Sminshall if (++count == 100) { 106327106Smckusick reply(452, "Unique file name not cannot be created."); 106426044Sminshall return((char *) 0); 106526044Sminshall } 106626044Sminshall *cp++ = ext; 106726044Sminshall *cp = '\0'; 106826044Sminshall if (ext == '9') { 106926044Sminshall ext = '0'; 107026044Sminshall } 107126044Sminshall else { 107226044Sminshall ext++; 107326044Sminshall } 107426044Sminshall if ((d = access(new, 0)) < 0) { 107526044Sminshall break; 107626044Sminshall } 107726044Sminshall if (ext != '0') { 107826044Sminshall cp--; 107926044Sminshall } 108026044Sminshall else if (*(cp - 2) == '.') { 108126044Sminshall *(cp - 1) = '1'; 108226044Sminshall } 108326044Sminshall else { 108426044Sminshall *(cp - 2) = *(cp - 2) + 1; 108526044Sminshall cp--; 108626044Sminshall } 108726044Sminshall } 108826044Sminshall return(new); 108926044Sminshall } 1090