xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 17580)
110275Ssam #ifndef lint
2*17580Ssam static	char sccsid[] = "@(#)ftpd.c	4.33 (Berkeley) 12/23/84";
310275Ssam #endif
410275Ssam 
510275Ssam /*
610275Ssam  * FTP server.
710275Ssam  */
810303Ssam #include <sys/param.h>
910275Ssam #include <sys/stat.h>
1010275Ssam #include <sys/ioctl.h>
1110275Ssam #include <sys/socket.h>
1213247Ssam #include <sys/file.h>
1313595Ssam #include <sys/wait.h>
1410275Ssam 
1510275Ssam #include <netinet/in.h>
1610275Ssam 
1713034Ssam #include <arpa/ftp.h>
1813211Sroot #include <arpa/inet.h>
1913034Ssam 
2010275Ssam #include <stdio.h>
2110275Ssam #include <signal.h>
2210275Ssam #include <pwd.h>
2310275Ssam #include <setjmp.h>
2410275Ssam #include <netdb.h>
2510423Ssam #include <errno.h>
2610275Ssam 
2710695Ssam /*
2810695Ssam  * File containing login names
2910695Ssam  * NOT to be used on this machine.
3010695Ssam  * Commonly used to disallow uucp.
3110695Ssam  */
3210695Ssam #define	FTPUSERS	"/etc/ftpusers"
3310695Ssam 
3410275Ssam extern	int errno;
3510275Ssam extern	char *sys_errlist[];
3610275Ssam extern	char *crypt();
3710275Ssam extern	char version[];
3810275Ssam extern	char *home;		/* pointer to home directory for glob */
3910275Ssam extern	FILE *popen(), *fopen();
4010275Ssam extern	int pclose(), fclose();
4110275Ssam 
4210275Ssam struct	sockaddr_in ctrl_addr;
4310275Ssam struct	sockaddr_in data_source;
4410275Ssam struct	sockaddr_in data_dest;
4510275Ssam struct	sockaddr_in his_addr;
4610275Ssam 
4710275Ssam struct	hostent *hp;
4810275Ssam 
4910275Ssam int	data;
5010275Ssam jmp_buf	errcatch;
5110275Ssam int	logged_in;
5210275Ssam struct	passwd *pw;
5310275Ssam int	debug;
5411653Ssam int	timeout;
5511757Ssam int	logging;
5610275Ssam int	guest;
5716033Sralph int	wtmp;
5810275Ssam int	type;
5910275Ssam int	form;
6010275Ssam int	stru;			/* avoid C keyword */
6110275Ssam int	mode;
6210321Ssam int	usedefault = 1;		/* for data transfers */
6310275Ssam char	hostname[32];
6413247Ssam char	remotehost[32];
6510275Ssam 
6611653Ssam /*
6711653Ssam  * Timeout intervals for retrying connections
6811653Ssam  * to hosts that don't accept PORT cmds.  This
6911653Ssam  * is a kludge, but given the problems with TCP...
7011653Ssam  */
7111653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
7211653Ssam #define	SWAITINT	5	/* interval between retries */
7311653Ssam 
7411653Ssam int	swaitmax = SWAITMAX;
7511653Ssam int	swaitint = SWAITINT;
7611653Ssam 
7710275Ssam int	lostconn();
7810419Ssam int	reapchild();
7910275Ssam FILE	*getdatasock(), *dataconn();
8010275Ssam 
8110275Ssam main(argc, argv)
8210275Ssam 	int argc;
8310275Ssam 	char *argv[];
8410275Ssam {
8516339Skarels 	int options = 0, addrlen;
8610275Ssam 	char *cp;
8710275Ssam 
8816339Skarels 	addrlen = sizeof (his_addr);
8916339Skarels 	if (getpeername(0, &his_addr, &addrlen) < 0) {
9016339Skarels 		fprintf(stderr, "%s: ", argv[0]);
9116339Skarels 		perror("getpeername");
9210275Ssam 		exit(1);
9310275Ssam 	}
9416339Skarels 	addrlen = sizeof (ctrl_addr);
9516339Skarels 	if (getsockname(0, &ctrl_addr, &addrlen) < 0) {
9616339Skarels 		fprintf(stderr, "%s: ", argv[0]);
9716339Skarels 		perror("getsockname");
9816339Skarels 		exit(1);
9916339Skarels 	}
10016339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
10110275Ssam 	debug = 0;
10210275Ssam 	argc--, argv++;
10310275Ssam 	while (argc > 0 && *argv[0] == '-') {
10410275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
10510275Ssam 
10611653Ssam 		case 'v':
10711653Ssam 			debug = 1;
10811653Ssam 			break;
10911653Ssam 
11010275Ssam 		case 'd':
11110275Ssam 			debug = 1;
11210275Ssam 			options |= SO_DEBUG;
11310275Ssam 			break;
11410275Ssam 
11511757Ssam 		case 'l':
11611757Ssam 			logging = 1;
11711757Ssam 			break;
11811757Ssam 
11911653Ssam 		case 't':
12011653Ssam 			timeout = atoi(++cp);
12111653Ssam 			goto nextopt;
12211653Ssam 			break;
12311653Ssam 
12410275Ssam 		default:
12516339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
12616339Skarels 			     *cp);
12710275Ssam 			break;
12810275Ssam 		}
12911653Ssam nextopt:
13010275Ssam 		argc--, argv++;
13110275Ssam 	}
13216339Skarels 	signal(SIGPIPE, lostconn);
13316339Skarels 	signal(SIGCHLD, SIG_IGN);
13416760Slepreau 	dolog(&his_addr);
13516339Skarels 	/* do telnet option negotiation here */
13616339Skarels 	/*
13716339Skarels 	 * Set up default state
13816339Skarels 	 */
13916339Skarels 	logged_in = 0;
14016339Skarels 	data = -1;
14116339Skarels 	type = TYPE_A;
14216339Skarels 	form = FORM_N;
14316339Skarels 	stru = STRU_F;
14416339Skarels 	mode = MODE_S;
14516339Skarels 	gethostname(hostname, sizeof (hostname));
14616339Skarels 	reply(220, "%s FTP server (%s) ready.",
14716339Skarels 		hostname, version);
14810275Ssam 	for (;;) {
14916339Skarels 		setjmp(errcatch);
15016339Skarels 		yyparse();
15110275Ssam 	}
15210275Ssam }
15310275Ssam 
15410419Ssam reapchild()
15510419Ssam {
15610419Ssam 	union wait status;
15710419Ssam 
15810419Ssam 	while (wait3(&status, WNOHANG, 0) > 0)
15910419Ssam 		;
16010419Ssam }
16110419Ssam 
16210275Ssam lostconn()
16310275Ssam {
16410275Ssam 
16514089Ssam 	if (debug)
16614089Ssam 		fprintf(stderr, "Lost connection.\n");
16714089Ssam 	dologout(-1);
16810275Ssam }
16910275Ssam 
17010275Ssam pass(passwd)
17110275Ssam 	char *passwd;
17210275Ssam {
17310303Ssam 	char *xpasswd, *savestr();
17410303Ssam 	static struct passwd save;
17510275Ssam 
17610275Ssam 	if (logged_in || pw == NULL) {
17710275Ssam 		reply(503, "Login with USER first.");
17810275Ssam 		return;
17910275Ssam 	}
18010275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
18110275Ssam 		xpasswd = crypt(passwd, pw->pw_passwd);
18216760Slepreau 		/* The strcmp does not catch null passwords! */
18316760Slepreau 		if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
18410275Ssam 			reply(530, "Login incorrect.");
18510275Ssam 			pw = NULL;
18610275Ssam 			return;
18710275Ssam 		}
18810275Ssam 	}
18910303Ssam 	setegid(pw->pw_gid);
19010275Ssam 	initgroups(pw->pw_name, pw->pw_gid);
19110275Ssam 	if (chdir(pw->pw_dir)) {
19210275Ssam 		reply(550, "User %s: can't change directory to $s.",
19310275Ssam 			pw->pw_name, pw->pw_dir);
19410303Ssam 		goto bad;
19510275Ssam 	}
19616033Sralph 
19716760Slepreau 	/* grab wtmp before chroot */
19816760Slepreau 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
19910303Ssam 	if (guest && chroot(pw->pw_dir) < 0) {
20010275Ssam 		reply(550, "Can't set guest privileges.");
20116760Slepreau 		if (wtmp >= 0) {
20216760Slepreau 			(void) close(wtmp);
20316760Slepreau 			wtmp = -1;
20416760Slepreau 		}
20510303Ssam 		goto bad;
20610275Ssam 	}
20710275Ssam 	if (!guest)
20810275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
20910275Ssam 	else
21010275Ssam 		reply(230, "Guest login ok, access restrictions apply.");
21110275Ssam 	logged_in = 1;
21213247Ssam 	dologin(pw);
21310303Ssam 	seteuid(pw->pw_uid);
21410303Ssam 	/*
21510303Ssam 	 * Save everything so globbing doesn't
21610303Ssam 	 * clobber the fields.
21710303Ssam 	 */
21810303Ssam 	save = *pw;
21910303Ssam 	save.pw_name = savestr(pw->pw_name);
22010303Ssam 	save.pw_passwd = savestr(pw->pw_passwd);
22110303Ssam 	save.pw_comment = savestr(pw->pw_comment);
22210303Ssam 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
22310303Ssam 	save.pw_dir = savestr(pw->pw_dir);
22410303Ssam 	save.pw_shell = savestr(pw->pw_shell);
22510303Ssam 	pw = &save;
22610303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
22710303Ssam 	return;
22810303Ssam bad:
22910303Ssam 	seteuid(0);
23010303Ssam 	pw = NULL;
23110275Ssam }
23210275Ssam 
23310303Ssam char *
23410303Ssam savestr(s)
23510303Ssam 	char *s;
23610303Ssam {
23710303Ssam 	char *malloc();
23810303Ssam 	char *new = malloc(strlen(s) + 1);
23910303Ssam 
24010303Ssam 	if (new != NULL)
24110303Ssam 		strcpy(new, s);
24211347Ssam 	return (new);
24310303Ssam }
24410303Ssam 
24510275Ssam retrieve(cmd, name)
24610275Ssam 	char *cmd, *name;
24710275Ssam {
24810275Ssam 	FILE *fin, *dout;
24910275Ssam 	struct stat st;
25010275Ssam 	int (*closefunc)();
25110275Ssam 
25210275Ssam 	if (cmd == 0) {
25310317Ssam #ifdef notdef
25410317Ssam 		/* no remote command execution -- it's a security hole */
25511653Ssam 		if (*name == '|')
25610275Ssam 			fin = popen(name + 1, "r"), closefunc = pclose;
25710275Ssam 		else
25810317Ssam #endif
25910275Ssam 			fin = fopen(name, "r"), closefunc = fclose;
26010275Ssam 	} else {
26110275Ssam 		char line[BUFSIZ];
26210275Ssam 
26310422Ssam 		sprintf(line, cmd, name), name = line;
26410275Ssam 		fin = popen(line, "r"), closefunc = pclose;
26510275Ssam 	}
26610275Ssam 	if (fin == NULL) {
26713152Ssam 		if (errno != 0)
26813152Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
26910275Ssam 		return;
27010275Ssam 	}
27110275Ssam 	st.st_size = 0;
27210275Ssam 	if (cmd == 0 &&
27310275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
27410275Ssam 		reply(550, "%s: not a plain file.", name);
27510275Ssam 		goto done;
27610275Ssam 	}
27710275Ssam 	dout = dataconn(name, st.st_size, "w");
27810275Ssam 	if (dout == NULL)
27910275Ssam 		goto done;
28010303Ssam 	if (send_data(fin, dout) || ferror(dout))
28110275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
28210275Ssam 	else
28310275Ssam 		reply(226, "Transfer complete.");
28410303Ssam 	fclose(dout), data = -1;
28510275Ssam done:
28610275Ssam 	(*closefunc)(fin);
28710275Ssam }
28810275Ssam 
28910275Ssam store(name, mode)
29010275Ssam 	char *name, *mode;
29110275Ssam {
29210275Ssam 	FILE *fout, *din;
29310303Ssam 	int (*closefunc)(), dochown = 0;
29410275Ssam 
29510317Ssam #ifdef notdef
29610317Ssam 	/* no remote command execution -- it's a security hole */
29711653Ssam 	if (name[0] == '|')
29810275Ssam 		fout = popen(&name[1], "w"), closefunc = pclose;
29910317Ssam 	else
30010317Ssam #endif
30110317Ssam 	{
30210303Ssam 		struct stat st;
30310303Ssam 
30410303Ssam 		if (stat(name, &st) < 0)
30510303Ssam 			dochown++;
30610275Ssam 		fout = fopen(name, mode), closefunc = fclose;
30710303Ssam 	}
30810275Ssam 	if (fout == NULL) {
30910275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
31010275Ssam 		return;
31110275Ssam 	}
31211653Ssam 	din = dataconn(name, (off_t)-1, "r");
31310275Ssam 	if (din == NULL)
31410275Ssam 		goto done;
31510303Ssam 	if (receive_data(din, fout) || ferror(fout))
31610275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
31710275Ssam 	else
31810275Ssam 		reply(226, "Transfer complete.");
31910275Ssam 	fclose(din), data = -1;
32010275Ssam done:
32110303Ssam 	if (dochown)
32210303Ssam 		(void) chown(name, pw->pw_uid, -1);
32310275Ssam 	(*closefunc)(fout);
32410275Ssam }
32510275Ssam 
32610275Ssam FILE *
32710275Ssam getdatasock(mode)
32810275Ssam 	char *mode;
32910275Ssam {
33017157Ssam 	int s, on = 1;
33110275Ssam 
33210275Ssam 	if (data >= 0)
33310275Ssam 		return (fdopen(data, mode));
33413247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
33510602Ssam 	if (s < 0)
33610275Ssam 		return (NULL);
33710275Ssam 	seteuid(0);
33817157Ssam 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
33910602Ssam 		goto bad;
34013152Ssam 	/* anchor socket to avoid multi-homing problems */
34113152Ssam 	data_source.sin_family = AF_INET;
34213152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
34310602Ssam 	if (bind(s, &data_source, sizeof (data_source), 0) < 0)
34410602Ssam 		goto bad;
34510311Ssam 	seteuid(pw->pw_uid);
34610275Ssam 	return (fdopen(s, mode));
34710602Ssam bad:
34810602Ssam 	seteuid(pw->pw_uid);
34910602Ssam 	close(s);
35010602Ssam 	return (NULL);
35110275Ssam }
35210275Ssam 
35310275Ssam FILE *
35410275Ssam dataconn(name, size, mode)
35510275Ssam 	char *name;
35611653Ssam 	off_t size;
35710275Ssam 	char *mode;
35810275Ssam {
35910275Ssam 	char sizebuf[32];
36010275Ssam 	FILE *file;
36111653Ssam 	int retry = 0;
36210275Ssam 
36310275Ssam 	if (size >= 0)
36411653Ssam 		sprintf (sizebuf, " (%ld bytes)", size);
36510275Ssam 	else
36610275Ssam 		(void) strcpy(sizebuf, "");
36710275Ssam 	if (data >= 0) {
36810275Ssam 		reply(125, "Using existing data connection for %s%s.",
36910275Ssam 		    name, sizebuf);
37010321Ssam 		usedefault = 1;
37110275Ssam 		return (fdopen(data, mode));
37210275Ssam 	}
37310566Ssam 	if (usedefault)
37410422Ssam 		data_dest = his_addr;
37510422Ssam 	usedefault = 1;
37610275Ssam 	file = getdatasock(mode);
37710275Ssam 	if (file == NULL) {
37810275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
37913247Ssam 		    inet_ntoa(data_source.sin_addr),
38010275Ssam 		    ntohs(data_source.sin_port),
38110275Ssam 		    sys_errlist[errno]);
38210275Ssam 		return (NULL);
38310275Ssam 	}
38410602Ssam 	reply(150, "Opening data connection for %s (%s,%d)%s.",
38513247Ssam 	    name, inet_ntoa(data_dest.sin_addr.s_addr),
38610602Ssam 	    ntohs(data_dest.sin_port), sizebuf);
38710275Ssam 	data = fileno(file);
38811653Ssam 	while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
38911653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
39011653Ssam 			sleep(swaitint);
39111653Ssam 			retry += swaitint;
39211653Ssam 			continue;
39311653Ssam 		}
39410275Ssam 		reply(425, "Can't build data connection: %s.",
39510275Ssam 		    sys_errlist[errno]);
39610275Ssam 		(void) fclose(file);
39710275Ssam 		data = -1;
39810275Ssam 		return (NULL);
39910275Ssam 	}
40010275Ssam 	return (file);
40110275Ssam }
40210275Ssam 
40310275Ssam /*
40410275Ssam  * Tranfer the contents of "instr" to
40510275Ssam  * "outstr" peer using the appropriate
40610275Ssam  * encapulation of the date subject
40710275Ssam  * to Mode, Structure, and Type.
40810275Ssam  *
40910275Ssam  * NB: Form isn't handled.
41010275Ssam  */
41110275Ssam send_data(instr, outstr)
41210275Ssam 	FILE *instr, *outstr;
41310275Ssam {
41410275Ssam 	register int c;
41510275Ssam 	int netfd, filefd, cnt;
41610275Ssam 	char buf[BUFSIZ];
41710275Ssam 
41810275Ssam 	switch (type) {
41910275Ssam 
42010275Ssam 	case TYPE_A:
42110275Ssam 		while ((c = getc(instr)) != EOF) {
42211220Ssam 			if (c == '\n') {
42311220Ssam 				if (ferror (outstr))
42411220Ssam 					return (1);
42510275Ssam 				putc('\r', outstr);
42611220Ssam 			}
42711220Ssam 			putc(c, outstr);
42811220Ssam 			if (c == '\r')
42911220Ssam 				putc ('\0', outstr);
43010275Ssam 		}
43111220Ssam 		if (ferror (instr) || ferror (outstr))
43211220Ssam 			return (1);
43310275Ssam 		return (0);
43410275Ssam 
43510275Ssam 	case TYPE_I:
43610275Ssam 	case TYPE_L:
43710275Ssam 		netfd = fileno(outstr);
43810275Ssam 		filefd = fileno(instr);
43910275Ssam 
44010303Ssam 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
44110275Ssam 			if (write(netfd, buf, cnt) < 0)
44210275Ssam 				return (1);
44310275Ssam 		return (cnt < 0);
44410275Ssam 	}
44510275Ssam 	reply(504,"Unimplemented TYPE %d in send_data", type);
44610275Ssam 	return (1);
44710275Ssam }
44810275Ssam 
44910275Ssam /*
45010275Ssam  * Transfer data from peer to
45110275Ssam  * "outstr" using the appropriate
45210275Ssam  * encapulation of the data subject
45310275Ssam  * to Mode, Structure, and Type.
45410275Ssam  *
45510275Ssam  * N.B.: Form isn't handled.
45610275Ssam  */
45710275Ssam receive_data(instr, outstr)
45810275Ssam 	FILE *instr, *outstr;
45910275Ssam {
46010275Ssam 	register int c;
46111220Ssam 	int cnt;
46210275Ssam 	char buf[BUFSIZ];
46310275Ssam 
46410275Ssam 
46510275Ssam 	switch (type) {
46610275Ssam 
46710275Ssam 	case TYPE_I:
46810275Ssam 	case TYPE_L:
46910616Ssam 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
47010616Ssam 			if (write(fileno(outstr), buf, cnt) < 0)
47110275Ssam 				return (1);
47210275Ssam 		return (cnt < 0);
47310275Ssam 
47410275Ssam 	case TYPE_E:
47510275Ssam 		reply(504, "TYPE E not implemented.");
47610275Ssam 		return (1);
47710275Ssam 
47810275Ssam 	case TYPE_A:
47910275Ssam 		while ((c = getc(instr)) != EOF) {
48010275Ssam 			if (c == '\r') {
48111220Ssam 				if (ferror (outstr))
48211220Ssam 					return (1);
48311220Ssam 				if ((c = getc(instr)) != '\n')
48411220Ssam 					putc ('\r', outstr);
48511220Ssam 				if (c == '\0')
48611220Ssam 					continue;
48710275Ssam 			}
48811220Ssam 			putc (c, outstr);
48910275Ssam 		}
49011220Ssam 		if (ferror (instr) || ferror (outstr))
49111220Ssam 			return (1);
49210275Ssam 		return (0);
49310275Ssam 	}
49410275Ssam 	fatal("Unknown type in receive_data.");
49510275Ssam 	/*NOTREACHED*/
49610275Ssam }
49710275Ssam 
49810275Ssam fatal(s)
49910275Ssam 	char *s;
50010275Ssam {
50110275Ssam 	reply(451, "Error in server: %s\n", s);
50210275Ssam 	reply(221, "Closing connection due to server error.");
50313247Ssam 	dologout(0);
50410275Ssam }
50510275Ssam 
50610275Ssam reply(n, s, args)
50710275Ssam 	int n;
50810275Ssam 	char *s;
50910275Ssam {
51010275Ssam 
51110275Ssam 	printf("%d ", n);
51210275Ssam 	_doprnt(s, &args, stdout);
51310275Ssam 	printf("\r\n");
51410275Ssam 	fflush(stdout);
51510275Ssam 	if (debug) {
51610275Ssam 		fprintf(stderr, "<--- %d ", n);
51710275Ssam 		_doprnt(s, &args, stderr);
51810275Ssam 		fprintf(stderr, "\n");
51910275Ssam 		fflush(stderr);
52010275Ssam 	}
52110275Ssam }
52210275Ssam 
52310275Ssam lreply(n, s, args)
52410275Ssam 	int n;
52510275Ssam 	char *s;
52610275Ssam {
52710275Ssam 	printf("%d-", n);
52810275Ssam 	_doprnt(s, &args, stdout);
52910275Ssam 	printf("\r\n");
53010275Ssam 	fflush(stdout);
53110275Ssam 	if (debug) {
53210275Ssam 		fprintf(stderr, "<--- %d-", n);
53310275Ssam 		_doprnt(s, &args, stderr);
53410275Ssam 		fprintf(stderr, "\n");
53510275Ssam 	}
53610275Ssam }
53710275Ssam 
53810275Ssam replystr(s)
53910275Ssam 	char *s;
54010275Ssam {
54110275Ssam 	printf("%s\r\n", s);
54210275Ssam 	fflush(stdout);
54310275Ssam 	if (debug)
54410275Ssam 		fprintf(stderr, "<--- %s\n", s);
54510275Ssam }
54610275Ssam 
54710275Ssam ack(s)
54810275Ssam 	char *s;
54910275Ssam {
55010275Ssam 	reply(200, "%s command okay.", s);
55110275Ssam }
55210275Ssam 
55310275Ssam nack(s)
55410275Ssam 	char *s;
55510275Ssam {
55610275Ssam 	reply(502, "%s command not implemented.", s);
55710275Ssam }
55810275Ssam 
55910275Ssam yyerror()
56010275Ssam {
56110275Ssam 	reply(500, "Command not understood.");
56210275Ssam }
56310275Ssam 
56410275Ssam delete(name)
56510275Ssam 	char *name;
56610275Ssam {
56710275Ssam 	struct stat st;
56810275Ssam 
56910275Ssam 	if (stat(name, &st) < 0) {
57010275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
57110275Ssam 		return;
57210275Ssam 	}
57310275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
57410275Ssam 		if (rmdir(name) < 0) {
57510275Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
57610275Ssam 			return;
57710275Ssam 		}
57810275Ssam 		goto done;
57910275Ssam 	}
58010275Ssam 	if (unlink(name) < 0) {
58110275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
58210275Ssam 		return;
58310275Ssam 	}
58410275Ssam done:
58510275Ssam 	ack("DELE");
58610275Ssam }
58710275Ssam 
58810275Ssam cwd(path)
58910275Ssam 	char *path;
59010275Ssam {
59110275Ssam 
59210275Ssam 	if (chdir(path) < 0) {
59310275Ssam 		reply(550, "%s: %s.", path, sys_errlist[errno]);
59410275Ssam 		return;
59510275Ssam 	}
59610275Ssam 	ack("CWD");
59710275Ssam }
59810275Ssam 
59910303Ssam makedir(name)
60010275Ssam 	char *name;
60110275Ssam {
60210303Ssam 	struct stat st;
60310303Ssam 	int dochown = stat(name, &st) < 0;
60410275Ssam 
60510275Ssam 	if (mkdir(name, 0777) < 0) {
60610275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
60710275Ssam 		return;
60810275Ssam 	}
60910303Ssam 	if (dochown)
61010303Ssam 		(void) chown(name, pw->pw_uid, -1);
61110275Ssam 	ack("MKDIR");
61210275Ssam }
61310275Ssam 
61410303Ssam removedir(name)
61510275Ssam 	char *name;
61610275Ssam {
61710275Ssam 
61810275Ssam 	if (rmdir(name) < 0) {
61910275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
62010275Ssam 		return;
62110275Ssam 	}
62210275Ssam 	ack("RMDIR");
62310275Ssam }
62410275Ssam 
62510303Ssam pwd()
62610275Ssam {
62710303Ssam 	char path[MAXPATHLEN + 1];
62810275Ssam 
62910275Ssam 	if (getwd(path) == NULL) {
63010275Ssam 		reply(451, "%s.", path);
63110275Ssam 		return;
63210275Ssam 	}
63310275Ssam 	reply(251, "\"%s\" is current directory.", path);
63410275Ssam }
63510275Ssam 
63610275Ssam char *
63710275Ssam renamefrom(name)
63810275Ssam 	char *name;
63910275Ssam {
64010275Ssam 	struct stat st;
64110275Ssam 
64210275Ssam 	if (stat(name, &st) < 0) {
64310275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
64410275Ssam 		return ((char *)0);
64510275Ssam 	}
64610303Ssam 	reply(350, "File exists, ready for destination name");
64710275Ssam 	return (name);
64810275Ssam }
64910275Ssam 
65010275Ssam renamecmd(from, to)
65110275Ssam 	char *from, *to;
65210275Ssam {
65310275Ssam 
65410275Ssam 	if (rename(from, to) < 0) {
65510275Ssam 		reply(550, "rename: %s.", sys_errlist[errno]);
65610275Ssam 		return;
65710275Ssam 	}
65810275Ssam 	ack("RNTO");
65910275Ssam }
66010275Ssam 
66110275Ssam dolog(sin)
66210275Ssam 	struct sockaddr_in *sin;
66310275Ssam {
66410275Ssam 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
66510275Ssam 		sizeof (struct in_addr), AF_INET);
66610275Ssam 	time_t t;
66710275Ssam 
66813247Ssam 	if (hp) {
66913247Ssam 		strncpy(remotehost, hp->h_name, sizeof (remotehost));
67013247Ssam 		endhostent();
67113247Ssam 	} else
67213247Ssam 		strncpy(remotehost, inet_ntoa(sin->sin_addr),
67313247Ssam 		    sizeof (remotehost));
67413247Ssam 	if (!logging)
67513247Ssam 		return;
67610275Ssam 	t = time(0);
67711757Ssam 	fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
67810275Ssam 	fflush(stderr);
67910275Ssam }
68010695Ssam 
68113247Ssam #include <utmp.h>
68213247Ssam 
68313247Ssam #define	SCPYN(a, b)	strncpy(a, b, sizeof (a))
68413247Ssam struct	utmp utmp;
68513247Ssam 
68610695Ssam /*
68713247Ssam  * Record login in wtmp file.
68813247Ssam  */
68913247Ssam dologin(pw)
69013247Ssam 	struct passwd *pw;
69113247Ssam {
69213247Ssam 	char line[32];
69313247Ssam 
69413247Ssam 	if (wtmp >= 0) {
69513247Ssam 		/* hack, but must be unique and no tty line */
69613247Ssam 		sprintf(line, "ftp%d", getpid());
69713247Ssam 		SCPYN(utmp.ut_line, line);
69813247Ssam 		SCPYN(utmp.ut_name, pw->pw_name);
69913247Ssam 		SCPYN(utmp.ut_host, remotehost);
70013247Ssam 		utmp.ut_time = time(0);
70113247Ssam 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
70216760Slepreau 		if (!guest) {		/* anon must hang on */
70316760Slepreau 			(void) close(wtmp);
70416760Slepreau 			wtmp = -1;
70516760Slepreau 		}
70613247Ssam 	}
70713247Ssam }
70813247Ssam 
70913247Ssam /*
71013247Ssam  * Record logout in wtmp file
71113247Ssam  * and exit with supplied status.
71213247Ssam  */
71313247Ssam dologout(status)
71413247Ssam 	int status;
71513247Ssam {
71616339Skarels 
717*17580Ssam 	if (logged_in) {
718*17580Ssam 		(void) seteuid(0);
719*17580Ssam 		if (wtmp < 0)
720*17580Ssam 			wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
721*17580Ssam 		if (wtmp >= 0) {
722*17580Ssam 			SCPYN(utmp.ut_name, "");
723*17580Ssam 			SCPYN(utmp.ut_host, "");
724*17580Ssam 			utmp.ut_time = time(0);
725*17580Ssam 			(void) write(wtmp, (char *)&utmp, sizeof (utmp));
726*17580Ssam 			(void) close(wtmp);
727*17580Ssam 		}
72813247Ssam 	}
72914436Ssam 	/* beware of flushing buffers after a SIGPIPE */
73014436Ssam 	_exit(status);
73113247Ssam }
73213247Ssam 
73313247Ssam /*
73410695Ssam  * Special version of popen which avoids
73510695Ssam  * call to shell.  This insures noone may
73610695Ssam  * create a pipe to a hidden program as a side
73710695Ssam  * effect of a list or dir command.
73810695Ssam  */
73910695Ssam #define	tst(a,b)	(*mode == 'r'? (b) : (a))
74010695Ssam #define	RDR	0
74110695Ssam #define	WTR	1
74210695Ssam static	int popen_pid[5];
74310695Ssam 
74410695Ssam static char *
74510695Ssam nextarg(cpp)
74610695Ssam 	char *cpp;
74710695Ssam {
74810695Ssam 	register char *cp = cpp;
74910695Ssam 
75010695Ssam 	if (cp == 0)
75110695Ssam 		return (cp);
75210695Ssam 	while (*cp && *cp != ' ' && *cp != '\t')
75310695Ssam 		cp++;
75410695Ssam 	if (*cp == ' ' || *cp == '\t') {
75510695Ssam 		*cp++ = '\0';
75610695Ssam 		while (*cp == ' ' || *cp == '\t')
75710695Ssam 			cp++;
75810695Ssam 	}
75910695Ssam 	if (cp == cpp)
76010695Ssam 		return ((char *)0);
76110695Ssam 	return (cp);
76210695Ssam }
76310695Ssam 
76410695Ssam FILE *
76510695Ssam popen(cmd, mode)
76610695Ssam 	char *cmd, *mode;
76710695Ssam {
76813211Sroot 	int p[2], ac, gac;
76910695Ssam 	register myside, hisside, pid;
77013211Sroot 	char *av[20], *gav[512];
77110695Ssam 	register char *cp;
77210695Ssam 
77310695Ssam 	if (pipe(p) < 0)
77410695Ssam 		return (NULL);
77510695Ssam 	cp = cmd, ac = 0;
77613211Sroot 	/* break up string into pieces */
77710695Ssam 	do {
77810695Ssam 		av[ac++] = cp;
77910695Ssam 		cp = nextarg(cp);
78013211Sroot 	} while (cp && *cp && ac < 20);
78110695Ssam 	av[ac] = (char *)0;
78213211Sroot 	gav[0] = av[0];
78313211Sroot 	/* glob each piece */
78413211Sroot 	for (gac = ac = 1; av[ac] != NULL; ac++) {
78513211Sroot 		char **pop;
78613211Sroot 		extern char **glob();
78713211Sroot 
78813211Sroot 		pop = glob(av[ac]);
78913211Sroot 		if (pop) {
79013211Sroot 			av[ac] = (char *)pop;		/* save to free later */
79113211Sroot 			while (*pop && gac < 512)
79213211Sroot 				gav[gac++] = *pop++;
79313211Sroot 		}
79411757Ssam 	}
79513211Sroot 	gav[gac] = (char *)0;
79610695Ssam 	myside = tst(p[WTR], p[RDR]);
79710695Ssam 	hisside = tst(p[RDR], p[WTR]);
79810695Ssam 	if ((pid = fork()) == 0) {
79910695Ssam 		/* myside and hisside reverse roles in child */
80010695Ssam 		close(myside);
80110695Ssam 		dup2(hisside, tst(0, 1));
80210695Ssam 		close(hisside);
80313211Sroot 		execv(gav[0], gav);
80410695Ssam 		_exit(1);
80510695Ssam 	}
80613211Sroot 	for (ac = 1; av[ac] != NULL; ac++)
80713211Sroot 		blkfree((char **)av[ac]);
80810695Ssam 	if (pid == -1)
80910695Ssam 		return (NULL);
81010695Ssam 	popen_pid[myside] = pid;
81110695Ssam 	close(hisside);
81210695Ssam 	return (fdopen(myside, mode));
81310695Ssam }
81410695Ssam 
81510695Ssam pclose(ptr)
81610695Ssam 	FILE *ptr;
81710695Ssam {
81810695Ssam 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
81910695Ssam 	int status;
82010695Ssam 
82110695Ssam 	f = fileno(ptr);
82210695Ssam 	fclose(ptr);
82310695Ssam 	istat = signal(SIGINT, SIG_IGN);
82410695Ssam 	qstat = signal(SIGQUIT, SIG_IGN);
82510695Ssam 	hstat = signal(SIGHUP, SIG_IGN);
82610695Ssam 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
82710695Ssam 		;
82810695Ssam 	if (r == -1)
82910695Ssam 		status = -1;
83010695Ssam 	signal(SIGINT, istat);
83110695Ssam 	signal(SIGQUIT, qstat);
83210695Ssam 	signal(SIGHUP, hstat);
83310695Ssam 	return (status);
83410695Ssam }
83510695Ssam 
83610695Ssam /*
83710695Ssam  * Check user requesting login priviledges.
83810695Ssam  * Disallow anyone mentioned in the file FTPUSERS
83910695Ssam  * to allow people such as uucp to be avoided.
84010695Ssam  */
84110695Ssam checkuser(name)
84210695Ssam 	register char *name;
84310695Ssam {
84410695Ssam 	char line[BUFSIZ], *index();
84510695Ssam 	FILE *fd;
84610695Ssam 	int found = 0;
84710695Ssam 
84810695Ssam 	fd = fopen(FTPUSERS, "r");
84910695Ssam 	if (fd == NULL)
85010695Ssam 		return (1);
85110695Ssam 	while (fgets(line, sizeof (line), fd) != NULL) {
85210695Ssam 		register char *cp = index(line, '\n');
85310695Ssam 
85410695Ssam 		if (cp)
85510695Ssam 			*cp = '\0';
85610695Ssam 		if (strcmp(line, name) == 0) {
85710695Ssam 			found++;
85810695Ssam 			break;
85910695Ssam 		}
86010695Ssam 	}
86110695Ssam 	fclose(fd);
86210695Ssam 	return (!found);
86310695Ssam }
864