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*36276Sbostic static char sccsid[] = "@(#)ftpd.c 5.19 (Berkeley) 11/30/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 */ 65*36276Sbostic extern FILE *ftpd_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]; 92*36276Sbostic char hostname[MAXHOSTNAMELEN]; 93*36276Sbostic char remotehost[MAXHOSTNAMELEN]; 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 */ 167*36276Sbostic /* Sequent defines this, but it doesn't work */ 16827750Sminshall #ifdef SO_OOBINLINE 169*36276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 17027750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 171*36276Sbostic #endif 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)); 188*36276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 18910275Ssam for (;;) { 19026493Sminshall (void) setjmp(errcatch); 19126493Sminshall (void) yyparse(); 19210275Ssam } 19310275Ssam } 19410419Ssam 19510275Ssam lostconn() 19610275Ssam { 19710275Ssam 19814089Ssam if (debug) 19926493Sminshall syslog(LOG_DEBUG, "lost connection"); 20014089Ssam dologout(-1); 20110275Ssam } 20210275Ssam 20335672Sbostic static char ttyline[20]; 20435672Sbostic 20536185Sbostic /* 20636185Sbostic * Helper function for sgetpwnam(). 20736185Sbostic */ 20836185Sbostic char * 20936185Sbostic sgetsave(s) 21036185Sbostic char *s; 21136185Sbostic { 21236185Sbostic #ifdef notdef 21336185Sbostic char *new = strdup(s); 21436185Sbostic #else 21536185Sbostic char *malloc(); 21636185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 21736185Sbostic #endif 21836185Sbostic 21936185Sbostic if (new == NULL) { 220*36276Sbostic reply(553, "Local resource failure: malloc"); 22136185Sbostic dologout(1); 22236185Sbostic } 22336185Sbostic #ifndef notdef 22436185Sbostic (void) strcpy(new, s); 22536185Sbostic #endif 22636185Sbostic return (new); 22736185Sbostic } 22836185Sbostic 22936185Sbostic /* 23036185Sbostic * Save the result of a getpwnam. Used for USER command, since 23136185Sbostic * the data returned must not be clobbered by any other command 23236185Sbostic * (e.g., globbing). 23336185Sbostic */ 23436185Sbostic struct passwd * 23536185Sbostic sgetpwnam(name) 23636185Sbostic char *name; 23736185Sbostic { 23836185Sbostic static struct passwd save; 23936185Sbostic register struct passwd *p; 24036185Sbostic char *sgetsave(); 24136185Sbostic 24236185Sbostic if ((p = getpwnam(name)) == NULL) 24336185Sbostic return (p); 24436185Sbostic if (save.pw_name) { 24536185Sbostic free(save.pw_name); 24636185Sbostic free(save.pw_passwd); 24736185Sbostic free(save.pw_comment); 24836185Sbostic free(save.pw_gecos); 24936185Sbostic free(save.pw_dir); 25036185Sbostic free(save.pw_shell); 25136185Sbostic } 25236185Sbostic save = *p; 25336185Sbostic save.pw_name = sgetsave(p->pw_name); 25436185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 25536185Sbostic save.pw_comment = sgetsave(p->pw_comment); 25636185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 25736185Sbostic save.pw_dir = sgetsave(p->pw_dir); 25836185Sbostic save.pw_shell = sgetsave(p->pw_shell); 25936185Sbostic return (&save); 26036185Sbostic } 26136185Sbostic 26210275Ssam pass(passwd) 26310275Ssam char *passwd; 26410275Ssam { 26536185Sbostic char *xpasswd; 26610275Ssam 26710275Ssam if (logged_in || pw == NULL) { 26810275Ssam reply(503, "Login with USER first."); 26910275Ssam return; 27010275Ssam } 27110275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 27210275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 27316760Slepreau /* The strcmp does not catch null passwords! */ 27416760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 27510275Ssam reply(530, "Login incorrect."); 27610275Ssam pw = NULL; 27710275Ssam return; 27810275Ssam } 27910275Ssam } 28010303Ssam setegid(pw->pw_gid); 28110275Ssam initgroups(pw->pw_name, pw->pw_gid); 28210275Ssam if (chdir(pw->pw_dir)) { 28327106Smckusick reply(530, "User %s: can't change directory to %s.", 28410275Ssam pw->pw_name, pw->pw_dir); 28510303Ssam goto bad; 28610275Ssam } 28716033Sralph 28836192Sbostic /* open wtmp before chroot */ 28936192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 29036192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 29136192Sbostic logged_in = 1; 29236192Sbostic 29336192Sbostic if (guest) { 29436192Sbostic if (chroot(pw->pw_dir) < 0) { 29536192Sbostic reply(550, "Can't set guest privileges."); 29636192Sbostic goto bad; 29716760Slepreau } 29836192Sbostic reply(230, "Guest login ok, access restrictions apply."); 29936192Sbostic } else 30010275Ssam reply(230, "User %s logged in.", pw->pw_name); 30110303Ssam seteuid(pw->pw_uid); 30210303Ssam home = pw->pw_dir; /* home dir for globbing */ 30310303Ssam return; 30410303Ssam bad: 30510303Ssam seteuid(0); 30610303Ssam pw = NULL; 30710275Ssam } 30810275Ssam 30910275Ssam retrieve(cmd, name) 31010275Ssam char *cmd, *name; 31110275Ssam { 31210275Ssam FILE *fin, *dout; 31310275Ssam struct stat st; 31426044Sminshall int (*closefunc)(), tmp; 31510275Ssam 31610275Ssam if (cmd == 0) { 31710317Ssam #ifdef notdef 31810317Ssam /* no remote command execution -- it's a security hole */ 31911653Ssam if (*name == '|') 320*36276Sbostic fin = ftpd_popen(name + 1, "r"), closefunc = pclose; 32110275Ssam else 32210317Ssam #endif 32310275Ssam fin = fopen(name, "r"), closefunc = fclose; 32410275Ssam } else { 32510275Ssam char line[BUFSIZ]; 32610275Ssam 32726493Sminshall (void) sprintf(line, cmd, name), name = line; 328*36276Sbostic fin = ftpd_popen(line, "r"), closefunc = pclose; 32910275Ssam } 33010275Ssam if (fin == NULL) { 33113152Ssam if (errno != 0) 33213152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 33310275Ssam return; 33410275Ssam } 33510275Ssam st.st_size = 0; 33610275Ssam if (cmd == 0 && 33710275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 33810275Ssam reply(550, "%s: not a plain file.", name); 33910275Ssam goto done; 34010275Ssam } 34110275Ssam dout = dataconn(name, st.st_size, "w"); 34210275Ssam if (dout == NULL) 34310275Ssam goto done; 34426044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 34510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34626044Sminshall } 34726044Sminshall else if (tmp == 0) { 34810275Ssam reply(226, "Transfer complete."); 34926044Sminshall } 35026493Sminshall (void) fclose(dout); 35126044Sminshall data = -1; 35226044Sminshall pdata = -1; 35310275Ssam done: 35410275Ssam (*closefunc)(fin); 35510275Ssam } 35610275Ssam 35710275Ssam store(name, mode) 35810275Ssam char *name, *mode; 35910275Ssam { 36010275Ssam FILE *fout, *din; 36126044Sminshall int (*closefunc)(), dochown = 0, tmp; 36226044Sminshall char *gunique(), *local; 36310275Ssam 36410317Ssam #ifdef notdef 36510317Ssam /* no remote command execution -- it's a security hole */ 36611653Ssam if (name[0] == '|') 367*36276Sbostic fout = ftpd_popen(&name[1], "w"), closefunc = pclose; 36810317Ssam else 36910317Ssam #endif 37010317Ssam { 37110303Ssam struct stat st; 37210303Ssam 37326044Sminshall local = name; 37426044Sminshall if (stat(name, &st) < 0) { 37510303Ssam dochown++; 37626044Sminshall } 37726044Sminshall else if (unique) { 37826044Sminshall if ((local = gunique(name)) == NULL) { 37926044Sminshall return; 38026044Sminshall } 38126044Sminshall dochown++; 38226044Sminshall } 38326044Sminshall fout = fopen(local, mode), closefunc = fclose; 38410303Ssam } 38510275Ssam if (fout == NULL) { 38627106Smckusick reply(553, "%s: %s.", local, sys_errlist[errno]); 38710275Ssam return; 38810275Ssam } 38926044Sminshall din = dataconn(local, (off_t)-1, "r"); 39010275Ssam if (din == NULL) 39110275Ssam goto done; 39226044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 39327106Smckusick reply(552, "%s: %s.", local, sys_errlist[errno]); 39426044Sminshall } 39526044Sminshall else if (tmp == 0 && !unique) { 39610275Ssam reply(226, "Transfer complete."); 39726044Sminshall } 39826044Sminshall else if (tmp == 0 && unique) { 39926044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 40026044Sminshall } 40126493Sminshall (void) fclose(din); 40226044Sminshall data = -1; 40326044Sminshall pdata = -1; 40410275Ssam done: 40510303Ssam if (dochown) 406*36276Sbostic (void) fchown(fileno(fout), pw->pw_uid, -1); 40710275Ssam (*closefunc)(fout); 40810275Ssam } 40910275Ssam 41010275Ssam FILE * 41110275Ssam getdatasock(mode) 41210275Ssam char *mode; 41310275Ssam { 41417157Ssam int s, on = 1; 41510275Ssam 41610275Ssam if (data >= 0) 41710275Ssam return (fdopen(data, mode)); 41813247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 41910602Ssam if (s < 0) 42010275Ssam return (NULL); 42110275Ssam seteuid(0); 42226493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 42310602Ssam goto bad; 42413152Ssam /* anchor socket to avoid multi-homing problems */ 42513152Ssam data_source.sin_family = AF_INET; 42613152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 42726493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 42810602Ssam goto bad; 42910311Ssam seteuid(pw->pw_uid); 43010275Ssam return (fdopen(s, mode)); 43110602Ssam bad: 43210602Ssam seteuid(pw->pw_uid); 43326493Sminshall (void) close(s); 43410602Ssam return (NULL); 43510275Ssam } 43610275Ssam 43710275Ssam FILE * 43810275Ssam dataconn(name, size, mode) 43910275Ssam char *name; 44011653Ssam off_t size; 44110275Ssam char *mode; 44210275Ssam { 44310275Ssam char sizebuf[32]; 44410275Ssam FILE *file; 44511653Ssam int retry = 0; 44610275Ssam 44710275Ssam if (size >= 0) 44826493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 44910275Ssam else 45010275Ssam (void) strcpy(sizebuf, ""); 45126044Sminshall if (pdata > 0) { 45226044Sminshall struct sockaddr_in from; 45326044Sminshall int s, fromlen = sizeof(from); 45426044Sminshall 45526493Sminshall s = accept(pdata, &from, &fromlen); 45626044Sminshall if (s < 0) { 45726044Sminshall reply(425, "Can't open data connection."); 45826044Sminshall (void) close(pdata); 45926044Sminshall pdata = -1; 46026044Sminshall return(NULL); 46126044Sminshall } 46226044Sminshall (void) close(pdata); 46326044Sminshall pdata = s; 46436235Skarels reply(150, "Opening %s mode data connection for %s%s.", 46536235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 46626044Sminshall return(fdopen(pdata, mode)); 46726044Sminshall } 46810275Ssam if (data >= 0) { 46910275Ssam reply(125, "Using existing data connection for %s%s.", 47010275Ssam name, sizebuf); 47110321Ssam usedefault = 1; 47210275Ssam return (fdopen(data, mode)); 47310275Ssam } 47410566Ssam if (usedefault) 47510422Ssam data_dest = his_addr; 47610422Ssam usedefault = 1; 47710275Ssam file = getdatasock(mode); 47810275Ssam if (file == NULL) { 47910275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 48013247Ssam inet_ntoa(data_source.sin_addr), 48110275Ssam ntohs(data_source.sin_port), 48210275Ssam sys_errlist[errno]); 48310275Ssam return (NULL); 48410275Ssam } 48510275Ssam data = fileno(file); 48626044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 48711653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 48826493Sminshall sleep((unsigned) swaitint); 48911653Ssam retry += swaitint; 49011653Ssam continue; 49111653Ssam } 49210275Ssam reply(425, "Can't build data connection: %s.", 49310275Ssam sys_errlist[errno]); 49410275Ssam (void) fclose(file); 49510275Ssam data = -1; 49610275Ssam return (NULL); 49710275Ssam } 49836235Skarels reply(150, "Opening %s mode data connection for %s%s.", 49936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 50010275Ssam return (file); 50110275Ssam } 50210275Ssam 50310275Ssam /* 50410275Ssam * Tranfer the contents of "instr" to 50510275Ssam * "outstr" peer using the appropriate 50610275Ssam * encapulation of the date subject 50710275Ssam * to Mode, Structure, and Type. 50810275Ssam * 50910275Ssam * NB: Form isn't handled. 51010275Ssam */ 51110275Ssam send_data(instr, outstr) 51210275Ssam FILE *instr, *outstr; 51310275Ssam { 51410275Ssam register int c; 51510275Ssam int netfd, filefd, cnt; 51610275Ssam char buf[BUFSIZ]; 51710275Ssam 51826044Sminshall transflag++; 51926044Sminshall if (setjmp(urgcatch)) { 52026044Sminshall transflag = 0; 52126044Sminshall return(-1); 52226044Sminshall } 52310275Ssam switch (type) { 52410275Ssam 52510275Ssam case TYPE_A: 52610275Ssam while ((c = getc(instr)) != EOF) { 52711220Ssam if (c == '\n') { 52826044Sminshall if (ferror (outstr)) { 52926044Sminshall transflag = 0; 53011220Ssam return (1); 53126044Sminshall } 53227750Sminshall (void) putc('\r', outstr); 53311220Ssam } 53427750Sminshall (void) putc(c, outstr); 53526044Sminshall /* if (c == '\r') */ 53626044Sminshall /* putc ('\0', outstr); */ 53710275Ssam } 53826044Sminshall transflag = 0; 53926044Sminshall if (ferror (instr) || ferror (outstr)) { 54011220Ssam return (1); 54126044Sminshall } 54210275Ssam return (0); 54310275Ssam 54410275Ssam case TYPE_I: 54510275Ssam case TYPE_L: 54610275Ssam netfd = fileno(outstr); 54710275Ssam filefd = fileno(instr); 54810275Ssam 54926044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 55026044Sminshall if (write(netfd, buf, cnt) < 0) { 55126044Sminshall transflag = 0; 55210275Ssam return (1); 55326044Sminshall } 55426044Sminshall } 55526044Sminshall transflag = 0; 55610275Ssam return (cnt < 0); 55710275Ssam } 55827106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 55926044Sminshall transflag = 0; 56027106Smckusick return (-1); 56110275Ssam } 56210275Ssam 56310275Ssam /* 56410275Ssam * Transfer data from peer to 56510275Ssam * "outstr" using the appropriate 56610275Ssam * encapulation of the data subject 56710275Ssam * to Mode, Structure, and Type. 56810275Ssam * 56910275Ssam * N.B.: Form isn't handled. 57010275Ssam */ 57110275Ssam receive_data(instr, outstr) 57210275Ssam FILE *instr, *outstr; 57310275Ssam { 57410275Ssam register int c; 57511220Ssam int cnt; 57610275Ssam char buf[BUFSIZ]; 57710275Ssam 57810275Ssam 57926044Sminshall transflag++; 58026044Sminshall if (setjmp(urgcatch)) { 58126044Sminshall transflag = 0; 58226044Sminshall return(-1); 58326044Sminshall } 58410275Ssam switch (type) { 58510275Ssam 58610275Ssam case TYPE_I: 58710275Ssam case TYPE_L: 58826044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 58926044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 59026044Sminshall transflag = 0; 59110275Ssam return (1); 59226044Sminshall } 59326044Sminshall } 59426044Sminshall transflag = 0; 59510275Ssam return (cnt < 0); 59610275Ssam 59710275Ssam case TYPE_E: 59827106Smckusick reply(553, "TYPE E not implemented."); 59926044Sminshall transflag = 0; 60027106Smckusick return (-1); 60110275Ssam 60210275Ssam case TYPE_A: 60310275Ssam while ((c = getc(instr)) != EOF) { 60427750Sminshall while (c == '\r') { 60526044Sminshall if (ferror (outstr)) { 60626044Sminshall transflag = 0; 60711220Ssam return (1); 60826044Sminshall } 60911220Ssam if ((c = getc(instr)) != '\n') 61027750Sminshall (void) putc ('\r', outstr); 61126044Sminshall /* if (c == '\0') */ 61226044Sminshall /* continue; */ 61310275Ssam } 61427750Sminshall (void) putc (c, outstr); 61510275Ssam } 61626044Sminshall transflag = 0; 61711220Ssam if (ferror (instr) || ferror (outstr)) 61811220Ssam return (1); 61910275Ssam return (0); 62010275Ssam } 62126044Sminshall transflag = 0; 62210275Ssam fatal("Unknown type in receive_data."); 62310275Ssam /*NOTREACHED*/ 62410275Ssam } 62510275Ssam 62610275Ssam fatal(s) 62710275Ssam char *s; 62810275Ssam { 62910275Ssam reply(451, "Error in server: %s\n", s); 63010275Ssam reply(221, "Closing connection due to server error."); 63113247Ssam dologout(0); 63210275Ssam } 63310275Ssam 63432110Smckusick reply(n, s, p0, p1, p2, p3, p4) 63510275Ssam int n; 63610275Ssam char *s; 63710275Ssam { 63810275Ssam 63910275Ssam printf("%d ", n); 64032110Smckusick printf(s, p0, p1, p2, p3, p4); 64110275Ssam printf("\r\n"); 64226493Sminshall (void) fflush(stdout); 64310275Ssam if (debug) { 64426493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 64532110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 64610275Ssam } 64710275Ssam } 64810275Ssam 64932110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 65010275Ssam int n; 65110275Ssam char *s; 65210275Ssam { 65310275Ssam printf("%d-", n); 65432110Smckusick printf(s, p0, p1, p2, p3, p4); 65510275Ssam printf("\r\n"); 65626493Sminshall (void) fflush(stdout); 65710275Ssam if (debug) { 65826493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 65932110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 66010275Ssam } 66110275Ssam } 66210275Ssam 66310275Ssam ack(s) 66410275Ssam char *s; 66510275Ssam { 66627106Smckusick reply(250, "%s command successful.", s); 66710275Ssam } 66810275Ssam 66910275Ssam nack(s) 67010275Ssam char *s; 67110275Ssam { 67210275Ssam reply(502, "%s command not implemented.", s); 67310275Ssam } 67410275Ssam 67526493Sminshall yyerror(s) 67626493Sminshall char *s; 67710275Ssam { 67826044Sminshall char *cp; 67926044Sminshall 68026044Sminshall cp = index(cbuf,'\n'); 68126044Sminshall *cp = '\0'; 68226044Sminshall reply(500, "'%s': command not understood.",cbuf); 68310275Ssam } 68410275Ssam 68510275Ssam delete(name) 68610275Ssam char *name; 68710275Ssam { 68810275Ssam struct stat st; 68910275Ssam 69010275Ssam if (stat(name, &st) < 0) { 69110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 69210275Ssam return; 69310275Ssam } 69410275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 69510275Ssam if (rmdir(name) < 0) { 69610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 69710275Ssam return; 69810275Ssam } 69910275Ssam goto done; 70010275Ssam } 70110275Ssam if (unlink(name) < 0) { 70210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70310275Ssam return; 70410275Ssam } 70510275Ssam done: 70610275Ssam ack("DELE"); 70710275Ssam } 70810275Ssam 70910275Ssam cwd(path) 71010275Ssam char *path; 71110275Ssam { 71210275Ssam 71310275Ssam if (chdir(path) < 0) { 71410275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 71510275Ssam return; 71610275Ssam } 71710275Ssam ack("CWD"); 71810275Ssam } 71910275Ssam 72010303Ssam makedir(name) 72110275Ssam char *name; 72210275Ssam { 723*36276Sbostic uid_t oldeuid; 724*36276Sbostic 725*36276Sbostic oldeuid = geteuid(); 726*36276Sbostic seteuid(pw->pw_uid); 727*36276Sbostic if (mkdir(name, 0777) < 0) 72810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 729*36276Sbostic else 730*36276Sbostic reply(257, "MKD command successful."); 731*36276Sbostic seteuid(oldeuid); 73210275Ssam } 73310275Ssam 73410303Ssam removedir(name) 73510275Ssam char *name; 73610275Ssam { 73710275Ssam 73810275Ssam if (rmdir(name) < 0) { 73910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 74010275Ssam return; 74110275Ssam } 74227106Smckusick ack("RMD"); 74310275Ssam } 74410275Ssam 74510303Ssam pwd() 74610275Ssam { 74710303Ssam char path[MAXPATHLEN + 1]; 74810275Ssam 74910275Ssam if (getwd(path) == NULL) { 75027106Smckusick reply(550, "%s.", path); 75110275Ssam return; 75210275Ssam } 75327106Smckusick reply(257, "\"%s\" is current directory.", path); 75410275Ssam } 75510275Ssam 75610275Ssam char * 75710275Ssam renamefrom(name) 75810275Ssam char *name; 75910275Ssam { 76010275Ssam struct stat st; 76110275Ssam 76210275Ssam if (stat(name, &st) < 0) { 76310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 76410275Ssam return ((char *)0); 76510275Ssam } 76610303Ssam reply(350, "File exists, ready for destination name"); 76710275Ssam return (name); 76810275Ssam } 76910275Ssam 77010275Ssam renamecmd(from, to) 77110275Ssam char *from, *to; 77210275Ssam { 77310275Ssam 77410275Ssam if (rename(from, to) < 0) { 77510275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 77610275Ssam return; 77710275Ssam } 77810275Ssam ack("RNTO"); 77910275Ssam } 78010275Ssam 78110275Ssam dolog(sin) 78210275Ssam struct sockaddr_in *sin; 78310275Ssam { 78410275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 78510275Ssam sizeof (struct in_addr), AF_INET); 78610275Ssam time_t t; 78726493Sminshall extern char *ctime(); 78810275Ssam 78913247Ssam if (hp) { 79026493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 79113247Ssam endhostent(); 79213247Ssam } else 79326493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 79413247Ssam sizeof (remotehost)); 79513247Ssam if (!logging) 79613247Ssam return; 79726493Sminshall t = time((time_t *) 0); 79826493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 79910275Ssam } 80010695Ssam 80110695Ssam /* 80213247Ssam * Record logout in wtmp file 80313247Ssam * and exit with supplied status. 80413247Ssam */ 80513247Ssam dologout(status) 80613247Ssam int status; 80713247Ssam { 80817580Ssam if (logged_in) { 80917580Ssam (void) seteuid(0); 81035672Sbostic logwtmp(ttyline, "", ""); 81113247Ssam } 81214436Ssam /* beware of flushing buffers after a SIGPIPE */ 81314436Ssam _exit(status); 81413247Ssam } 81513247Ssam 81613247Ssam /* 81710695Ssam * Check user requesting login priviledges. 81828864Smckusick * Disallow anyone who does not have a standard 81928864Smckusick * shell returned by getusershell() (/etc/shells). 82010695Ssam * Disallow anyone mentioned in the file FTPUSERS 82110695Ssam * to allow people such as uucp to be avoided. 82210695Ssam */ 82310695Ssam checkuser(name) 82410695Ssam register char *name; 82510695Ssam { 82628864Smckusick register char *cp; 82710695Ssam FILE *fd; 82836185Sbostic struct passwd *p; 82936185Sbostic char *shell; 83010695Ssam int found = 0; 83136185Sbostic char line[BUFSIZ], *index(), *getusershell(); 83210695Ssam 83336185Sbostic if ((p = getpwnam(name)) == NULL) 83428864Smckusick return (0); 83536185Sbostic if ((shell = p->pw_shell) == NULL || *shell == 0) 83636185Sbostic shell = "/bin/sh"; 83728864Smckusick while ((cp = getusershell()) != NULL) 83836185Sbostic if (strcmp(cp, shell) == 0) 83928864Smckusick break; 84028864Smckusick endusershell(); 84128864Smckusick if (cp == NULL) 84228864Smckusick return (0); 84336185Sbostic if ((fd = fopen(FTPUSERS, "r")) == NULL) 84410695Ssam return (1); 84510695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 84636185Sbostic if ((cp = index(line, '\n')) != NULL) 84710695Ssam *cp = '\0'; 84810695Ssam if (strcmp(line, name) == 0) { 84910695Ssam found++; 85010695Ssam break; 85110695Ssam } 85210695Ssam } 85326493Sminshall (void) fclose(fd); 85410695Ssam return (!found); 85510695Ssam } 85626044Sminshall 85726044Sminshall myoob() 85826044Sminshall { 85927750Sminshall char *cp; 86026044Sminshall 86127750Sminshall /* only process if transfer occurring */ 86226044Sminshall if (!transflag) { 86326044Sminshall return; 86426044Sminshall } 86527750Sminshall cp = tmpline; 86627750Sminshall if (getline(cp, 7, stdin) == NULL) { 86727750Sminshall reply(221, "You could at least say goodby."); 86827750Sminshall dologout(0); 86926044Sminshall } 87026044Sminshall upper(cp); 87126227Ssam if (strcmp(cp, "ABOR\r\n")) 87226044Sminshall return; 87326044Sminshall tmpline[0] = '\0'; 87426044Sminshall reply(426,"Transfer aborted. Data connection closed."); 87526044Sminshall reply(226,"Abort successful"); 87626044Sminshall longjmp(urgcatch, 1); 87726044Sminshall } 87826044Sminshall 87927106Smckusick /* 88027106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 88127106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 88227106Smckusick */ 88326044Sminshall passive() 88426044Sminshall { 88526044Sminshall int len; 88626044Sminshall struct sockaddr_in tmp; 88726044Sminshall register char *p, *a; 88826044Sminshall 88926044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 89026044Sminshall if (pdata < 0) { 89127106Smckusick reply(530, "Can't open passive connection"); 89226044Sminshall return; 89326044Sminshall } 89426044Sminshall tmp = ctrl_addr; 89526044Sminshall tmp.sin_port = 0; 89626044Sminshall seteuid(0); 89726493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 89826044Sminshall seteuid(pw->pw_uid); 89926044Sminshall (void) close(pdata); 90026044Sminshall pdata = -1; 90127106Smckusick reply(530, "Can't open passive connection"); 90226044Sminshall return; 90326044Sminshall } 90426044Sminshall seteuid(pw->pw_uid); 90526044Sminshall len = sizeof(tmp); 90626044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 90726044Sminshall (void) close(pdata); 90826044Sminshall pdata = -1; 90927106Smckusick reply(530, "Can't open passive connection"); 91026044Sminshall return; 91126044Sminshall } 91226044Sminshall if (listen(pdata, 1) < 0) { 91326044Sminshall (void) close(pdata); 91426044Sminshall pdata = -1; 91527106Smckusick reply(530, "Can't open passive connection"); 91626044Sminshall return; 91726044Sminshall } 91826044Sminshall a = (char *) &tmp.sin_addr; 91926044Sminshall p = (char *) &tmp.sin_port; 92026044Sminshall 92126044Sminshall #define UC(b) (((int) b) & 0xff) 92226044Sminshall 92326044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 92426044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 92526044Sminshall } 92626044Sminshall 92726044Sminshall char * 92826044Sminshall gunique(local) 92926044Sminshall char *local; 93026044Sminshall { 93126044Sminshall static char new[MAXPATHLEN]; 93226044Sminshall char *cp = rindex(local, '/'); 93326044Sminshall int d, count=0; 93426044Sminshall char ext = '1'; 93526044Sminshall 93626044Sminshall if (cp) { 93726044Sminshall *cp = '\0'; 93826044Sminshall } 93926044Sminshall d = access(cp ? local : ".", 2); 94026044Sminshall if (cp) { 94126044Sminshall *cp = '/'; 94226044Sminshall } 94326044Sminshall if (d < 0) { 94426493Sminshall syslog(LOG_ERR, "%s: %m", local); 94526044Sminshall return((char *) 0); 94626044Sminshall } 94726044Sminshall (void) strcpy(new, local); 94826044Sminshall cp = new + strlen(new); 94926044Sminshall *cp++ = '.'; 95026044Sminshall while (!d) { 95126044Sminshall if (++count == 100) { 95227106Smckusick reply(452, "Unique file name not cannot be created."); 95326044Sminshall return((char *) 0); 95426044Sminshall } 95526044Sminshall *cp++ = ext; 95626044Sminshall *cp = '\0'; 95726044Sminshall if (ext == '9') { 95826044Sminshall ext = '0'; 95926044Sminshall } 96026044Sminshall else { 96126044Sminshall ext++; 96226044Sminshall } 96326044Sminshall if ((d = access(new, 0)) < 0) { 96426044Sminshall break; 96526044Sminshall } 96626044Sminshall if (ext != '0') { 96726044Sminshall cp--; 96826044Sminshall } 96926044Sminshall else if (*(cp - 2) == '.') { 97026044Sminshall *(cp - 1) = '1'; 97126044Sminshall } 97226044Sminshall else { 97326044Sminshall *(cp - 2) = *(cp - 2) + 1; 97426044Sminshall cp--; 97526044Sminshall } 97626044Sminshall } 97726044Sminshall return(new); 97826044Sminshall } 979