122499Sdist /* 2*26044Sminshall * 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[] = 9*26044Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\ 1022499Sdist All rights reserved.\n"; 1122499Sdist #endif not lint 1210275Ssam 1322499Sdist #ifndef lint 14*26044Sminshall static char sccsid[] = "@(#)ftpd.c 5.2 (Berkeley) 02/03/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> 31*26044Sminshall #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> 39*26044Sminshall #include <strings.h> 4010275Ssam 4110695Ssam /* 4210695Ssam * File containing login names 4310695Ssam * NOT to be used on this machine. 4410695Ssam * Commonly used to disallow uucp. 4510695Ssam */ 4610695Ssam #define FTPUSERS "/etc/ftpusers" 4710695Ssam 4810275Ssam extern int errno; 4910275Ssam extern char *sys_errlist[]; 5010275Ssam extern char *crypt(); 5110275Ssam extern char version[]; 5210275Ssam extern char *home; /* pointer to home directory for glob */ 53*26044Sminshall extern FILE *popen(), *fopen(), *freopen(); 5410275Ssam extern int pclose(), fclose(); 55*26044Sminshall extern char *getline(); 56*26044Sminshall extern char cbuf[]; 5710275Ssam 5810275Ssam struct sockaddr_in ctrl_addr; 5910275Ssam struct sockaddr_in data_source; 6010275Ssam struct sockaddr_in data_dest; 6110275Ssam struct sockaddr_in his_addr; 6210275Ssam 6310275Ssam struct hostent *hp; 6410275Ssam 6510275Ssam int data; 66*26044Sminshall jmp_buf errcatch, urgcatch; 6710275Ssam int logged_in; 6810275Ssam struct passwd *pw; 6910275Ssam int debug; 7011653Ssam int timeout; 7111757Ssam int logging; 7210275Ssam int guest; 7316033Sralph int wtmp; 7410275Ssam int type; 7510275Ssam int form; 7610275Ssam int stru; /* avoid C keyword */ 7710275Ssam int mode; 7810321Ssam int usedefault = 1; /* for data transfers */ 79*26044Sminshall int pdata; /* for passive mode */ 80*26044Sminshall int unique; 81*26044Sminshall int transflag; 82*26044Sminshall char tmpline[7]; 8310275Ssam char hostname[32]; 8413247Ssam char remotehost[32]; 8510275Ssam 8611653Ssam /* 8711653Ssam * Timeout intervals for retrying connections 8811653Ssam * to hosts that don't accept PORT cmds. This 8911653Ssam * is a kludge, but given the problems with TCP... 9011653Ssam */ 9111653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 9211653Ssam #define SWAITINT 5 /* interval between retries */ 9311653Ssam 9411653Ssam int swaitmax = SWAITMAX; 9511653Ssam int swaitint = SWAITINT; 9611653Ssam 9710275Ssam int lostconn(); 9810419Ssam int reapchild(); 99*26044Sminshall int myoob(); 10010275Ssam FILE *getdatasock(), *dataconn(); 10110275Ssam 10210275Ssam main(argc, argv) 10310275Ssam int argc; 10410275Ssam char *argv[]; 10510275Ssam { 10616339Skarels int options = 0, addrlen; 107*26044Sminshall long pgid; 10810275Ssam char *cp; 10910275Ssam 11016339Skarels addrlen = sizeof (his_addr); 11116339Skarels if (getpeername(0, &his_addr, &addrlen) < 0) { 11216339Skarels fprintf(stderr, "%s: ", argv[0]); 11316339Skarels perror("getpeername"); 11410275Ssam exit(1); 11510275Ssam } 11616339Skarels addrlen = sizeof (ctrl_addr); 11716339Skarels if (getsockname(0, &ctrl_addr, &addrlen) < 0) { 11816339Skarels fprintf(stderr, "%s: ", argv[0]); 11916339Skarels perror("getsockname"); 12016339Skarels exit(1); 12116339Skarels } 12216339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 12310275Ssam debug = 0; 12410275Ssam argc--, argv++; 12510275Ssam while (argc > 0 && *argv[0] == '-') { 12610275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 12710275Ssam 12811653Ssam case 'v': 12911653Ssam debug = 1; 13011653Ssam break; 13111653Ssam 13210275Ssam case 'd': 13310275Ssam debug = 1; 13410275Ssam options |= SO_DEBUG; 13510275Ssam break; 13610275Ssam 13711757Ssam case 'l': 13811757Ssam logging = 1; 139*26044Sminshall (void) freopen("/tmp/ftplog", "a", stderr); 14011757Ssam break; 14111757Ssam 14211653Ssam case 't': 14311653Ssam timeout = atoi(++cp); 14411653Ssam goto nextopt; 14511653Ssam break; 14611653Ssam 14710275Ssam default: 14816339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 14916339Skarels *cp); 15010275Ssam break; 15110275Ssam } 15211653Ssam nextopt: 15310275Ssam argc--, argv++; 15410275Ssam } 15516339Skarels signal(SIGPIPE, lostconn); 15616339Skarels signal(SIGCHLD, SIG_IGN); 157*26044Sminshall if (signal(SIGURG, myoob) < 0) { 158*26044Sminshall perror("signal"); 159*26044Sminshall } 160*26044Sminshall pgid = getpid(); 161*26044Sminshall if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) { 162*26044Sminshall perror("ioctl"); 163*26044Sminshall } 16416760Slepreau dolog(&his_addr); 16516339Skarels /* do telnet option negotiation here */ 16616339Skarels /* 16716339Skarels * Set up default state 16816339Skarels */ 16916339Skarels logged_in = 0; 17016339Skarels data = -1; 17116339Skarels type = TYPE_A; 17216339Skarels form = FORM_N; 17316339Skarels stru = STRU_F; 17416339Skarels mode = MODE_S; 175*26044Sminshall tmpline[0] = '\0'; 17616339Skarels gethostname(hostname, sizeof (hostname)); 17716339Skarels reply(220, "%s FTP server (%s) ready.", 17816339Skarels hostname, version); 17910275Ssam for (;;) { 18016339Skarels setjmp(errcatch); 18116339Skarels yyparse(); 18210275Ssam } 18310275Ssam } 18410275Ssam 18510419Ssam reapchild() 18610419Ssam { 18710419Ssam union wait status; 18810419Ssam 18910419Ssam while (wait3(&status, WNOHANG, 0) > 0) 19010419Ssam ; 19110419Ssam } 19210419Ssam 19310275Ssam lostconn() 19410275Ssam { 19510275Ssam 19614089Ssam if (debug) 19714089Ssam fprintf(stderr, "Lost connection.\n"); 19814089Ssam dologout(-1); 19910275Ssam } 20010275Ssam 20110275Ssam pass(passwd) 20210275Ssam char *passwd; 20310275Ssam { 20410303Ssam char *xpasswd, *savestr(); 20510303Ssam static struct passwd save; 20610275Ssam 20710275Ssam if (logged_in || pw == NULL) { 20810275Ssam reply(503, "Login with USER first."); 20910275Ssam return; 21010275Ssam } 21110275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 21210275Ssam xpasswd = crypt(passwd, pw->pw_passwd); 21316760Slepreau /* The strcmp does not catch null passwords! */ 21416760Slepreau if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { 21510275Ssam reply(530, "Login incorrect."); 21610275Ssam pw = NULL; 21710275Ssam return; 21810275Ssam } 21910275Ssam } 22010303Ssam setegid(pw->pw_gid); 22110275Ssam initgroups(pw->pw_name, pw->pw_gid); 22210275Ssam if (chdir(pw->pw_dir)) { 22318107Sbloom reply(550, "User %s: can't change directory to %s.", 22410275Ssam pw->pw_name, pw->pw_dir); 22510303Ssam goto bad; 22610275Ssam } 22716033Sralph 22816760Slepreau /* grab wtmp before chroot */ 22916760Slepreau wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 23010303Ssam if (guest && chroot(pw->pw_dir) < 0) { 23110275Ssam reply(550, "Can't set guest privileges."); 23216760Slepreau if (wtmp >= 0) { 23316760Slepreau (void) close(wtmp); 23416760Slepreau wtmp = -1; 23516760Slepreau } 23610303Ssam goto bad; 23710275Ssam } 23810275Ssam if (!guest) 23910275Ssam reply(230, "User %s logged in.", pw->pw_name); 24010275Ssam else 24110275Ssam reply(230, "Guest login ok, access restrictions apply."); 24210275Ssam logged_in = 1; 24313247Ssam dologin(pw); 24410303Ssam seteuid(pw->pw_uid); 24510303Ssam /* 24610303Ssam * Save everything so globbing doesn't 24710303Ssam * clobber the fields. 24810303Ssam */ 24910303Ssam save = *pw; 25010303Ssam save.pw_name = savestr(pw->pw_name); 25110303Ssam save.pw_passwd = savestr(pw->pw_passwd); 25210303Ssam save.pw_comment = savestr(pw->pw_comment); 25310303Ssam save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 25410303Ssam save.pw_dir = savestr(pw->pw_dir); 25510303Ssam save.pw_shell = savestr(pw->pw_shell); 25610303Ssam pw = &save; 25710303Ssam home = pw->pw_dir; /* home dir for globbing */ 25810303Ssam return; 25910303Ssam bad: 26010303Ssam seteuid(0); 26110303Ssam pw = NULL; 26210275Ssam } 26310275Ssam 26410303Ssam char * 26510303Ssam savestr(s) 26610303Ssam char *s; 26710303Ssam { 26810303Ssam char *malloc(); 26910303Ssam char *new = malloc(strlen(s) + 1); 27010303Ssam 27110303Ssam if (new != NULL) 27210303Ssam strcpy(new, s); 27311347Ssam return (new); 27410303Ssam } 27510303Ssam 27610275Ssam retrieve(cmd, name) 27710275Ssam char *cmd, *name; 27810275Ssam { 27910275Ssam FILE *fin, *dout; 28010275Ssam struct stat st; 281*26044Sminshall int (*closefunc)(), tmp; 28210275Ssam 28310275Ssam if (cmd == 0) { 28410317Ssam #ifdef notdef 28510317Ssam /* no remote command execution -- it's a security hole */ 28611653Ssam if (*name == '|') 28710275Ssam fin = popen(name + 1, "r"), closefunc = pclose; 28810275Ssam else 28910317Ssam #endif 29010275Ssam fin = fopen(name, "r"), closefunc = fclose; 29110275Ssam } else { 29210275Ssam char line[BUFSIZ]; 29310275Ssam 29410422Ssam sprintf(line, cmd, name), name = line; 29510275Ssam fin = popen(line, "r"), closefunc = pclose; 29610275Ssam } 29710275Ssam if (fin == NULL) { 29813152Ssam if (errno != 0) 29913152Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 30010275Ssam return; 30110275Ssam } 30210275Ssam st.st_size = 0; 30310275Ssam if (cmd == 0 && 30410275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 30510275Ssam reply(550, "%s: not a plain file.", name); 30610275Ssam goto done; 30710275Ssam } 30810275Ssam dout = dataconn(name, st.st_size, "w"); 30910275Ssam if (dout == NULL) 31010275Ssam goto done; 311*26044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 31210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 313*26044Sminshall } 314*26044Sminshall else if (tmp == 0) { 31510275Ssam reply(226, "Transfer complete."); 316*26044Sminshall } 317*26044Sminshall fclose(dout); 318*26044Sminshall data = -1; 319*26044Sminshall pdata = -1; 32010275Ssam done: 32110275Ssam (*closefunc)(fin); 32210275Ssam } 32310275Ssam 32410275Ssam store(name, mode) 32510275Ssam char *name, *mode; 32610275Ssam { 32710275Ssam FILE *fout, *din; 328*26044Sminshall int (*closefunc)(), dochown = 0, tmp; 329*26044Sminshall char *gunique(), *local; 33010275Ssam 33110317Ssam #ifdef notdef 33210317Ssam /* no remote command execution -- it's a security hole */ 33311653Ssam if (name[0] == '|') 33410275Ssam fout = popen(&name[1], "w"), closefunc = pclose; 33510317Ssam else 33610317Ssam #endif 33710317Ssam { 33810303Ssam struct stat st; 33910303Ssam 340*26044Sminshall local = name; 341*26044Sminshall if (stat(name, &st) < 0) { 34210303Ssam dochown++; 343*26044Sminshall } 344*26044Sminshall else if (unique) { 345*26044Sminshall if ((local = gunique(name)) == NULL) { 346*26044Sminshall return; 347*26044Sminshall } 348*26044Sminshall dochown++; 349*26044Sminshall } 350*26044Sminshall fout = fopen(local, mode), closefunc = fclose; 35110303Ssam } 35210275Ssam if (fout == NULL) { 353*26044Sminshall reply(550, "%s: %s.", local, sys_errlist[errno]); 35410275Ssam return; 35510275Ssam } 356*26044Sminshall din = dataconn(local, (off_t)-1, "r"); 35710275Ssam if (din == NULL) 35810275Ssam goto done; 359*26044Sminshall if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { 360*26044Sminshall reply(550, "%s: %s.", local, sys_errlist[errno]); 361*26044Sminshall } 362*26044Sminshall else if (tmp == 0 && !unique) { 36310275Ssam reply(226, "Transfer complete."); 364*26044Sminshall } 365*26044Sminshall else if (tmp == 0 && unique) { 366*26044Sminshall reply(226, "Transfer complete (unique file name:%s).", local); 367*26044Sminshall } 368*26044Sminshall fclose(din); 369*26044Sminshall data = -1; 370*26044Sminshall pdata = -1; 37110275Ssam done: 37210303Ssam if (dochown) 373*26044Sminshall (void) chown(local, pw->pw_uid, -1); 37410275Ssam (*closefunc)(fout); 37510275Ssam } 37610275Ssam 37710275Ssam FILE * 37810275Ssam getdatasock(mode) 37910275Ssam char *mode; 38010275Ssam { 38117157Ssam int s, on = 1; 38210275Ssam 38310275Ssam if (data >= 0) 38410275Ssam return (fdopen(data, mode)); 38513247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 38610602Ssam if (s < 0) 38710275Ssam return (NULL); 38810275Ssam seteuid(0); 38917157Ssam if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) 39010602Ssam goto bad; 39113152Ssam /* anchor socket to avoid multi-homing problems */ 39213152Ssam data_source.sin_family = AF_INET; 39313152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 39410602Ssam if (bind(s, &data_source, sizeof (data_source), 0) < 0) 39510602Ssam goto bad; 39610311Ssam seteuid(pw->pw_uid); 39710275Ssam return (fdopen(s, mode)); 39810602Ssam bad: 39910602Ssam seteuid(pw->pw_uid); 40010602Ssam close(s); 40110602Ssam return (NULL); 40210275Ssam } 40310275Ssam 40410275Ssam FILE * 40510275Ssam dataconn(name, size, mode) 40610275Ssam char *name; 40711653Ssam off_t size; 40810275Ssam char *mode; 40910275Ssam { 41010275Ssam char sizebuf[32]; 41110275Ssam FILE *file; 41211653Ssam int retry = 0; 41310275Ssam 41410275Ssam if (size >= 0) 41511653Ssam sprintf (sizebuf, " (%ld bytes)", size); 41610275Ssam else 41710275Ssam (void) strcpy(sizebuf, ""); 418*26044Sminshall if (pdata > 0) { 419*26044Sminshall struct sockaddr_in from; 420*26044Sminshall int s, fromlen = sizeof(from); 421*26044Sminshall 422*26044Sminshall s = accept(pdata, &from, &fromlen, 0); 423*26044Sminshall if (s < 0) { 424*26044Sminshall reply(425, "Can't open data connection."); 425*26044Sminshall (void) close(pdata); 426*26044Sminshall pdata = -1; 427*26044Sminshall return(NULL); 428*26044Sminshall } 429*26044Sminshall (void) close(pdata); 430*26044Sminshall pdata = s; 431*26044Sminshall reply(150, "Openning data connection for %s (%s,%d)%s.", 432*26044Sminshall name, inet_ntoa(from.sin_addr.s_addr), 433*26044Sminshall ntohs(from.sin_port), sizebuf); 434*26044Sminshall return(fdopen(pdata, mode)); 435*26044Sminshall } 43610275Ssam if (data >= 0) { 43710275Ssam reply(125, "Using existing data connection for %s%s.", 43810275Ssam name, sizebuf); 43910321Ssam usedefault = 1; 44010275Ssam return (fdopen(data, mode)); 44110275Ssam } 44210566Ssam if (usedefault) 44310422Ssam data_dest = his_addr; 44410422Ssam usedefault = 1; 44510275Ssam file = getdatasock(mode); 44610275Ssam if (file == NULL) { 44710275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 44813247Ssam inet_ntoa(data_source.sin_addr), 44910275Ssam ntohs(data_source.sin_port), 45010275Ssam sys_errlist[errno]); 45110275Ssam return (NULL); 45210275Ssam } 45310602Ssam reply(150, "Opening data connection for %s (%s,%d)%s.", 45413247Ssam name, inet_ntoa(data_dest.sin_addr.s_addr), 45510602Ssam ntohs(data_dest.sin_port), sizebuf); 45610275Ssam data = fileno(file); 457*26044Sminshall while (connect(data, &data_dest, sizeof (data_dest)) < 0) { 45811653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 45911653Ssam sleep(swaitint); 46011653Ssam retry += swaitint; 46111653Ssam continue; 46211653Ssam } 46310275Ssam reply(425, "Can't build data connection: %s.", 46410275Ssam sys_errlist[errno]); 46510275Ssam (void) fclose(file); 46610275Ssam data = -1; 46710275Ssam return (NULL); 46810275Ssam } 46910275Ssam return (file); 47010275Ssam } 47110275Ssam 47210275Ssam /* 47310275Ssam * Tranfer the contents of "instr" to 47410275Ssam * "outstr" peer using the appropriate 47510275Ssam * encapulation of the date subject 47610275Ssam * to Mode, Structure, and Type. 47710275Ssam * 47810275Ssam * NB: Form isn't handled. 47910275Ssam */ 48010275Ssam send_data(instr, outstr) 48110275Ssam FILE *instr, *outstr; 48210275Ssam { 48310275Ssam register int c; 48410275Ssam int netfd, filefd, cnt; 48510275Ssam char buf[BUFSIZ]; 48610275Ssam 487*26044Sminshall transflag++; 488*26044Sminshall if (setjmp(urgcatch)) { 489*26044Sminshall transflag = 0; 490*26044Sminshall return(-1); 491*26044Sminshall } 49210275Ssam switch (type) { 49310275Ssam 49410275Ssam case TYPE_A: 49510275Ssam while ((c = getc(instr)) != EOF) { 49611220Ssam if (c == '\n') { 497*26044Sminshall if (ferror (outstr)) { 498*26044Sminshall transflag = 0; 49911220Ssam return (1); 500*26044Sminshall } 50110275Ssam putc('\r', outstr); 50211220Ssam } 50311220Ssam putc(c, outstr); 504*26044Sminshall /* if (c == '\r') */ 505*26044Sminshall /* putc ('\0', outstr); */ 50610275Ssam } 507*26044Sminshall transflag = 0; 508*26044Sminshall if (ferror (instr) || ferror (outstr)) { 50911220Ssam return (1); 510*26044Sminshall } 51110275Ssam return (0); 51210275Ssam 51310275Ssam case TYPE_I: 51410275Ssam case TYPE_L: 51510275Ssam netfd = fileno(outstr); 51610275Ssam filefd = fileno(instr); 51710275Ssam 518*26044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 519*26044Sminshall if (write(netfd, buf, cnt) < 0) { 520*26044Sminshall transflag = 0; 52110275Ssam return (1); 522*26044Sminshall } 523*26044Sminshall } 524*26044Sminshall transflag = 0; 52510275Ssam return (cnt < 0); 52610275Ssam } 52710275Ssam reply(504,"Unimplemented TYPE %d in send_data", type); 528*26044Sminshall transflag = 0; 52910275Ssam return (1); 53010275Ssam } 53110275Ssam 53210275Ssam /* 53310275Ssam * Transfer data from peer to 53410275Ssam * "outstr" using the appropriate 53510275Ssam * encapulation of the data subject 53610275Ssam * to Mode, Structure, and Type. 53710275Ssam * 53810275Ssam * N.B.: Form isn't handled. 53910275Ssam */ 54010275Ssam receive_data(instr, outstr) 54110275Ssam FILE *instr, *outstr; 54210275Ssam { 54310275Ssam register int c; 54411220Ssam int cnt; 54510275Ssam char buf[BUFSIZ]; 54610275Ssam 54710275Ssam 548*26044Sminshall transflag++; 549*26044Sminshall if (setjmp(urgcatch)) { 550*26044Sminshall transflag = 0; 551*26044Sminshall return(-1); 552*26044Sminshall } 55310275Ssam switch (type) { 55410275Ssam 55510275Ssam case TYPE_I: 55610275Ssam case TYPE_L: 557*26044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 558*26044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 559*26044Sminshall transflag = 0; 56010275Ssam return (1); 561*26044Sminshall } 562*26044Sminshall } 563*26044Sminshall transflag = 0; 56410275Ssam return (cnt < 0); 56510275Ssam 56610275Ssam case TYPE_E: 56710275Ssam reply(504, "TYPE E not implemented."); 568*26044Sminshall transflag = 0; 56910275Ssam return (1); 57010275Ssam 57110275Ssam case TYPE_A: 57210275Ssam while ((c = getc(instr)) != EOF) { 57310275Ssam if (c == '\r') { 574*26044Sminshall if (ferror (outstr)) { 575*26044Sminshall transflag = 0; 57611220Ssam return (1); 577*26044Sminshall } 57811220Ssam if ((c = getc(instr)) != '\n') 57911220Ssam putc ('\r', outstr); 580*26044Sminshall /* if (c == '\0') */ 581*26044Sminshall /* continue; */ 58210275Ssam } 58311220Ssam putc (c, outstr); 58410275Ssam } 585*26044Sminshall transflag = 0; 58611220Ssam if (ferror (instr) || ferror (outstr)) 58711220Ssam return (1); 58810275Ssam return (0); 58910275Ssam } 590*26044Sminshall transflag = 0; 59110275Ssam fatal("Unknown type in receive_data."); 59210275Ssam /*NOTREACHED*/ 59310275Ssam } 59410275Ssam 59510275Ssam fatal(s) 59610275Ssam char *s; 59710275Ssam { 59810275Ssam reply(451, "Error in server: %s\n", s); 59910275Ssam reply(221, "Closing connection due to server error."); 60013247Ssam dologout(0); 60110275Ssam } 60210275Ssam 60310275Ssam reply(n, s, args) 60410275Ssam int n; 60510275Ssam char *s; 60610275Ssam { 60710275Ssam 60810275Ssam printf("%d ", n); 60910275Ssam _doprnt(s, &args, stdout); 61010275Ssam printf("\r\n"); 61110275Ssam fflush(stdout); 61210275Ssam if (debug) { 61310275Ssam fprintf(stderr, "<--- %d ", n); 61410275Ssam _doprnt(s, &args, stderr); 61510275Ssam fprintf(stderr, "\n"); 61610275Ssam fflush(stderr); 61710275Ssam } 61810275Ssam } 61910275Ssam 62010275Ssam lreply(n, s, args) 62110275Ssam int n; 62210275Ssam char *s; 62310275Ssam { 62410275Ssam printf("%d-", n); 62510275Ssam _doprnt(s, &args, stdout); 62610275Ssam printf("\r\n"); 62710275Ssam fflush(stdout); 62810275Ssam if (debug) { 62910275Ssam fprintf(stderr, "<--- %d-", n); 63010275Ssam _doprnt(s, &args, stderr); 63110275Ssam fprintf(stderr, "\n"); 63210275Ssam } 63310275Ssam } 63410275Ssam 63510275Ssam replystr(s) 63610275Ssam char *s; 63710275Ssam { 63810275Ssam printf("%s\r\n", s); 63910275Ssam fflush(stdout); 64010275Ssam if (debug) 64110275Ssam fprintf(stderr, "<--- %s\n", s); 64210275Ssam } 64310275Ssam 64410275Ssam ack(s) 64510275Ssam char *s; 64610275Ssam { 647*26044Sminshall reply(200, "%s command successful.", s); 64810275Ssam } 64910275Ssam 65010275Ssam nack(s) 65110275Ssam char *s; 65210275Ssam { 65310275Ssam reply(502, "%s command not implemented.", s); 65410275Ssam } 65510275Ssam 65610275Ssam yyerror() 65710275Ssam { 658*26044Sminshall char *cp; 659*26044Sminshall 660*26044Sminshall cp = index(cbuf,'\n'); 661*26044Sminshall *cp = '\0'; 662*26044Sminshall reply(500, "'%s': command not understood.",cbuf); 66310275Ssam } 66410275Ssam 66510275Ssam delete(name) 66610275Ssam char *name; 66710275Ssam { 66810275Ssam struct stat st; 66910275Ssam 67010275Ssam if (stat(name, &st) < 0) { 67110275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67210275Ssam return; 67310275Ssam } 67410275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 67510275Ssam if (rmdir(name) < 0) { 67610275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 67710275Ssam return; 67810275Ssam } 67910275Ssam goto done; 68010275Ssam } 68110275Ssam if (unlink(name) < 0) { 68210275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 68310275Ssam return; 68410275Ssam } 68510275Ssam done: 68610275Ssam ack("DELE"); 68710275Ssam } 68810275Ssam 68910275Ssam cwd(path) 69010275Ssam char *path; 69110275Ssam { 69210275Ssam 69310275Ssam if (chdir(path) < 0) { 69410275Ssam reply(550, "%s: %s.", path, sys_errlist[errno]); 69510275Ssam return; 69610275Ssam } 69710275Ssam ack("CWD"); 69810275Ssam } 69910275Ssam 70010303Ssam makedir(name) 70110275Ssam char *name; 70210275Ssam { 70310303Ssam struct stat st; 70410303Ssam int dochown = stat(name, &st) < 0; 70510275Ssam 70610275Ssam if (mkdir(name, 0777) < 0) { 70710275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 70810275Ssam return; 70910275Ssam } 71010303Ssam if (dochown) 71110303Ssam (void) chown(name, pw->pw_uid, -1); 71210275Ssam ack("MKDIR"); 71310275Ssam } 71410275Ssam 71510303Ssam removedir(name) 71610275Ssam char *name; 71710275Ssam { 71810275Ssam 71910275Ssam if (rmdir(name) < 0) { 72010275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 72110275Ssam return; 72210275Ssam } 72310275Ssam ack("RMDIR"); 72410275Ssam } 72510275Ssam 72610303Ssam pwd() 72710275Ssam { 72810303Ssam char path[MAXPATHLEN + 1]; 72910275Ssam 73010275Ssam if (getwd(path) == NULL) { 73110275Ssam reply(451, "%s.", path); 73210275Ssam return; 73310275Ssam } 73410275Ssam reply(251, "\"%s\" is current directory.", path); 73510275Ssam } 73610275Ssam 73710275Ssam char * 73810275Ssam renamefrom(name) 73910275Ssam char *name; 74010275Ssam { 74110275Ssam struct stat st; 74210275Ssam 74310275Ssam if (stat(name, &st) < 0) { 74410275Ssam reply(550, "%s: %s.", name, sys_errlist[errno]); 74510275Ssam return ((char *)0); 74610275Ssam } 74710303Ssam reply(350, "File exists, ready for destination name"); 74810275Ssam return (name); 74910275Ssam } 75010275Ssam 75110275Ssam renamecmd(from, to) 75210275Ssam char *from, *to; 75310275Ssam { 75410275Ssam 75510275Ssam if (rename(from, to) < 0) { 75610275Ssam reply(550, "rename: %s.", sys_errlist[errno]); 75710275Ssam return; 75810275Ssam } 75910275Ssam ack("RNTO"); 76010275Ssam } 76110275Ssam 76210275Ssam dolog(sin) 76310275Ssam struct sockaddr_in *sin; 76410275Ssam { 76510275Ssam struct hostent *hp = gethostbyaddr(&sin->sin_addr, 76610275Ssam sizeof (struct in_addr), AF_INET); 76710275Ssam time_t t; 76810275Ssam 76913247Ssam if (hp) { 77013247Ssam strncpy(remotehost, hp->h_name, sizeof (remotehost)); 77113247Ssam endhostent(); 77213247Ssam } else 77313247Ssam strncpy(remotehost, inet_ntoa(sin->sin_addr), 77413247Ssam sizeof (remotehost)); 77513247Ssam if (!logging) 77613247Ssam return; 77710275Ssam t = time(0); 77811757Ssam fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 77910275Ssam fflush(stderr); 78010275Ssam } 78110695Ssam 78213247Ssam #include <utmp.h> 78313247Ssam 78413247Ssam #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 78513247Ssam struct utmp utmp; 78613247Ssam 78710695Ssam /* 78813247Ssam * Record login in wtmp file. 78913247Ssam */ 79013247Ssam dologin(pw) 79113247Ssam struct passwd *pw; 79213247Ssam { 79313247Ssam char line[32]; 79413247Ssam 79513247Ssam if (wtmp >= 0) { 79613247Ssam /* hack, but must be unique and no tty line */ 79713247Ssam sprintf(line, "ftp%d", getpid()); 79813247Ssam SCPYN(utmp.ut_line, line); 79913247Ssam SCPYN(utmp.ut_name, pw->pw_name); 80013247Ssam SCPYN(utmp.ut_host, remotehost); 80113247Ssam utmp.ut_time = time(0); 80213247Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 80316760Slepreau if (!guest) { /* anon must hang on */ 80416760Slepreau (void) close(wtmp); 80516760Slepreau wtmp = -1; 80616760Slepreau } 80713247Ssam } 80813247Ssam } 80913247Ssam 81013247Ssam /* 81113247Ssam * Record logout in wtmp file 81213247Ssam * and exit with supplied status. 81313247Ssam */ 81413247Ssam dologout(status) 81513247Ssam int status; 81613247Ssam { 81716339Skarels 81817580Ssam if (logged_in) { 81917580Ssam (void) seteuid(0); 82017580Ssam if (wtmp < 0) 82117580Ssam wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 82217580Ssam if (wtmp >= 0) { 82317580Ssam SCPYN(utmp.ut_name, ""); 82417580Ssam SCPYN(utmp.ut_host, ""); 82517580Ssam utmp.ut_time = time(0); 82617580Ssam (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 82717580Ssam (void) close(wtmp); 82817580Ssam } 82913247Ssam } 83014436Ssam /* beware of flushing buffers after a SIGPIPE */ 83114436Ssam _exit(status); 83213247Ssam } 83313247Ssam 83413247Ssam /* 83510695Ssam * Special version of popen which avoids 83610695Ssam * call to shell. This insures noone may 83710695Ssam * create a pipe to a hidden program as a side 83810695Ssam * effect of a list or dir command. 83910695Ssam */ 84010695Ssam #define tst(a,b) (*mode == 'r'? (b) : (a)) 84110695Ssam #define RDR 0 84210695Ssam #define WTR 1 84310695Ssam static int popen_pid[5]; 84410695Ssam 84510695Ssam static char * 84610695Ssam nextarg(cpp) 84710695Ssam char *cpp; 84810695Ssam { 84910695Ssam register char *cp = cpp; 85010695Ssam 85110695Ssam if (cp == 0) 85210695Ssam return (cp); 85310695Ssam while (*cp && *cp != ' ' && *cp != '\t') 85410695Ssam cp++; 85510695Ssam if (*cp == ' ' || *cp == '\t') { 85610695Ssam *cp++ = '\0'; 85710695Ssam while (*cp == ' ' || *cp == '\t') 85810695Ssam cp++; 85910695Ssam } 86010695Ssam if (cp == cpp) 86110695Ssam return ((char *)0); 86210695Ssam return (cp); 86310695Ssam } 86410695Ssam 86510695Ssam FILE * 86610695Ssam popen(cmd, mode) 86710695Ssam char *cmd, *mode; 86810695Ssam { 86913211Sroot int p[2], ac, gac; 87010695Ssam register myside, hisside, pid; 87113211Sroot char *av[20], *gav[512]; 87210695Ssam register char *cp; 87310695Ssam 87410695Ssam if (pipe(p) < 0) 87510695Ssam return (NULL); 87610695Ssam cp = cmd, ac = 0; 87713211Sroot /* break up string into pieces */ 87810695Ssam do { 87910695Ssam av[ac++] = cp; 88010695Ssam cp = nextarg(cp); 88113211Sroot } while (cp && *cp && ac < 20); 88210695Ssam av[ac] = (char *)0; 88313211Sroot gav[0] = av[0]; 88413211Sroot /* glob each piece */ 88513211Sroot for (gac = ac = 1; av[ac] != NULL; ac++) { 88613211Sroot char **pop; 88722024Ssam extern char **glob(), **copyblk(); 88813211Sroot 88913211Sroot pop = glob(av[ac]); 89022024Ssam if (pop == (char **)NULL) { /* globbing failed */ 89122024Ssam char *vv[2]; 89222024Ssam 89322024Ssam vv[0] = av[ac]; 89422024Ssam vv[1] = 0; 89522024Ssam pop = copyblk(vv); 89613211Sroot } 89722024Ssam av[ac] = (char *)pop; /* save to free later */ 89822024Ssam while (*pop && gac < 512) 89922024Ssam gav[gac++] = *pop++; 90011757Ssam } 90113211Sroot gav[gac] = (char *)0; 90210695Ssam myside = tst(p[WTR], p[RDR]); 90310695Ssam hisside = tst(p[RDR], p[WTR]); 90410695Ssam if ((pid = fork()) == 0) { 90510695Ssam /* myside and hisside reverse roles in child */ 90610695Ssam close(myside); 90710695Ssam dup2(hisside, tst(0, 1)); 90810695Ssam close(hisside); 90913211Sroot execv(gav[0], gav); 91010695Ssam _exit(1); 91110695Ssam } 91213211Sroot for (ac = 1; av[ac] != NULL; ac++) 91313211Sroot blkfree((char **)av[ac]); 91410695Ssam if (pid == -1) 91510695Ssam return (NULL); 91610695Ssam popen_pid[myside] = pid; 91710695Ssam close(hisside); 91810695Ssam return (fdopen(myside, mode)); 91910695Ssam } 92010695Ssam 92110695Ssam pclose(ptr) 92210695Ssam FILE *ptr; 92310695Ssam { 92410695Ssam register f, r, (*hstat)(), (*istat)(), (*qstat)(); 92510695Ssam int status; 92610695Ssam 92710695Ssam f = fileno(ptr); 92810695Ssam fclose(ptr); 92910695Ssam istat = signal(SIGINT, SIG_IGN); 93010695Ssam qstat = signal(SIGQUIT, SIG_IGN); 93110695Ssam hstat = signal(SIGHUP, SIG_IGN); 93210695Ssam while ((r = wait(&status)) != popen_pid[f] && r != -1) 93310695Ssam ; 93410695Ssam if (r == -1) 93510695Ssam status = -1; 93610695Ssam signal(SIGINT, istat); 93710695Ssam signal(SIGQUIT, qstat); 93810695Ssam signal(SIGHUP, hstat); 93910695Ssam return (status); 94010695Ssam } 94110695Ssam 94210695Ssam /* 94310695Ssam * Check user requesting login priviledges. 94410695Ssam * Disallow anyone mentioned in the file FTPUSERS 94510695Ssam * to allow people such as uucp to be avoided. 94610695Ssam */ 94710695Ssam checkuser(name) 94810695Ssam register char *name; 94910695Ssam { 95010695Ssam char line[BUFSIZ], *index(); 95110695Ssam FILE *fd; 95210695Ssam int found = 0; 95310695Ssam 95410695Ssam fd = fopen(FTPUSERS, "r"); 95510695Ssam if (fd == NULL) 95610695Ssam return (1); 95710695Ssam while (fgets(line, sizeof (line), fd) != NULL) { 95810695Ssam register char *cp = index(line, '\n'); 95910695Ssam 96010695Ssam if (cp) 96110695Ssam *cp = '\0'; 96210695Ssam if (strcmp(line, name) == 0) { 96310695Ssam found++; 96410695Ssam break; 96510695Ssam } 96610695Ssam } 96710695Ssam fclose(fd); 96810695Ssam return (!found); 96910695Ssam } 970*26044Sminshall 971*26044Sminshall myoob() 972*26044Sminshall { 973*26044Sminshall char mark, *cp; 974*26044Sminshall int aflag = 0, count = 0, iacflag = 0, c; 975*26044Sminshall 976*26044Sminshall if (!transflag) { 977*26044Sminshall for (;;) { 978*26044Sminshall if (ioctl(fileno(stdin), SIOCATMARK, &mark) < 0) { 979*26044Sminshall perror("ioctl"); 980*26044Sminshall break; 981*26044Sminshall } 982*26044Sminshall if (mark) { 983*26044Sminshall break; 984*26044Sminshall } 985*26044Sminshall read(fileno(stdin), &mark, 1); 986*26044Sminshall c = 0377 & mark; 987*26044Sminshall } 988*26044Sminshall recv(fileno(stdin), &mark, 1, MSG_OOB); 989*26044Sminshall c = 0377 & mark; 990*26044Sminshall read(fileno(stdin), &mark, 1); 991*26044Sminshall return; 992*26044Sminshall } 993*26044Sminshall for (;;) { 994*26044Sminshall if (ioctl(fileno(stdin), SIOCATMARK, &mark) < 0) { 995*26044Sminshall perror("ioctl"); 996*26044Sminshall break; 997*26044Sminshall } 998*26044Sminshall if (mark) { 999*26044Sminshall break; 1000*26044Sminshall } 1001*26044Sminshall read(fileno(stdin), &mark, 1); 1002*26044Sminshall c = 0377 & mark; 1003*26044Sminshall if (c == IAC) { 1004*26044Sminshall aflag++; 1005*26044Sminshall } 1006*26044Sminshall else if (c == IP) { 1007*26044Sminshall aflag++; 1008*26044Sminshall } 1009*26044Sminshall } 1010*26044Sminshall recv(fileno(stdin), &mark, 1, MSG_OOB); 1011*26044Sminshall c = 0377 & mark; 1012*26044Sminshall if (c == IAC) { 1013*26044Sminshall aflag++; 1014*26044Sminshall } 1015*26044Sminshall read(fileno(stdin), &mark, 1); 1016*26044Sminshall c = 0377 & mark; 1017*26044Sminshall if (c == DM) { 1018*26044Sminshall aflag++; 1019*26044Sminshall } 1020*26044Sminshall if (aflag != 4) { 1021*26044Sminshall return; 1022*26044Sminshall } 1023*26044Sminshall cp = tmpline; 1024*26044Sminshall (void) getline(cp, 7, stdin); 1025*26044Sminshall upper(cp); 1026*26044Sminshall if (strcmp(cp, "ABOR\r\n")) { 1027*26044Sminshall return; 1028*26044Sminshall } 1029*26044Sminshall tmpline[0] = '\0'; 1030*26044Sminshall reply(426,"Transfer aborted. Data connection closed."); 1031*26044Sminshall reply(226,"Abort successful"); 1032*26044Sminshall longjmp(urgcatch, 1); 1033*26044Sminshall } 1034*26044Sminshall 1035*26044Sminshall passive() 1036*26044Sminshall { 1037*26044Sminshall int len; 1038*26044Sminshall struct sockaddr_in tmp; 1039*26044Sminshall register char *p, *a; 1040*26044Sminshall 1041*26044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 1042*26044Sminshall if (pdata < 0) { 1043*26044Sminshall reply(451, "Can't open passive connection"); 1044*26044Sminshall return; 1045*26044Sminshall } 1046*26044Sminshall tmp = ctrl_addr; 1047*26044Sminshall tmp.sin_port = 0; 1048*26044Sminshall seteuid(0); 1049*26044Sminshall if (bind(pdata, (char *) &tmp, sizeof(tmp), 0) < 0) { 1050*26044Sminshall seteuid(pw->pw_uid); 1051*26044Sminshall (void) close(pdata); 1052*26044Sminshall pdata = -1; 1053*26044Sminshall reply(451, "Can't open passive connection"); 1054*26044Sminshall return; 1055*26044Sminshall } 1056*26044Sminshall seteuid(pw->pw_uid); 1057*26044Sminshall len = sizeof(tmp); 1058*26044Sminshall if (getsockname(pdata, (char *) &tmp, &len) < 0) { 1059*26044Sminshall (void) close(pdata); 1060*26044Sminshall pdata = -1; 1061*26044Sminshall reply(451, "Can't open passive connection"); 1062*26044Sminshall return; 1063*26044Sminshall } 1064*26044Sminshall if (listen(pdata, 1) < 0) { 1065*26044Sminshall (void) close(pdata); 1066*26044Sminshall pdata = -1; 1067*26044Sminshall reply(451, "Can't open passive connection"); 1068*26044Sminshall return; 1069*26044Sminshall } 1070*26044Sminshall a = (char *) &tmp.sin_addr; 1071*26044Sminshall p = (char *) &tmp.sin_port; 1072*26044Sminshall 1073*26044Sminshall #define UC(b) (((int) b) & 0xff) 1074*26044Sminshall 1075*26044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 1076*26044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1077*26044Sminshall } 1078*26044Sminshall 1079*26044Sminshall char * 1080*26044Sminshall gunique(local) 1081*26044Sminshall char *local; 1082*26044Sminshall { 1083*26044Sminshall static char new[MAXPATHLEN]; 1084*26044Sminshall char *cp = rindex(local, '/'); 1085*26044Sminshall int d, count=0; 1086*26044Sminshall char ext = '1'; 1087*26044Sminshall 1088*26044Sminshall if (cp) { 1089*26044Sminshall *cp = '\0'; 1090*26044Sminshall } 1091*26044Sminshall d = access(cp ? local : ".", 2); 1092*26044Sminshall if (cp) { 1093*26044Sminshall *cp = '/'; 1094*26044Sminshall } 1095*26044Sminshall if (d < 0) { 1096*26044Sminshall perror(local); 1097*26044Sminshall return((char *) 0); 1098*26044Sminshall } 1099*26044Sminshall (void) strcpy(new, local); 1100*26044Sminshall cp = new + strlen(new); 1101*26044Sminshall *cp++ = '.'; 1102*26044Sminshall while (!d) { 1103*26044Sminshall if (++count == 100) { 1104*26044Sminshall reply(451, "Unique file name not cannot be created."); 1105*26044Sminshall return((char *) 0); 1106*26044Sminshall } 1107*26044Sminshall *cp++ = ext; 1108*26044Sminshall *cp = '\0'; 1109*26044Sminshall if (ext == '9') { 1110*26044Sminshall ext = '0'; 1111*26044Sminshall } 1112*26044Sminshall else { 1113*26044Sminshall ext++; 1114*26044Sminshall } 1115*26044Sminshall if ((d = access(new, 0)) < 0) { 1116*26044Sminshall break; 1117*26044Sminshall } 1118*26044Sminshall if (ext != '0') { 1119*26044Sminshall cp--; 1120*26044Sminshall } 1121*26044Sminshall else if (*(cp - 2) == '.') { 1122*26044Sminshall *(cp - 1) = '1'; 1123*26044Sminshall } 1124*26044Sminshall else { 1125*26044Sminshall *(cp - 2) = *(cp - 2) + 1; 1126*26044Sminshall cp--; 1127*26044Sminshall } 1128*26044Sminshall } 1129*26044Sminshall return(new); 1130*26044Sminshall } 1131