122499Sdist /* 226044Sminshall * Copyright (c) 1985 Regents of the University of California. 322499Sdist * All rights reserved. The Berkeley software License Agreement 422499Sdist * specifies the terms and conditions for redistribution. 522499Sdist */ 622499Sdist 710275Ssam #ifndef lint 822499Sdist char copyright[] = 926044Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 1022499Sdist All rights reserved.\n"; 1122499Sdist #endif not lint 1210275Ssam 1322499Sdist #ifndef lint 14*26493Sminshall static char sccsid[] = "@(#)ftpd.c 5.4 (Berkeley) 03/07/86"; 1522499Sdist #endif not lint 1622499Sdist 1710275Ssam /* 1810275Ssam * FTP server. 1910275Ssam */ 2010303Ssam #include <sys/param.h> 2110275Ssam #include <sys/stat.h> 2210275Ssam #include <sys/ioctl.h> 2310275Ssam #include <sys/socket.h> 2413247Ssam #include <sys/file.h> 2513595Ssam #include <sys/wait.h> 2610275Ssam 2710275Ssam #include <netinet/in.h> 2810275Ssam 2913034Ssam #include <arpa/ftp.h> 3013211Sroot #include <arpa/inet.h> 3126044Sminshall #include <arpa/telnet.h> 3213034Ssam 3310275Ssam #include <stdio.h> 3410275Ssam #include <signal.h> 3510275Ssam #include <pwd.h> 3610275Ssam #include <setjmp.h> 3710275Ssam #include <netdb.h> 3810423Ssam #include <errno.h> 3926044Sminshall #include <strings.h> 40*26493Sminshall #include <syslog.h> 4110275Ssam 4210695Ssam /* 4310695Ssam * File containing login names 4410695Ssam * NOT to be used on this machine. 4510695Ssam * Commonly used to disallow uucp. 4610695Ssam */ 4710695Ssam #define FTPUSERS "/etc/ftpusers" 4810695Ssam 4910275Ssam extern int errno; 5010275Ssam extern char *sys_errlist[]; 5110275Ssam extern char *crypt(); 5210275Ssam extern char version[]; 5310275Ssam extern char *home; /* pointer to home directory for glob */ 5426044Sminshall extern FILE *popen(), *fopen(), *freopen(); 55*26493Sminshall extern int pclose(), fclose(); 5626044Sminshall extern char *getline(); 5726044Sminshall extern char cbuf[]; 5810275Ssam 5910275Ssam struct sockaddr_in ctrl_addr; 6010275Ssam struct sockaddr_in data_source; 6110275Ssam struct sockaddr_in data_dest; 6210275Ssam struct sockaddr_in his_addr; 6310275Ssam 6410275Ssam int data; 6526044Sminshall jmp_buf errcatch, urgcatch; 6610275Ssam int logged_in; 6710275Ssam struct passwd *pw; 6810275Ssam int debug; 69*26493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 7011757Ssam int logging; 7110275Ssam int guest; 7216033Sralph int wtmp; 7310275Ssam int type; 7410275Ssam int form; 7510275Ssam int stru; /* avoid C keyword */ 7610275Ssam int mode; 7710321Ssam int usedefault = 1; /* for data transfers */ 7826044Sminshall int pdata; /* for passive mode */ 7926044Sminshall int unique; 8026044Sminshall int transflag; 8126044Sminshall char tmpline[7]; 8210275Ssam char hostname[32]; 8313247Ssam char remotehost[32]; 8410275Ssam 8511653Ssam /* 8611653Ssam * Timeout intervals for retrying connections 8711653Ssam * to hosts that don't accept PORT cmds. This 8811653Ssam * is a kludge, but given the problems with TCP... 8911653Ssam */ 9011653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 9111653Ssam #define SWAITINT 5 /* interval between retries */ 9211653Ssam 9311653Ssam int swaitmax = SWAITMAX; 9411653Ssam int swaitint = SWAITINT; 9511653Ssam 9610275Ssam int lostconn(); 9726044Sminshall int myoob(); 9810275Ssam FILE *getdatasock(), *dataconn(); 9910275Ssam 10010275Ssam main(argc, argv) 10110275Ssam int argc; 10210275Ssam char *argv[]; 10310275Ssam { 104*26493Sminshall int addrlen; 10526044Sminshall long pgid; 10610275Ssam char *cp; 10710275Ssam 10816339Skarels addrlen = sizeof (his_addr); 10916339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 110*26493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 11110275Ssam exit(1); 11210275Ssam } 11316339Skarels addrlen = sizeof (ctrl_addr); 114*26493Sminshall if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) { 115*26493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 11616339Skarels exit(1); 11716339Skarels } 11816339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 11910275Ssam debug = 0; 120*26493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 12110275Ssam argc--, argv++; 12210275Ssam while (argc > 0 && *argv[0] == '-') { 12310275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 12410275Ssam 12511653Ssam case 'v': 12611653Ssam debug = 1; 12711653Ssam break; 12811653Ssam 12910275Ssam case 'd': 13010275Ssam debug = 1; 13110275Ssam break; 13210275Ssam 13311757Ssam case 'l': 13411757Ssam logging = 1; 13511757Ssam break; 13611757Ssam 13711653Ssam case 't': 13811653Ssam timeout = atoi(++cp); 13911653Ssam goto nextopt; 14011653Ssam break; 14111653Ssam 14210275Ssam default: 14316339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 14416339Skarels *cp); 14510275Ssam break; 14610275Ssam } 14711653Ssam nextopt: 14810275Ssam argc--, argv++; 14910275Ssam } 150*26493Sminshall (void) signal(SIGPIPE, lostconn); 151*26493Sminshall (void) signal(SIGCHLD, SIG_IGN); 15226044Sminshall if (signal(SIGURG, myoob) < 0) { 153*26493Sminshall syslog(LOG_ERR, "signal: %m"); 15426044Sminshall } 15526044Sminshall pgid = getpid(); 156*26493Sminshall if (ioctl(fileno(stdin), (int) SIOCSPGRP, (char *) &pgid) < 0) { 157*26493Sminshall syslog(LOG_ERR, "ioctl: %m"); 15826044Sminshall } 15916760Slepreau dolog(&his_addr); 16016339Skarels /* do telnet option negotiation here */ 16116339Skarels /* 16216339Skarels * Set up default state 16316339Skarels */ 16416339Skarels logged_in = 0; 16516339Skarels data = -1; 16616339Skarels type = TYPE_A; 16716339Skarels form = FORM_N; 16816339Skarels stru = STRU_F; 16916339Skarels mode = MODE_S; 17026044Sminshall tmpline[0] = '\0'; 171*26493Sminshall (void) gethostname(hostname, sizeof (hostname)); 17216339Skarels reply(220, "%s FTP server (%s) ready.", 17316339Skarels hostname, version); 17410275Ssam for (;;) { 175*26493Sminshall (void) setjmp(errcatch); 176*26493Sminshall (void) yyparse(); 17710275Ssam } 17810275Ssam } 179*26493Sminshall /* 18010419Ssam reapchild() 18110419Ssam { 18210419Ssam union wait status; 18310419Ssam 18410419Ssam while (wait3(&status, WNOHANG, 0) > 0) 18510419Ssam ; 18610419Ssam } 187*26493Sminshall */ 18810419Ssam 18910275Ssam lostconn() 19010275Ssam { 19110275Ssam 19214089Ssam if (debug) 193*26493Sminshall syslog(LOG_DEBUG, "lost connection"); 19414089Ssam dologout(-1); 19510275Ssam } 19610275Ssam 19710275Ssam pass(passwd) 19810275Ssam char *passwd; 19910275Ssam { 20010303Ssam char *xpasswd, *savestr(); 20110303Ssam static struct passwd save; 20210275Ssam 20310275Ssam if (logged_in || pw == NULL) { 20410275Ssam reply(503, "Login with USER first."); 20510275Ssam return; 20610275Ssam } 20710275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 20810275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 20916760Slepreau /* The strcmp does not catch null passwords! */ 21016760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 21110275Ssam reply(530, "Login incorrect."); 21210275Ssam pw = NULL; 21310275Ssam return; 21410275Ssam } 21510275Ssam } 21610303Ssam setegid(pw->pw_gid); 21710275Ssam initgroups(pw->pw_name, pw->pw_gid); 21810275Ssam if (chdir(pw->pw_dir)) { 21918107Sbloom reply(550, "User %s: can't change directory to %s.", 22010275Ssam pw->pw_name, pw->pw_dir); 22110303Ssam goto bad; 22210275Ssam } 22316033Sralph 22416760Slepreau /* grab wtmp before chroot */ 22516760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 22610303Ssam if (guest && chroot(pw->pw_dir) < 0) { 22710275Ssam reply(550, "Can't set guest privileges."); 22816760Slepreau if (wtmp >= 0) { 22916760Slepreau (void) close(wtmp); 23016760Slepreau wtmp = -1; 23116760Slepreau } 23210303Ssam goto bad; 23310275Ssam } 23410275Ssam if (!guest) 23510275Ssam reply(230, "User %s logged in.", pw->pw_name); 23610275Ssam else 23710275Ssam reply(230, "Guest login ok, access restrictions apply."); 23810275Ssam logged_in = 1; 23913247Ssam dologin(pw); 24010303Ssam seteuid(pw->pw_uid); 24110303Ssam /* 24210303Ssam * Save everything so globbing doesn't 24310303Ssam * clobber the fields. 24410303Ssam */ 24510303Ssam save = *pw; 24610303Ssam save.pw_name = savestr(pw->pw_name); 24710303Ssam save.pw_passwd = savestr(pw->pw_passwd); 24810303Ssam save.pw_comment = savestr(pw->pw_comment); 249*26493Sminshall save.pw_gecos = savestr(pw->pw_gecos); 25010303Ssam save.pw_dir = savestr(pw->pw_dir); 25110303Ssam save.pw_shell = savestr(pw->pw_shell); 25210303Ssam pw = &save; 25310303Ssam home = pw->pw_dir; /* home dir for globbing */ 25410303Ssam return; 25510303Ssam bad: 25610303Ssam seteuid(0); 25710303Ssam pw = NULL; 25810275Ssam } 25910275Ssam 26010303Ssam char * 26110303Ssam savestr(s) 26210303Ssam char *s; 26310303Ssam { 26410303Ssam char *malloc(); 265*26493Sminshall char *new = malloc((unsigned) strlen(s) + 1); 26610303Ssam 26710303Ssam if (new != NULL) 268*26493Sminshall (void) strcpy(new, s); 26911347Ssam return (new); 27010303Ssam } 27110303Ssam 27210275Ssam retrieve(cmd, name) 27310275Ssam char *cmd, *name; 27410275Ssam { 27510275Ssam FILE *fin, *dout; 27610275Ssam struct stat st; 27726044Sminshall int (*closefunc)(), tmp; 27810275Ssam 27910275Ssam if (cmd == 0) { 28010317Ssam #ifdef notdef 28110317Ssam /* no remote command execution -- it's a security hole */ 28211653Ssam if (*name == '|') 28310275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 28410275Ssam else 28510317Ssam #endif 28610275Ssam fin = fopen(name, "r"), closefunc = fclose; 28710275Ssam } else { 28810275Ssam char line[BUFSIZ]; 28910275Ssam 290*26493Sminshall (void) sprintf(line, cmd, name), name = line; 29110275Ssam fin = popen(line, "r"), closefunc = pclose; 29210275Ssam } 29310275Ssam if (fin == NULL) { 29413152Ssam if (errno != 0) 29513152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 29610275Ssam return; 29710275Ssam } 29810275Ssam st.st_size = 0; 29910275Ssam if (cmd == 0 && 30010275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 30110275Ssam reply(550, "%s: not a plain file.", name); 30210275Ssam goto done; 30310275Ssam } 30410275Ssam dout = dataconn(name, st.st_size, "w"); 30510275Ssam if (dout == NULL) 30610275Ssam goto done; 30726044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 30810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30926044Sminshall } 31026044Sminshall else if (tmp == 0) { 31110275Ssam reply(226, "Transfer complete."); 31226044Sminshall } 313*26493Sminshall (void) fclose(dout); 31426044Sminshall data = -1; 31526044Sminshall pdata = -1; 31610275Ssam done: 31710275Ssam (*closefunc)(fin); 31810275Ssam } 31910275Ssam 32010275Ssam store(name, mode) 32110275Ssam char *name, *mode; 32210275Ssam { 32310275Ssam FILE *fout, *din; 32426044Sminshall int (*closefunc)(), dochown = 0, tmp; 32526044Sminshall char *gunique(), *local; 32610275Ssam 32710317Ssam #ifdef notdef 32810317Ssam /* no remote command execution -- it's a security hole */ 32911653Ssam if (name[0] == '|') 33010275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33110317Ssam else 33210317Ssam #endif 33310317Ssam { 33410303Ssam struct stat st; 33510303Ssam 33626044Sminshall local = name; 33726044Sminshall if (stat(name, &st) < 0) { 33810303Ssam dochown++; 33926044Sminshall } 34026044Sminshall else if (unique) { 34126044Sminshall if ((local = gunique(name)) == NULL) { 34226044Sminshall return; 34326044Sminshall } 34426044Sminshall dochown++; 34526044Sminshall } 34626044Sminshall fout = fopen(local, mode), closefunc = fclose; 34710303Ssam } 34810275Ssam if (fout == NULL) { 34926044Sminshall reply(550, "%s: %s.", local, sys_errlist[errno]); 35010275Ssam return; 35110275Ssam } 35226044Sminshall din = dataconn(local, (off_t)-1, "r"); 35310275Ssam if (din == NULL) 35410275Ssam goto done; 35526044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 35626044Sminshall reply(550, "%s: %s.", local, sys_errlist[errno]); 35726044Sminshall } 35826044Sminshall else if (tmp == 0 && !unique) { 35910275Ssam reply(226, "Transfer complete."); 36026044Sminshall } 36126044Sminshall else if (tmp == 0 && unique) { 36226044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 36326044Sminshall } 364*26493Sminshall (void) fclose(din); 36526044Sminshall data = -1; 36626044Sminshall pdata = -1; 36710275Ssam done: 36810303Ssam if (dochown) 36926044Sminshall (void) chown(local, pw->pw_uid, -1); 37010275Ssam (*closefunc)(fout); 37110275Ssam } 37210275Ssam 37310275Ssam FILE * 37410275Ssam getdatasock(mode) 37510275Ssam char *mode; 37610275Ssam { 37717157Ssam int s, on = 1; 37810275Ssam 37910275Ssam if (data >= 0) 38010275Ssam return (fdopen(data, mode)); 38113247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 38210602Ssam if (s < 0) 38310275Ssam return (NULL); 38410275Ssam seteuid(0); 385*26493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 38610602Ssam goto bad; 38713152Ssam /* anchor socket to avoid multi-homing problems */ 38813152Ssam data_source.sin_family = AF_INET; 38913152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 390*26493Sminshall if (bind(s, &data_source, sizeof (data_source)) < 0) 39110602Ssam goto bad; 39210311Ssam seteuid(pw->pw_uid); 39310275Ssam return (fdopen(s, mode)); 39410602Ssam bad: 39510602Ssam seteuid(pw->pw_uid); 396*26493Sminshall (void) close(s); 39710602Ssam return (NULL); 39810275Ssam } 39910275Ssam 40010275Ssam FILE * 40110275Ssam dataconn(name, size, mode) 40210275Ssam char *name; 40311653Ssam off_t size; 40410275Ssam char *mode; 40510275Ssam { 40610275Ssam char sizebuf[32]; 40710275Ssam FILE *file; 40811653Ssam int retry = 0; 40910275Ssam 41010275Ssam if (size >= 0) 411*26493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 41210275Ssam else 41310275Ssam (void) strcpy(sizebuf, ""); 41426044Sminshall if (pdata > 0) { 41526044Sminshall struct sockaddr_in from; 41626044Sminshall int s, fromlen = sizeof(from); 41726044Sminshall 418*26493Sminshall s = accept(pdata, &from, &fromlen); 41926044Sminshall if (s < 0) { 42026044Sminshall reply(425, "Can't open data connection."); 42126044Sminshall (void) close(pdata); 42226044Sminshall pdata = -1; 42326044Sminshall return(NULL); 42426044Sminshall } 42526044Sminshall (void) close(pdata); 42626044Sminshall pdata = s; 42726044Sminshall reply(150, "Openning data connection for %s (%s,%d)%s.", 428*26493Sminshall name, inet_ntoa(from.sin_addr), 42926044Sminshall ntohs(from.sin_port), sizebuf); 43026044Sminshall return(fdopen(pdata, mode)); 43126044Sminshall } 43210275Ssam if (data >= 0) { 43310275Ssam reply(125, "Using existing data connection for %s%s.", 43410275Ssam name, sizebuf); 43510321Ssam usedefault = 1; 43610275Ssam return (fdopen(data, mode)); 43710275Ssam } 43810566Ssam if (usedefault) 43910422Ssam data_dest = his_addr; 44010422Ssam usedefault = 1; 44110275Ssam file = getdatasock(mode); 44210275Ssam if (file == NULL) { 44310275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 44413247Ssam inet_ntoa(data_source.sin_addr), 44510275Ssam ntohs(data_source.sin_port), 44610275Ssam sys_errlist[errno]); 44710275Ssam return (NULL); 44810275Ssam } 44910602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 450*26493Sminshall name, inet_ntoa(data_dest.sin_addr), 45110602Ssam ntohs(data_dest.sin_port), sizebuf); 45210275Ssam data = fileno(file); 45326044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 45411653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 455*26493Sminshall sleep((unsigned) swaitint); 45611653Ssam retry += swaitint; 45711653Ssam continue; 45811653Ssam } 45910275Ssam reply(425, "Can't build data connection: %s.", 46010275Ssam sys_errlist[errno]); 46110275Ssam (void) fclose(file); 46210275Ssam data = -1; 46310275Ssam return (NULL); 46410275Ssam } 46510275Ssam return (file); 46610275Ssam } 46710275Ssam 46810275Ssam /* 46910275Ssam * Tranfer the contents of "instr" to 47010275Ssam * "outstr" peer using the appropriate 47110275Ssam * encapulation of the date subject 47210275Ssam * to Mode, Structure, and Type. 47310275Ssam * 47410275Ssam * NB: Form isn't handled. 47510275Ssam */ 47610275Ssam send_data(instr, outstr) 47710275Ssam FILE *instr, *outstr; 47810275Ssam { 47910275Ssam register int c; 48010275Ssam int netfd, filefd, cnt; 48110275Ssam char buf[BUFSIZ]; 48210275Ssam 48326044Sminshall transflag++; 48426044Sminshall if (setjmp(urgcatch)) { 48526044Sminshall transflag = 0; 48626044Sminshall return(-1); 48726044Sminshall } 48810275Ssam switch (type) { 48910275Ssam 49010275Ssam case TYPE_A: 49110275Ssam while ((c = getc(instr)) != EOF) { 49211220Ssam if (c == '\n') { 49326044Sminshall if (ferror (outstr)) { 49426044Sminshall transflag = 0; 49511220Ssam return (1); 49626044Sminshall } 49710275Ssam putc('\r', outstr); 49811220Ssam } 49911220Ssam putc(c, outstr); 50026044Sminshall /* if (c == '\r') */ 50126044Sminshall /* putc ('\0', outstr); */ 50210275Ssam } 50326044Sminshall transflag = 0; 50426044Sminshall if (ferror (instr) || ferror (outstr)) { 50511220Ssam return (1); 50626044Sminshall } 50710275Ssam return (0); 50810275Ssam 50910275Ssam case TYPE_I: 51010275Ssam case TYPE_L: 51110275Ssam netfd = fileno(outstr); 51210275Ssam filefd = fileno(instr); 51310275Ssam 51426044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 51526044Sminshall if (write(netfd, buf, cnt) < 0) { 51626044Sminshall transflag = 0; 51710275Ssam return (1); 51826044Sminshall } 51926044Sminshall } 52026044Sminshall transflag = 0; 52110275Ssam return (cnt < 0); 52210275Ssam } 52310275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 52426044Sminshall transflag = 0; 52510275Ssam return (1); 52610275Ssam } 52710275Ssam 52810275Ssam /* 52910275Ssam * Transfer data from peer to 53010275Ssam * "outstr" using the appropriate 53110275Ssam * encapulation of the data subject 53210275Ssam * to Mode, Structure, and Type. 53310275Ssam * 53410275Ssam * N.B.: Form isn't handled. 53510275Ssam */ 53610275Ssam receive_data(instr, outstr) 53710275Ssam FILE *instr, *outstr; 53810275Ssam { 53910275Ssam register int c; 54011220Ssam int cnt; 54110275Ssam char buf[BUFSIZ]; 54210275Ssam 54310275Ssam 54426044Sminshall transflag++; 54526044Sminshall if (setjmp(urgcatch)) { 54626044Sminshall transflag = 0; 54726044Sminshall return(-1); 54826044Sminshall } 54910275Ssam switch (type) { 55010275Ssam 55110275Ssam case TYPE_I: 55210275Ssam case TYPE_L: 55326044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 55426044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 55526044Sminshall transflag = 0; 55610275Ssam return (1); 55726044Sminshall } 55826044Sminshall } 55926044Sminshall transflag = 0; 56010275Ssam return (cnt < 0); 56110275Ssam 56210275Ssam case TYPE_E: 56310275Ssam reply(504, "TYPE E not implemented."); 56426044Sminshall transflag = 0; 56510275Ssam return (1); 56610275Ssam 56710275Ssam case TYPE_A: 56810275Ssam while ((c = getc(instr)) != EOF) { 56910275Ssam if (c == '\r') { 57026044Sminshall if (ferror (outstr)) { 57126044Sminshall transflag = 0; 57211220Ssam return (1); 57326044Sminshall } 57411220Ssam if ((c = getc(instr)) != '\n') 57511220Ssam putc ('\r', outstr); 57626044Sminshall /* if (c == '\0') */ 57726044Sminshall /* continue; */ 57810275Ssam } 57911220Ssam putc (c, outstr); 58010275Ssam } 58126044Sminshall transflag = 0; 58211220Ssam if (ferror (instr) || ferror (outstr)) 58311220Ssam return (1); 58410275Ssam return (0); 58510275Ssam } 58626044Sminshall transflag = 0; 58710275Ssam fatal("Unknown type in receive_data."); 58810275Ssam /*NOTREACHED*/ 58910275Ssam } 59010275Ssam 59110275Ssam fatal(s) 59210275Ssam char *s; 59310275Ssam { 59410275Ssam reply(451, "Error in server: %s\n", s); 59510275Ssam reply(221, "Closing connection due to server error."); 59613247Ssam dologout(0); 59710275Ssam } 59810275Ssam 599*26493Sminshall /*VARARGS2*/ 60010275Ssam reply(n, s, args) 60110275Ssam int n; 60210275Ssam char *s; 60310275Ssam { 60410275Ssam 60510275Ssam printf("%d ", n); 60610275Ssam _doprnt(s, &args, stdout); 60710275Ssam printf("\r\n"); 608*26493Sminshall (void) fflush(stdout); 60910275Ssam if (debug) { 610*26493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 611*26493Sminshall syslog(LOG_DEBUG, s, &args); 61210275Ssam } 61310275Ssam } 61410275Ssam 615*26493Sminshall /*VARARGS2*/ 61610275Ssam lreply(n, s, args) 61710275Ssam int n; 61810275Ssam char *s; 61910275Ssam { 62010275Ssam printf("%d-", n); 62110275Ssam _doprnt(s, &args, stdout); 62210275Ssam printf("\r\n"); 623*26493Sminshall (void) fflush(stdout); 62410275Ssam if (debug) { 625*26493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 626*26493Sminshall syslog(LOG_DEBUG, s, &args); 62710275Ssam } 62810275Ssam } 62910275Ssam 630*26493Sminshall /* NOT CALLED ANYWHERE 63110275Ssam replystr(s) 63210275Ssam char *s; 63310275Ssam { 63410275Ssam printf("%s\r\n", s); 635*26493Sminshall (void) fflush(stdout); 63610275Ssam if (debug) 63710275Ssam fprintf(stderr, "<--- %s\n", s); 63810275Ssam } 639*26493Sminshall */ 64010275Ssam 64110275Ssam ack(s) 64210275Ssam char *s; 64310275Ssam { 64426044Sminshall reply(200, "%s command successful.", s); 64510275Ssam } 64610275Ssam 64710275Ssam nack(s) 64810275Ssam char *s; 64910275Ssam { 65010275Ssam reply(502, "%s command not implemented.", s); 65110275Ssam } 65210275Ssam 653*26493Sminshall yyerror(s) 654*26493Sminshall char *s; 65510275Ssam { 65626044Sminshall char *cp; 65726044Sminshall 65826044Sminshall cp = index(cbuf,'\n'); 65926044Sminshall *cp = '\0'; 66026044Sminshall reply(500, "'%s': command not understood.",cbuf); 66110275Ssam } 66210275Ssam 66310275Ssam delete(name) 66410275Ssam char *name; 66510275Ssam { 66610275Ssam struct stat st; 66710275Ssam 66810275Ssam if (stat(name, &st) < 0) { 66910275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67010275Ssam return; 67110275Ssam } 67210275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 67310275Ssam if (rmdir(name) < 0) { 67410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67510275Ssam return; 67610275Ssam } 67710275Ssam goto done; 67810275Ssam } 67910275Ssam if (unlink(name) < 0) { 68010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 68110275Ssam return; 68210275Ssam } 68310275Ssam done: 68410275Ssam ack("DELE"); 68510275Ssam } 68610275Ssam 68710275Ssam cwd(path) 68810275Ssam char *path; 68910275Ssam { 69010275Ssam 69110275Ssam if (chdir(path) < 0) { 69210275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 69310275Ssam return; 69410275Ssam } 69510275Ssam ack("CWD"); 69610275Ssam } 69710275Ssam 69810303Ssam makedir(name) 69910275Ssam char *name; 70010275Ssam { 70110303Ssam struct stat st; 70210303Ssam int dochown = stat(name, &st) < 0; 70310275Ssam 70410275Ssam if (mkdir(name, 0777) < 0) { 70510275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70610275Ssam return; 70710275Ssam } 70810303Ssam if (dochown) 70910303Ssam (void) chown(name, pw->pw_uid, -1); 71010275Ssam ack("MKDIR"); 71110275Ssam } 71210275Ssam 71310303Ssam removedir(name) 71410275Ssam char *name; 71510275Ssam { 71610275Ssam 71710275Ssam if (rmdir(name) < 0) { 71810275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 71910275Ssam return; 72010275Ssam } 72110275Ssam ack("RMDIR"); 72210275Ssam } 72310275Ssam 72410303Ssam pwd() 72510275Ssam { 72610303Ssam char path[MAXPATHLEN + 1]; 72710275Ssam 72810275Ssam if (getwd(path) == NULL) { 72910275Ssam reply(451, "%s.", path); 73010275Ssam return; 73110275Ssam } 73210275Ssam reply(251, "\"%s\" is current directory.", path); 73310275Ssam } 73410275Ssam 73510275Ssam char * 73610275Ssam renamefrom(name) 73710275Ssam char *name; 73810275Ssam { 73910275Ssam struct stat st; 74010275Ssam 74110275Ssam if (stat(name, &st) < 0) { 74210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 74310275Ssam return ((char *)0); 74410275Ssam } 74510303Ssam reply(350, "File exists, ready for destination name"); 74610275Ssam return (name); 74710275Ssam } 74810275Ssam 74910275Ssam renamecmd(from, to) 75010275Ssam char *from, *to; 75110275Ssam { 75210275Ssam 75310275Ssam if (rename(from, to) < 0) { 75410275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 75510275Ssam return; 75610275Ssam } 75710275Ssam ack("RNTO"); 75810275Ssam } 75910275Ssam 76010275Ssam dolog(sin) 76110275Ssam struct sockaddr_in *sin; 76210275Ssam { 76310275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 76410275Ssam sizeof (struct in_addr), AF_INET); 76510275Ssam time_t t; 766*26493Sminshall extern char *ctime(); 76710275Ssam 76813247Ssam if (hp) { 769*26493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 77013247Ssam endhostent(); 77113247Ssam } else 772*26493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 77313247Ssam sizeof (remotehost)); 77413247Ssam if (!logging) 77513247Ssam return; 776*26493Sminshall t = time((time_t *) 0); 777*26493Sminshall syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 77810275Ssam } 77910695Ssam 78013247Ssam #include <utmp.h> 78113247Ssam 782*26493Sminshall #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a)) 78313247Ssam struct utmp utmp; 78413247Ssam 78510695Ssam /* 78613247Ssam * Record login in wtmp file. 78713247Ssam */ 78813247Ssam dologin(pw) 78913247Ssam struct passwd *pw; 79013247Ssam { 79113247Ssam char line[32]; 79213247Ssam 79313247Ssam if (wtmp >= 0) { 79413247Ssam /* hack, but must be unique and no tty line */ 795*26493Sminshall (void) sprintf(line, "ftp%d", getpid()); 79613247Ssam SCPYN(utmp.ut_line, line); 79713247Ssam SCPYN(utmp.ut_name, pw->pw_name); 79813247Ssam SCPYN(utmp.ut_host, remotehost); 799*26493Sminshall utmp.ut_time = (long) time((time_t *) 0); 80013247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 80116760Slepreau if (!guest) { /* anon must hang on */ 80216760Slepreau (void) close(wtmp); 80316760Slepreau wtmp = -1; 80416760Slepreau } 80513247Ssam } 80613247Ssam } 80713247Ssam 80813247Ssam /* 80913247Ssam * Record logout in wtmp file 81013247Ssam * and exit with supplied status. 81113247Ssam */ 81213247Ssam dologout(status) 81313247Ssam int status; 81413247Ssam { 81516339Skarels 81617580Ssam if (logged_in) { 81717580Ssam (void) seteuid(0); 81817580Ssam if (wtmp < 0) 81917580Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 82017580Ssam if (wtmp >= 0) { 82117580Ssam SCPYN(utmp.ut_name, ""); 82217580Ssam SCPYN(utmp.ut_host, ""); 823*26493Sminshall utmp.ut_time = (long) time((time_t *) 0); 82417580Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 82517580Ssam (void) close(wtmp); 82617580Ssam } 82713247Ssam } 82814436Ssam /* beware of flushing buffers after a SIGPIPE */ 82914436Ssam _exit(status); 83013247Ssam } 83113247Ssam 83213247Ssam /* 83310695Ssam * Special version of popen which avoids 83410695Ssam * call to shell. This insures noone may 83510695Ssam * create a pipe to a hidden program as a side 83610695Ssam * effect of a list or dir command. 83710695Ssam */ 83810695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 83910695Ssam #define RDR 0 84010695Ssam #define WTR 1 84110695Ssam static int popen_pid[5]; 84210695Ssam 84310695Ssam static char * 84410695Ssam nextarg(cpp) 84510695Ssam char *cpp; 84610695Ssam { 84710695Ssam register char *cp = cpp; 84810695Ssam 84910695Ssam if (cp == 0) 85010695Ssam return (cp); 85110695Ssam while (*cp && *cp != ' ' && *cp != '\t') 85210695Ssam cp++; 85310695Ssam if (*cp == ' ' || *cp == '\t') { 85410695Ssam *cp++ = '\0'; 85510695Ssam while (*cp == ' ' || *cp == '\t') 85610695Ssam cp++; 85710695Ssam } 85810695Ssam if (cp == cpp) 85910695Ssam return ((char *)0); 86010695Ssam return (cp); 86110695Ssam } 86210695Ssam 86310695Ssam FILE * 86410695Ssam popen(cmd, mode) 86510695Ssam char *cmd, *mode; 86610695Ssam { 86713211Sroot int p[2], ac, gac; 86810695Ssam register myside, hisside, pid; 86913211Sroot char *av[20], *gav[512]; 87010695Ssam register char *cp; 87110695Ssam 87210695Ssam if (pipe(p) < 0) 87310695Ssam return (NULL); 87410695Ssam cp = cmd, ac = 0; 87513211Sroot /* break up string into pieces */ 87610695Ssam do { 87710695Ssam av[ac++] = cp; 87810695Ssam cp = nextarg(cp); 87913211Sroot } while (cp && *cp && ac < 20); 88010695Ssam av[ac] = (char *)0; 88113211Sroot gav[0] = av[0]; 88213211Sroot /* glob each piece */ 88313211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 88413211Sroot char **pop; 88522024Ssam extern char **glob(), **copyblk(); 88613211Sroot 88713211Sroot pop = glob(av[ac]); 88822024Ssam if (pop == (char **)NULL) { /* globbing failed */ 88922024Ssam char *vv[2]; 89022024Ssam 89122024Ssam vv[0] = av[ac]; 89222024Ssam vv[1] = 0; 89322024Ssam pop = copyblk(vv); 89413211Sroot } 89522024Ssam av[ac] = (char *)pop; /* save to free later */ 89622024Ssam while (*pop && gac < 512) 89722024Ssam gav[gac++] = *pop++; 89811757Ssam } 89913211Sroot gav[gac] = (char *)0; 90010695Ssam myside = tst(p[WTR], p[RDR]); 90110695Ssam hisside = tst(p[RDR], p[WTR]); 90210695Ssam if ((pid = fork()) == 0) { 90310695Ssam /* myside and hisside reverse roles in child */ 904*26493Sminshall (void) close(myside); 905*26493Sminshall (void) dup2(hisside, tst(0, 1)); 906*26493Sminshall (void) close(hisside); 90713211Sroot execv(gav[0], gav); 90810695Ssam _exit(1); 90910695Ssam } 91013211Sroot for (ac = 1; av[ac] != NULL; ac++) 91113211Sroot blkfree((char **)av[ac]); 91210695Ssam if (pid == -1) 91310695Ssam return (NULL); 91410695Ssam popen_pid[myside] = pid; 915*26493Sminshall (void) close(hisside); 91610695Ssam return (fdopen(myside, mode)); 91710695Ssam } 91810695Ssam 91910695Ssam pclose(ptr) 92010695Ssam FILE *ptr; 92110695Ssam { 92210695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 92310695Ssam int status; 92410695Ssam 92510695Ssam f = fileno(ptr); 926*26493Sminshall (void) fclose(ptr); 92710695Ssam istat = signal(SIGINT, SIG_IGN); 92810695Ssam qstat = signal(SIGQUIT, SIG_IGN); 92910695Ssam hstat = signal(SIGHUP, SIG_IGN); 93010695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 93110695Ssam ; 93210695Ssam if (r == -1) 93310695Ssam status = -1; 934*26493Sminshall (void) signal(SIGINT, istat); 935*26493Sminshall (void) signal(SIGQUIT, qstat); 936*26493Sminshall (void) signal(SIGHUP, hstat); 93710695Ssam return (status); 93810695Ssam } 93910695Ssam 94010695Ssam /* 94110695Ssam * Check user requesting login priviledges. 94210695Ssam * Disallow anyone mentioned in the file FTPUSERS 94310695Ssam * to allow people such as uucp to be avoided. 94410695Ssam */ 94510695Ssam checkuser(name) 94610695Ssam register char *name; 94710695Ssam { 94810695Ssam char line[BUFSIZ], *index(); 94910695Ssam FILE *fd; 95010695Ssam int found = 0; 95110695Ssam 95210695Ssam fd = fopen(FTPUSERS, "r"); 95310695Ssam if (fd == NULL) 95410695Ssam return (1); 95510695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 95610695Ssam register char *cp = index(line, '\n'); 95710695Ssam 95810695Ssam if (cp) 95910695Ssam *cp = '\0'; 96010695Ssam if (strcmp(line, name) == 0) { 96110695Ssam found++; 96210695Ssam break; 96310695Ssam } 96410695Ssam } 965*26493Sminshall (void) fclose(fd); 96610695Ssam return (!found); 96710695Ssam } 96826044Sminshall 96926044Sminshall myoob() 97026044Sminshall { 971*26493Sminshall int aflag = 0, atmark; 97226227Ssam char c, *cp; 97326044Sminshall 97426044Sminshall if (!transflag) { 97526044Sminshall for (;;) { 976*26493Sminshall if (ioctl(fileno(stdin), (int) SIOCATMARK, (char *) &atmark) < 0) { 977*26493Sminshall syslog(LOG_ERR, "ioctl: %m"); 97826044Sminshall break; 97926044Sminshall } 98026227Ssam if (atmark) 98126044Sminshall break; 982*26493Sminshall (void) read(fileno(stdin), &c, 1); 98326044Sminshall } 984*26493Sminshall (void) recv(fileno(stdin), &c, 1, MSG_OOB); 985*26493Sminshall (void) read(fileno(stdin), &c, 1); 98626044Sminshall return; 98726044Sminshall } 98826044Sminshall for (;;) { 989*26493Sminshall if (ioctl(fileno(stdin), (int) SIOCATMARK, (char *) &atmark) < 0) { 990*26493Sminshall syslog(LOG_ERR, "ioctl: %m"); 99126044Sminshall break; 99226044Sminshall } 99326227Ssam if (atmark) 99426044Sminshall break; 995*26493Sminshall (void) read(fileno(stdin), &c, 1); 99626227Ssam if (c == IAC || c == IP) 99726044Sminshall aflag++; 99826044Sminshall } 999*26493Sminshall (void) recv(fileno(stdin), &c, 1, MSG_OOB); 100026227Ssam if (c == IAC) 100126044Sminshall aflag++; 1002*26493Sminshall (void) read(fileno(stdin), &c, 1); 100326227Ssam if (c == DM) 100426044Sminshall aflag++; 100526227Ssam if (aflag != 4) 100626044Sminshall return; 100726044Sminshall cp = tmpline; 100826044Sminshall (void) getline(cp, 7, stdin); 100926044Sminshall upper(cp); 101026227Ssam if (strcmp(cp, "ABOR\r\n")) 101126044Sminshall return; 101226044Sminshall tmpline[0] = '\0'; 101326044Sminshall reply(426,"Transfer aborted. Data connection closed."); 101426044Sminshall reply(226,"Abort successful"); 101526044Sminshall longjmp(urgcatch, 1); 101626044Sminshall } 101726044Sminshall 101826044Sminshall passive() 101926044Sminshall { 102026044Sminshall int len; 102126044Sminshall struct sockaddr_in tmp; 102226044Sminshall register char *p, *a; 102326044Sminshall 102426044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 102526044Sminshall if (pdata < 0) { 102626044Sminshall reply(451, "Can't open passive connection"); 102726044Sminshall return; 102826044Sminshall } 102926044Sminshall tmp = ctrl_addr; 103026044Sminshall tmp.sin_port = 0; 103126044Sminshall seteuid(0); 1032*26493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 103326044Sminshall seteuid(pw->pw_uid); 103426044Sminshall (void) close(pdata); 103526044Sminshall pdata = -1; 103626044Sminshall reply(451, "Can't open passive connection"); 103726044Sminshall return; 103826044Sminshall } 103926044Sminshall seteuid(pw->pw_uid); 104026044Sminshall len = sizeof(tmp); 104126044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 104226044Sminshall (void) close(pdata); 104326044Sminshall pdata = -1; 104426044Sminshall reply(451, "Can't open passive connection"); 104526044Sminshall return; 104626044Sminshall } 104726044Sminshall if (listen(pdata, 1) < 0) { 104826044Sminshall (void) close(pdata); 104926044Sminshall pdata = -1; 105026044Sminshall reply(451, "Can't open passive connection"); 105126044Sminshall return; 105226044Sminshall } 105326044Sminshall a = (char *) &tmp.sin_addr; 105426044Sminshall p = (char *) &tmp.sin_port; 105526044Sminshall 105626044Sminshall #define UC(b) (((int) b) & 0xff) 105726044Sminshall 105826044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 105926044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 106026044Sminshall } 106126044Sminshall 106226044Sminshall char * 106326044Sminshall gunique(local) 106426044Sminshall char *local; 106526044Sminshall { 106626044Sminshall static char new[MAXPATHLEN]; 106726044Sminshall char *cp = rindex(local, '/'); 106826044Sminshall int d, count=0; 106926044Sminshall char ext = '1'; 107026044Sminshall 107126044Sminshall if (cp) { 107226044Sminshall *cp = '\0'; 107326044Sminshall } 107426044Sminshall d = access(cp ? local : ".", 2); 107526044Sminshall if (cp) { 107626044Sminshall *cp = '/'; 107726044Sminshall } 107826044Sminshall if (d < 0) { 1079*26493Sminshall syslog(LOG_ERR, "%s: %m", local); 108026044Sminshall return((char *) 0); 108126044Sminshall } 108226044Sminshall (void) strcpy(new, local); 108326044Sminshall cp = new + strlen(new); 108426044Sminshall *cp++ = '.'; 108526044Sminshall while (!d) { 108626044Sminshall if (++count == 100) { 108726044Sminshall reply(451, "Unique file name not cannot be created."); 108826044Sminshall return((char *) 0); 108926044Sminshall } 109026044Sminshall *cp++ = ext; 109126044Sminshall *cp = '\0'; 109226044Sminshall if (ext == '9') { 109326044Sminshall ext = '0'; 109426044Sminshall } 109526044Sminshall else { 109626044Sminshall ext++; 109726044Sminshall } 109826044Sminshall if ((d = access(new, 0)) < 0) { 109926044Sminshall break; 110026044Sminshall } 110126044Sminshall if (ext != '0') { 110226044Sminshall cp--; 110326044Sminshall } 110426044Sminshall else if (*(cp - 2) == '.') { 110526044Sminshall *(cp - 1) = '1'; 110626044Sminshall } 110726044Sminshall else { 110826044Sminshall *(cp - 2) = *(cp - 2) + 1; 110926044Sminshall cp--; 111026044Sminshall } 111126044Sminshall } 111226044Sminshall return(new); 111326044Sminshall } 1114