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*36192Sbostic static char sccsid[] = "@(#)ftpd.c 5.17 (Berkeley) 11/01/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; 8310275Ssam int type; 8410275Ssam int form; 8510275Ssam int stru; /* avoid C keyword */ 8610275Ssam int mode; 8710321Ssam int usedefault = 1; /* for data transfers */ 8826044Sminshall int pdata; /* for passive mode */ 8926044Sminshall int unique; 9026044Sminshall int transflag; 9126044Sminshall char tmpline[7]; 9210275Ssam char hostname[32]; 9313247Ssam char remotehost[32]; 9410275Ssam 9511653Ssam /* 9611653Ssam * Timeout intervals for retrying connections 9711653Ssam * to hosts that don't accept PORT cmds. This 9811653Ssam * is a kludge, but given the problems with TCP... 9911653Ssam */ 10011653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10111653Ssam #define SWAITINT 5 /* interval between retries */ 10211653Ssam 10311653Ssam int swaitmax = SWAITMAX; 10411653Ssam int swaitint = SWAITINT; 10511653Ssam 10610275Ssam int lostconn(); 10726044Sminshall int myoob(); 10810275Ssam FILE *getdatasock(), *dataconn(); 10910275Ssam 11010275Ssam main(argc, argv) 11110275Ssam int argc; 11210275Ssam char *argv[]; 11310275Ssam { 11427750Sminshall int addrlen, on = 1; 11526044Sminshall long pgid; 11610275Ssam char *cp; 11710275Ssam 11816339Skarels addrlen = sizeof (his_addr); 11916339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 12026493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12110275Ssam exit(1); 12210275Ssam } 12316339Skarels addrlen = sizeof (ctrl_addr); 12426493Sminshall if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) { 12526493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 12616339Skarels exit(1); 12716339Skarels } 12816339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 12910275Ssam debug = 0; 13026493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 13110275Ssam argc--, argv++; 13210275Ssam while (argc > 0 && *argv[0] == '-') { 13310275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 13410275Ssam 13511653Ssam case 'v': 13611653Ssam debug = 1; 13711653Ssam break; 13811653Ssam 13910275Ssam case 'd': 14010275Ssam debug = 1; 14110275Ssam break; 14210275Ssam 14311757Ssam case 'l': 14411757Ssam logging = 1; 14511757Ssam break; 14611757Ssam 14711653Ssam case 't': 14811653Ssam timeout = atoi(++cp); 14911653Ssam goto nextopt; 15011653Ssam break; 15111653Ssam 15210275Ssam default: 15316339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 15416339Skarels *cp); 15510275Ssam break; 15610275Ssam } 15711653Ssam nextopt: 15810275Ssam argc--, argv++; 15910275Ssam } 16030944Scsvsj (void) freopen("/dev/null", "w", stderr); 16126493Sminshall (void) signal(SIGPIPE, lostconn); 16226493Sminshall (void) signal(SIGCHLD, SIG_IGN); 16335691Sbostic if ((int)signal(SIGURG, myoob) < 0) 16426493Sminshall syslog(LOG_ERR, "signal: %m"); 16535691Sbostic 16627750Sminshall /* handle urgent data inline */ 16727750Sminshall #ifdef SO_OOBINLINE 16827750Sminshall if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { 16927750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 17027750Sminshall } 17127750Sminshall #endif SO_OOBINLINE 17226044Sminshall pgid = getpid(); 17327750Sminshall if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) { 17426493Sminshall syslog(LOG_ERR, "ioctl: %m"); 17526044Sminshall } 17616760Slepreau dolog(&his_addr); 17716339Skarels /* do telnet option negotiation here */ 17816339Skarels /* 17916339Skarels * Set up default state 18016339Skarels */ 18116339Skarels data = -1; 18216339Skarels type = TYPE_A; 18316339Skarels form = FORM_N; 18416339Skarels stru = STRU_F; 18516339Skarels mode = MODE_S; 18626044Sminshall tmpline[0] = '\0'; 18726493Sminshall (void) gethostname(hostname, sizeof (hostname)); 18816339Skarels reply(220, "%s FTP server (%s) ready.", 18916339Skarels hostname, version); 19010275Ssam for (;;) { 19126493Sminshall (void) setjmp(errcatch); 19226493Sminshall (void) yyparse(); 19310275Ssam } 19410275Ssam } 19510419Ssam 19610275Ssam lostconn() 19710275Ssam { 19810275Ssam 19914089Ssam if (debug) 20026493Sminshall syslog(LOG_DEBUG, "lost connection"); 20114089Ssam dologout(-1); 20210275Ssam } 20310275Ssam 20435672Sbostic static char ttyline[20]; 20535672Sbostic 20636185Sbostic /* 20736185Sbostic * Helper function for sgetpwnam(). 20836185Sbostic */ 20936185Sbostic char * 21036185Sbostic sgetsave(s) 21136185Sbostic char *s; 21236185Sbostic { 21336185Sbostic #ifdef notdef 21436185Sbostic char *new = strdup(s); 21536185Sbostic #else 21636185Sbostic char *malloc(); 21736185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 21836185Sbostic #endif 21936185Sbostic 22036185Sbostic if (new == NULL) { 22136185Sbostic reply(553, "Local resource failure"); 22236185Sbostic dologout(1); 22336185Sbostic } 22436185Sbostic #ifndef notdef 22536185Sbostic (void) strcpy(new, s); 22636185Sbostic #endif 22736185Sbostic return (new); 22836185Sbostic } 22936185Sbostic 23036185Sbostic /* 23136185Sbostic * Save the result of a getpwnam. Used for USER command, since 23236185Sbostic * the data returned must not be clobbered by any other command 23336185Sbostic * (e.g., globbing). 23436185Sbostic */ 23536185Sbostic struct passwd * 23636185Sbostic sgetpwnam(name) 23736185Sbostic char *name; 23836185Sbostic { 23936185Sbostic static struct passwd save; 24036185Sbostic register struct passwd *p; 24136185Sbostic char *sgetsave(); 24236185Sbostic 24336185Sbostic if ((p = getpwnam(name)) == NULL) 24436185Sbostic return (p); 24536185Sbostic if (save.pw_name) { 24636185Sbostic free(save.pw_name); 24736185Sbostic free(save.pw_passwd); 24836185Sbostic free(save.pw_comment); 24936185Sbostic free(save.pw_gecos); 25036185Sbostic free(save.pw_dir); 25136185Sbostic free(save.pw_shell); 25236185Sbostic } 25336185Sbostic save = *p; 25436185Sbostic save.pw_name = sgetsave(p->pw_name); 25536185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 25636185Sbostic save.pw_comment = sgetsave(p->pw_comment); 25736185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 25836185Sbostic save.pw_dir = sgetsave(p->pw_dir); 25936185Sbostic save.pw_shell = sgetsave(p->pw_shell); 26036185Sbostic return (&save); 26136185Sbostic } 26236185Sbostic 26310275Ssam pass(passwd) 26410275Ssam char *passwd; 26510275Ssam { 26636185Sbostic char *xpasswd; 26710275Ssam 26810275Ssam if (logged_in || pw == NULL) { 26910275Ssam reply(503, "Login with USER first."); 27010275Ssam return; 27110275Ssam } 27210275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 27310275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 27416760Slepreau /* The strcmp does not catch null passwords! */ 27516760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 27610275Ssam reply(530, "Login incorrect."); 27710275Ssam pw = NULL; 27810275Ssam return; 27910275Ssam } 28010275Ssam } 28110303Ssam setegid(pw->pw_gid); 28210275Ssam initgroups(pw->pw_name, pw->pw_gid); 28310275Ssam if (chdir(pw->pw_dir)) { 28427106Smckusick reply(530, "User %s: can't change directory to %s.", 28510275Ssam pw->pw_name, pw->pw_dir); 28610303Ssam goto bad; 28710275Ssam } 28816033Sralph 289*36192Sbostic /* open wtmp before chroot */ 290*36192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 291*36192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 292*36192Sbostic logged_in = 1; 293*36192Sbostic 294*36192Sbostic if (guest) { 295*36192Sbostic if (chroot(pw->pw_dir) < 0) { 296*36192Sbostic reply(550, "Can't set guest privileges."); 297*36192Sbostic goto bad; 29816760Slepreau } 299*36192Sbostic reply(230, "Guest login ok, access restrictions apply."); 300*36192Sbostic } else 30110275Ssam reply(230, "User %s logged in.", pw->pw_name); 30210303Ssam seteuid(pw->pw_uid); 30310303Ssam home = pw->pw_dir; /* home dir for globbing */ 30410303Ssam return; 30510303Ssam bad: 30610303Ssam seteuid(0); 30710303Ssam pw = NULL; 30810275Ssam } 30910275Ssam 31010275Ssam retrieve(cmd, name) 31110275Ssam char *cmd, *name; 31210275Ssam { 31310275Ssam FILE *fin, *dout; 31410275Ssam struct stat st; 31526044Sminshall int (*closefunc)(), tmp; 31610275Ssam 31710275Ssam if (cmd == 0) { 31810317Ssam #ifdef notdef 31910317Ssam /* no remote command execution -- it's a security hole */ 32011653Ssam if (*name == '|') 32110275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 32210275Ssam else 32310317Ssam #endif 32410275Ssam fin = fopen(name, "r"), closefunc = fclose; 32510275Ssam } else { 32610275Ssam char line[BUFSIZ]; 32710275Ssam 32826493Sminshall (void) sprintf(line, cmd, name), name = line; 32910275Ssam fin = popen(line, "r"), closefunc = pclose; 33010275Ssam } 33110275Ssam if (fin == NULL) { 33213152Ssam if (errno != 0) 33313152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 33410275Ssam return; 33510275Ssam } 33610275Ssam st.st_size = 0; 33710275Ssam if (cmd == 0 && 33810275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 33910275Ssam reply(550, "%s: not a plain file.", name); 34010275Ssam goto done; 34110275Ssam } 34210275Ssam dout = dataconn(name, st.st_size, "w"); 34310275Ssam if (dout == NULL) 34410275Ssam goto done; 34526044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 34610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34726044Sminshall } 34826044Sminshall else if (tmp == 0) { 34910275Ssam reply(226, "Transfer complete."); 35026044Sminshall } 35126493Sminshall (void) fclose(dout); 35226044Sminshall data = -1; 35326044Sminshall pdata = -1; 35410275Ssam done: 35510275Ssam (*closefunc)(fin); 35610275Ssam } 35710275Ssam 35810275Ssam store(name, mode) 35910275Ssam char *name, *mode; 36010275Ssam { 36110275Ssam FILE *fout, *din; 36226044Sminshall int (*closefunc)(), dochown = 0, tmp; 36326044Sminshall char *gunique(), *local; 36410275Ssam 36510317Ssam #ifdef notdef 36610317Ssam /* no remote command execution -- it's a security hole */ 36711653Ssam if (name[0] == '|') 36810275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 36910317Ssam else 37010317Ssam #endif 37110317Ssam { 37210303Ssam struct stat st; 37310303Ssam 37426044Sminshall local = name; 37526044Sminshall if (stat(name, &st) < 0) { 37610303Ssam dochown++; 37726044Sminshall } 37826044Sminshall else if (unique) { 37926044Sminshall if ((local = gunique(name)) == NULL) { 38026044Sminshall return; 38126044Sminshall } 38226044Sminshall dochown++; 38326044Sminshall } 38426044Sminshall fout = fopen(local, mode), closefunc = fclose; 38510303Ssam } 38610275Ssam if (fout == NULL) { 38727106Smckusick reply(553, "%s: %s.", local, sys_errlist[errno]); 38810275Ssam return; 38910275Ssam } 39026044Sminshall din = dataconn(local, (off_t)-1, "r"); 39110275Ssam if (din == NULL) 39210275Ssam goto done; 39326044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 39427106Smckusick reply(552, "%s: %s.", local, sys_errlist[errno]); 39526044Sminshall } 39626044Sminshall else if (tmp == 0 && !unique) { 39710275Ssam reply(226, "Transfer complete."); 39826044Sminshall } 39926044Sminshall else if (tmp == 0 && unique) { 40026044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 40126044Sminshall } 40226493Sminshall (void) fclose(din); 40326044Sminshall data = -1; 40426044Sminshall pdata = -1; 40510275Ssam done: 40610303Ssam if (dochown) 40726044Sminshall (void) chown(local, pw->pw_uid, -1); 40810275Ssam (*closefunc)(fout); 40910275Ssam } 41010275Ssam 41110275Ssam FILE * 41210275Ssam getdatasock(mode) 41310275Ssam char *mode; 41410275Ssam { 41517157Ssam int s, on = 1; 41610275Ssam 41710275Ssam if (data >= 0) 41810275Ssam return (fdopen(data, mode)); 41913247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 42010602Ssam if (s < 0) 42110275Ssam return (NULL); 42210275Ssam seteuid(0); 42326493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 42410602Ssam goto bad; 42513152Ssam /* anchor socket to avoid multi-homing problems */ 42613152Ssam data_source.sin_family = AF_INET; 42713152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 42826493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 42910602Ssam goto bad; 43010311Ssam seteuid(pw->pw_uid); 43110275Ssam return (fdopen(s, mode)); 43210602Ssam bad: 43310602Ssam seteuid(pw->pw_uid); 43426493Sminshall (void) close(s); 43510602Ssam return (NULL); 43610275Ssam } 43710275Ssam 43810275Ssam FILE * 43910275Ssam dataconn(name, size, mode) 44010275Ssam char *name; 44111653Ssam off_t size; 44210275Ssam char *mode; 44310275Ssam { 44410275Ssam char sizebuf[32]; 44510275Ssam FILE *file; 44611653Ssam int retry = 0; 44710275Ssam 44810275Ssam if (size >= 0) 44926493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 45010275Ssam else 45110275Ssam (void) strcpy(sizebuf, ""); 45226044Sminshall if (pdata > 0) { 45326044Sminshall struct sockaddr_in from; 45426044Sminshall int s, fromlen = sizeof(from); 45526044Sminshall 45626493Sminshall s = accept(pdata, &from, &fromlen); 45726044Sminshall if (s < 0) { 45826044Sminshall reply(425, "Can't open data connection."); 45926044Sminshall (void) close(pdata); 46026044Sminshall pdata = -1; 46126044Sminshall return(NULL); 46226044Sminshall } 46326044Sminshall (void) close(pdata); 46426044Sminshall pdata = s; 46535700Sbostic reply(150, "Opening data connection for %s (%s mode)%s.", 46635700Sbostic name, type == TYPE_A ? "ascii" : "binary", sizebuf); 46726044Sminshall return(fdopen(pdata, mode)); 46826044Sminshall } 46910275Ssam if (data >= 0) { 47010275Ssam reply(125, "Using existing data connection for %s%s.", 47110275Ssam name, sizebuf); 47210321Ssam usedefault = 1; 47310275Ssam return (fdopen(data, mode)); 47410275Ssam } 47510566Ssam if (usedefault) 47610422Ssam data_dest = his_addr; 47710422Ssam usedefault = 1; 47810275Ssam file = getdatasock(mode); 47910275Ssam if (file == NULL) { 48010275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 48113247Ssam inet_ntoa(data_source.sin_addr), 48210275Ssam ntohs(data_source.sin_port), 48310275Ssam sys_errlist[errno]); 48410275Ssam return (NULL); 48510275Ssam } 48610275Ssam data = fileno(file); 48726044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 48811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 48926493Sminshall sleep((unsigned) swaitint); 49011653Ssam retry += swaitint; 49111653Ssam continue; 49211653Ssam } 49310275Ssam reply(425, "Can't build data connection: %s.", 49410275Ssam sys_errlist[errno]); 49510275Ssam (void) fclose(file); 49610275Ssam data = -1; 49710275Ssam return (NULL); 49810275Ssam } 49935700Sbostic reply(150, "Opening data connection for %s (%s mode)%s.", 50035700Sbostic name, type == TYPE_A ? "ascii" : "binary", sizebuf); 50110275Ssam return (file); 50210275Ssam } 50310275Ssam 50410275Ssam /* 50510275Ssam * Tranfer the contents of "instr" to 50610275Ssam * "outstr" peer using the appropriate 50710275Ssam * encapulation of the date subject 50810275Ssam * to Mode, Structure, and Type. 50910275Ssam * 51010275Ssam * NB: Form isn't handled. 51110275Ssam */ 51210275Ssam send_data(instr, outstr) 51310275Ssam FILE *instr, *outstr; 51410275Ssam { 51510275Ssam register int c; 51610275Ssam int netfd, filefd, cnt; 51710275Ssam char buf[BUFSIZ]; 51810275Ssam 51926044Sminshall transflag++; 52026044Sminshall if (setjmp(urgcatch)) { 52126044Sminshall transflag = 0; 52226044Sminshall return(-1); 52326044Sminshall } 52410275Ssam switch (type) { 52510275Ssam 52610275Ssam case TYPE_A: 52710275Ssam while ((c = getc(instr)) != EOF) { 52811220Ssam if (c == '\n') { 52926044Sminshall if (ferror (outstr)) { 53026044Sminshall transflag = 0; 53111220Ssam return (1); 53226044Sminshall } 53327750Sminshall (void) putc('\r', outstr); 53411220Ssam } 53527750Sminshall (void) putc(c, outstr); 53626044Sminshall /* if (c == '\r') */ 53726044Sminshall /* putc ('\0', outstr); */ 53810275Ssam } 53926044Sminshall transflag = 0; 54026044Sminshall if (ferror (instr) || ferror (outstr)) { 54111220Ssam return (1); 54226044Sminshall } 54310275Ssam return (0); 54410275Ssam 54510275Ssam case TYPE_I: 54610275Ssam case TYPE_L: 54710275Ssam netfd = fileno(outstr); 54810275Ssam filefd = fileno(instr); 54910275Ssam 55026044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 55126044Sminshall if (write(netfd, buf, cnt) < 0) { 55226044Sminshall transflag = 0; 55310275Ssam return (1); 55426044Sminshall } 55526044Sminshall } 55626044Sminshall transflag = 0; 55710275Ssam return (cnt < 0); 55810275Ssam } 55927106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 56026044Sminshall transflag = 0; 56127106Smckusick return (-1); 56210275Ssam } 56310275Ssam 56410275Ssam /* 56510275Ssam * Transfer data from peer to 56610275Ssam * "outstr" using the appropriate 56710275Ssam * encapulation of the data subject 56810275Ssam * to Mode, Structure, and Type. 56910275Ssam * 57010275Ssam * N.B.: Form isn't handled. 57110275Ssam */ 57210275Ssam receive_data(instr, outstr) 57310275Ssam FILE *instr, *outstr; 57410275Ssam { 57510275Ssam register int c; 57611220Ssam int cnt; 57710275Ssam char buf[BUFSIZ]; 57810275Ssam 57910275Ssam 58026044Sminshall transflag++; 58126044Sminshall if (setjmp(urgcatch)) { 58226044Sminshall transflag = 0; 58326044Sminshall return(-1); 58426044Sminshall } 58510275Ssam switch (type) { 58610275Ssam 58710275Ssam case TYPE_I: 58810275Ssam case TYPE_L: 58926044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 59026044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 59126044Sminshall transflag = 0; 59210275Ssam return (1); 59326044Sminshall } 59426044Sminshall } 59526044Sminshall transflag = 0; 59610275Ssam return (cnt < 0); 59710275Ssam 59810275Ssam case TYPE_E: 59927106Smckusick reply(553, "TYPE E not implemented."); 60026044Sminshall transflag = 0; 60127106Smckusick return (-1); 60210275Ssam 60310275Ssam case TYPE_A: 60410275Ssam while ((c = getc(instr)) != EOF) { 60527750Sminshall while (c == '\r') { 60626044Sminshall if (ferror (outstr)) { 60726044Sminshall transflag = 0; 60811220Ssam return (1); 60926044Sminshall } 61011220Ssam if ((c = getc(instr)) != '\n') 61127750Sminshall (void) putc ('\r', outstr); 61226044Sminshall /* if (c == '\0') */ 61326044Sminshall /* continue; */ 61410275Ssam } 61527750Sminshall (void) putc (c, outstr); 61610275Ssam } 61726044Sminshall transflag = 0; 61811220Ssam if (ferror (instr) || ferror (outstr)) 61911220Ssam return (1); 62010275Ssam return (0); 62110275Ssam } 62226044Sminshall transflag = 0; 62310275Ssam fatal("Unknown type in receive_data."); 62410275Ssam /*NOTREACHED*/ 62510275Ssam } 62610275Ssam 62710275Ssam fatal(s) 62810275Ssam char *s; 62910275Ssam { 63010275Ssam reply(451, "Error in server: %s\n", s); 63110275Ssam reply(221, "Closing connection due to server error."); 63213247Ssam dologout(0); 63310275Ssam } 63410275Ssam 63532110Smckusick reply(n, s, p0, p1, p2, p3, p4) 63610275Ssam int n; 63710275Ssam char *s; 63810275Ssam { 63910275Ssam 64010275Ssam printf("%d ", n); 64132110Smckusick printf(s, p0, p1, p2, p3, p4); 64210275Ssam printf("\r\n"); 64326493Sminshall (void) fflush(stdout); 64410275Ssam if (debug) { 64526493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 64632110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 64710275Ssam } 64810275Ssam } 64910275Ssam 65032110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 65110275Ssam int n; 65210275Ssam char *s; 65310275Ssam { 65410275Ssam printf("%d-", n); 65532110Smckusick printf(s, p0, p1, p2, p3, p4); 65610275Ssam printf("\r\n"); 65726493Sminshall (void) fflush(stdout); 65810275Ssam if (debug) { 65926493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 66032110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 66110275Ssam } 66210275Ssam } 66310275Ssam 66410275Ssam ack(s) 66510275Ssam char *s; 66610275Ssam { 66727106Smckusick reply(250, "%s command successful.", s); 66810275Ssam } 66910275Ssam 67010275Ssam nack(s) 67110275Ssam char *s; 67210275Ssam { 67310275Ssam reply(502, "%s command not implemented.", s); 67410275Ssam } 67510275Ssam 67626493Sminshall yyerror(s) 67726493Sminshall char *s; 67810275Ssam { 67926044Sminshall char *cp; 68026044Sminshall 68126044Sminshall cp = index(cbuf,'\n'); 68226044Sminshall *cp = '\0'; 68326044Sminshall reply(500, "'%s': command not understood.",cbuf); 68410275Ssam } 68510275Ssam 68610275Ssam delete(name) 68710275Ssam char *name; 68810275Ssam { 68910275Ssam struct stat st; 69010275Ssam 69110275Ssam if (stat(name, &st) < 0) { 69210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 69310275Ssam return; 69410275Ssam } 69510275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 69610275Ssam if (rmdir(name) < 0) { 69710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 69810275Ssam return; 69910275Ssam } 70010275Ssam goto done; 70110275Ssam } 70210275Ssam if (unlink(name) < 0) { 70310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70410275Ssam return; 70510275Ssam } 70610275Ssam done: 70710275Ssam ack("DELE"); 70810275Ssam } 70910275Ssam 71010275Ssam cwd(path) 71110275Ssam char *path; 71210275Ssam { 71310275Ssam 71410275Ssam if (chdir(path) < 0) { 71510275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 71610275Ssam return; 71710275Ssam } 71810275Ssam ack("CWD"); 71910275Ssam } 72010275Ssam 72110303Ssam makedir(name) 72210275Ssam char *name; 72310275Ssam { 72410303Ssam struct stat st; 72510303Ssam int dochown = stat(name, &st) < 0; 72610275Ssam 72710275Ssam if (mkdir(name, 0777) < 0) { 72810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 72910275Ssam return; 73010275Ssam } 73110303Ssam if (dochown) 73210303Ssam (void) chown(name, pw->pw_uid, -1); 73327106Smckusick reply(257, "MKD command successful."); 73410275Ssam } 73510275Ssam 73610303Ssam removedir(name) 73710275Ssam char *name; 73810275Ssam { 73910275Ssam 74010275Ssam if (rmdir(name) < 0) { 74110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 74210275Ssam return; 74310275Ssam } 74427106Smckusick ack("RMD"); 74510275Ssam } 74610275Ssam 74710303Ssam pwd() 74810275Ssam { 74910303Ssam char path[MAXPATHLEN + 1]; 75010275Ssam 75110275Ssam if (getwd(path) == NULL) { 75227106Smckusick reply(550, "%s.", path); 75310275Ssam return; 75410275Ssam } 75527106Smckusick reply(257, "\"%s\" is current directory.", path); 75610275Ssam } 75710275Ssam 75810275Ssam char * 75910275Ssam renamefrom(name) 76010275Ssam char *name; 76110275Ssam { 76210275Ssam struct stat st; 76310275Ssam 76410275Ssam if (stat(name, &st) < 0) { 76510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 76610275Ssam return ((char *)0); 76710275Ssam } 76810303Ssam reply(350, "File exists, ready for destination name"); 76910275Ssam return (name); 77010275Ssam } 77110275Ssam 77210275Ssam renamecmd(from, to) 77310275Ssam char *from, *to; 77410275Ssam { 77510275Ssam 77610275Ssam if (rename(from, to) < 0) { 77710275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 77810275Ssam return; 77910275Ssam } 78010275Ssam ack("RNTO"); 78110275Ssam } 78210275Ssam 78310275Ssam dolog(sin) 78410275Ssam struct sockaddr_in *sin; 78510275Ssam { 78610275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 78710275Ssam sizeof (struct in_addr), AF_INET); 78810275Ssam time_t t; 78926493Sminshall extern char *ctime(); 79010275Ssam 79113247Ssam if (hp) { 79226493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 79313247Ssam endhostent(); 79413247Ssam } else 79526493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 79613247Ssam sizeof (remotehost)); 79713247Ssam if (!logging) 79813247Ssam return; 79926493Sminshall t = time((time_t *) 0); 80026493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 80110275Ssam } 80210695Ssam 80310695Ssam /* 80413247Ssam * Record logout in wtmp file 80513247Ssam * and exit with supplied status. 80613247Ssam */ 80713247Ssam dologout(status) 80813247Ssam int status; 80913247Ssam { 81017580Ssam if (logged_in) { 81117580Ssam (void) seteuid(0); 81235672Sbostic logwtmp(ttyline, "", ""); 81313247Ssam } 81414436Ssam /* beware of flushing buffers after a SIGPIPE */ 81514436Ssam _exit(status); 81613247Ssam } 81713247Ssam 81813247Ssam /* 81910695Ssam * Check user requesting login priviledges. 82028864Smckusick * Disallow anyone who does not have a standard 82128864Smckusick * shell returned by getusershell() (/etc/shells). 82210695Ssam * Disallow anyone mentioned in the file FTPUSERS 82310695Ssam * to allow people such as uucp to be avoided. 82410695Ssam */ 82510695Ssam checkuser(name) 82610695Ssam register char *name; 82710695Ssam { 82828864Smckusick register char *cp; 82910695Ssam FILE *fd; 83036185Sbostic struct passwd *p; 83136185Sbostic char *shell; 83210695Ssam int found = 0; 83336185Sbostic char line[BUFSIZ], *index(), *getusershell(); 83410695Ssam 83536185Sbostic if ((p = getpwnam(name)) == NULL) 83628864Smckusick return (0); 83736185Sbostic if ((shell = p->pw_shell) == NULL || *shell == 0) 83836185Sbostic shell = "/bin/sh"; 83928864Smckusick while ((cp = getusershell()) != NULL) 84036185Sbostic if (strcmp(cp, shell) == 0) 84128864Smckusick break; 84228864Smckusick endusershell(); 84328864Smckusick if (cp == NULL) 84428864Smckusick return (0); 84536185Sbostic if ((fd = fopen(FTPUSERS, "r")) == NULL) 84610695Ssam return (1); 84710695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 84836185Sbostic if ((cp = index(line, '\n')) != NULL) 84910695Ssam *cp = '\0'; 85010695Ssam if (strcmp(line, name) == 0) { 85110695Ssam found++; 85210695Ssam break; 85310695Ssam } 85410695Ssam } 85526493Sminshall (void) fclose(fd); 85610695Ssam return (!found); 85710695Ssam } 85826044Sminshall 85926044Sminshall myoob() 86026044Sminshall { 86127750Sminshall char *cp; 86226044Sminshall 86327750Sminshall /* only process if transfer occurring */ 86426044Sminshall if (!transflag) { 86526044Sminshall return; 86626044Sminshall } 86727750Sminshall cp = tmpline; 86827750Sminshall if (getline(cp, 7, stdin) == NULL) { 86927750Sminshall reply(221, "You could at least say goodby."); 87027750Sminshall dologout(0); 87126044Sminshall } 87226044Sminshall upper(cp); 87326227Ssam if (strcmp(cp, "ABOR\r\n")) 87426044Sminshall return; 87526044Sminshall tmpline[0] = '\0'; 87626044Sminshall reply(426,"Transfer aborted. Data connection closed."); 87726044Sminshall reply(226,"Abort successful"); 87826044Sminshall longjmp(urgcatch, 1); 87926044Sminshall } 88026044Sminshall 88127106Smckusick /* 88227106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 88327106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 88427106Smckusick */ 88526044Sminshall passive() 88626044Sminshall { 88726044Sminshall int len; 88826044Sminshall struct sockaddr_in tmp; 88926044Sminshall register char *p, *a; 89026044Sminshall 89126044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 89226044Sminshall if (pdata < 0) { 89327106Smckusick reply(530, "Can't open passive connection"); 89426044Sminshall return; 89526044Sminshall } 89626044Sminshall tmp = ctrl_addr; 89726044Sminshall tmp.sin_port = 0; 89826044Sminshall seteuid(0); 89926493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 90026044Sminshall seteuid(pw->pw_uid); 90126044Sminshall (void) close(pdata); 90226044Sminshall pdata = -1; 90327106Smckusick reply(530, "Can't open passive connection"); 90426044Sminshall return; 90526044Sminshall } 90626044Sminshall seteuid(pw->pw_uid); 90726044Sminshall len = sizeof(tmp); 90826044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 90926044Sminshall (void) close(pdata); 91026044Sminshall pdata = -1; 91127106Smckusick reply(530, "Can't open passive connection"); 91226044Sminshall return; 91326044Sminshall } 91426044Sminshall if (listen(pdata, 1) < 0) { 91526044Sminshall (void) close(pdata); 91626044Sminshall pdata = -1; 91727106Smckusick reply(530, "Can't open passive connection"); 91826044Sminshall return; 91926044Sminshall } 92026044Sminshall a = (char *) &tmp.sin_addr; 92126044Sminshall p = (char *) &tmp.sin_port; 92226044Sminshall 92326044Sminshall #define UC(b) (((int) b) & 0xff) 92426044Sminshall 92526044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 92626044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 92726044Sminshall } 92826044Sminshall 92926044Sminshall char * 93026044Sminshall gunique(local) 93126044Sminshall char *local; 93226044Sminshall { 93326044Sminshall static char new[MAXPATHLEN]; 93426044Sminshall char *cp = rindex(local, '/'); 93526044Sminshall int d, count=0; 93626044Sminshall char ext = '1'; 93726044Sminshall 93826044Sminshall if (cp) { 93926044Sminshall *cp = '\0'; 94026044Sminshall } 94126044Sminshall d = access(cp ? local : ".", 2); 94226044Sminshall if (cp) { 94326044Sminshall *cp = '/'; 94426044Sminshall } 94526044Sminshall if (d < 0) { 94626493Sminshall syslog(LOG_ERR, "%s: %m", local); 94726044Sminshall return((char *) 0); 94826044Sminshall } 94926044Sminshall (void) strcpy(new, local); 95026044Sminshall cp = new + strlen(new); 95126044Sminshall *cp++ = '.'; 95226044Sminshall while (!d) { 95326044Sminshall if (++count == 100) { 95427106Smckusick reply(452, "Unique file name not cannot be created."); 95526044Sminshall return((char *) 0); 95626044Sminshall } 95726044Sminshall *cp++ = ext; 95826044Sminshall *cp = '\0'; 95926044Sminshall if (ext == '9') { 96026044Sminshall ext = '0'; 96126044Sminshall } 96226044Sminshall else { 96326044Sminshall ext++; 96426044Sminshall } 96526044Sminshall if ((d = access(new, 0)) < 0) { 96626044Sminshall break; 96726044Sminshall } 96826044Sminshall if (ext != '0') { 96926044Sminshall cp--; 97026044Sminshall } 97126044Sminshall else if (*(cp - 2) == '.') { 97226044Sminshall *(cp - 1) = '1'; 97326044Sminshall } 97426044Sminshall else { 97526044Sminshall *(cp - 2) = *(cp - 2) + 1; 97626044Sminshall cp--; 97726044Sminshall } 97826044Sminshall } 97926044Sminshall return(new); 98026044Sminshall } 981