xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 14089)
1 #ifndef lint
2 static char sccsid[] = "@(#)ftpd.c	4.25 (Berkeley) 07/24/83";
3 #endif
4 
5 /*
6  * FTP server.
7  */
8 #include <sys/param.h>
9 #include <sys/stat.h>
10 #include <sys/ioctl.h>
11 #include <sys/socket.h>
12 #include <sys/file.h>
13 #include <sys/wait.h>
14 
15 #include <netinet/in.h>
16 
17 #include <arpa/ftp.h>
18 #include <arpa/inet.h>
19 
20 #include <stdio.h>
21 #include <signal.h>
22 #include <pwd.h>
23 #include <setjmp.h>
24 #include <netdb.h>
25 #include <errno.h>
26 
27 /*
28  * File containing login names
29  * NOT to be used on this machine.
30  * Commonly used to disallow uucp.
31  */
32 #define	FTPUSERS	"/etc/ftpusers"
33 
34 extern	int errno;
35 extern	char *sys_errlist[];
36 extern	char *crypt();
37 extern	char version[];
38 extern	char *home;		/* pointer to home directory for glob */
39 extern	FILE *popen(), *fopen();
40 extern	int pclose(), fclose();
41 
42 struct	sockaddr_in ctrl_addr;
43 struct	sockaddr_in data_source;
44 struct	sockaddr_in data_dest;
45 struct	sockaddr_in his_addr;
46 
47 struct	hostent *hp;
48 
49 int	data;
50 jmp_buf	errcatch;
51 int	logged_in;
52 struct	passwd *pw;
53 int	debug;
54 int	timeout;
55 int	logging;
56 int	guest;
57 int	type;
58 int	form;
59 int	stru;			/* avoid C keyword */
60 int	mode;
61 int	usedefault = 1;		/* for data transfers */
62 char	hostname[32];
63 char	remotehost[32];
64 struct	servent *sp;
65 
66 /*
67  * Timeout intervals for retrying connections
68  * to hosts that don't accept PORT cmds.  This
69  * is a kludge, but given the problems with TCP...
70  */
71 #define	SWAITMAX	90	/* wait at most 90 seconds */
72 #define	SWAITINT	5	/* interval between retries */
73 
74 int	swaitmax = SWAITMAX;
75 int	swaitint = SWAITINT;
76 
77 int	lostconn();
78 int	reapchild();
79 FILE	*getdatasock(), *dataconn();
80 
81 main(argc, argv)
82 	int argc;
83 	char *argv[];
84 {
85 	int ctrl, s, options = 0;
86 	char *cp;
87 
88 	sp = getservbyname("ftp", "tcp");
89 	if (sp == 0) {
90 		fprintf(stderr, "ftpd: ftp/tcp: unknown service\n");
91 		exit(1);
92 	}
93 	ctrl_addr.sin_port = sp->s_port;
94 	data_source.sin_port = htons(ntohs(sp->s_port) - 1);
95 	signal(SIGPIPE, lostconn);
96 	debug = 0;
97 	argc--, argv++;
98 	while (argc > 0 && *argv[0] == '-') {
99 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
100 
101 		case 'v':
102 			debug = 1;
103 			break;
104 
105 		case 'd':
106 			debug = 1;
107 			options |= SO_DEBUG;
108 			break;
109 
110 		case 'l':
111 			logging = 1;
112 			break;
113 
114 		case 't':
115 			timeout = atoi(++cp);
116 			goto nextopt;
117 			break;
118 
119 		default:
120 			fprintf(stderr, "Unknown flag -%c ignored.\n", *cp);
121 			break;
122 		}
123 nextopt:
124 		argc--, argv++;
125 	}
126 #ifndef DEBUG
127 	if (fork())
128 		exit(0);
129 	for (s = 0; s < 10; s++)
130 		if (!logging || (s != 2))
131 			(void) close(s);
132 	(void) open("/", O_RDONLY);
133 	(void) dup2(0, 1);
134 	if (!logging)
135 		(void) dup2(0, 2);
136 	{ int tt = open("/dev/tty", O_RDWR);
137 	  if (tt > 0) {
138 		ioctl(tt, TIOCNOTTY, 0);
139 		close(tt);
140 	  }
141 	}
142 #endif
143 	while ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
144 		perror("ftpd: socket");
145 		sleep(5);
146 	}
147 	if (options & SO_DEBUG)
148 		if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
149 			perror("ftpd: setsockopt (SO_DEBUG)");
150 	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
151 		perror("ftpd: setsockopt (SO_KEEPALIVE)");
152 	while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
153 		perror("ftpd: bind");
154 		sleep(5);
155 	}
156 	signal(SIGCHLD, reapchild);
157 	listen(s, 10);
158 	for (;;) {
159 		int hisaddrlen = sizeof (his_addr);
160 
161 		ctrl = accept(s, &his_addr, &hisaddrlen, 0);
162 		if (ctrl < 0) {
163 			if (errno == EINTR)
164 				continue;
165 			perror("ftpd: accept");
166 			continue;
167 		}
168 		if (fork() == 0) {
169 			signal (SIGCHLD, SIG_IGN);
170 			dolog(&his_addr);
171 			close(s);
172 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
173 			/* do telnet option negotiation here */
174 			/*
175 			 * Set up default state
176 			 */
177 			logged_in = 0;
178 			data = -1;
179 			type = TYPE_A;
180 			form = FORM_N;
181 			stru = STRU_F;
182 			mode = MODE_S;
183 			(void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr));
184 			gethostname(hostname, sizeof (hostname));
185 			reply(220, "%s FTP server (%s) ready.",
186 				hostname, version);
187 			for (;;) {
188 				setjmp(errcatch);
189 				yyparse();
190 			}
191 		}
192 		close(ctrl);
193 	}
194 }
195 
196 reapchild()
197 {
198 	union wait status;
199 
200 	while (wait3(&status, WNOHANG, 0) > 0)
201 		;
202 }
203 
204 lostconn()
205 {
206 
207 	if (debug)
208 		fprintf(stderr, "Lost connection.\n");
209 	dologout(-1);
210 }
211 
212 pass(passwd)
213 	char *passwd;
214 {
215 	char *xpasswd, *savestr();
216 	static struct passwd save;
217 
218 	if (logged_in || pw == NULL) {
219 		reply(503, "Login with USER first.");
220 		return;
221 	}
222 	if (!guest) {		/* "ftp" is only account allowed no password */
223 		xpasswd = crypt(passwd, pw->pw_passwd);
224 		if (strcmp(xpasswd, pw->pw_passwd) != 0) {
225 			reply(530, "Login incorrect.");
226 			pw = NULL;
227 			return;
228 		}
229 	}
230 	setegid(pw->pw_gid);
231 	initgroups(pw->pw_name, pw->pw_gid);
232 	if (chdir(pw->pw_dir)) {
233 		reply(550, "User %s: can't change directory to $s.",
234 			pw->pw_name, pw->pw_dir);
235 		goto bad;
236 	}
237 	if (guest && chroot(pw->pw_dir) < 0) {
238 		reply(550, "Can't set guest privileges.");
239 		goto bad;
240 	}
241 	if (!guest)
242 		reply(230, "User %s logged in.", pw->pw_name);
243 	else
244 		reply(230, "Guest login ok, access restrictions apply.");
245 	logged_in = 1;
246 	dologin(pw);
247 	seteuid(pw->pw_uid);
248 	/*
249 	 * Save everything so globbing doesn't
250 	 * clobber the fields.
251 	 */
252 	save = *pw;
253 	save.pw_name = savestr(pw->pw_name);
254 	save.pw_passwd = savestr(pw->pw_passwd);
255 	save.pw_comment = savestr(pw->pw_comment);
256 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
257 	save.pw_dir = savestr(pw->pw_dir);
258 	save.pw_shell = savestr(pw->pw_shell);
259 	pw = &save;
260 	home = pw->pw_dir;		/* home dir for globbing */
261 	return;
262 bad:
263 	seteuid(0);
264 	pw = NULL;
265 }
266 
267 char *
268 savestr(s)
269 	char *s;
270 {
271 	char *malloc();
272 	char *new = malloc(strlen(s) + 1);
273 
274 	if (new != NULL)
275 		strcpy(new, s);
276 	return (new);
277 }
278 
279 retrieve(cmd, name)
280 	char *cmd, *name;
281 {
282 	FILE *fin, *dout;
283 	struct stat st;
284 	int (*closefunc)();
285 
286 	if (cmd == 0) {
287 #ifdef notdef
288 		/* no remote command execution -- it's a security hole */
289 		if (*name == '|')
290 			fin = popen(name + 1, "r"), closefunc = pclose;
291 		else
292 #endif
293 			fin = fopen(name, "r"), closefunc = fclose;
294 	} else {
295 		char line[BUFSIZ];
296 
297 		sprintf(line, cmd, name), name = line;
298 		fin = popen(line, "r"), closefunc = pclose;
299 	}
300 	if (fin == NULL) {
301 		if (errno != 0)
302 			reply(550, "%s: %s.", name, sys_errlist[errno]);
303 		return;
304 	}
305 	st.st_size = 0;
306 	if (cmd == 0 &&
307 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
308 		reply(550, "%s: not a plain file.", name);
309 		goto done;
310 	}
311 	dout = dataconn(name, st.st_size, "w");
312 	if (dout == NULL)
313 		goto done;
314 	if (send_data(fin, dout) || ferror(dout))
315 		reply(550, "%s: %s.", name, sys_errlist[errno]);
316 	else
317 		reply(226, "Transfer complete.");
318 	fclose(dout), data = -1;
319 done:
320 	(*closefunc)(fin);
321 }
322 
323 store(name, mode)
324 	char *name, *mode;
325 {
326 	FILE *fout, *din;
327 	int (*closefunc)(), dochown = 0;
328 
329 #ifdef notdef
330 	/* no remote command execution -- it's a security hole */
331 	if (name[0] == '|')
332 		fout = popen(&name[1], "w"), closefunc = pclose;
333 	else
334 #endif
335 	{
336 		struct stat st;
337 
338 		if (stat(name, &st) < 0)
339 			dochown++;
340 		fout = fopen(name, mode), closefunc = fclose;
341 	}
342 	if (fout == NULL) {
343 		reply(550, "%s: %s.", name, sys_errlist[errno]);
344 		return;
345 	}
346 	din = dataconn(name, (off_t)-1, "r");
347 	if (din == NULL)
348 		goto done;
349 	if (receive_data(din, fout) || ferror(fout))
350 		reply(550, "%s: %s.", name, sys_errlist[errno]);
351 	else
352 		reply(226, "Transfer complete.");
353 	fclose(din), data = -1;
354 done:
355 	if (dochown)
356 		(void) chown(name, pw->pw_uid, -1);
357 	(*closefunc)(fout);
358 }
359 
360 FILE *
361 getdatasock(mode)
362 	char *mode;
363 {
364 	int s;
365 
366 	if (data >= 0)
367 		return (fdopen(data, mode));
368 	s = socket(AF_INET, SOCK_STREAM, 0);
369 	if (s < 0)
370 		return (NULL);
371 	seteuid(0);
372 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
373 		goto bad;
374 	/* anchor socket to avoid multi-homing problems */
375 	data_source.sin_family = AF_INET;
376 	data_source.sin_addr = ctrl_addr.sin_addr;
377 	if (bind(s, &data_source, sizeof (data_source), 0) < 0)
378 		goto bad;
379 	seteuid(pw->pw_uid);
380 	return (fdopen(s, mode));
381 bad:
382 	seteuid(pw->pw_uid);
383 	close(s);
384 	return (NULL);
385 }
386 
387 FILE *
388 dataconn(name, size, mode)
389 	char *name;
390 	off_t size;
391 	char *mode;
392 {
393 	char sizebuf[32];
394 	FILE *file;
395 	int retry = 0;
396 
397 	if (size >= 0)
398 		sprintf (sizebuf, " (%ld bytes)", size);
399 	else
400 		(void) strcpy(sizebuf, "");
401 	if (data >= 0) {
402 		reply(125, "Using existing data connection for %s%s.",
403 		    name, sizebuf);
404 		usedefault = 1;
405 		return (fdopen(data, mode));
406 	}
407 	if (usedefault)
408 		data_dest = his_addr;
409 	usedefault = 1;
410 	file = getdatasock(mode);
411 	if (file == NULL) {
412 		reply(425, "Can't create data socket (%s,%d): %s.",
413 		    inet_ntoa(data_source.sin_addr),
414 		    ntohs(data_source.sin_port),
415 		    sys_errlist[errno]);
416 		return (NULL);
417 	}
418 	reply(150, "Opening data connection for %s (%s,%d)%s.",
419 	    name, inet_ntoa(data_dest.sin_addr.s_addr),
420 	    ntohs(data_dest.sin_port), sizebuf);
421 	data = fileno(file);
422 	while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
423 		if (errno == EADDRINUSE && retry < swaitmax) {
424 			sleep(swaitint);
425 			retry += swaitint;
426 			continue;
427 		}
428 		reply(425, "Can't build data connection: %s.",
429 		    sys_errlist[errno]);
430 		(void) fclose(file);
431 		data = -1;
432 		return (NULL);
433 	}
434 	return (file);
435 }
436 
437 /*
438  * Tranfer the contents of "instr" to
439  * "outstr" peer using the appropriate
440  * encapulation of the date subject
441  * to Mode, Structure, and Type.
442  *
443  * NB: Form isn't handled.
444  */
445 send_data(instr, outstr)
446 	FILE *instr, *outstr;
447 {
448 	register int c;
449 	int netfd, filefd, cnt;
450 	char buf[BUFSIZ];
451 
452 	switch (type) {
453 
454 	case TYPE_A:
455 		while ((c = getc(instr)) != EOF) {
456 			if (c == '\n') {
457 				if (ferror (outstr))
458 					return (1);
459 				putc('\r', outstr);
460 			}
461 			putc(c, outstr);
462 			if (c == '\r')
463 				putc ('\0', outstr);
464 		}
465 		if (ferror (instr) || ferror (outstr))
466 			return (1);
467 		return (0);
468 
469 	case TYPE_I:
470 	case TYPE_L:
471 		netfd = fileno(outstr);
472 		filefd = fileno(instr);
473 
474 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
475 			if (write(netfd, buf, cnt) < 0)
476 				return (1);
477 		return (cnt < 0);
478 	}
479 	reply(504,"Unimplemented TYPE %d in send_data", type);
480 	return (1);
481 }
482 
483 /*
484  * Transfer data from peer to
485  * "outstr" using the appropriate
486  * encapulation of the data subject
487  * to Mode, Structure, and Type.
488  *
489  * N.B.: Form isn't handled.
490  */
491 receive_data(instr, outstr)
492 	FILE *instr, *outstr;
493 {
494 	register int c;
495 	int cnt;
496 	char buf[BUFSIZ];
497 
498 
499 	switch (type) {
500 
501 	case TYPE_I:
502 	case TYPE_L:
503 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
504 			if (write(fileno(outstr), buf, cnt) < 0)
505 				return (1);
506 		return (cnt < 0);
507 
508 	case TYPE_E:
509 		reply(504, "TYPE E not implemented.");
510 		return (1);
511 
512 	case TYPE_A:
513 		while ((c = getc(instr)) != EOF) {
514 			if (c == '\r') {
515 				if (ferror (outstr))
516 					return (1);
517 				if ((c = getc(instr)) != '\n')
518 					putc ('\r', outstr);
519 				if (c == '\0')
520 					continue;
521 			}
522 			putc (c, outstr);
523 		}
524 		if (ferror (instr) || ferror (outstr))
525 			return (1);
526 		return (0);
527 	}
528 	fatal("Unknown type in receive_data.");
529 	/*NOTREACHED*/
530 }
531 
532 fatal(s)
533 	char *s;
534 {
535 	reply(451, "Error in server: %s\n", s);
536 	reply(221, "Closing connection due to server error.");
537 	dologout(0);
538 }
539 
540 reply(n, s, args)
541 	int n;
542 	char *s;
543 {
544 
545 	printf("%d ", n);
546 	_doprnt(s, &args, stdout);
547 	printf("\r\n");
548 	fflush(stdout);
549 	if (debug) {
550 		fprintf(stderr, "<--- %d ", n);
551 		_doprnt(s, &args, stderr);
552 		fprintf(stderr, "\n");
553 		fflush(stderr);
554 	}
555 }
556 
557 lreply(n, s, args)
558 	int n;
559 	char *s;
560 {
561 	printf("%d-", n);
562 	_doprnt(s, &args, stdout);
563 	printf("\r\n");
564 	fflush(stdout);
565 	if (debug) {
566 		fprintf(stderr, "<--- %d-", n);
567 		_doprnt(s, &args, stderr);
568 		fprintf(stderr, "\n");
569 	}
570 }
571 
572 replystr(s)
573 	char *s;
574 {
575 	printf("%s\r\n", s);
576 	fflush(stdout);
577 	if (debug)
578 		fprintf(stderr, "<--- %s\n", s);
579 }
580 
581 ack(s)
582 	char *s;
583 {
584 	reply(200, "%s command okay.", s);
585 }
586 
587 nack(s)
588 	char *s;
589 {
590 	reply(502, "%s command not implemented.", s);
591 }
592 
593 yyerror()
594 {
595 	reply(500, "Command not understood.");
596 }
597 
598 delete(name)
599 	char *name;
600 {
601 	struct stat st;
602 
603 	if (stat(name, &st) < 0) {
604 		reply(550, "%s: %s.", name, sys_errlist[errno]);
605 		return;
606 	}
607 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
608 		if (rmdir(name) < 0) {
609 			reply(550, "%s: %s.", name, sys_errlist[errno]);
610 			return;
611 		}
612 		goto done;
613 	}
614 	if (unlink(name) < 0) {
615 		reply(550, "%s: %s.", name, sys_errlist[errno]);
616 		return;
617 	}
618 done:
619 	ack("DELE");
620 }
621 
622 cwd(path)
623 	char *path;
624 {
625 
626 	if (chdir(path) < 0) {
627 		reply(550, "%s: %s.", path, sys_errlist[errno]);
628 		return;
629 	}
630 	ack("CWD");
631 }
632 
633 makedir(name)
634 	char *name;
635 {
636 	struct stat st;
637 	int dochown = stat(name, &st) < 0;
638 
639 	if (mkdir(name, 0777) < 0) {
640 		reply(550, "%s: %s.", name, sys_errlist[errno]);
641 		return;
642 	}
643 	if (dochown)
644 		(void) chown(name, pw->pw_uid, -1);
645 	ack("MKDIR");
646 }
647 
648 removedir(name)
649 	char *name;
650 {
651 
652 	if (rmdir(name) < 0) {
653 		reply(550, "%s: %s.", name, sys_errlist[errno]);
654 		return;
655 	}
656 	ack("RMDIR");
657 }
658 
659 pwd()
660 {
661 	char path[MAXPATHLEN + 1];
662 
663 	if (getwd(path) == NULL) {
664 		reply(451, "%s.", path);
665 		return;
666 	}
667 	reply(251, "\"%s\" is current directory.", path);
668 }
669 
670 char *
671 renamefrom(name)
672 	char *name;
673 {
674 	struct stat st;
675 
676 	if (stat(name, &st) < 0) {
677 		reply(550, "%s: %s.", name, sys_errlist[errno]);
678 		return ((char *)0);
679 	}
680 	reply(350, "File exists, ready for destination name");
681 	return (name);
682 }
683 
684 renamecmd(from, to)
685 	char *from, *to;
686 {
687 
688 	if (rename(from, to) < 0) {
689 		reply(550, "rename: %s.", sys_errlist[errno]);
690 		return;
691 	}
692 	ack("RNTO");
693 }
694 
695 dolog(sin)
696 	struct sockaddr_in *sin;
697 {
698 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
699 		sizeof (struct in_addr), AF_INET);
700 	time_t t;
701 
702 	if (hp) {
703 		strncpy(remotehost, hp->h_name, sizeof (remotehost));
704 		endhostent();
705 	} else
706 		strncpy(remotehost, inet_ntoa(sin->sin_addr),
707 		    sizeof (remotehost));
708 	if (!logging)
709 		return;
710 	t = time(0);
711 	fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
712 	fflush(stderr);
713 }
714 
715 #include <utmp.h>
716 
717 #define	SCPYN(a, b)	strncpy(a, b, sizeof (a))
718 struct	utmp utmp;
719 
720 /*
721  * Record login in wtmp file.
722  */
723 dologin(pw)
724 	struct passwd *pw;
725 {
726 	int wtmp;
727 	char line[32];
728 
729 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
730 	if (wtmp >= 0) {
731 		/* hack, but must be unique and no tty line */
732 		sprintf(line, "ftp%d", getpid());
733 		SCPYN(utmp.ut_line, line);
734 		SCPYN(utmp.ut_name, pw->pw_name);
735 		SCPYN(utmp.ut_host, remotehost);
736 		utmp.ut_time = time(0);
737 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
738 		(void) close(wtmp);
739 	}
740 }
741 
742 /*
743  * Record logout in wtmp file
744  * and exit with supplied status.
745  */
746 dologout(status)
747 	int status;
748 {
749 	int wtmp;
750 
751 	if (!logged_in)
752 		return;
753 	seteuid(0);
754 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
755 	if (wtmp >= 0) {
756 		SCPYN(utmp.ut_name, "");
757 		SCPYN(utmp.ut_host, "");
758 		utmp.ut_time = time(0);
759 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
760 		(void) close(wtmp);
761 	}
762 	exit(status);
763 }
764 
765 /*
766  * Special version of popen which avoids
767  * call to shell.  This insures noone may
768  * create a pipe to a hidden program as a side
769  * effect of a list or dir command.
770  */
771 #define	tst(a,b)	(*mode == 'r'? (b) : (a))
772 #define	RDR	0
773 #define	WTR	1
774 static	int popen_pid[5];
775 
776 static char *
777 nextarg(cpp)
778 	char *cpp;
779 {
780 	register char *cp = cpp;
781 
782 	if (cp == 0)
783 		return (cp);
784 	while (*cp && *cp != ' ' && *cp != '\t')
785 		cp++;
786 	if (*cp == ' ' || *cp == '\t') {
787 		*cp++ = '\0';
788 		while (*cp == ' ' || *cp == '\t')
789 			cp++;
790 	}
791 	if (cp == cpp)
792 		return ((char *)0);
793 	return (cp);
794 }
795 
796 FILE *
797 popen(cmd, mode)
798 	char *cmd, *mode;
799 {
800 	int p[2], ac, gac;
801 	register myside, hisside, pid;
802 	char *av[20], *gav[512];
803 	register char *cp;
804 
805 	if (pipe(p) < 0)
806 		return (NULL);
807 	cp = cmd, ac = 0;
808 	/* break up string into pieces */
809 	do {
810 		av[ac++] = cp;
811 		cp = nextarg(cp);
812 	} while (cp && *cp && ac < 20);
813 	av[ac] = (char *)0;
814 	gav[0] = av[0];
815 	/* glob each piece */
816 	for (gac = ac = 1; av[ac] != NULL; ac++) {
817 		char **pop;
818 		extern char **glob();
819 
820 		pop = glob(av[ac]);
821 		if (pop) {
822 			av[ac] = (char *)pop;		/* save to free later */
823 			while (*pop && gac < 512)
824 				gav[gac++] = *pop++;
825 		}
826 	}
827 	gav[gac] = (char *)0;
828 	myside = tst(p[WTR], p[RDR]);
829 	hisside = tst(p[RDR], p[WTR]);
830 	if ((pid = fork()) == 0) {
831 		/* myside and hisside reverse roles in child */
832 		close(myside);
833 		dup2(hisside, tst(0, 1));
834 		close(hisside);
835 		execv(gav[0], gav);
836 		_exit(1);
837 	}
838 	for (ac = 1; av[ac] != NULL; ac++)
839 		blkfree((char **)av[ac]);
840 	if (pid == -1)
841 		return (NULL);
842 	popen_pid[myside] = pid;
843 	close(hisside);
844 	return (fdopen(myside, mode));
845 }
846 
847 pclose(ptr)
848 	FILE *ptr;
849 {
850 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
851 	int status;
852 
853 	f = fileno(ptr);
854 	fclose(ptr);
855 	istat = signal(SIGINT, SIG_IGN);
856 	qstat = signal(SIGQUIT, SIG_IGN);
857 	hstat = signal(SIGHUP, SIG_IGN);
858 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
859 		;
860 	if (r == -1)
861 		status = -1;
862 	signal(SIGINT, istat);
863 	signal(SIGQUIT, qstat);
864 	signal(SIGHUP, hstat);
865 	return (status);
866 }
867 
868 /*
869  * Check user requesting login priviledges.
870  * Disallow anyone mentioned in the file FTPUSERS
871  * to allow people such as uucp to be avoided.
872  */
873 checkuser(name)
874 	register char *name;
875 {
876 	char line[BUFSIZ], *index();
877 	FILE *fd;
878 	int found = 0;
879 
880 	fd = fopen(FTPUSERS, "r");
881 	if (fd == NULL)
882 		return (1);
883 	while (fgets(line, sizeof (line), fd) != NULL) {
884 		register char *cp = index(line, '\n');
885 
886 		if (cp)
887 			*cp = '\0';
888 		if (strcmp(line, name) == 0) {
889 			found++;
890 			break;
891 		}
892 	}
893 	fclose(fd);
894 	return (!found);
895 }
896