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*36185Sbostic static char sccsid[] = "@(#)ftpd.c 5.16 (Berkeley) 10/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 */ 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); 16435691Sbostic if ((int)signal(SIGURG, myoob) < 0) 16526493Sminshall syslog(LOG_ERR, "signal: %m"); 16635691Sbostic 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 20635672Sbostic static char ttyline[20]; 20735672Sbostic 208*36185Sbostic /* 209*36185Sbostic * Helper function for sgetpwnam(). 210*36185Sbostic */ 211*36185Sbostic char * 212*36185Sbostic sgetsave(s) 213*36185Sbostic char *s; 214*36185Sbostic { 215*36185Sbostic #ifdef notdef 216*36185Sbostic char *new = strdup(s); 217*36185Sbostic #else 218*36185Sbostic char *malloc(); 219*36185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 220*36185Sbostic #endif 221*36185Sbostic 222*36185Sbostic if (new == NULL) { 223*36185Sbostic reply(553, "Local resource failure"); 224*36185Sbostic dologout(1); 225*36185Sbostic } 226*36185Sbostic #ifndef notdef 227*36185Sbostic (void) strcpy(new, s); 228*36185Sbostic #endif 229*36185Sbostic return (new); 230*36185Sbostic } 231*36185Sbostic 232*36185Sbostic /* 233*36185Sbostic * Save the result of a getpwnam. Used for USER command, since 234*36185Sbostic * the data returned must not be clobbered by any other command 235*36185Sbostic * (e.g., globbing). 236*36185Sbostic */ 237*36185Sbostic struct passwd * 238*36185Sbostic sgetpwnam(name) 239*36185Sbostic char *name; 240*36185Sbostic { 241*36185Sbostic static struct passwd save; 242*36185Sbostic register struct passwd *p; 243*36185Sbostic char *sgetsave(); 244*36185Sbostic 245*36185Sbostic if ((p = getpwnam(name)) == NULL) 246*36185Sbostic return (p); 247*36185Sbostic if (save.pw_name) { 248*36185Sbostic free(save.pw_name); 249*36185Sbostic free(save.pw_passwd); 250*36185Sbostic free(save.pw_comment); 251*36185Sbostic free(save.pw_gecos); 252*36185Sbostic free(save.pw_dir); 253*36185Sbostic free(save.pw_shell); 254*36185Sbostic } 255*36185Sbostic save = *p; 256*36185Sbostic save.pw_name = sgetsave(p->pw_name); 257*36185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 258*36185Sbostic save.pw_comment = sgetsave(p->pw_comment); 259*36185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 260*36185Sbostic save.pw_dir = sgetsave(p->pw_dir); 261*36185Sbostic save.pw_shell = sgetsave(p->pw_shell); 262*36185Sbostic return (&save); 263*36185Sbostic } 264*36185Sbostic 26510275Ssam pass(passwd) 26610275Ssam char *passwd; 26710275Ssam { 268*36185Sbostic char *xpasswd; 26910275Ssam 27010275Ssam if (logged_in || pw == NULL) { 27110275Ssam reply(503, "Login with USER first."); 27210275Ssam return; 27310275Ssam } 27410275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 27510275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 27616760Slepreau /* The strcmp does not catch null passwords! */ 27716760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 27810275Ssam reply(530, "Login incorrect."); 27910275Ssam pw = NULL; 28010275Ssam return; 28110275Ssam } 28210275Ssam } 28310303Ssam setegid(pw->pw_gid); 28410275Ssam initgroups(pw->pw_name, pw->pw_gid); 28510275Ssam if (chdir(pw->pw_dir)) { 28627106Smckusick reply(530, "User %s: can't change directory to %s.", 28710275Ssam pw->pw_name, pw->pw_dir); 28810303Ssam goto bad; 28910275Ssam } 29016033Sralph 29116760Slepreau /* grab wtmp before chroot */ 29216760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 29310303Ssam if (guest && chroot(pw->pw_dir) < 0) { 29410275Ssam reply(550, "Can't set guest privileges."); 29516760Slepreau if (wtmp >= 0) { 29616760Slepreau (void) close(wtmp); 29716760Slepreau wtmp = -1; 29816760Slepreau } 29910303Ssam goto bad; 30010275Ssam } 30110275Ssam if (!guest) 30210275Ssam reply(230, "User %s logged in.", pw->pw_name); 30310275Ssam else 30410275Ssam reply(230, "Guest login ok, access restrictions apply."); 30510275Ssam logged_in = 1; 30635672Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 30735672Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 30810303Ssam seteuid(pw->pw_uid); 30910303Ssam home = pw->pw_dir; /* home dir for globbing */ 31010303Ssam return; 31110303Ssam bad: 31210303Ssam seteuid(0); 31310303Ssam pw = NULL; 31410275Ssam } 31510275Ssam 31610275Ssam retrieve(cmd, name) 31710275Ssam char *cmd, *name; 31810275Ssam { 31910275Ssam FILE *fin, *dout; 32010275Ssam struct stat st; 32126044Sminshall int (*closefunc)(), tmp; 32210275Ssam 32310275Ssam if (cmd == 0) { 32410317Ssam #ifdef notdef 32510317Ssam /* no remote command execution -- it's a security hole */ 32611653Ssam if (*name == '|') 32710275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 32810275Ssam else 32910317Ssam #endif 33010275Ssam fin = fopen(name, "r"), closefunc = fclose; 33110275Ssam } else { 33210275Ssam char line[BUFSIZ]; 33310275Ssam 33426493Sminshall (void) sprintf(line, cmd, name), name = line; 33510275Ssam fin = popen(line, "r"), closefunc = pclose; 33610275Ssam } 33710275Ssam if (fin == NULL) { 33813152Ssam if (errno != 0) 33913152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 34010275Ssam return; 34110275Ssam } 34210275Ssam st.st_size = 0; 34310275Ssam if (cmd == 0 && 34410275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 34510275Ssam reply(550, "%s: not a plain file.", name); 34610275Ssam goto done; 34710275Ssam } 34810275Ssam dout = dataconn(name, st.st_size, "w"); 34910275Ssam if (dout == NULL) 35010275Ssam goto done; 35126044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 35210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 35326044Sminshall } 35426044Sminshall else if (tmp == 0) { 35510275Ssam reply(226, "Transfer complete."); 35626044Sminshall } 35726493Sminshall (void) fclose(dout); 35826044Sminshall data = -1; 35926044Sminshall pdata = -1; 36010275Ssam done: 36110275Ssam (*closefunc)(fin); 36210275Ssam } 36310275Ssam 36410275Ssam store(name, mode) 36510275Ssam char *name, *mode; 36610275Ssam { 36710275Ssam FILE *fout, *din; 36826044Sminshall int (*closefunc)(), dochown = 0, tmp; 36926044Sminshall char *gunique(), *local; 37010275Ssam 37110317Ssam #ifdef notdef 37210317Ssam /* no remote command execution -- it's a security hole */ 37311653Ssam if (name[0] == '|') 37410275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 37510317Ssam else 37610317Ssam #endif 37710317Ssam { 37810303Ssam struct stat st; 37910303Ssam 38026044Sminshall local = name; 38126044Sminshall if (stat(name, &st) < 0) { 38210303Ssam dochown++; 38326044Sminshall } 38426044Sminshall else if (unique) { 38526044Sminshall if ((local = gunique(name)) == NULL) { 38626044Sminshall return; 38726044Sminshall } 38826044Sminshall dochown++; 38926044Sminshall } 39026044Sminshall fout = fopen(local, mode), closefunc = fclose; 39110303Ssam } 39210275Ssam if (fout == NULL) { 39327106Smckusick reply(553, "%s: %s.", local, sys_errlist[errno]); 39410275Ssam return; 39510275Ssam } 39626044Sminshall din = dataconn(local, (off_t)-1, "r"); 39710275Ssam if (din == NULL) 39810275Ssam goto done; 39926044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 40027106Smckusick reply(552, "%s: %s.", local, sys_errlist[errno]); 40126044Sminshall } 40226044Sminshall else if (tmp == 0 && !unique) { 40310275Ssam reply(226, "Transfer complete."); 40426044Sminshall } 40526044Sminshall else if (tmp == 0 && unique) { 40626044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 40726044Sminshall } 40826493Sminshall (void) fclose(din); 40926044Sminshall data = -1; 41026044Sminshall pdata = -1; 41110275Ssam done: 41210303Ssam if (dochown) 41326044Sminshall (void) chown(local, pw->pw_uid, -1); 41410275Ssam (*closefunc)(fout); 41510275Ssam } 41610275Ssam 41710275Ssam FILE * 41810275Ssam getdatasock(mode) 41910275Ssam char *mode; 42010275Ssam { 42117157Ssam int s, on = 1; 42210275Ssam 42310275Ssam if (data >= 0) 42410275Ssam return (fdopen(data, mode)); 42513247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 42610602Ssam if (s < 0) 42710275Ssam return (NULL); 42810275Ssam seteuid(0); 42926493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 43010602Ssam goto bad; 43113152Ssam /* anchor socket to avoid multi-homing problems */ 43213152Ssam data_source.sin_family = AF_INET; 43313152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 43426493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 43510602Ssam goto bad; 43610311Ssam seteuid(pw->pw_uid); 43710275Ssam return (fdopen(s, mode)); 43810602Ssam bad: 43910602Ssam seteuid(pw->pw_uid); 44026493Sminshall (void) close(s); 44110602Ssam return (NULL); 44210275Ssam } 44310275Ssam 44410275Ssam FILE * 44510275Ssam dataconn(name, size, mode) 44610275Ssam char *name; 44711653Ssam off_t size; 44810275Ssam char *mode; 44910275Ssam { 45010275Ssam char sizebuf[32]; 45110275Ssam FILE *file; 45211653Ssam int retry = 0; 45310275Ssam 45410275Ssam if (size >= 0) 45526493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 45610275Ssam else 45710275Ssam (void) strcpy(sizebuf, ""); 45826044Sminshall if (pdata > 0) { 45926044Sminshall struct sockaddr_in from; 46026044Sminshall int s, fromlen = sizeof(from); 46126044Sminshall 46226493Sminshall s = accept(pdata, &from, &fromlen); 46326044Sminshall if (s < 0) { 46426044Sminshall reply(425, "Can't open data connection."); 46526044Sminshall (void) close(pdata); 46626044Sminshall pdata = -1; 46726044Sminshall return(NULL); 46826044Sminshall } 46926044Sminshall (void) close(pdata); 47026044Sminshall pdata = s; 47135700Sbostic reply(150, "Opening data connection for %s (%s mode)%s.", 47235700Sbostic name, type == TYPE_A ? "ascii" : "binary", sizebuf); 47326044Sminshall return(fdopen(pdata, mode)); 47426044Sminshall } 47510275Ssam if (data >= 0) { 47610275Ssam reply(125, "Using existing data connection for %s%s.", 47710275Ssam name, sizebuf); 47810321Ssam usedefault = 1; 47910275Ssam return (fdopen(data, mode)); 48010275Ssam } 48110566Ssam if (usedefault) 48210422Ssam data_dest = his_addr; 48310422Ssam usedefault = 1; 48410275Ssam file = getdatasock(mode); 48510275Ssam if (file == NULL) { 48610275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 48713247Ssam inet_ntoa(data_source.sin_addr), 48810275Ssam ntohs(data_source.sin_port), 48910275Ssam sys_errlist[errno]); 49010275Ssam return (NULL); 49110275Ssam } 49210275Ssam data = fileno(file); 49326044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 49411653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 49526493Sminshall sleep((unsigned) swaitint); 49611653Ssam retry += swaitint; 49711653Ssam continue; 49811653Ssam } 49910275Ssam reply(425, "Can't build data connection: %s.", 50010275Ssam sys_errlist[errno]); 50110275Ssam (void) fclose(file); 50210275Ssam data = -1; 50310275Ssam return (NULL); 50410275Ssam } 50535700Sbostic reply(150, "Opening data connection for %s (%s mode)%s.", 50635700Sbostic name, type == TYPE_A ? "ascii" : "binary", sizebuf); 50710275Ssam return (file); 50810275Ssam } 50910275Ssam 51010275Ssam /* 51110275Ssam * Tranfer the contents of "instr" to 51210275Ssam * "outstr" peer using the appropriate 51310275Ssam * encapulation of the date subject 51410275Ssam * to Mode, Structure, and Type. 51510275Ssam * 51610275Ssam * NB: Form isn't handled. 51710275Ssam */ 51810275Ssam send_data(instr, outstr) 51910275Ssam FILE *instr, *outstr; 52010275Ssam { 52110275Ssam register int c; 52210275Ssam int netfd, filefd, cnt; 52310275Ssam char buf[BUFSIZ]; 52410275Ssam 52526044Sminshall transflag++; 52626044Sminshall if (setjmp(urgcatch)) { 52726044Sminshall transflag = 0; 52826044Sminshall return(-1); 52926044Sminshall } 53010275Ssam switch (type) { 53110275Ssam 53210275Ssam case TYPE_A: 53310275Ssam while ((c = getc(instr)) != EOF) { 53411220Ssam if (c == '\n') { 53526044Sminshall if (ferror (outstr)) { 53626044Sminshall transflag = 0; 53711220Ssam return (1); 53826044Sminshall } 53927750Sminshall (void) putc('\r', outstr); 54011220Ssam } 54127750Sminshall (void) putc(c, outstr); 54226044Sminshall /* if (c == '\r') */ 54326044Sminshall /* putc ('\0', outstr); */ 54410275Ssam } 54526044Sminshall transflag = 0; 54626044Sminshall if (ferror (instr) || ferror (outstr)) { 54711220Ssam return (1); 54826044Sminshall } 54910275Ssam return (0); 55010275Ssam 55110275Ssam case TYPE_I: 55210275Ssam case TYPE_L: 55310275Ssam netfd = fileno(outstr); 55410275Ssam filefd = fileno(instr); 55510275Ssam 55626044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 55726044Sminshall if (write(netfd, buf, cnt) < 0) { 55826044Sminshall transflag = 0; 55910275Ssam return (1); 56026044Sminshall } 56126044Sminshall } 56226044Sminshall transflag = 0; 56310275Ssam return (cnt < 0); 56410275Ssam } 56527106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 56626044Sminshall transflag = 0; 56727106Smckusick return (-1); 56810275Ssam } 56910275Ssam 57010275Ssam /* 57110275Ssam * Transfer data from peer to 57210275Ssam * "outstr" using the appropriate 57310275Ssam * encapulation of the data subject 57410275Ssam * to Mode, Structure, and Type. 57510275Ssam * 57610275Ssam * N.B.: Form isn't handled. 57710275Ssam */ 57810275Ssam receive_data(instr, outstr) 57910275Ssam FILE *instr, *outstr; 58010275Ssam { 58110275Ssam register int c; 58211220Ssam int cnt; 58310275Ssam char buf[BUFSIZ]; 58410275Ssam 58510275Ssam 58626044Sminshall transflag++; 58726044Sminshall if (setjmp(urgcatch)) { 58826044Sminshall transflag = 0; 58926044Sminshall return(-1); 59026044Sminshall } 59110275Ssam switch (type) { 59210275Ssam 59310275Ssam case TYPE_I: 59410275Ssam case TYPE_L: 59526044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 59626044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 59726044Sminshall transflag = 0; 59810275Ssam return (1); 59926044Sminshall } 60026044Sminshall } 60126044Sminshall transflag = 0; 60210275Ssam return (cnt < 0); 60310275Ssam 60410275Ssam case TYPE_E: 60527106Smckusick reply(553, "TYPE E not implemented."); 60626044Sminshall transflag = 0; 60727106Smckusick return (-1); 60810275Ssam 60910275Ssam case TYPE_A: 61010275Ssam while ((c = getc(instr)) != EOF) { 61127750Sminshall while (c == '\r') { 61226044Sminshall if (ferror (outstr)) { 61326044Sminshall transflag = 0; 61411220Ssam return (1); 61526044Sminshall } 61611220Ssam if ((c = getc(instr)) != '\n') 61727750Sminshall (void) putc ('\r', outstr); 61826044Sminshall /* if (c == '\0') */ 61926044Sminshall /* continue; */ 62010275Ssam } 62127750Sminshall (void) putc (c, outstr); 62210275Ssam } 62326044Sminshall transflag = 0; 62411220Ssam if (ferror (instr) || ferror (outstr)) 62511220Ssam return (1); 62610275Ssam return (0); 62710275Ssam } 62826044Sminshall transflag = 0; 62910275Ssam fatal("Unknown type in receive_data."); 63010275Ssam /*NOTREACHED*/ 63110275Ssam } 63210275Ssam 63310275Ssam fatal(s) 63410275Ssam char *s; 63510275Ssam { 63610275Ssam reply(451, "Error in server: %s\n", s); 63710275Ssam reply(221, "Closing connection due to server error."); 63813247Ssam dologout(0); 63910275Ssam } 64010275Ssam 64132110Smckusick reply(n, s, p0, p1, p2, p3, p4) 64210275Ssam int n; 64310275Ssam char *s; 64410275Ssam { 64510275Ssam 64610275Ssam printf("%d ", n); 64732110Smckusick printf(s, p0, p1, p2, p3, p4); 64810275Ssam printf("\r\n"); 64926493Sminshall (void) fflush(stdout); 65010275Ssam if (debug) { 65126493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 65232110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 65310275Ssam } 65410275Ssam } 65510275Ssam 65632110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 65710275Ssam int n; 65810275Ssam char *s; 65910275Ssam { 66010275Ssam printf("%d-", n); 66132110Smckusick printf(s, p0, p1, p2, p3, p4); 66210275Ssam printf("\r\n"); 66326493Sminshall (void) fflush(stdout); 66410275Ssam if (debug) { 66526493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 66632110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 66710275Ssam } 66810275Ssam } 66910275Ssam 67010275Ssam ack(s) 67110275Ssam char *s; 67210275Ssam { 67327106Smckusick reply(250, "%s command successful.", s); 67410275Ssam } 67510275Ssam 67610275Ssam nack(s) 67710275Ssam char *s; 67810275Ssam { 67910275Ssam reply(502, "%s command not implemented.", s); 68010275Ssam } 68110275Ssam 68226493Sminshall yyerror(s) 68326493Sminshall char *s; 68410275Ssam { 68526044Sminshall char *cp; 68626044Sminshall 68726044Sminshall cp = index(cbuf,'\n'); 68826044Sminshall *cp = '\0'; 68926044Sminshall reply(500, "'%s': command not understood.",cbuf); 69010275Ssam } 69110275Ssam 69210275Ssam delete(name) 69310275Ssam char *name; 69410275Ssam { 69510275Ssam struct stat st; 69610275Ssam 69710275Ssam if (stat(name, &st) < 0) { 69810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 69910275Ssam return; 70010275Ssam } 70110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 70210275Ssam if (rmdir(name) < 0) { 70310275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70410275Ssam return; 70510275Ssam } 70610275Ssam goto done; 70710275Ssam } 70810275Ssam if (unlink(name) < 0) { 70910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 71010275Ssam return; 71110275Ssam } 71210275Ssam done: 71310275Ssam ack("DELE"); 71410275Ssam } 71510275Ssam 71610275Ssam cwd(path) 71710275Ssam char *path; 71810275Ssam { 71910275Ssam 72010275Ssam if (chdir(path) < 0) { 72110275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 72210275Ssam return; 72310275Ssam } 72410275Ssam ack("CWD"); 72510275Ssam } 72610275Ssam 72710303Ssam makedir(name) 72810275Ssam char *name; 72910275Ssam { 73010303Ssam struct stat st; 73110303Ssam int dochown = stat(name, &st) < 0; 73210275Ssam 73310275Ssam if (mkdir(name, 0777) < 0) { 73410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 73510275Ssam return; 73610275Ssam } 73710303Ssam if (dochown) 73810303Ssam (void) chown(name, pw->pw_uid, -1); 73927106Smckusick reply(257, "MKD command successful."); 74010275Ssam } 74110275Ssam 74210303Ssam removedir(name) 74310275Ssam char *name; 74410275Ssam { 74510275Ssam 74610275Ssam if (rmdir(name) < 0) { 74710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 74810275Ssam return; 74910275Ssam } 75027106Smckusick ack("RMD"); 75110275Ssam } 75210275Ssam 75310303Ssam pwd() 75410275Ssam { 75510303Ssam char path[MAXPATHLEN + 1]; 75610275Ssam 75710275Ssam if (getwd(path) == NULL) { 75827106Smckusick reply(550, "%s.", path); 75910275Ssam return; 76010275Ssam } 76127106Smckusick reply(257, "\"%s\" is current directory.", path); 76210275Ssam } 76310275Ssam 76410275Ssam char * 76510275Ssam renamefrom(name) 76610275Ssam char *name; 76710275Ssam { 76810275Ssam struct stat st; 76910275Ssam 77010275Ssam if (stat(name, &st) < 0) { 77110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 77210275Ssam return ((char *)0); 77310275Ssam } 77410303Ssam reply(350, "File exists, ready for destination name"); 77510275Ssam return (name); 77610275Ssam } 77710275Ssam 77810275Ssam renamecmd(from, to) 77910275Ssam char *from, *to; 78010275Ssam { 78110275Ssam 78210275Ssam if (rename(from, to) < 0) { 78310275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 78410275Ssam return; 78510275Ssam } 78610275Ssam ack("RNTO"); 78710275Ssam } 78810275Ssam 78910275Ssam dolog(sin) 79010275Ssam struct sockaddr_in *sin; 79110275Ssam { 79210275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 79310275Ssam sizeof (struct in_addr), AF_INET); 79410275Ssam time_t t; 79526493Sminshall extern char *ctime(); 79610275Ssam 79713247Ssam if (hp) { 79826493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 79913247Ssam endhostent(); 80013247Ssam } else 80126493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 80213247Ssam sizeof (remotehost)); 80313247Ssam if (!logging) 80413247Ssam return; 80526493Sminshall t = time((time_t *) 0); 80626493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 80710275Ssam } 80810695Ssam 80910695Ssam /* 81013247Ssam * Record logout in wtmp file 81113247Ssam * and exit with supplied status. 81213247Ssam */ 81313247Ssam dologout(status) 81413247Ssam int status; 81513247Ssam { 81617580Ssam if (logged_in) { 81717580Ssam (void) seteuid(0); 81835672Sbostic logwtmp(ttyline, "", ""); 81913247Ssam } 82014436Ssam /* beware of flushing buffers after a SIGPIPE */ 82114436Ssam _exit(status); 82213247Ssam } 82313247Ssam 82413247Ssam /* 82510695Ssam * Check user requesting login priviledges. 82628864Smckusick * Disallow anyone who does not have a standard 82728864Smckusick * shell returned by getusershell() (/etc/shells). 82810695Ssam * Disallow anyone mentioned in the file FTPUSERS 82910695Ssam * to allow people such as uucp to be avoided. 83010695Ssam */ 83110695Ssam checkuser(name) 83210695Ssam register char *name; 83310695Ssam { 83428864Smckusick register char *cp; 83510695Ssam FILE *fd; 836*36185Sbostic struct passwd *p; 837*36185Sbostic char *shell; 83810695Ssam int found = 0; 839*36185Sbostic char line[BUFSIZ], *index(), *getusershell(); 84010695Ssam 841*36185Sbostic if ((p = getpwnam(name)) == NULL) 84228864Smckusick return (0); 843*36185Sbostic if ((shell = p->pw_shell) == NULL || *shell == 0) 844*36185Sbostic shell = "/bin/sh"; 84528864Smckusick while ((cp = getusershell()) != NULL) 846*36185Sbostic if (strcmp(cp, shell) == 0) 84728864Smckusick break; 84828864Smckusick endusershell(); 84928864Smckusick if (cp == NULL) 85028864Smckusick return (0); 851*36185Sbostic if ((fd = fopen(FTPUSERS, "r")) == NULL) 85210695Ssam return (1); 85310695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 854*36185Sbostic if ((cp = index(line, '\n')) != NULL) 85510695Ssam *cp = '\0'; 85610695Ssam if (strcmp(line, name) == 0) { 85710695Ssam found++; 85810695Ssam break; 85910695Ssam } 86010695Ssam } 86126493Sminshall (void) fclose(fd); 86210695Ssam return (!found); 86310695Ssam } 86426044Sminshall 86526044Sminshall myoob() 86626044Sminshall { 86727750Sminshall char *cp; 86826044Sminshall 86927750Sminshall /* only process if transfer occurring */ 87026044Sminshall if (!transflag) { 87126044Sminshall return; 87226044Sminshall } 87327750Sminshall cp = tmpline; 87427750Sminshall if (getline(cp, 7, stdin) == NULL) { 87527750Sminshall reply(221, "You could at least say goodby."); 87627750Sminshall dologout(0); 87726044Sminshall } 87826044Sminshall upper(cp); 87926227Ssam if (strcmp(cp, "ABOR\r\n")) 88026044Sminshall return; 88126044Sminshall tmpline[0] = '\0'; 88226044Sminshall reply(426,"Transfer aborted. Data connection closed."); 88326044Sminshall reply(226,"Abort successful"); 88426044Sminshall longjmp(urgcatch, 1); 88526044Sminshall } 88626044Sminshall 88727106Smckusick /* 88827106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 88927106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 89027106Smckusick */ 89126044Sminshall passive() 89226044Sminshall { 89326044Sminshall int len; 89426044Sminshall struct sockaddr_in tmp; 89526044Sminshall register char *p, *a; 89626044Sminshall 89726044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 89826044Sminshall if (pdata < 0) { 89927106Smckusick reply(530, "Can't open passive connection"); 90026044Sminshall return; 90126044Sminshall } 90226044Sminshall tmp = ctrl_addr; 90326044Sminshall tmp.sin_port = 0; 90426044Sminshall seteuid(0); 90526493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 90626044Sminshall seteuid(pw->pw_uid); 90726044Sminshall (void) close(pdata); 90826044Sminshall pdata = -1; 90927106Smckusick reply(530, "Can't open passive connection"); 91026044Sminshall return; 91126044Sminshall } 91226044Sminshall seteuid(pw->pw_uid); 91326044Sminshall len = sizeof(tmp); 91426044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 91526044Sminshall (void) close(pdata); 91626044Sminshall pdata = -1; 91727106Smckusick reply(530, "Can't open passive connection"); 91826044Sminshall return; 91926044Sminshall } 92026044Sminshall if (listen(pdata, 1) < 0) { 92126044Sminshall (void) close(pdata); 92226044Sminshall pdata = -1; 92327106Smckusick reply(530, "Can't open passive connection"); 92426044Sminshall return; 92526044Sminshall } 92626044Sminshall a = (char *) &tmp.sin_addr; 92726044Sminshall p = (char *) &tmp.sin_port; 92826044Sminshall 92926044Sminshall #define UC(b) (((int) b) & 0xff) 93026044Sminshall 93126044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 93226044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 93326044Sminshall } 93426044Sminshall 93526044Sminshall char * 93626044Sminshall gunique(local) 93726044Sminshall char *local; 93826044Sminshall { 93926044Sminshall static char new[MAXPATHLEN]; 94026044Sminshall char *cp = rindex(local, '/'); 94126044Sminshall int d, count=0; 94226044Sminshall char ext = '1'; 94326044Sminshall 94426044Sminshall if (cp) { 94526044Sminshall *cp = '\0'; 94626044Sminshall } 94726044Sminshall d = access(cp ? local : ".", 2); 94826044Sminshall if (cp) { 94926044Sminshall *cp = '/'; 95026044Sminshall } 95126044Sminshall if (d < 0) { 95226493Sminshall syslog(LOG_ERR, "%s: %m", local); 95326044Sminshall return((char *) 0); 95426044Sminshall } 95526044Sminshall (void) strcpy(new, local); 95626044Sminshall cp = new + strlen(new); 95726044Sminshall *cp++ = '.'; 95826044Sminshall while (!d) { 95926044Sminshall if (++count == 100) { 96027106Smckusick reply(452, "Unique file name not cannot be created."); 96126044Sminshall return((char *) 0); 96226044Sminshall } 96326044Sminshall *cp++ = ext; 96426044Sminshall *cp = '\0'; 96526044Sminshall if (ext == '9') { 96626044Sminshall ext = '0'; 96726044Sminshall } 96826044Sminshall else { 96926044Sminshall ext++; 97026044Sminshall } 97126044Sminshall if ((d = access(new, 0)) < 0) { 97226044Sminshall break; 97326044Sminshall } 97426044Sminshall if (ext != '0') { 97526044Sminshall cp--; 97626044Sminshall } 97726044Sminshall else if (*(cp - 2) == '.') { 97826044Sminshall *(cp - 1) = '1'; 97926044Sminshall } 98026044Sminshall else { 98126044Sminshall *(cp - 2) = *(cp - 2) + 1; 98226044Sminshall cp--; 98326044Sminshall } 98426044Sminshall } 98526044Sminshall return(new); 98626044Sminshall } 987