xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 10695)
110275Ssam #ifndef lint
2*10695Ssam static char sccsid[] = "@(#)ftpd.c	4.15 (Berkeley) 02/02/83";
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>
1210275Ssam 
1310275Ssam #include <netinet/in.h>
1410275Ssam 
1510275Ssam #include <stdio.h>
1610275Ssam #include <signal.h>
1710275Ssam #include <wait.h>
1810275Ssam #include <pwd.h>
1910275Ssam #include <setjmp.h>
2010275Ssam #include <netdb.h>
2110423Ssam #include <errno.h>
2210275Ssam 
2310275Ssam #include "ftp.h"
2410275Ssam 
25*10695Ssam /*
26*10695Ssam  * File containing login names
27*10695Ssam  * NOT to be used on this machine.
28*10695Ssam  * Commonly used to disallow uucp.
29*10695Ssam  */
30*10695Ssam #define	FTPUSERS	"/etc/ftpusers"
31*10695Ssam 
3210275Ssam extern	int errno;
3310275Ssam extern	char *sys_errlist[];
3410275Ssam extern	char *crypt();
3510275Ssam extern	char version[];
3610275Ssam extern	char *home;		/* pointer to home directory for glob */
3710275Ssam extern	FILE *popen(), *fopen();
3810275Ssam extern	int pclose(), fclose();
3910275Ssam 
4010275Ssam struct	sockaddr_in ctrl_addr;
4110275Ssam struct	sockaddr_in data_source;
4210275Ssam struct	sockaddr_in data_dest;
4310275Ssam struct	sockaddr_in his_addr;
4410275Ssam 
4510275Ssam struct	hostent *hp;
4610275Ssam 
4710275Ssam int	data;
4810275Ssam jmp_buf	errcatch;
4910275Ssam int	logged_in;
5010275Ssam struct	passwd *pw;
5110275Ssam int	debug;
5210275Ssam int	logging = 1;
5310275Ssam int	guest;
5410275Ssam int	type;
5510275Ssam int	form;
5610275Ssam int	stru;			/* avoid C keyword */
5710275Ssam int	mode;
5810321Ssam int	usedefault = 1;		/* for data transfers */
5910275Ssam char	hostname[32];
6010275Ssam char	*remotehost;
6110321Ssam struct	servent *sp;
6210275Ssam 
6310275Ssam int	lostconn();
6410419Ssam int	reapchild();
6510275Ssam FILE	*getdatasock(), *dataconn();
6610275Ssam char	*ntoa();
6710275Ssam 
6810275Ssam main(argc, argv)
6910275Ssam 	int argc;
7010275Ssam 	char *argv[];
7110275Ssam {
7210275Ssam 	int ctrl, s, options = 0;
7310275Ssam 	char *cp;
7410275Ssam 
7510275Ssam 	sp = getservbyname("ftp", "tcp");
7610275Ssam 	if (sp == 0) {
7710275Ssam 		fprintf(stderr, "ftpd: fpt/tcp: unknown service\n");
7810275Ssam 		exit(1);
7910275Ssam 	}
8010275Ssam 	ctrl_addr.sin_port = sp->s_port;
8110275Ssam 	data_source.sin_port = htons(ntohs(sp->s_port) - 1);
8210275Ssam 	signal(SIGPIPE, lostconn);
8310275Ssam 	debug = 0;
8410275Ssam 	argc--, argv++;
8510275Ssam 	while (argc > 0 && *argv[0] == '-') {
8610275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
8710275Ssam 
8810275Ssam 		case 'd':
8910275Ssam 			debug = 1;
9010275Ssam 			options |= SO_DEBUG;
9110275Ssam 			break;
9210275Ssam 
9310275Ssam 		default:
9410275Ssam 			fprintf(stderr, "Unknown flag -%c ignored.\n", cp);
9510275Ssam 			break;
9610275Ssam 		}
9710275Ssam 		argc--, argv++;
9810275Ssam 	}
9910275Ssam #ifndef DEBUG
10010275Ssam 	if (fork())
10110275Ssam 		exit(0);
10210275Ssam 	for (s = 0; s < 10; s++)
10310317Ssam 		(void) close(s);
10410275Ssam 	(void) open("/dev/null", 0);
10510275Ssam 	(void) dup2(0, 1);
10610275Ssam 	{ int tt = open("/dev/tty", 2);
10710275Ssam 	  if (tt > 0) {
10810275Ssam 		ioctl(tt, TIOCNOTTY, 0);
10910275Ssam 		close(tt);
11010275Ssam 	  }
11110275Ssam 	}
11210275Ssam #endif
11310303Ssam 	while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
11410275Ssam 		perror("ftpd: socket");
11510275Ssam 		sleep(5);
11610275Ssam 	}
11710419Ssam 	if (options & SO_DEBUG)
11810419Ssam 		if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
11910419Ssam 			perror("ftpd: setsockopt (SO_DEBUG)");
12010419Ssam #ifdef notdef
12110419Ssam 	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
12210419Ssam 		perror("ftpd: setsockopt (SO_KEEPALIVE)");
12310419Ssam #endif
12410275Ssam 	while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
12510275Ssam 		perror("ftpd: bind");
12610275Ssam 		sleep(5);
12710275Ssam 	}
12810588Ssam 	sigset(SIGCHLD, reapchild);
12910303Ssam 	listen(s, 10);
13010275Ssam 	for (;;) {
13110275Ssam 		int hisaddrlen = sizeof (his_addr);
13210275Ssam 
13310275Ssam 		ctrl = accept(s, &his_addr, &hisaddrlen, 0);
13410275Ssam 		if (ctrl < 0) {
13510419Ssam 			if (errno == EINTR)
13610419Ssam 				continue;
13710275Ssam 			perror("ftpd: accept");
13810275Ssam 			continue;
13910275Ssam 		}
14010275Ssam 		if (fork() == 0) {
14110275Ssam 			if (logging)
14210275Ssam 				dolog(&his_addr);
14310275Ssam 			close(s);
14410275Ssam 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
14510275Ssam 			/* do telnet option negotiation here */
14610303Ssam 			/*
14710303Ssam 			 * Set up default state
14810303Ssam 			 */
14910275Ssam 			logged_in = 0;
15010275Ssam 			data = -1;
15110303Ssam 			type = TYPE_A;
15210303Ssam 			form = FORM_N;
15310303Ssam 			stru = STRU_F;
15410303Ssam 			mode = MODE_S;
15510275Ssam 			gethostname(hostname, sizeof (hostname));
15610275Ssam 			reply(220, "%s FTP server (%s) ready.",
15710275Ssam 				hostname, version);
15810275Ssam 			for (;;) {
15910275Ssam 				setjmp(errcatch);
16010275Ssam 				yyparse();
16110275Ssam 			}
16210275Ssam 		}
16310275Ssam 		close(ctrl);
16410275Ssam 	}
16510275Ssam }
16610275Ssam 
16710419Ssam reapchild()
16810419Ssam {
16910419Ssam 	union wait status;
17010419Ssam 
17110419Ssam 	while (wait3(&status, WNOHANG, 0) > 0)
17210419Ssam 		;
17310419Ssam }
17410419Ssam 
17510275Ssam lostconn()
17610275Ssam {
17710275Ssam 
17810275Ssam 	fatal("Connection closed.");
17910275Ssam }
18010275Ssam 
18110275Ssam pass(passwd)
18210275Ssam 	char *passwd;
18310275Ssam {
18410303Ssam 	char *xpasswd, *savestr();
18510303Ssam 	static struct passwd save;
18610275Ssam 
18710275Ssam 	if (logged_in || pw == NULL) {
18810275Ssam 		reply(503, "Login with USER first.");
18910275Ssam 		return;
19010275Ssam 	}
19110275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
19210275Ssam 		xpasswd = crypt(passwd, pw->pw_passwd);
19310275Ssam 		if (strcmp(xpasswd, pw->pw_passwd) != 0) {
19410275Ssam 			reply(530, "Login incorrect.");
19510275Ssam 			pw = NULL;
19610275Ssam 			return;
19710275Ssam 		}
19810275Ssam 	}
19910303Ssam 	setegid(pw->pw_gid);
20010275Ssam 	initgroups(pw->pw_name, pw->pw_gid);
20110275Ssam 	if (chdir(pw->pw_dir)) {
20210275Ssam 		reply(550, "User %s: can't change directory to $s.",
20310275Ssam 			pw->pw_name, pw->pw_dir);
20410303Ssam 		goto bad;
20510275Ssam 	}
20610303Ssam 	if (guest && chroot(pw->pw_dir) < 0) {
20710275Ssam 		reply(550, "Can't set guest privileges.");
20810303Ssam 		goto bad;
20910275Ssam 	}
21010275Ssam 	if (!guest)
21110275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
21210275Ssam 	else
21310275Ssam 		reply(230, "Guest login ok, access restrictions apply.");
21410275Ssam 	logged_in = 1;
21510303Ssam 	seteuid(pw->pw_uid);
21610303Ssam 	/*
21710303Ssam 	 * Save everything so globbing doesn't
21810303Ssam 	 * clobber the fields.
21910303Ssam 	 */
22010303Ssam 	save = *pw;
22110303Ssam 	save.pw_name = savestr(pw->pw_name);
22210303Ssam 	save.pw_passwd = savestr(pw->pw_passwd);
22310303Ssam 	save.pw_comment = savestr(pw->pw_comment);
22410303Ssam 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
22510303Ssam 	save.pw_dir = savestr(pw->pw_dir);
22610303Ssam 	save.pw_shell = savestr(pw->pw_shell);
22710303Ssam 	pw = &save;
22810303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
22910303Ssam 	return;
23010303Ssam bad:
23110303Ssam 	seteuid(0);
23210303Ssam 	pw = NULL;
23310275Ssam }
23410275Ssam 
23510303Ssam char *
23610303Ssam savestr(s)
23710303Ssam 	char *s;
23810303Ssam {
23910303Ssam 	char *malloc();
24010303Ssam 	char *new = malloc(strlen(s) + 1);
24110303Ssam 
24210303Ssam 	if (new != NULL)
24310303Ssam 		strcpy(new, s);
24410303Ssam 	return(new);
24510303Ssam }
24610303Ssam 
24710275Ssam retrieve(cmd, name)
24810275Ssam 	char *cmd, *name;
24910275Ssam {
25010275Ssam 	FILE *fin, *dout;
25110275Ssam 	struct stat st;
25210275Ssam 	int (*closefunc)();
25310275Ssam 
25410275Ssam 	if (cmd == 0) {
25510317Ssam #ifdef notdef
25610317Ssam 		/* no remote command execution -- it's a security hole */
25710275Ssam 		if (*name == '!')
25810275Ssam 			fin = popen(name + 1, "r"), closefunc = pclose;
25910275Ssam 		else
26010317Ssam #endif
26110275Ssam 			fin = fopen(name, "r"), closefunc = fclose;
26210275Ssam 	} else {
26310275Ssam 		char line[BUFSIZ];
26410275Ssam 
26510422Ssam 		sprintf(line, cmd, name), name = line;
26610275Ssam 		fin = popen(line, "r"), closefunc = pclose;
26710275Ssam 	}
26810275Ssam 	if (fin == NULL) {
26910275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
27010275Ssam 		return;
27110275Ssam 	}
27210275Ssam 	st.st_size = 0;
27310275Ssam 	if (cmd == 0 &&
27410275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
27510275Ssam 		reply(550, "%s: not a plain file.", name);
27610275Ssam 		goto done;
27710275Ssam 	}
27810275Ssam 	dout = dataconn(name, st.st_size, "w");
27910275Ssam 	if (dout == NULL)
28010275Ssam 		goto done;
28110303Ssam 	if (send_data(fin, dout) || ferror(dout))
28210275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
28310275Ssam 	else
28410275Ssam 		reply(226, "Transfer complete.");
28510303Ssam 	fclose(dout), data = -1;
28610275Ssam done:
28710275Ssam 	(*closefunc)(fin);
28810275Ssam }
28910275Ssam 
29010275Ssam store(name, mode)
29110275Ssam 	char *name, *mode;
29210275Ssam {
29310275Ssam 	FILE *fout, *din;
29410303Ssam 	int (*closefunc)(), dochown = 0;
29510275Ssam 
29610317Ssam #ifdef notdef
29710317Ssam 	/* no remote command execution -- it's a security hole */
29810275Ssam 	if (name[0] == '!')
29910275Ssam 		fout = popen(&name[1], "w"), closefunc = pclose;
30010317Ssam 	else
30110317Ssam #endif
30210317Ssam 	{
30310303Ssam 		struct stat st;
30410303Ssam 
30510303Ssam 		if (stat(name, &st) < 0)
30610303Ssam 			dochown++;
30710275Ssam 		fout = fopen(name, mode), closefunc = fclose;
30810303Ssam 	}
30910275Ssam 	if (fout == NULL) {
31010275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
31110275Ssam 		return;
31210275Ssam 	}
31310275Ssam 	din = dataconn(name, -1, "r");
31410275Ssam 	if (din == NULL)
31510275Ssam 		goto done;
31610303Ssam 	if (receive_data(din, fout) || ferror(fout))
31710275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
31810275Ssam 	else
31910275Ssam 		reply(226, "Transfer complete.");
32010275Ssam 	fclose(din), data = -1;
32110275Ssam done:
32210303Ssam 	if (dochown)
32310303Ssam 		(void) chown(name, pw->pw_uid, -1);
32410275Ssam 	(*closefunc)(fout);
32510275Ssam }
32610275Ssam 
32710275Ssam FILE *
32810275Ssam getdatasock(mode)
32910275Ssam 	char *mode;
33010275Ssam {
33110602Ssam 	int s;
33210275Ssam 
33310275Ssam 	if (data >= 0)
33410275Ssam 		return (fdopen(data, mode));
33510602Ssam 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
33610602Ssam 	if (s < 0)
33710275Ssam 		return (NULL);
33810275Ssam 	seteuid(0);
33910602Ssam 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
34010602Ssam 		goto bad;
34110602Ssam 	if (bind(s, &data_source, sizeof (data_source), 0) < 0)
34210602Ssam 		goto bad;
34310311Ssam 	seteuid(pw->pw_uid);
34410275Ssam 	return (fdopen(s, mode));
34510602Ssam bad:
34610602Ssam 	seteuid(pw->pw_uid);
34710602Ssam 	close(s);
34810602Ssam 	return (NULL);
34910275Ssam }
35010275Ssam 
35110275Ssam FILE *
35210275Ssam dataconn(name, size, mode)
35310275Ssam 	char *name;
35410275Ssam 	int size;
35510275Ssam 	char *mode;
35610275Ssam {
35710275Ssam 	char sizebuf[32];
35810275Ssam 	FILE *file;
35910275Ssam 
36010275Ssam 	if (size >= 0)
36110275Ssam 		sprintf(sizebuf, " (%d bytes)", size);
36210275Ssam 	else
36310275Ssam 		(void) strcpy(sizebuf, "");
36410275Ssam 	if (data >= 0) {
36510275Ssam 		reply(125, "Using existing data connection for %s%s.",
36610275Ssam 		    name, sizebuf);
36710321Ssam 		usedefault = 1;
36810275Ssam 		return (fdopen(data, mode));
36910275Ssam 	}
37010566Ssam 	if (usedefault)
37110422Ssam 		data_dest = his_addr;
37210422Ssam 	usedefault = 1;
37310275Ssam 	file = getdatasock(mode);
37410275Ssam 	if (file == NULL) {
37510275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
37610275Ssam 		    ntoa(data_source.sin_addr),
37710275Ssam 		    ntohs(data_source.sin_port),
37810275Ssam 		    sys_errlist[errno]);
37910275Ssam 		return (NULL);
38010275Ssam 	}
38110602Ssam 	reply(150, "Opening data connection for %s (%s,%d)%s.",
38210602Ssam 	    name, ntoa(data_dest.sin_addr.s_addr),
38310602Ssam 	    ntohs(data_dest.sin_port), sizebuf);
38410275Ssam 	data = fileno(file);
38510275Ssam 	if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
38610275Ssam 		reply(425, "Can't build data connection: %s.",
38710275Ssam 		    sys_errlist[errno]);
38810275Ssam 		(void) fclose(file);
38910275Ssam 		data = -1;
39010275Ssam 		return (NULL);
39110275Ssam 	}
39210275Ssam 	return (file);
39310275Ssam }
39410275Ssam 
39510275Ssam /*
39610275Ssam  * Tranfer the contents of "instr" to
39710275Ssam  * "outstr" peer using the appropriate
39810275Ssam  * encapulation of the date subject
39910275Ssam  * to Mode, Structure, and Type.
40010275Ssam  *
40110275Ssam  * NB: Form isn't handled.
40210275Ssam  */
40310275Ssam send_data(instr, outstr)
40410275Ssam 	FILE *instr, *outstr;
40510275Ssam {
40610275Ssam 	register int c;
40710275Ssam 	int netfd, filefd, cnt;
40810275Ssam 	char buf[BUFSIZ];
40910275Ssam 
41010275Ssam 	switch (type) {
41110275Ssam 
41210275Ssam 	case TYPE_A:
41310275Ssam 		while ((c = getc(instr)) != EOF) {
41410275Ssam 			if (c == '\n')
41510275Ssam 				putc('\r', outstr);
41610275Ssam 			if (putc(c, outstr) == EOF)
41710275Ssam 				return (1);
41810275Ssam 		}
41910275Ssam 		return (0);
42010275Ssam 
42110275Ssam 	case TYPE_I:
42210275Ssam 	case TYPE_L:
42310275Ssam 		netfd = fileno(outstr);
42410275Ssam 		filefd = fileno(instr);
42510275Ssam 
42610303Ssam 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
42710275Ssam 			if (write(netfd, buf, cnt) < 0)
42810275Ssam 				return (1);
42910275Ssam 		return (cnt < 0);
43010275Ssam 	}
43110275Ssam 	reply(504,"Unimplemented TYPE %d in send_data", type);
43210275Ssam 	return (1);
43310275Ssam }
43410275Ssam 
43510275Ssam /*
43610275Ssam  * Transfer data from peer to
43710275Ssam  * "outstr" using the appropriate
43810275Ssam  * encapulation of the data subject
43910275Ssam  * to Mode, Structure, and Type.
44010275Ssam  *
44110275Ssam  * N.B.: Form isn't handled.
44210275Ssam  */
44310275Ssam receive_data(instr, outstr)
44410275Ssam 	FILE *instr, *outstr;
44510275Ssam {
44610275Ssam 	register int c;
44710616Ssam 	int cr, escape, eof, cnt;
44810275Ssam 	char buf[BUFSIZ];
44910275Ssam 
45010275Ssam 
45110275Ssam 	switch (type) {
45210275Ssam 
45310275Ssam 	case TYPE_I:
45410275Ssam 	case TYPE_L:
45510616Ssam 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
45610616Ssam 			if (write(fileno(outstr), buf, cnt) < 0)
45710275Ssam 				return (1);
45810275Ssam 		return (cnt < 0);
45910275Ssam 
46010275Ssam 	case TYPE_E:
46110275Ssam 		reply(504, "TYPE E not implemented.");
46210275Ssam 		return (1);
46310275Ssam 
46410275Ssam 	case TYPE_A:
46510275Ssam 		cr = 0;
46610275Ssam 		while ((c = getc(instr)) != EOF) {
46710275Ssam 			if (cr) {
46810275Ssam 				if (c != '\r' && c != '\n')
46910275Ssam 					putc('\r', outstr);
47010275Ssam 				putc(c, outstr);
47110275Ssam 				cr = c == '\r';
47210275Ssam 				continue;
47310275Ssam 			}
47410275Ssam 			if (c == '\r') {
47510275Ssam 				cr = 1;
47610275Ssam 				continue;
47710275Ssam 			}
47810275Ssam 			putc(c, outstr);
47910275Ssam 		}
48010275Ssam 		if (cr)
48110275Ssam 			putc('\r', outstr);
48210275Ssam 		return (0);
48310275Ssam 	}
48410275Ssam 	fatal("Unknown type in receive_data.");
48510275Ssam 	/*NOTREACHED*/
48610275Ssam }
48710275Ssam 
48810275Ssam fatal(s)
48910275Ssam 	char *s;
49010275Ssam {
49110275Ssam 	reply(451, "Error in server: %s\n", s);
49210275Ssam 	reply(221, "Closing connection due to server error.");
49310275Ssam 	exit(0);
49410275Ssam }
49510275Ssam 
49610275Ssam reply(n, s, args)
49710275Ssam 	int n;
49810275Ssam 	char *s;
49910275Ssam {
50010275Ssam 
50110275Ssam 	printf("%d ", n);
50210275Ssam 	_doprnt(s, &args, stdout);
50310275Ssam 	printf("\r\n");
50410275Ssam 	fflush(stdout);
50510275Ssam 	if (debug) {
50610275Ssam 		fprintf(stderr, "<--- %d ", n);
50710275Ssam 		_doprnt(s, &args, stderr);
50810275Ssam 		fprintf(stderr, "\n");
50910275Ssam 		fflush(stderr);
51010275Ssam 	}
51110275Ssam }
51210275Ssam 
51310275Ssam lreply(n, s, args)
51410275Ssam 	int n;
51510275Ssam 	char *s;
51610275Ssam {
51710275Ssam 	printf("%d-", n);
51810275Ssam 	_doprnt(s, &args, stdout);
51910275Ssam 	printf("\r\n");
52010275Ssam 	fflush(stdout);
52110275Ssam 	if (debug) {
52210275Ssam 		fprintf(stderr, "<--- %d-", n);
52310275Ssam 		_doprnt(s, &args, stderr);
52410275Ssam 		fprintf(stderr, "\n");
52510275Ssam 	}
52610275Ssam }
52710275Ssam 
52810275Ssam replystr(s)
52910275Ssam 	char *s;
53010275Ssam {
53110275Ssam 	printf("%s\r\n", s);
53210275Ssam 	fflush(stdout);
53310275Ssam 	if (debug)
53410275Ssam 		fprintf(stderr, "<--- %s\n", s);
53510275Ssam }
53610275Ssam 
53710275Ssam ack(s)
53810275Ssam 	char *s;
53910275Ssam {
54010275Ssam 	reply(200, "%s command okay.", s);
54110275Ssam }
54210275Ssam 
54310275Ssam nack(s)
54410275Ssam 	char *s;
54510275Ssam {
54610275Ssam 	reply(502, "%s command not implemented.", s);
54710275Ssam }
54810275Ssam 
54910275Ssam yyerror()
55010275Ssam {
55110275Ssam 	reply(500, "Command not understood.");
55210275Ssam }
55310275Ssam 
55410275Ssam delete(name)
55510275Ssam 	char *name;
55610275Ssam {
55710275Ssam 	struct stat st;
55810275Ssam 
55910275Ssam 	if (stat(name, &st) < 0) {
56010275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
56110275Ssam 		return;
56210275Ssam 	}
56310275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
56410275Ssam 		if (rmdir(name) < 0) {
56510275Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
56610275Ssam 			return;
56710275Ssam 		}
56810275Ssam 		goto done;
56910275Ssam 	}
57010275Ssam 	if (unlink(name) < 0) {
57110275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
57210275Ssam 		return;
57310275Ssam 	}
57410275Ssam done:
57510275Ssam 	ack("DELE");
57610275Ssam }
57710275Ssam 
57810275Ssam cwd(path)
57910275Ssam 	char *path;
58010275Ssam {
58110275Ssam 
58210275Ssam 	if (chdir(path) < 0) {
58310275Ssam 		reply(550, "%s: %s.", path, sys_errlist[errno]);
58410275Ssam 		return;
58510275Ssam 	}
58610275Ssam 	ack("CWD");
58710275Ssam }
58810275Ssam 
58910303Ssam makedir(name)
59010275Ssam 	char *name;
59110275Ssam {
59210303Ssam 	struct stat st;
59310303Ssam 	int dochown = stat(name, &st) < 0;
59410275Ssam 
59510275Ssam 	if (mkdir(name, 0777) < 0) {
59610275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
59710275Ssam 		return;
59810275Ssam 	}
59910303Ssam 	if (dochown)
60010303Ssam 		(void) chown(name, pw->pw_uid, -1);
60110275Ssam 	ack("MKDIR");
60210275Ssam }
60310275Ssam 
60410303Ssam removedir(name)
60510275Ssam 	char *name;
60610275Ssam {
60710275Ssam 
60810275Ssam 	if (rmdir(name) < 0) {
60910275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
61010275Ssam 		return;
61110275Ssam 	}
61210275Ssam 	ack("RMDIR");
61310275Ssam }
61410275Ssam 
61510303Ssam pwd()
61610275Ssam {
61710303Ssam 	char path[MAXPATHLEN + 1];
61810275Ssam 	char *p;
61910275Ssam 
62010275Ssam 	if (getwd(path) == NULL) {
62110275Ssam 		reply(451, "%s.", path);
62210275Ssam 		return;
62310275Ssam 	}
62410275Ssam 	reply(251, "\"%s\" is current directory.", path);
62510275Ssam }
62610275Ssam 
62710275Ssam char *
62810275Ssam renamefrom(name)
62910275Ssam 	char *name;
63010275Ssam {
63110275Ssam 	struct stat st;
63210275Ssam 
63310275Ssam 	if (stat(name, &st) < 0) {
63410275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
63510275Ssam 		return ((char *)0);
63610275Ssam 	}
63710303Ssam 	reply(350, "File exists, ready for destination name");
63810275Ssam 	return (name);
63910275Ssam }
64010275Ssam 
64110275Ssam renamecmd(from, to)
64210275Ssam 	char *from, *to;
64310275Ssam {
64410275Ssam 
64510275Ssam 	if (rename(from, to) < 0) {
64610275Ssam 		reply(550, "rename: %s.", sys_errlist[errno]);
64710275Ssam 		return;
64810275Ssam 	}
64910275Ssam 	ack("RNTO");
65010275Ssam }
65110275Ssam 
65210275Ssam int guest;
65310275Ssam /*
65410275Ssam  * Test pathname for guest-user safety.
65510275Ssam  */
65610275Ssam inappropriate_request(name)
65710275Ssam 	char *name;
65810275Ssam {
65910275Ssam 	int bogus = 0, depth = 0, length = strlen(name);
66010275Ssam 	char *p, *s;
66110275Ssam 
66210275Ssam 	if (!guest)
66310275Ssam 		return (0);
66410275Ssam 	if (name[0] == '/' || name[0] == '|')
66510275Ssam 		bogus = 1;
66610275Ssam 	for (p = name; p < name+length;) {
66710275Ssam 		s = p;				/* start of token */
66810275Ssam 		while ( *p && *p!= '/')
66910275Ssam 			p++;
67010275Ssam 		*p = 0;
67110275Ssam 		if (strcmp(s, "..") == 0)
67210275Ssam 			depth -= 1;		/* backing up */
67310275Ssam 		else if (strcmp(s, ".") == 0)
67410275Ssam 			depth += 0;		/* no change */
67510275Ssam 		else
67610275Ssam 			depth += 1;		/* descending */
67710275Ssam 		if (depth < 0) {
67810275Ssam 			bogus = 1;
67910275Ssam 			break;
68010275Ssam 		}
68110275Ssam 	}
68210275Ssam 	if (bogus)
68310275Ssam 		reply(553, "%s: pathname disallowed guest users", name);
68410275Ssam 	return (bogus);
68510275Ssam }
68610275Ssam 
68710275Ssam /*
68810275Ssam  * Convert network-format internet address
68910275Ssam  * to base 256 d.d.d.d representation.
69010275Ssam  */
69110275Ssam char *
69210275Ssam ntoa(in)
69310275Ssam 	struct in_addr in;
69410275Ssam {
69510275Ssam 	static char b[18];
69610275Ssam 	register char *p;
69710275Ssam 
69810275Ssam 	in.s_addr = ntohl(in.s_addr);
69910275Ssam 	p = (char *)&in;
70010275Ssam #define	UC(b)	(((int)b)&0xff)
70110275Ssam 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
70210275Ssam 	return (b);
70310275Ssam }
70410275Ssam 
70510275Ssam dolog(sin)
70610275Ssam 	struct sockaddr_in *sin;
70710275Ssam {
70810275Ssam 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
70910275Ssam 		sizeof (struct in_addr), AF_INET);
71010275Ssam 	char *remotehost;
71110275Ssam 	time_t t;
71210275Ssam 
71310275Ssam 	if (hp)
71410275Ssam 		remotehost = hp->h_name;
71510275Ssam 	else
71610275Ssam 		remotehost = "UNKNOWNHOST";
71710275Ssam 	t = time(0);
71810303Ssam 	fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t));
71910275Ssam 	fflush(stderr);
72010275Ssam }
721*10695Ssam 
722*10695Ssam /*
723*10695Ssam  * Special version of popen which avoids
724*10695Ssam  * call to shell.  This insures noone may
725*10695Ssam  * create a pipe to a hidden program as a side
726*10695Ssam  * effect of a list or dir command.
727*10695Ssam  */
728*10695Ssam #define	tst(a,b)	(*mode == 'r'? (b) : (a))
729*10695Ssam #define	RDR	0
730*10695Ssam #define	WTR	1
731*10695Ssam static	int popen_pid[5];
732*10695Ssam 
733*10695Ssam static char *
734*10695Ssam nextarg(cpp)
735*10695Ssam 	char *cpp;
736*10695Ssam {
737*10695Ssam 	register char *cp = cpp;
738*10695Ssam 
739*10695Ssam 	if (cp == 0)
740*10695Ssam 		return (cp);
741*10695Ssam 	while (*cp && *cp != ' ' && *cp != '\t')
742*10695Ssam 		cp++;
743*10695Ssam 	if (*cp == ' ' || *cp == '\t') {
744*10695Ssam 		*cp++ = '\0';
745*10695Ssam 		while (*cp == ' ' || *cp == '\t')
746*10695Ssam 			cp++;
747*10695Ssam 	}
748*10695Ssam 	if (cp == cpp)
749*10695Ssam 		return ((char *)0);
750*10695Ssam 	return (cp);
751*10695Ssam }
752*10695Ssam 
753*10695Ssam FILE *
754*10695Ssam popen(cmd, mode)
755*10695Ssam 	char *cmd, *mode;
756*10695Ssam {
757*10695Ssam 	int p[2], ac;
758*10695Ssam 	register myside, hisside, pid;
759*10695Ssam 	char *av[10];
760*10695Ssam 	register char *cp;
761*10695Ssam 
762*10695Ssam 	if (pipe(p) < 0)
763*10695Ssam 		return (NULL);
764*10695Ssam 	cp = cmd, ac = 0;
765*10695Ssam 	do {
766*10695Ssam 		av[ac++] = cp;
767*10695Ssam 		cp = nextarg(cp);
768*10695Ssam 	} while (cp && *cp);
769*10695Ssam 	av[ac] = (char *)0;
770*10695Ssam 	myside = tst(p[WTR], p[RDR]);
771*10695Ssam 	hisside = tst(p[RDR], p[WTR]);
772*10695Ssam 	if ((pid = fork()) == 0) {
773*10695Ssam 		/* myside and hisside reverse roles in child */
774*10695Ssam 		close(myside);
775*10695Ssam 		dup2(hisside, tst(0, 1));
776*10695Ssam 		close(hisside);
777*10695Ssam 		execv(av[0], av);
778*10695Ssam 		_exit(1);
779*10695Ssam 	}
780*10695Ssam 	if (pid == -1)
781*10695Ssam 		return (NULL);
782*10695Ssam 	popen_pid[myside] = pid;
783*10695Ssam 	close(hisside);
784*10695Ssam 	return (fdopen(myside, mode));
785*10695Ssam }
786*10695Ssam 
787*10695Ssam pclose(ptr)
788*10695Ssam 	FILE *ptr;
789*10695Ssam {
790*10695Ssam 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
791*10695Ssam 	int status;
792*10695Ssam 
793*10695Ssam 	f = fileno(ptr);
794*10695Ssam 	fclose(ptr);
795*10695Ssam 	istat = signal(SIGINT, SIG_IGN);
796*10695Ssam 	qstat = signal(SIGQUIT, SIG_IGN);
797*10695Ssam 	hstat = signal(SIGHUP, SIG_IGN);
798*10695Ssam 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
799*10695Ssam 		;
800*10695Ssam 	if (r == -1)
801*10695Ssam 		status = -1;
802*10695Ssam 	signal(SIGINT, istat);
803*10695Ssam 	signal(SIGQUIT, qstat);
804*10695Ssam 	signal(SIGHUP, hstat);
805*10695Ssam 	return (status);
806*10695Ssam }
807*10695Ssam 
808*10695Ssam /*
809*10695Ssam  * Check user requesting login priviledges.
810*10695Ssam  * Disallow anyone mentioned in the file FTPUSERS
811*10695Ssam  * to allow people such as uucp to be avoided.
812*10695Ssam  */
813*10695Ssam checkuser(name)
814*10695Ssam 	register char *name;
815*10695Ssam {
816*10695Ssam 	char line[BUFSIZ], *index();
817*10695Ssam 	FILE *fd;
818*10695Ssam 	int found = 0;
819*10695Ssam 
820*10695Ssam 	fd = fopen(FTPUSERS, "r");
821*10695Ssam 	if (fd == NULL)
822*10695Ssam 		return (1);
823*10695Ssam 	while (fgets(line, sizeof (line), fd) != NULL) {
824*10695Ssam 		register char *cp = index(line, '\n');
825*10695Ssam 
826*10695Ssam 		if (cp)
827*10695Ssam 			*cp = '\0';
828*10695Ssam 		if (strcmp(line, name) == 0) {
829*10695Ssam 			found++;
830*10695Ssam 			break;
831*10695Ssam 		}
832*10695Ssam 	}
833*10695Ssam 	fclose(fd);
834*10695Ssam 	return (!found);
835*10695Ssam }
836