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 634769Sbostic * provided that the above copyright notice and this paragraph are 734769Sbostic * duplicated in all such forms and that any documentation, 834769Sbostic * advertising materials, and other materials related to such 934769Sbostic * distribution and use acknowledge that the software was developed 1034769Sbostic * by the University of California, Berkeley. The name of the 1134769Sbostic * University may not be used to endorse or promote products derived 1234769Sbostic * from this software without specific prior written permission. 1334769Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434769Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534769Sbostic * 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*35672Sbostic static char sccsid[] = "@(#)ftpd.c 5.13 (Berkeley) 09/22/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 206*35672Sbostic static char ttyline[20]; 207*35672Sbostic 20810275Ssam pass(passwd) 20910275Ssam char *passwd; 21010275Ssam { 21110303Ssam char *xpasswd, *savestr(); 21210303Ssam static struct passwd save; 21310275Ssam 21410275Ssam if (logged_in || pw == NULL) { 21510275Ssam reply(503, "Login with USER first."); 21610275Ssam return; 21710275Ssam } 21810275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 21910275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 22016760Slepreau /* The strcmp does not catch null passwords! */ 22116760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 22210275Ssam reply(530, "Login incorrect."); 22310275Ssam pw = NULL; 22410275Ssam return; 22510275Ssam } 22610275Ssam } 22710303Ssam setegid(pw->pw_gid); 22810275Ssam initgroups(pw->pw_name, pw->pw_gid); 22910275Ssam if (chdir(pw->pw_dir)) { 23027106Smckusick reply(530, "User %s: can't change directory to %s.", 23110275Ssam pw->pw_name, pw->pw_dir); 23210303Ssam goto bad; 23310275Ssam } 23416033Sralph 23516760Slepreau /* grab wtmp before chroot */ 23616760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 23710303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23810275Ssam reply(550, "Can't set guest privileges."); 23916760Slepreau if (wtmp >= 0) { 24016760Slepreau (void) close(wtmp); 24116760Slepreau wtmp = -1; 24216760Slepreau } 24310303Ssam goto bad; 24410275Ssam } 24510275Ssam if (!guest) 24610275Ssam reply(230, "User %s logged in.", pw->pw_name); 24710275Ssam else 24810275Ssam reply(230, "Guest login ok, access restrictions apply."); 24910275Ssam logged_in = 1; 250*35672Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 251*35672Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 25210303Ssam seteuid(pw->pw_uid); 25310303Ssam /* 25410303Ssam * Save everything so globbing doesn't 25510303Ssam * clobber the fields. 25610303Ssam */ 25710303Ssam save = *pw; 25810303Ssam save.pw_name = savestr(pw->pw_name); 25910303Ssam save.pw_passwd = savestr(pw->pw_passwd); 26010303Ssam save.pw_comment = savestr(pw->pw_comment); 26126493Sminshall save.pw_gecos = savestr(pw->pw_gecos); 26210303Ssam save.pw_dir = savestr(pw->pw_dir); 26310303Ssam save.pw_shell = savestr(pw->pw_shell); 26410303Ssam pw = &save; 26510303Ssam home = pw->pw_dir; /* home dir for globbing */ 26610303Ssam return; 26710303Ssam bad: 26810303Ssam seteuid(0); 26910303Ssam pw = NULL; 27010275Ssam } 27110275Ssam 27210303Ssam char * 27310303Ssam savestr(s) 27410303Ssam char *s; 27510303Ssam { 27610303Ssam char *malloc(); 27726493Sminshall char *new = malloc((unsigned) strlen(s) + 1); 27810303Ssam 27910303Ssam if (new != NULL) 28026493Sminshall (void) strcpy(new, s); 28111347Ssam return (new); 28210303Ssam } 28310303Ssam 28410275Ssam retrieve(cmd, name) 28510275Ssam char *cmd, *name; 28610275Ssam { 28710275Ssam FILE *fin, *dout; 28810275Ssam struct stat st; 28926044Sminshall int (*closefunc)(), tmp; 29010275Ssam 29110275Ssam if (cmd == 0) { 29210317Ssam #ifdef notdef 29310317Ssam /* no remote command execution -- it's a security hole */ 29411653Ssam if (*name == '|') 29510275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 29610275Ssam else 29710317Ssam #endif 29810275Ssam fin = fopen(name, "r"), closefunc = fclose; 29910275Ssam } else { 30010275Ssam char line[BUFSIZ]; 30110275Ssam 30226493Sminshall (void) sprintf(line, cmd, name), name = line; 30310275Ssam fin = popen(line, "r"), closefunc = pclose; 30410275Ssam } 30510275Ssam if (fin == NULL) { 30613152Ssam if (errno != 0) 30713152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30810275Ssam return; 30910275Ssam } 31010275Ssam st.st_size = 0; 31110275Ssam if (cmd == 0 && 31210275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 31310275Ssam reply(550, "%s: not a plain file.", name); 31410275Ssam goto done; 31510275Ssam } 31610275Ssam dout = dataconn(name, st.st_size, "w"); 31710275Ssam if (dout == NULL) 31810275Ssam goto done; 31926044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 32010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 32126044Sminshall } 32226044Sminshall else if (tmp == 0) { 32310275Ssam reply(226, "Transfer complete."); 32426044Sminshall } 32526493Sminshall (void) fclose(dout); 32626044Sminshall data = -1; 32726044Sminshall pdata = -1; 32810275Ssam done: 32910275Ssam (*closefunc)(fin); 33010275Ssam } 33110275Ssam 33210275Ssam store(name, mode) 33310275Ssam char *name, *mode; 33410275Ssam { 33510275Ssam FILE *fout, *din; 33626044Sminshall int (*closefunc)(), dochown = 0, tmp; 33726044Sminshall char *gunique(), *local; 33810275Ssam 33910317Ssam #ifdef notdef 34010317Ssam /* no remote command execution -- it's a security hole */ 34111653Ssam if (name[0] == '|') 34210275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 34310317Ssam else 34410317Ssam #endif 34510317Ssam { 34610303Ssam struct stat st; 34710303Ssam 34826044Sminshall local = name; 34926044Sminshall if (stat(name, &st) < 0) { 35010303Ssam dochown++; 35126044Sminshall } 35226044Sminshall else if (unique) { 35326044Sminshall if ((local = gunique(name)) == NULL) { 35426044Sminshall return; 35526044Sminshall } 35626044Sminshall dochown++; 35726044Sminshall } 35826044Sminshall fout = fopen(local, mode), closefunc = fclose; 35910303Ssam } 36010275Ssam if (fout == NULL) { 36127106Smckusick reply(553, "%s: %s.", local, sys_errlist[errno]); 36210275Ssam return; 36310275Ssam } 36426044Sminshall din = dataconn(local, (off_t)-1, "r"); 36510275Ssam if (din == NULL) 36610275Ssam goto done; 36726044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 36827106Smckusick reply(552, "%s: %s.", local, sys_errlist[errno]); 36926044Sminshall } 37026044Sminshall else if (tmp == 0 && !unique) { 37110275Ssam reply(226, "Transfer complete."); 37226044Sminshall } 37326044Sminshall else if (tmp == 0 && unique) { 37426044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 37526044Sminshall } 37626493Sminshall (void) fclose(din); 37726044Sminshall data = -1; 37826044Sminshall pdata = -1; 37910275Ssam done: 38010303Ssam if (dochown) 38126044Sminshall (void) chown(local, pw->pw_uid, -1); 38210275Ssam (*closefunc)(fout); 38310275Ssam } 38410275Ssam 38510275Ssam FILE * 38610275Ssam getdatasock(mode) 38710275Ssam char *mode; 38810275Ssam { 38917157Ssam int s, on = 1; 39010275Ssam 39110275Ssam if (data >= 0) 39210275Ssam return (fdopen(data, mode)); 39313247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 39410602Ssam if (s < 0) 39510275Ssam return (NULL); 39610275Ssam seteuid(0); 39726493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 39810602Ssam goto bad; 39913152Ssam /* anchor socket to avoid multi-homing problems */ 40013152Ssam data_source.sin_family = AF_INET; 40113152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 40226493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 40310602Ssam goto bad; 40410311Ssam seteuid(pw->pw_uid); 40510275Ssam return (fdopen(s, mode)); 40610602Ssam bad: 40710602Ssam seteuid(pw->pw_uid); 40826493Sminshall (void) close(s); 40910602Ssam return (NULL); 41010275Ssam } 41110275Ssam 41210275Ssam FILE * 41310275Ssam dataconn(name, size, mode) 41410275Ssam char *name; 41511653Ssam off_t size; 41610275Ssam char *mode; 41710275Ssam { 41810275Ssam char sizebuf[32]; 41910275Ssam FILE *file; 42011653Ssam int retry = 0; 42110275Ssam 42210275Ssam if (size >= 0) 42326493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 42410275Ssam else 42510275Ssam (void) strcpy(sizebuf, ""); 42626044Sminshall if (pdata > 0) { 42726044Sminshall struct sockaddr_in from; 42826044Sminshall int s, fromlen = sizeof(from); 42926044Sminshall 43026493Sminshall s = accept(pdata, &from, &fromlen); 43126044Sminshall if (s < 0) { 43226044Sminshall reply(425, "Can't open data connection."); 43326044Sminshall (void) close(pdata); 43426044Sminshall pdata = -1; 43526044Sminshall return(NULL); 43626044Sminshall } 43726044Sminshall (void) close(pdata); 43826044Sminshall pdata = s; 43926044Sminshall reply(150, "Openning data connection for %s (%s,%d)%s.", 44026493Sminshall name, inet_ntoa(from.sin_addr), 44126044Sminshall ntohs(from.sin_port), sizebuf); 44226044Sminshall return(fdopen(pdata, mode)); 44326044Sminshall } 44410275Ssam if (data >= 0) { 44510275Ssam reply(125, "Using existing data connection for %s%s.", 44610275Ssam name, sizebuf); 44710321Ssam usedefault = 1; 44810275Ssam return (fdopen(data, mode)); 44910275Ssam } 45010566Ssam if (usedefault) 45110422Ssam data_dest = his_addr; 45210422Ssam usedefault = 1; 45310275Ssam file = getdatasock(mode); 45410275Ssam if (file == NULL) { 45510275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 45613247Ssam inet_ntoa(data_source.sin_addr), 45710275Ssam ntohs(data_source.sin_port), 45810275Ssam sys_errlist[errno]); 45910275Ssam return (NULL); 46010275Ssam } 46110275Ssam data = fileno(file); 46226044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 46311653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 46426493Sminshall sleep((unsigned) swaitint); 46511653Ssam retry += swaitint; 46611653Ssam continue; 46711653Ssam } 46810275Ssam reply(425, "Can't build data connection: %s.", 46910275Ssam sys_errlist[errno]); 47010275Ssam (void) fclose(file); 47110275Ssam data = -1; 47210275Ssam return (NULL); 47310275Ssam } 47427750Sminshall reply(150, "Opening data connection for %s (%s,%d)%s.", 47527750Sminshall name, inet_ntoa(data_dest.sin_addr), 47627750Sminshall ntohs(data_dest.sin_port), sizebuf); 47710275Ssam return (file); 47810275Ssam } 47910275Ssam 48010275Ssam /* 48110275Ssam * Tranfer the contents of "instr" to 48210275Ssam * "outstr" peer using the appropriate 48310275Ssam * encapulation of the date subject 48410275Ssam * to Mode, Structure, and Type. 48510275Ssam * 48610275Ssam * NB: Form isn't handled. 48710275Ssam */ 48810275Ssam send_data(instr, outstr) 48910275Ssam FILE *instr, *outstr; 49010275Ssam { 49110275Ssam register int c; 49210275Ssam int netfd, filefd, cnt; 49310275Ssam char buf[BUFSIZ]; 49410275Ssam 49526044Sminshall transflag++; 49626044Sminshall if (setjmp(urgcatch)) { 49726044Sminshall transflag = 0; 49826044Sminshall return(-1); 49926044Sminshall } 50010275Ssam switch (type) { 50110275Ssam 50210275Ssam case TYPE_A: 50310275Ssam while ((c = getc(instr)) != EOF) { 50411220Ssam if (c == '\n') { 50526044Sminshall if (ferror (outstr)) { 50626044Sminshall transflag = 0; 50711220Ssam return (1); 50826044Sminshall } 50927750Sminshall (void) putc('\r', outstr); 51011220Ssam } 51127750Sminshall (void) putc(c, outstr); 51226044Sminshall /* if (c == '\r') */ 51326044Sminshall /* putc ('\0', outstr); */ 51410275Ssam } 51526044Sminshall transflag = 0; 51626044Sminshall if (ferror (instr) || ferror (outstr)) { 51711220Ssam return (1); 51826044Sminshall } 51910275Ssam return (0); 52010275Ssam 52110275Ssam case TYPE_I: 52210275Ssam case TYPE_L: 52310275Ssam netfd = fileno(outstr); 52410275Ssam filefd = fileno(instr); 52510275Ssam 52626044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 52726044Sminshall if (write(netfd, buf, cnt) < 0) { 52826044Sminshall transflag = 0; 52910275Ssam return (1); 53026044Sminshall } 53126044Sminshall } 53226044Sminshall transflag = 0; 53310275Ssam return (cnt < 0); 53410275Ssam } 53527106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 53626044Sminshall transflag = 0; 53727106Smckusick return (-1); 53810275Ssam } 53910275Ssam 54010275Ssam /* 54110275Ssam * Transfer data from peer to 54210275Ssam * "outstr" using the appropriate 54310275Ssam * encapulation of the data subject 54410275Ssam * to Mode, Structure, and Type. 54510275Ssam * 54610275Ssam * N.B.: Form isn't handled. 54710275Ssam */ 54810275Ssam receive_data(instr, outstr) 54910275Ssam FILE *instr, *outstr; 55010275Ssam { 55110275Ssam register int c; 55211220Ssam int cnt; 55310275Ssam char buf[BUFSIZ]; 55410275Ssam 55510275Ssam 55626044Sminshall transflag++; 55726044Sminshall if (setjmp(urgcatch)) { 55826044Sminshall transflag = 0; 55926044Sminshall return(-1); 56026044Sminshall } 56110275Ssam switch (type) { 56210275Ssam 56310275Ssam case TYPE_I: 56410275Ssam case TYPE_L: 56526044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 56626044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 56726044Sminshall transflag = 0; 56810275Ssam return (1); 56926044Sminshall } 57026044Sminshall } 57126044Sminshall transflag = 0; 57210275Ssam return (cnt < 0); 57310275Ssam 57410275Ssam case TYPE_E: 57527106Smckusick reply(553, "TYPE E not implemented."); 57626044Sminshall transflag = 0; 57727106Smckusick return (-1); 57810275Ssam 57910275Ssam case TYPE_A: 58010275Ssam while ((c = getc(instr)) != EOF) { 58127750Sminshall while (c == '\r') { 58226044Sminshall if (ferror (outstr)) { 58326044Sminshall transflag = 0; 58411220Ssam return (1); 58526044Sminshall } 58611220Ssam if ((c = getc(instr)) != '\n') 58727750Sminshall (void) putc ('\r', outstr); 58826044Sminshall /* if (c == '\0') */ 58926044Sminshall /* continue; */ 59010275Ssam } 59127750Sminshall (void) putc (c, outstr); 59210275Ssam } 59326044Sminshall transflag = 0; 59411220Ssam if (ferror (instr) || ferror (outstr)) 59511220Ssam return (1); 59610275Ssam return (0); 59710275Ssam } 59826044Sminshall transflag = 0; 59910275Ssam fatal("Unknown type in receive_data."); 60010275Ssam /*NOTREACHED*/ 60110275Ssam } 60210275Ssam 60310275Ssam fatal(s) 60410275Ssam char *s; 60510275Ssam { 60610275Ssam reply(451, "Error in server: %s\n", s); 60710275Ssam reply(221, "Closing connection due to server error."); 60813247Ssam dologout(0); 60910275Ssam } 61010275Ssam 61132110Smckusick reply(n, s, p0, p1, p2, p3, p4) 61210275Ssam int n; 61310275Ssam char *s; 61410275Ssam { 61510275Ssam 61610275Ssam printf("%d ", n); 61732110Smckusick printf(s, p0, p1, p2, p3, p4); 61810275Ssam printf("\r\n"); 61926493Sminshall (void) fflush(stdout); 62010275Ssam if (debug) { 62126493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 62232110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 62310275Ssam } 62410275Ssam } 62510275Ssam 62632110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 62710275Ssam int n; 62810275Ssam char *s; 62910275Ssam { 63010275Ssam printf("%d-", n); 63132110Smckusick printf(s, p0, p1, p2, p3, p4); 63210275Ssam printf("\r\n"); 63326493Sminshall (void) fflush(stdout); 63410275Ssam if (debug) { 63526493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 63632110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 63710275Ssam } 63810275Ssam } 63910275Ssam 64010275Ssam ack(s) 64110275Ssam char *s; 64210275Ssam { 64327106Smckusick reply(250, "%s command successful.", s); 64410275Ssam } 64510275Ssam 64610275Ssam nack(s) 64710275Ssam char *s; 64810275Ssam { 64910275Ssam reply(502, "%s command not implemented.", s); 65010275Ssam } 65110275Ssam 65226493Sminshall yyerror(s) 65326493Sminshall char *s; 65410275Ssam { 65526044Sminshall char *cp; 65626044Sminshall 65726044Sminshall cp = index(cbuf,'\n'); 65826044Sminshall *cp = '\0'; 65926044Sminshall reply(500, "'%s': command not understood.",cbuf); 66010275Ssam } 66110275Ssam 66210275Ssam delete(name) 66310275Ssam char *name; 66410275Ssam { 66510275Ssam struct stat st; 66610275Ssam 66710275Ssam if (stat(name, &st) < 0) { 66810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 66910275Ssam return; 67010275Ssam } 67110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 67210275Ssam if (rmdir(name) < 0) { 67310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67410275Ssam return; 67510275Ssam } 67610275Ssam goto done; 67710275Ssam } 67810275Ssam if (unlink(name) < 0) { 67910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 68010275Ssam return; 68110275Ssam } 68210275Ssam done: 68310275Ssam ack("DELE"); 68410275Ssam } 68510275Ssam 68610275Ssam cwd(path) 68710275Ssam char *path; 68810275Ssam { 68910275Ssam 69010275Ssam if (chdir(path) < 0) { 69110275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 69210275Ssam return; 69310275Ssam } 69410275Ssam ack("CWD"); 69510275Ssam } 69610275Ssam 69710303Ssam makedir(name) 69810275Ssam char *name; 69910275Ssam { 70010303Ssam struct stat st; 70110303Ssam int dochown = stat(name, &st) < 0; 70210275Ssam 70310275Ssam if (mkdir(name, 0777) < 0) { 70410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70510275Ssam return; 70610275Ssam } 70710303Ssam if (dochown) 70810303Ssam (void) chown(name, pw->pw_uid, -1); 70927106Smckusick reply(257, "MKD command successful."); 71010275Ssam } 71110275Ssam 71210303Ssam removedir(name) 71310275Ssam char *name; 71410275Ssam { 71510275Ssam 71610275Ssam if (rmdir(name) < 0) { 71710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 71810275Ssam return; 71910275Ssam } 72027106Smckusick ack("RMD"); 72110275Ssam } 72210275Ssam 72310303Ssam pwd() 72410275Ssam { 72510303Ssam char path[MAXPATHLEN + 1]; 72610275Ssam 72710275Ssam if (getwd(path) == NULL) { 72827106Smckusick reply(550, "%s.", path); 72910275Ssam return; 73010275Ssam } 73127106Smckusick reply(257, "\"%s\" is current directory.", path); 73210275Ssam } 73310275Ssam 73410275Ssam char * 73510275Ssam renamefrom(name) 73610275Ssam char *name; 73710275Ssam { 73810275Ssam struct stat st; 73910275Ssam 74010275Ssam if (stat(name, &st) < 0) { 74110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 74210275Ssam return ((char *)0); 74310275Ssam } 74410303Ssam reply(350, "File exists, ready for destination name"); 74510275Ssam return (name); 74610275Ssam } 74710275Ssam 74810275Ssam renamecmd(from, to) 74910275Ssam char *from, *to; 75010275Ssam { 75110275Ssam 75210275Ssam if (rename(from, to) < 0) { 75310275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 75410275Ssam return; 75510275Ssam } 75610275Ssam ack("RNTO"); 75710275Ssam } 75810275Ssam 75910275Ssam dolog(sin) 76010275Ssam struct sockaddr_in *sin; 76110275Ssam { 76210275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 76310275Ssam sizeof (struct in_addr), AF_INET); 76410275Ssam time_t t; 76526493Sminshall extern char *ctime(); 76610275Ssam 76713247Ssam if (hp) { 76826493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 76913247Ssam endhostent(); 77013247Ssam } else 77126493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 77213247Ssam sizeof (remotehost)); 77313247Ssam if (!logging) 77413247Ssam return; 77526493Sminshall t = time((time_t *) 0); 77626493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 77710275Ssam } 77810695Ssam 77910695Ssam /* 78013247Ssam * Record logout in wtmp file 78113247Ssam * and exit with supplied status. 78213247Ssam */ 78313247Ssam dologout(status) 78413247Ssam int status; 78513247Ssam { 78617580Ssam if (logged_in) { 78717580Ssam (void) seteuid(0); 788*35672Sbostic logwtmp(ttyline, "", ""); 78913247Ssam } 79014436Ssam /* beware of flushing buffers after a SIGPIPE */ 79114436Ssam _exit(status); 79213247Ssam } 79313247Ssam 79413247Ssam /* 79510695Ssam * Check user requesting login priviledges. 79628864Smckusick * Disallow anyone who does not have a standard 79728864Smckusick * shell returned by getusershell() (/etc/shells). 79810695Ssam * Disallow anyone mentioned in the file FTPUSERS 79910695Ssam * to allow people such as uucp to be avoided. 80010695Ssam */ 80110695Ssam checkuser(name) 80210695Ssam register char *name; 80310695Ssam { 80428864Smckusick register char *cp; 80528864Smckusick char line[BUFSIZ], *index(), *getusershell(); 80610695Ssam FILE *fd; 80728864Smckusick struct passwd *pw; 80810695Ssam int found = 0; 80910695Ssam 81028864Smckusick pw = getpwnam(name); 81128864Smckusick if (pw == NULL) 81228864Smckusick return (0); 81330102Sbostic if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL) 81430102Sbostic pw->pw_shell = "/bin/sh"; 81528864Smckusick while ((cp = getusershell()) != NULL) 81628864Smckusick if (strcmp(cp, pw->pw_shell) == 0) 81728864Smckusick break; 81828864Smckusick endusershell(); 81928864Smckusick if (cp == NULL) 82028864Smckusick return (0); 82110695Ssam fd = fopen(FTPUSERS, "r"); 82210695Ssam if (fd == NULL) 82310695Ssam return (1); 82410695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 82528864Smckusick cp = index(line, '\n'); 82610695Ssam if (cp) 82710695Ssam *cp = '\0'; 82810695Ssam if (strcmp(line, name) == 0) { 82910695Ssam found++; 83010695Ssam break; 83110695Ssam } 83210695Ssam } 83326493Sminshall (void) fclose(fd); 83410695Ssam return (!found); 83510695Ssam } 83626044Sminshall 83726044Sminshall myoob() 83826044Sminshall { 83927750Sminshall char *cp; 84026044Sminshall 84127750Sminshall /* only process if transfer occurring */ 84226044Sminshall if (!transflag) { 84326044Sminshall return; 84426044Sminshall } 84527750Sminshall cp = tmpline; 84627750Sminshall if (getline(cp, 7, stdin) == NULL) { 84727750Sminshall reply(221, "You could at least say goodby."); 84827750Sminshall dologout(0); 84926044Sminshall } 85026044Sminshall upper(cp); 85126227Ssam if (strcmp(cp, "ABOR\r\n")) 85226044Sminshall return; 85326044Sminshall tmpline[0] = '\0'; 85426044Sminshall reply(426,"Transfer aborted. Data connection closed."); 85526044Sminshall reply(226,"Abort successful"); 85626044Sminshall longjmp(urgcatch, 1); 85726044Sminshall } 85826044Sminshall 85927106Smckusick /* 86027106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 86127106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 86227106Smckusick */ 86326044Sminshall passive() 86426044Sminshall { 86526044Sminshall int len; 86626044Sminshall struct sockaddr_in tmp; 86726044Sminshall register char *p, *a; 86826044Sminshall 86926044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 87026044Sminshall if (pdata < 0) { 87127106Smckusick reply(530, "Can't open passive connection"); 87226044Sminshall return; 87326044Sminshall } 87426044Sminshall tmp = ctrl_addr; 87526044Sminshall tmp.sin_port = 0; 87626044Sminshall seteuid(0); 87726493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 87826044Sminshall seteuid(pw->pw_uid); 87926044Sminshall (void) close(pdata); 88026044Sminshall pdata = -1; 88127106Smckusick reply(530, "Can't open passive connection"); 88226044Sminshall return; 88326044Sminshall } 88426044Sminshall seteuid(pw->pw_uid); 88526044Sminshall len = sizeof(tmp); 88626044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 88726044Sminshall (void) close(pdata); 88826044Sminshall pdata = -1; 88927106Smckusick reply(530, "Can't open passive connection"); 89026044Sminshall return; 89126044Sminshall } 89226044Sminshall if (listen(pdata, 1) < 0) { 89326044Sminshall (void) close(pdata); 89426044Sminshall pdata = -1; 89527106Smckusick reply(530, "Can't open passive connection"); 89626044Sminshall return; 89726044Sminshall } 89826044Sminshall a = (char *) &tmp.sin_addr; 89926044Sminshall p = (char *) &tmp.sin_port; 90026044Sminshall 90126044Sminshall #define UC(b) (((int) b) & 0xff) 90226044Sminshall 90326044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 90426044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 90526044Sminshall } 90626044Sminshall 90726044Sminshall char * 90826044Sminshall gunique(local) 90926044Sminshall char *local; 91026044Sminshall { 91126044Sminshall static char new[MAXPATHLEN]; 91226044Sminshall char *cp = rindex(local, '/'); 91326044Sminshall int d, count=0; 91426044Sminshall char ext = '1'; 91526044Sminshall 91626044Sminshall if (cp) { 91726044Sminshall *cp = '\0'; 91826044Sminshall } 91926044Sminshall d = access(cp ? local : ".", 2); 92026044Sminshall if (cp) { 92126044Sminshall *cp = '/'; 92226044Sminshall } 92326044Sminshall if (d < 0) { 92426493Sminshall syslog(LOG_ERR, "%s: %m", local); 92526044Sminshall return((char *) 0); 92626044Sminshall } 92726044Sminshall (void) strcpy(new, local); 92826044Sminshall cp = new + strlen(new); 92926044Sminshall *cp++ = '.'; 93026044Sminshall while (!d) { 93126044Sminshall if (++count == 100) { 93227106Smckusick reply(452, "Unique file name not cannot be created."); 93326044Sminshall return((char *) 0); 93426044Sminshall } 93526044Sminshall *cp++ = ext; 93626044Sminshall *cp = '\0'; 93726044Sminshall if (ext == '9') { 93826044Sminshall ext = '0'; 93926044Sminshall } 94026044Sminshall else { 94126044Sminshall ext++; 94226044Sminshall } 94326044Sminshall if ((d = access(new, 0)) < 0) { 94426044Sminshall break; 94526044Sminshall } 94626044Sminshall if (ext != '0') { 94726044Sminshall cp--; 94826044Sminshall } 94926044Sminshall else if (*(cp - 2) == '.') { 95026044Sminshall *(cp - 1) = '1'; 95126044Sminshall } 95226044Sminshall else { 95326044Sminshall *(cp - 2) = *(cp - 2) + 1; 95426044Sminshall cp--; 95526044Sminshall } 95626044Sminshall } 95726044Sminshall return(new); 95826044Sminshall } 959