122499Sdist /* 2*36304Skarels * Copyright (c) 1985, 1988 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[] = 20*36304Skarels "@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\ 2122499Sdist All rights reserved.\n"; 2233738Sbostic #endif /* not lint */ 2310275Ssam 2422499Sdist #ifndef lint 25*36304Skarels static char sccsid[] = "@(#)ftpd.c 5.20 (Berkeley) 12/07/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[]; 62*36304Skarels extern int sys_nerr; 6310275Ssam extern char *crypt(); 6410275Ssam extern char version[]; 6510275Ssam extern char *home; /* pointer to home directory for glob */ 6636276Sbostic extern FILE *ftpd_popen(), *fopen(), *freopen(); 67*36304Skarels extern int ftpd_pclose(), fclose(); 6826044Sminshall extern char *getline(); 6926044Sminshall extern char cbuf[]; 7010275Ssam 7110275Ssam struct sockaddr_in ctrl_addr; 7210275Ssam struct sockaddr_in data_source; 7310275Ssam struct sockaddr_in data_dest; 7410275Ssam struct sockaddr_in his_addr; 7510275Ssam 7610275Ssam int data; 7726044Sminshall jmp_buf errcatch, urgcatch; 7810275Ssam int logged_in; 7910275Ssam struct passwd *pw; 8010275Ssam int debug; 8126493Sminshall int timeout = 900; /* timeout after 15 minutes of inactivity */ 8211757Ssam int logging; 8310275Ssam int guest; 8410275Ssam int type; 8510275Ssam int form; 8610275Ssam int stru; /* avoid C keyword */ 8710275Ssam int mode; 8810321Ssam int usedefault = 1; /* for data transfers */ 89*36304Skarels int pdata = -1; /* for passive mode */ 9026044Sminshall int transflag; 9126044Sminshall char tmpline[7]; 9236276Sbostic char hostname[MAXHOSTNAMELEN]; 9336276Sbostic char remotehost[MAXHOSTNAMELEN]; 9410275Ssam 9511653Ssam /* 9611653Ssam * Timeout intervals for retrying connections 9711653Ssam * to hosts that don't accept PORT cmds. This 9811653Ssam * is a kludge, but given the problems with TCP... 9911653Ssam */ 10011653Ssam #define SWAITMAX 90 /* wait at most 90 seconds */ 10111653Ssam #define SWAITINT 5 /* interval between retries */ 10211653Ssam 10311653Ssam int swaitmax = SWAITMAX; 10411653Ssam int swaitint = SWAITINT; 10511653Ssam 10610275Ssam int lostconn(); 10726044Sminshall int myoob(); 10810275Ssam FILE *getdatasock(), *dataconn(); 10910275Ssam 11010275Ssam main(argc, argv) 11110275Ssam int argc; 11210275Ssam char *argv[]; 11310275Ssam { 11427750Sminshall int addrlen, on = 1; 11510275Ssam char *cp; 11610275Ssam 11716339Skarels addrlen = sizeof (his_addr); 118*36304Skarels if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 11926493Sminshall syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 12010275Ssam exit(1); 12110275Ssam } 12216339Skarels addrlen = sizeof (ctrl_addr); 123*36304Skarels if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 12426493Sminshall syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 12516339Skarels exit(1); 12616339Skarels } 12716339Skarels data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 12810275Ssam debug = 0; 12926493Sminshall openlog("ftpd", LOG_PID, LOG_DAEMON); 13010275Ssam argc--, argv++; 13110275Ssam while (argc > 0 && *argv[0] == '-') { 13210275Ssam for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 13310275Ssam 13411653Ssam case 'v': 13511653Ssam debug = 1; 13611653Ssam break; 13711653Ssam 13810275Ssam case 'd': 13910275Ssam debug = 1; 14010275Ssam break; 14110275Ssam 14211757Ssam case 'l': 14311757Ssam logging = 1; 14411757Ssam break; 14511757Ssam 14611653Ssam case 't': 14711653Ssam timeout = atoi(++cp); 14811653Ssam goto nextopt; 14911653Ssam break; 15011653Ssam 15110275Ssam default: 15216339Skarels fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", 15316339Skarels *cp); 15410275Ssam break; 15510275Ssam } 15611653Ssam nextopt: 15710275Ssam argc--, argv++; 15810275Ssam } 15930944Scsvsj (void) freopen("/dev/null", "w", stderr); 16026493Sminshall (void) signal(SIGPIPE, lostconn); 16126493Sminshall (void) signal(SIGCHLD, SIG_IGN); 16235691Sbostic if ((int)signal(SIGURG, myoob) < 0) 16326493Sminshall syslog(LOG_ERR, "signal: %m"); 16435691Sbostic 16527750Sminshall /* handle urgent data inline */ 16636276Sbostic /* Sequent defines this, but it doesn't work */ 16727750Sminshall #ifdef SO_OOBINLINE 16836276Sbostic if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 16927750Sminshall syslog(LOG_ERR, "setsockopt: %m"); 17036276Sbostic #endif 171*36304Skarels if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 172*36304Skarels syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 17316760Slepreau dolog(&his_addr); 17416339Skarels /* do telnet option negotiation here */ 17516339Skarels /* 17616339Skarels * Set up default state 17716339Skarels */ 17816339Skarels data = -1; 17916339Skarels type = TYPE_A; 18016339Skarels form = FORM_N; 18116339Skarels stru = STRU_F; 18216339Skarels mode = MODE_S; 18326044Sminshall tmpline[0] = '\0'; 18426493Sminshall (void) gethostname(hostname, sizeof (hostname)); 18536276Sbostic reply(220, "%s FTP server (%s) ready.", hostname, version); 186*36304Skarels (void) setjmp(errcatch); 187*36304Skarels for (;;) 18826493Sminshall (void) yyparse(); 18910275Ssam } 19010419Ssam 19110275Ssam lostconn() 19210275Ssam { 19310275Ssam 19414089Ssam if (debug) 19526493Sminshall syslog(LOG_DEBUG, "lost connection"); 19614089Ssam dologout(-1); 19710275Ssam } 19810275Ssam 19935672Sbostic static char ttyline[20]; 20035672Sbostic 20136185Sbostic /* 20236185Sbostic * Helper function for sgetpwnam(). 20336185Sbostic */ 20436185Sbostic char * 20536185Sbostic sgetsave(s) 20636185Sbostic char *s; 20736185Sbostic { 20836185Sbostic char *malloc(); 20936185Sbostic char *new = malloc((unsigned) strlen(s) + 1); 21036185Sbostic 21136185Sbostic if (new == NULL) { 21236276Sbostic reply(553, "Local resource failure: malloc"); 21336185Sbostic dologout(1); 21436185Sbostic } 21536185Sbostic (void) strcpy(new, s); 21636185Sbostic return (new); 21736185Sbostic } 21836185Sbostic 21936185Sbostic /* 22036185Sbostic * Save the result of a getpwnam. Used for USER command, since 22136185Sbostic * the data returned must not be clobbered by any other command 22236185Sbostic * (e.g., globbing). 22336185Sbostic */ 22436185Sbostic struct passwd * 22536185Sbostic sgetpwnam(name) 22636185Sbostic char *name; 22736185Sbostic { 22836185Sbostic static struct passwd save; 22936185Sbostic register struct passwd *p; 23036185Sbostic char *sgetsave(); 23136185Sbostic 23236185Sbostic if ((p = getpwnam(name)) == NULL) 23336185Sbostic return (p); 23436185Sbostic if (save.pw_name) { 23536185Sbostic free(save.pw_name); 23636185Sbostic free(save.pw_passwd); 23736185Sbostic free(save.pw_comment); 23836185Sbostic free(save.pw_gecos); 23936185Sbostic free(save.pw_dir); 24036185Sbostic free(save.pw_shell); 24136185Sbostic } 24236185Sbostic save = *p; 24336185Sbostic save.pw_name = sgetsave(p->pw_name); 24436185Sbostic save.pw_passwd = sgetsave(p->pw_passwd); 24536185Sbostic save.pw_comment = sgetsave(p->pw_comment); 24636185Sbostic save.pw_gecos = sgetsave(p->pw_gecos); 24736185Sbostic save.pw_dir = sgetsave(p->pw_dir); 24836185Sbostic save.pw_shell = sgetsave(p->pw_shell); 24936185Sbostic return (&save); 25036185Sbostic } 25136185Sbostic 252*36304Skarels int login_attempts; /* number of failed login attempts */ 253*36304Skarels int askpasswd; /* had user command, ask for passwd */ 254*36304Skarels 255*36304Skarels /* 256*36304Skarels * USER command. 257*36304Skarels * Sets global passwd pointer pw if named account exists 258*36304Skarels * and is acceptable; sets askpasswd if a PASS command is 259*36304Skarels * expected. If logged in previously, need to reset state. 260*36304Skarels * If name is "ftp" or "anonymous" and ftp account exists, 261*36304Skarels * set guest and pw, then just return. 262*36304Skarels * If account doesn't exist, ask for passwd anyway. 263*36304Skarels * Otherwise, check user requesting login privileges. 264*36304Skarels * Disallow anyone who does not have a standard 265*36304Skarels * shell returned by getusershell() (/etc/shells). 266*36304Skarels * Disallow anyone mentioned in the file FTPUSERS 267*36304Skarels * to allow people such as root and uucp to be avoided. 268*36304Skarels */ 269*36304Skarels user(name) 270*36304Skarels char *name; 271*36304Skarels { 272*36304Skarels register char *cp; 273*36304Skarels FILE *fd; 274*36304Skarels char *shell; 275*36304Skarels char line[BUFSIZ], *index(), *getusershell(); 276*36304Skarels 277*36304Skarels if (logged_in) { 278*36304Skarels if (guest) { 279*36304Skarels reply(530, "Can't change user from guest login."); 280*36304Skarels return; 281*36304Skarels } 282*36304Skarels end_login(); 283*36304Skarels } 284*36304Skarels 285*36304Skarels guest = 0; 286*36304Skarels if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 287*36304Skarels if ((pw = sgetpwnam("ftp")) != NULL) { 288*36304Skarels guest = 1; 289*36304Skarels askpasswd = 1; 290*36304Skarels reply(331, "Guest login ok, send ident as password."); 291*36304Skarels } else 292*36304Skarels reply(530, "User %s unknown.", name); 293*36304Skarels return; 294*36304Skarels } 295*36304Skarels if (pw = sgetpwnam(name)) { 296*36304Skarels if ((shell = pw->pw_shell) == NULL || *shell == 0) 297*36304Skarels shell = "/bin/sh"; 298*36304Skarels while ((cp = getusershell()) != NULL) 299*36304Skarels if (strcmp(cp, shell) == 0) 300*36304Skarels break; 301*36304Skarels endusershell(); 302*36304Skarels if (cp == NULL) { 303*36304Skarels reply(530, "User %s access denied.", name); 304*36304Skarels pw = (struct passwd *) NULL; 305*36304Skarels return; 306*36304Skarels } 307*36304Skarels if ((fd = fopen(FTPUSERS, "r")) != NULL) { 308*36304Skarels while (fgets(line, sizeof (line), fd) != NULL) { 309*36304Skarels if ((cp = index(line, '\n')) != NULL) 310*36304Skarels *cp = '\0'; 311*36304Skarels if (strcmp(line, name) == 0) { 312*36304Skarels reply(530, "User %s access denied.", name); 313*36304Skarels pw = (struct passwd *) NULL; 314*36304Skarels return; 315*36304Skarels } 316*36304Skarels } 317*36304Skarels } 318*36304Skarels (void) fclose(fd); 319*36304Skarels } 320*36304Skarels reply(331, "Password required for %s.", name); 321*36304Skarels askpasswd = 1; 322*36304Skarels /* 323*36304Skarels * Delay before reading passwd after first failed 324*36304Skarels * attempt to slow down passwd-guessing programs. 325*36304Skarels */ 326*36304Skarels if (login_attempts) 327*36304Skarels sleep((unsigned) login_attempts); 328*36304Skarels } 329*36304Skarels 330*36304Skarels /* 331*36304Skarels * Terminate login as previous user, if any, resetting state; 332*36304Skarels * used when USER command is given or login fails. 333*36304Skarels */ 334*36304Skarels end_login() 335*36304Skarels { 336*36304Skarels 337*36304Skarels (void) seteuid((uid_t)0); 338*36304Skarels if (logged_in) 339*36304Skarels logwtmp(ttyline, "", ""); 340*36304Skarels pw = NULL; 341*36304Skarels logged_in = 0; 342*36304Skarels guest = 0; 343*36304Skarels } 344*36304Skarels 34510275Ssam pass(passwd) 34610275Ssam char *passwd; 34710275Ssam { 348*36304Skarels char *xpasswd, *salt; 34910275Ssam 350*36304Skarels if (logged_in || askpasswd == 0) { 35110275Ssam reply(503, "Login with USER first."); 35210275Ssam return; 35310275Ssam } 354*36304Skarels askpasswd = 0; 35510275Ssam if (!guest) { /* "ftp" is only account allowed no password */ 356*36304Skarels if (pw == NULL) 357*36304Skarels salt = "xx"; 358*36304Skarels else 359*36304Skarels salt = pw->pw_passwd; 360*36304Skarels xpasswd = crypt(passwd, salt); 36116760Slepreau /* The strcmp does not catch null passwords! */ 362*36304Skarels if (pw == NULL || *pw->pw_passwd == '\0' || 363*36304Skarels strcmp(xpasswd, pw->pw_passwd)) { 36410275Ssam reply(530, "Login incorrect."); 36510275Ssam pw = NULL; 366*36304Skarels if (login_attempts++ >= 5) { 367*36304Skarels syslog(LOG_ERR, 368*36304Skarels "repeated login failures from %s", 369*36304Skarels remotehost); 370*36304Skarels exit(0); 371*36304Skarels } 37210275Ssam return; 37310275Ssam } 37410275Ssam } 375*36304Skarels login_attempts = 0; /* this time successful */ 376*36304Skarels (void) setegid((gid_t)pw->pw_gid); 377*36304Skarels (void) initgroups(pw->pw_name, pw->pw_gid); 37810275Ssam if (chdir(pw->pw_dir)) { 37927106Smckusick reply(530, "User %s: can't change directory to %s.", 38010275Ssam pw->pw_name, pw->pw_dir); 38110303Ssam goto bad; 38210275Ssam } 38316033Sralph 38436192Sbostic /* open wtmp before chroot */ 38536192Sbostic (void)sprintf(ttyline, "ftp%d", getpid()); 38636192Sbostic logwtmp(ttyline, pw->pw_name, remotehost); 38736192Sbostic logged_in = 1; 38836192Sbostic 389*36304Skarels if (guest && chroot(pw->pw_dir) < 0) { 390*36304Skarels reply(550, "Can't set guest privileges."); 391*36304Skarels goto bad; 392*36304Skarels } 393*36304Skarels if (seteuid((uid_t)pw->pw_uid) < 0) { 394*36304Skarels reply(550, "Can't set uid."); 395*36304Skarels goto bad; 396*36304Skarels } 397*36304Skarels if (guest) 39836192Sbostic reply(230, "Guest login ok, access restrictions apply."); 399*36304Skarels else 40010275Ssam reply(230, "User %s logged in.", pw->pw_name); 40110303Ssam home = pw->pw_dir; /* home dir for globbing */ 40210303Ssam return; 40310303Ssam bad: 404*36304Skarels /* Forget all about it... */ 405*36304Skarels end_login(); 40610275Ssam } 40710275Ssam 40810275Ssam retrieve(cmd, name) 40910275Ssam char *cmd, *name; 41010275Ssam { 41110275Ssam FILE *fin, *dout; 41210275Ssam struct stat st; 41326044Sminshall int (*closefunc)(), tmp; 41410275Ssam 41510275Ssam if (cmd == 0) { 41610317Ssam #ifdef notdef 41710317Ssam /* no remote command execution -- it's a security hole */ 41811653Ssam if (*name == '|') 419*36304Skarels fin = ftpd_popen(name + 1, "r"), 420*36304Skarels closefunc = ftpd_pclose; 42110275Ssam else 42210317Ssam #endif 42310275Ssam fin = fopen(name, "r"), closefunc = fclose; 42410275Ssam } else { 42510275Ssam char line[BUFSIZ]; 42610275Ssam 42726493Sminshall (void) sprintf(line, cmd, name), name = line; 428*36304Skarels fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 42910275Ssam } 43010275Ssam if (fin == NULL) { 43113152Ssam if (errno != 0) 432*36304Skarels perror_reply(550, name); 43310275Ssam return; 43410275Ssam } 43510275Ssam st.st_size = 0; 43610275Ssam if (cmd == 0 && 43710275Ssam (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 43810275Ssam reply(550, "%s: not a plain file.", name); 43910275Ssam goto done; 44010275Ssam } 44110275Ssam dout = dataconn(name, st.st_size, "w"); 44210275Ssam if (dout == NULL) 44310275Ssam goto done; 44426044Sminshall if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { 445*36304Skarels perror_reply(550, name); 44626044Sminshall } 44726044Sminshall else if (tmp == 0) { 44810275Ssam reply(226, "Transfer complete."); 44926044Sminshall } 45026493Sminshall (void) fclose(dout); 45126044Sminshall data = -1; 45226044Sminshall pdata = -1; 45310275Ssam done: 45410275Ssam (*closefunc)(fin); 45510275Ssam } 45610275Ssam 457*36304Skarels store(name, mode, unique) 45810275Ssam char *name, *mode; 459*36304Skarels int unique; 46010275Ssam { 46110275Ssam FILE *fout, *din; 462*36304Skarels int (*closefunc)(), tmp; 463*36304Skarels char *gunique(); 46410275Ssam 46510317Ssam #ifdef notdef 46610317Ssam /* no remote command execution -- it's a security hole */ 46711653Ssam if (name[0] == '|') 468*36304Skarels fout = ftpd_popen(&name[1], "w"), closefunc = ftpd_pclose; 46910317Ssam else 47010317Ssam #endif 47110317Ssam { 47210303Ssam struct stat st; 47310303Ssam 474*36304Skarels if (unique && stat(name, &st) == 0 && 475*36304Skarels (name = gunique(name)) == NULL) 476*36304Skarels return; 477*36304Skarels fout = fopen(name, mode), closefunc = fclose; 47810303Ssam } 47910275Ssam if (fout == NULL) { 480*36304Skarels perror_reply(553, name); 48110275Ssam return; 48210275Ssam } 483*36304Skarels din = dataconn(name, (off_t)-1, "r"); 48410275Ssam if (din == NULL) 48510275Ssam goto done; 486*36304Skarels if ((tmp = receive_data(din, fout)) > 0) 487*36304Skarels perror_reply(552, name); 488*36304Skarels else if (tmp == 0) { 489*36304Skarels if (ferror(fout) > 0) 490*36304Skarels perror_reply(552, name); 491*36304Skarels else if (unique) 492*36304Skarels reply(226, "Transfer complete (unique file name:%s).", 493*36304Skarels name); 494*36304Skarels else 495*36304Skarels reply(226, "Transfer complete."); 49626044Sminshall } 49726493Sminshall (void) fclose(din); 49826044Sminshall data = -1; 49926044Sminshall pdata = -1; 50010275Ssam done: 50110275Ssam (*closefunc)(fout); 50210275Ssam } 50310275Ssam 50410275Ssam FILE * 50510275Ssam getdatasock(mode) 50610275Ssam char *mode; 50710275Ssam { 50817157Ssam int s, on = 1; 50910275Ssam 51010275Ssam if (data >= 0) 51110275Ssam return (fdopen(data, mode)); 51213247Ssam s = socket(AF_INET, SOCK_STREAM, 0); 51310602Ssam if (s < 0) 51410275Ssam return (NULL); 515*36304Skarels (void) seteuid((uid_t)0); 51626493Sminshall if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) 51710602Ssam goto bad; 51813152Ssam /* anchor socket to avoid multi-homing problems */ 51913152Ssam data_source.sin_family = AF_INET; 52013152Ssam data_source.sin_addr = ctrl_addr.sin_addr; 521*36304Skarels if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) 52210602Ssam goto bad; 523*36304Skarels (void) seteuid((uid_t)pw->pw_uid); 52410275Ssam return (fdopen(s, mode)); 52510602Ssam bad: 526*36304Skarels (void) seteuid((uid_t)pw->pw_uid); 52726493Sminshall (void) close(s); 52810602Ssam return (NULL); 52910275Ssam } 53010275Ssam 53110275Ssam FILE * 53210275Ssam dataconn(name, size, mode) 53310275Ssam char *name; 53411653Ssam off_t size; 53510275Ssam char *mode; 53610275Ssam { 53710275Ssam char sizebuf[32]; 53810275Ssam FILE *file; 53911653Ssam int retry = 0; 54010275Ssam 541*36304Skarels if (size != (off_t) -1) 54226493Sminshall (void) sprintf (sizebuf, " (%ld bytes)", size); 54310275Ssam else 54410275Ssam (void) strcpy(sizebuf, ""); 545*36304Skarels if (pdata >= 0) { 54626044Sminshall struct sockaddr_in from; 54726044Sminshall int s, fromlen = sizeof(from); 54826044Sminshall 549*36304Skarels s = accept(pdata, (struct sockaddr *)&from, &fromlen); 55026044Sminshall if (s < 0) { 55126044Sminshall reply(425, "Can't open data connection."); 55226044Sminshall (void) close(pdata); 55326044Sminshall pdata = -1; 55426044Sminshall return(NULL); 55526044Sminshall } 55626044Sminshall (void) close(pdata); 55726044Sminshall pdata = s; 55836235Skarels reply(150, "Opening %s mode data connection for %s%s.", 55936235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 56026044Sminshall return(fdopen(pdata, mode)); 56126044Sminshall } 56210275Ssam if (data >= 0) { 56310275Ssam reply(125, "Using existing data connection for %s%s.", 56410275Ssam name, sizebuf); 56510321Ssam usedefault = 1; 56610275Ssam return (fdopen(data, mode)); 56710275Ssam } 56810566Ssam if (usedefault) 56910422Ssam data_dest = his_addr; 57010422Ssam usedefault = 1; 57110275Ssam file = getdatasock(mode); 57210275Ssam if (file == NULL) { 57310275Ssam reply(425, "Can't create data socket (%s,%d): %s.", 57413247Ssam inet_ntoa(data_source.sin_addr), 57510275Ssam ntohs(data_source.sin_port), 576*36304Skarels errno < sys_nerr ? sys_errlist[errno] : "unknown error"); 57710275Ssam return (NULL); 57810275Ssam } 57910275Ssam data = fileno(file); 580*36304Skarels while (connect(data, (struct sockaddr *)&data_dest, 581*36304Skarels sizeof (data_dest)) < 0) { 58211653Ssam if (errno == EADDRINUSE && retry < swaitmax) { 58326493Sminshall sleep((unsigned) swaitint); 58411653Ssam retry += swaitint; 58511653Ssam continue; 58611653Ssam } 587*36304Skarels perror_reply(425, "Can't build data connection"); 58810275Ssam (void) fclose(file); 58910275Ssam data = -1; 59010275Ssam return (NULL); 59110275Ssam } 59236235Skarels reply(150, "Opening %s mode data connection for %s%s.", 59336235Skarels type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 59410275Ssam return (file); 59510275Ssam } 59610275Ssam 59710275Ssam /* 59810275Ssam * Tranfer the contents of "instr" to 59910275Ssam * "outstr" peer using the appropriate 60010275Ssam * encapulation of the date subject 60110275Ssam * to Mode, Structure, and Type. 60210275Ssam * 60310275Ssam * NB: Form isn't handled. 60410275Ssam */ 60510275Ssam send_data(instr, outstr) 60610275Ssam FILE *instr, *outstr; 60710275Ssam { 60810275Ssam register int c; 60910275Ssam int netfd, filefd, cnt; 61010275Ssam char buf[BUFSIZ]; 61110275Ssam 61226044Sminshall transflag++; 61326044Sminshall if (setjmp(urgcatch)) { 61426044Sminshall transflag = 0; 61526044Sminshall return(-1); 61626044Sminshall } 61710275Ssam switch (type) { 61810275Ssam 61910275Ssam case TYPE_A: 62010275Ssam while ((c = getc(instr)) != EOF) { 62111220Ssam if (c == '\n') { 62226044Sminshall if (ferror (outstr)) { 62326044Sminshall transflag = 0; 62411220Ssam return (1); 62526044Sminshall } 62627750Sminshall (void) putc('\r', outstr); 62711220Ssam } 62827750Sminshall (void) putc(c, outstr); 62926044Sminshall /* if (c == '\r') */ 63026044Sminshall /* putc ('\0', outstr); */ 63110275Ssam } 63226044Sminshall transflag = 0; 63326044Sminshall if (ferror (instr) || ferror (outstr)) { 63411220Ssam return (1); 63526044Sminshall } 63610275Ssam return (0); 63710275Ssam 63810275Ssam case TYPE_I: 63910275Ssam case TYPE_L: 64010275Ssam netfd = fileno(outstr); 64110275Ssam filefd = fileno(instr); 64210275Ssam 64326044Sminshall while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { 64426044Sminshall if (write(netfd, buf, cnt) < 0) { 64526044Sminshall transflag = 0; 64610275Ssam return (1); 64726044Sminshall } 64826044Sminshall } 64926044Sminshall transflag = 0; 65010275Ssam return (cnt < 0); 65110275Ssam } 65227106Smckusick reply(550, "Unimplemented TYPE %d in send_data", type); 65326044Sminshall transflag = 0; 65427106Smckusick return (-1); 65510275Ssam } 65610275Ssam 65710275Ssam /* 65810275Ssam * Transfer data from peer to 65910275Ssam * "outstr" using the appropriate 66010275Ssam * encapulation of the data subject 66110275Ssam * to Mode, Structure, and Type. 66210275Ssam * 66310275Ssam * N.B.: Form isn't handled. 66410275Ssam */ 66510275Ssam receive_data(instr, outstr) 66610275Ssam FILE *instr, *outstr; 66710275Ssam { 66810275Ssam register int c; 66911220Ssam int cnt; 67010275Ssam char buf[BUFSIZ]; 67110275Ssam 67210275Ssam 67326044Sminshall transflag++; 67426044Sminshall if (setjmp(urgcatch)) { 67526044Sminshall transflag = 0; 67626044Sminshall return(-1); 67726044Sminshall } 67810275Ssam switch (type) { 67910275Ssam 68010275Ssam case TYPE_I: 68110275Ssam case TYPE_L: 68226044Sminshall while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { 68326044Sminshall if (write(fileno(outstr), buf, cnt) < 0) { 68426044Sminshall transflag = 0; 68510275Ssam return (1); 68626044Sminshall } 68726044Sminshall } 68826044Sminshall transflag = 0; 68910275Ssam return (cnt < 0); 69010275Ssam 69110275Ssam case TYPE_E: 69227106Smckusick reply(553, "TYPE E not implemented."); 69326044Sminshall transflag = 0; 69427106Smckusick return (-1); 69510275Ssam 69610275Ssam case TYPE_A: 69710275Ssam while ((c = getc(instr)) != EOF) { 69827750Sminshall while (c == '\r') { 69926044Sminshall if (ferror (outstr)) { 70026044Sminshall transflag = 0; 70111220Ssam return (1); 70226044Sminshall } 70311220Ssam if ((c = getc(instr)) != '\n') 70427750Sminshall (void) putc ('\r', outstr); 70526044Sminshall /* if (c == '\0') */ 70626044Sminshall /* continue; */ 70710275Ssam } 70827750Sminshall (void) putc (c, outstr); 70910275Ssam } 71026044Sminshall transflag = 0; 71111220Ssam if (ferror (instr) || ferror (outstr)) 71211220Ssam return (1); 71310275Ssam return (0); 71410275Ssam } 71526044Sminshall transflag = 0; 71610275Ssam fatal("Unknown type in receive_data."); 71710275Ssam /*NOTREACHED*/ 71810275Ssam } 71910275Ssam 72010275Ssam fatal(s) 72110275Ssam char *s; 72210275Ssam { 72310275Ssam reply(451, "Error in server: %s\n", s); 72410275Ssam reply(221, "Closing connection due to server error."); 72513247Ssam dologout(0); 72610275Ssam } 72710275Ssam 728*36304Skarels /* VARARGS2 */ 72932110Smckusick reply(n, s, p0, p1, p2, p3, p4) 73010275Ssam int n; 73110275Ssam char *s; 73210275Ssam { 73310275Ssam 73410275Ssam printf("%d ", n); 73532110Smckusick printf(s, p0, p1, p2, p3, p4); 73610275Ssam printf("\r\n"); 73726493Sminshall (void) fflush(stdout); 73810275Ssam if (debug) { 73926493Sminshall syslog(LOG_DEBUG, "<--- %d ", n); 74032110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 74110275Ssam } 74210275Ssam } 74310275Ssam 744*36304Skarels /* VARARGS2 */ 74532110Smckusick lreply(n, s, p0, p1, p2, p3, p4) 74610275Ssam int n; 74710275Ssam char *s; 74810275Ssam { 74910275Ssam printf("%d-", n); 75032110Smckusick printf(s, p0, p1, p2, p3, p4); 75110275Ssam printf("\r\n"); 75226493Sminshall (void) fflush(stdout); 75310275Ssam if (debug) { 75426493Sminshall syslog(LOG_DEBUG, "<--- %d- ", n); 75532110Smckusick syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); 75610275Ssam } 75710275Ssam } 75810275Ssam 75910275Ssam ack(s) 76010275Ssam char *s; 76110275Ssam { 76227106Smckusick reply(250, "%s command successful.", s); 76310275Ssam } 76410275Ssam 76510275Ssam nack(s) 76610275Ssam char *s; 76710275Ssam { 76810275Ssam reply(502, "%s command not implemented.", s); 76910275Ssam } 77010275Ssam 771*36304Skarels /* ARGSUSED */ 77226493Sminshall yyerror(s) 77326493Sminshall char *s; 77410275Ssam { 77526044Sminshall char *cp; 77626044Sminshall 77726044Sminshall cp = index(cbuf,'\n'); 77826044Sminshall *cp = '\0'; 77926044Sminshall reply(500, "'%s': command not understood.",cbuf); 78010275Ssam } 78110275Ssam 78210275Ssam delete(name) 78310275Ssam char *name; 78410275Ssam { 78510275Ssam struct stat st; 78610275Ssam 78710275Ssam if (stat(name, &st) < 0) { 788*36304Skarels perror_reply(550, name); 78910275Ssam return; 79010275Ssam } 79110275Ssam if ((st.st_mode&S_IFMT) == S_IFDIR) { 79210275Ssam if (rmdir(name) < 0) { 793*36304Skarels perror_reply(550, name); 79410275Ssam return; 79510275Ssam } 79610275Ssam goto done; 79710275Ssam } 79810275Ssam if (unlink(name) < 0) { 799*36304Skarels perror_reply(550, name); 80010275Ssam return; 80110275Ssam } 80210275Ssam done: 80310275Ssam ack("DELE"); 80410275Ssam } 80510275Ssam 80610275Ssam cwd(path) 80710275Ssam char *path; 80810275Ssam { 80910275Ssam 81010275Ssam if (chdir(path) < 0) { 811*36304Skarels perror_reply(550, path); 81210275Ssam return; 81310275Ssam } 81410275Ssam ack("CWD"); 81510275Ssam } 81610275Ssam 81710303Ssam makedir(name) 81810275Ssam char *name; 81910275Ssam { 82036276Sbostic if (mkdir(name, 0777) < 0) 821*36304Skarels perror_reply(550, name); 82236276Sbostic else 82336276Sbostic reply(257, "MKD command successful."); 82410275Ssam } 82510275Ssam 82610303Ssam removedir(name) 82710275Ssam char *name; 82810275Ssam { 82910275Ssam 83010275Ssam if (rmdir(name) < 0) { 831*36304Skarels perror_reply(550, name); 83210275Ssam return; 83310275Ssam } 83427106Smckusick ack("RMD"); 83510275Ssam } 83610275Ssam 83710303Ssam pwd() 83810275Ssam { 83910303Ssam char path[MAXPATHLEN + 1]; 840*36304Skarels extern char *getwd(); 84110275Ssam 842*36304Skarels if (getwd(path) == (char *)NULL) { 84327106Smckusick reply(550, "%s.", path); 84410275Ssam return; 84510275Ssam } 84627106Smckusick reply(257, "\"%s\" is current directory.", path); 84710275Ssam } 84810275Ssam 84910275Ssam char * 85010275Ssam renamefrom(name) 85110275Ssam char *name; 85210275Ssam { 85310275Ssam struct stat st; 85410275Ssam 85510275Ssam if (stat(name, &st) < 0) { 856*36304Skarels perror_reply(550, name); 85710275Ssam return ((char *)0); 85810275Ssam } 85910303Ssam reply(350, "File exists, ready for destination name"); 86010275Ssam return (name); 86110275Ssam } 86210275Ssam 86310275Ssam renamecmd(from, to) 86410275Ssam char *from, *to; 86510275Ssam { 86610275Ssam 86710275Ssam if (rename(from, to) < 0) { 868*36304Skarels perror_reply(550, "rename"); 86910275Ssam return; 87010275Ssam } 87110275Ssam ack("RNTO"); 87210275Ssam } 87310275Ssam 87410275Ssam dolog(sin) 87510275Ssam struct sockaddr_in *sin; 87610275Ssam { 877*36304Skarels struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 87810275Ssam sizeof (struct in_addr), AF_INET); 879*36304Skarels time_t t, time(); 88026493Sminshall extern char *ctime(); 88110275Ssam 882*36304Skarels if (hp) 88326493Sminshall (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); 884*36304Skarels else 88526493Sminshall (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 88613247Ssam sizeof (remotehost)); 88713247Ssam if (!logging) 88813247Ssam return; 88926493Sminshall t = time((time_t *) 0); 890*36304Skarels syslog(LOG_INFO, "connection from %s at %s", 891*36304Skarels remotehost, ctime(&t)); 89210275Ssam } 89310695Ssam 89410695Ssam /* 89513247Ssam * Record logout in wtmp file 89613247Ssam * and exit with supplied status. 89713247Ssam */ 89813247Ssam dologout(status) 89913247Ssam int status; 90013247Ssam { 90117580Ssam if (logged_in) { 902*36304Skarels (void) seteuid((uid_t)0); 90335672Sbostic logwtmp(ttyline, "", ""); 90413247Ssam } 90514436Ssam /* beware of flushing buffers after a SIGPIPE */ 90614436Ssam _exit(status); 90713247Ssam } 90813247Ssam 90926044Sminshall myoob() 91026044Sminshall { 91127750Sminshall char *cp; 91226044Sminshall 91327750Sminshall /* only process if transfer occurring */ 914*36304Skarels if (!transflag) 91526044Sminshall return; 91627750Sminshall cp = tmpline; 91727750Sminshall if (getline(cp, 7, stdin) == NULL) { 918*36304Skarels reply(221, "You could at least say goodbye."); 91927750Sminshall dologout(0); 92026044Sminshall } 92126044Sminshall upper(cp); 92226227Ssam if (strcmp(cp, "ABOR\r\n")) 92326044Sminshall return; 92426044Sminshall tmpline[0] = '\0'; 92526044Sminshall reply(426,"Transfer aborted. Data connection closed."); 92626044Sminshall reply(226,"Abort successful"); 92726044Sminshall longjmp(urgcatch, 1); 92826044Sminshall } 92926044Sminshall 93027106Smckusick /* 93127106Smckusick * Note: The 530 reply codes could be 4xx codes, except nothing is 93227106Smckusick * given in the state tables except 421 which implies an exit. (RFC959) 93327106Smckusick */ 93426044Sminshall passive() 93526044Sminshall { 93626044Sminshall int len; 93726044Sminshall struct sockaddr_in tmp; 93826044Sminshall register char *p, *a; 93926044Sminshall 94026044Sminshall pdata = socket(AF_INET, SOCK_STREAM, 0); 94126044Sminshall if (pdata < 0) { 94227106Smckusick reply(530, "Can't open passive connection"); 94326044Sminshall return; 94426044Sminshall } 94526044Sminshall tmp = ctrl_addr; 94626044Sminshall tmp.sin_port = 0; 947*36304Skarels (void) seteuid((uid_t)0); 94826493Sminshall if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { 949*36304Skarels (void) seteuid((uid_t)pw->pw_uid); 95026044Sminshall (void) close(pdata); 95126044Sminshall pdata = -1; 95227106Smckusick reply(530, "Can't open passive connection"); 95326044Sminshall return; 95426044Sminshall } 955*36304Skarels (void) seteuid((uid_t)pw->pw_uid); 95626044Sminshall len = sizeof(tmp); 957*36304Skarels if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) { 95826044Sminshall (void) close(pdata); 95926044Sminshall pdata = -1; 96027106Smckusick reply(530, "Can't open passive connection"); 96126044Sminshall return; 96226044Sminshall } 96326044Sminshall if (listen(pdata, 1) < 0) { 96426044Sminshall (void) close(pdata); 96526044Sminshall pdata = -1; 96627106Smckusick reply(530, "Can't open passive connection"); 96726044Sminshall return; 96826044Sminshall } 96926044Sminshall a = (char *) &tmp.sin_addr; 97026044Sminshall p = (char *) &tmp.sin_port; 97126044Sminshall 97226044Sminshall #define UC(b) (((int) b) & 0xff) 97326044Sminshall 97426044Sminshall reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 97526044Sminshall UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 97626044Sminshall } 97726044Sminshall 978*36304Skarels /* 979*36304Skarels * Generate unique name for file with basename "local". 980*36304Skarels * The file named "local" is already known to exist. 981*36304Skarels * Generates failure reply on error. 982*36304Skarels */ 98326044Sminshall char * 98426044Sminshall gunique(local) 98526044Sminshall char *local; 98626044Sminshall { 98726044Sminshall static char new[MAXPATHLEN]; 988*36304Skarels struct stat st; 98926044Sminshall char *cp = rindex(local, '/'); 99026044Sminshall int d, count=0; 99126044Sminshall 992*36304Skarels if (cp) 99326044Sminshall *cp = '\0'; 994*36304Skarels d = stat(cp ? local : ".", &st); 995*36304Skarels if (cp) 99626044Sminshall *cp = '/'; 99726044Sminshall if (d < 0) { 998*36304Skarels perror_reply(553, local); 99926044Sminshall return((char *) 0); 100026044Sminshall } 100126044Sminshall (void) strcpy(new, local); 100226044Sminshall cp = new + strlen(new); 100326044Sminshall *cp++ = '.'; 1004*36304Skarels for (count = 1; count < 100; count++) { 1005*36304Skarels (void) sprintf(cp, "%d", count); 1006*36304Skarels if (stat(new, &st) < 0) 1007*36304Skarels return(new); 100826044Sminshall } 1009*36304Skarels reply(452, "Unique file name cannot be created."); 1010*36304Skarels return((char *) 0); 101126044Sminshall } 1012*36304Skarels 1013*36304Skarels /* 1014*36304Skarels * Format and send reply containing system error number. 1015*36304Skarels */ 1016*36304Skarels perror_reply(code, string) 1017*36304Skarels int code; 1018*36304Skarels char *string; 1019*36304Skarels { 1020*36304Skarels 1021*36304Skarels if (errno < sys_nerr) 1022*36304Skarels reply(code, "%s: %s.", string, sys_errlist[errno]); 1023*36304Skarels else 1024*36304Skarels reply(code, "%s: unknown error %d.", string, errno); 1025*36304Skarels } 1026