xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 13211)
1 #ifndef lint
2 static char sccsid[] = "@(#)ftpd.c	4.22 (Berkeley) 06/19/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 
13 #include <netinet/in.h>
14 
15 #include <arpa/ftp.h>
16 #include <arpa/inet.h>
17 
18 #include <stdio.h>
19 #include <signal.h>
20 #include <wait.h>
21 #include <pwd.h>
22 #include <setjmp.h>
23 #include <netdb.h>
24 #include <errno.h>
25 
26 /*
27  * File containing login names
28  * NOT to be used on this machine.
29  * Commonly used to disallow uucp.
30  */
31 #define	FTPUSERS	"/etc/ftpusers"
32 
33 extern	int errno;
34 extern	char *sys_errlist[];
35 extern	char *crypt();
36 extern	char version[];
37 extern	char *home;		/* pointer to home directory for glob */
38 extern	FILE *popen(), *fopen();
39 extern	int pclose(), fclose();
40 
41 struct	sockaddr_in ctrl_addr;
42 struct	sockaddr_in data_source;
43 struct	sockaddr_in data_dest;
44 struct	sockaddr_in his_addr;
45 
46 struct	hostent *hp;
47 
48 int	data;
49 jmp_buf	errcatch;
50 int	logged_in;
51 struct	passwd *pw;
52 int	debug;
53 int	timeout;
54 int	logging;
55 int	guest;
56 int	type;
57 int	form;
58 int	stru;			/* avoid C keyword */
59 int	mode;
60 int	usedefault = 1;		/* for data transfers */
61 char	hostname[32];
62 char	*remotehost;
63 struct	servent *sp;
64 
65 /*
66  * Timeout intervals for retrying connections
67  * to hosts that don't accept PORT cmds.  This
68  * is a kludge, but given the problems with TCP...
69  */
70 #define	SWAITMAX	90	/* wait at most 90 seconds */
71 #define	SWAITINT	5	/* interval between retries */
72 
73 int	swaitmax = SWAITMAX;
74 int	swaitint = SWAITINT;
75 
76 int	lostconn();
77 int	reapchild();
78 FILE	*getdatasock(), *dataconn();
79 char	*ntoa();
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("/", 0);
133 	(void) dup2(0, 1);
134 	if (!logging)
135 		(void) dup2(0, 2);
136 	{ int tt = open("/dev/tty", 2);
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)) < 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 #ifdef notdef
151 	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
152 		perror("ftpd: setsockopt (SO_KEEPALIVE)");
153 #endif
154 	while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
155 		perror("ftpd: bind");
156 		sleep(5);
157 	}
158 	signal(SIGCHLD, reapchild);
159 	listen(s, 10);
160 	for (;;) {
161 		int hisaddrlen = sizeof (his_addr);
162 
163 		ctrl = accept(s, &his_addr, &hisaddrlen, 0);
164 		if (ctrl < 0) {
165 			if (errno == EINTR)
166 				continue;
167 			perror("ftpd: accept");
168 			continue;
169 		}
170 		if (fork() == 0) {
171 			signal (SIGCHLD, SIG_IGN);
172 			if (logging)
173 				dolog(&his_addr);
174 			close(s);
175 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
176 			/* do telnet option negotiation here */
177 			/*
178 			 * Set up default state
179 			 */
180 			logged_in = 0;
181 			data = -1;
182 			type = TYPE_A;
183 			form = FORM_N;
184 			stru = STRU_F;
185 			mode = MODE_S;
186 			(void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr));
187 			gethostname(hostname, sizeof (hostname));
188 			reply(220, "%s FTP server (%s) ready.",
189 				hostname, version);
190 			for (;;) {
191 				setjmp(errcatch);
192 				yyparse();
193 			}
194 		}
195 		close(ctrl);
196 	}
197 }
198 
199 reapchild()
200 {
201 	union wait status;
202 
203 	while (wait3(&status, WNOHANG, 0) > 0)
204 		;
205 }
206 
207 lostconn()
208 {
209 
210 	fatal("Connection closed.");
211 }
212 
213 pass(passwd)
214 	char *passwd;
215 {
216 	char *xpasswd, *savestr();
217 	static struct passwd save;
218 
219 	if (logged_in || pw == NULL) {
220 		reply(503, "Login with USER first.");
221 		return;
222 	}
223 	if (!guest) {		/* "ftp" is only account allowed no password */
224 		xpasswd = crypt(passwd, pw->pw_passwd);
225 		if (strcmp(xpasswd, pw->pw_passwd) != 0) {
226 			reply(530, "Login incorrect.");
227 			pw = NULL;
228 			return;
229 		}
230 	}
231 	setegid(pw->pw_gid);
232 	initgroups(pw->pw_name, pw->pw_gid);
233 	if (chdir(pw->pw_dir)) {
234 		reply(550, "User %s: can't change directory to $s.",
235 			pw->pw_name, pw->pw_dir);
236 		goto bad;
237 	}
238 	if (guest && chroot(pw->pw_dir) < 0) {
239 		reply(550, "Can't set guest privileges.");
240 		goto bad;
241 	}
242 	if (!guest)
243 		reply(230, "User %s logged in.", pw->pw_name);
244 	else
245 		reply(230, "Guest login ok, access restrictions apply.");
246 	logged_in = 1;
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, linger;
365 
366 	if (data >= 0)
367 		return (fdopen(data, mode));
368 	s = socket(AF_INET, SOCK_STREAM, 0, 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 	linger = 60;			/* value ignored by system */
380 	(void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger));
381 	seteuid(pw->pw_uid);
382 	return (fdopen(s, mode));
383 bad:
384 	seteuid(pw->pw_uid);
385 	close(s);
386 	return (NULL);
387 }
388 
389 FILE *
390 dataconn(name, size, mode)
391 	char *name;
392 	off_t size;
393 	char *mode;
394 {
395 	char sizebuf[32];
396 	FILE *file;
397 	int retry = 0;
398 
399 	if (size >= 0)
400 		sprintf (sizebuf, " (%ld bytes)", size);
401 	else
402 		(void) strcpy(sizebuf, "");
403 	if (data >= 0) {
404 		reply(125, "Using existing data connection for %s%s.",
405 		    name, sizebuf);
406 		usedefault = 1;
407 		return (fdopen(data, mode));
408 	}
409 	if (usedefault)
410 		data_dest = his_addr;
411 	usedefault = 1;
412 	file = getdatasock(mode);
413 	if (file == NULL) {
414 		reply(425, "Can't create data socket (%s,%d): %s.",
415 		    ntoa(data_source.sin_addr),
416 		    ntohs(data_source.sin_port),
417 		    sys_errlist[errno]);
418 		return (NULL);
419 	}
420 	reply(150, "Opening data connection for %s (%s,%d)%s.",
421 	    name, ntoa(data_dest.sin_addr.s_addr),
422 	    ntohs(data_dest.sin_port), sizebuf);
423 	data = fileno(file);
424 	while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
425 		if (errno == EADDRINUSE && retry < swaitmax) {
426 			sleep(swaitint);
427 			retry += swaitint;
428 			continue;
429 		}
430 		reply(425, "Can't build data connection: %s.",
431 		    sys_errlist[errno]);
432 		(void) fclose(file);
433 		data = -1;
434 		return (NULL);
435 	}
436 	return (file);
437 }
438 
439 /*
440  * Tranfer the contents of "instr" to
441  * "outstr" peer using the appropriate
442  * encapulation of the date subject
443  * to Mode, Structure, and Type.
444  *
445  * NB: Form isn't handled.
446  */
447 send_data(instr, outstr)
448 	FILE *instr, *outstr;
449 {
450 	register int c;
451 	int netfd, filefd, cnt;
452 	char buf[BUFSIZ];
453 
454 	switch (type) {
455 
456 	case TYPE_A:
457 		while ((c = getc(instr)) != EOF) {
458 			if (c == '\n') {
459 				if (ferror (outstr))
460 					return (1);
461 				putc('\r', outstr);
462 			}
463 			putc(c, outstr);
464 			if (c == '\r')
465 				putc ('\0', outstr);
466 		}
467 		if (ferror (instr) || ferror (outstr))
468 			return (1);
469 		return (0);
470 
471 	case TYPE_I:
472 	case TYPE_L:
473 		netfd = fileno(outstr);
474 		filefd = fileno(instr);
475 
476 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
477 			if (write(netfd, buf, cnt) < 0)
478 				return (1);
479 		return (cnt < 0);
480 	}
481 	reply(504,"Unimplemented TYPE %d in send_data", type);
482 	return (1);
483 }
484 
485 /*
486  * Transfer data from peer to
487  * "outstr" using the appropriate
488  * encapulation of the data subject
489  * to Mode, Structure, and Type.
490  *
491  * N.B.: Form isn't handled.
492  */
493 receive_data(instr, outstr)
494 	FILE *instr, *outstr;
495 {
496 	register int c;
497 	int cnt;
498 	char buf[BUFSIZ];
499 
500 
501 	switch (type) {
502 
503 	case TYPE_I:
504 	case TYPE_L:
505 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
506 			if (write(fileno(outstr), buf, cnt) < 0)
507 				return (1);
508 		return (cnt < 0);
509 
510 	case TYPE_E:
511 		reply(504, "TYPE E not implemented.");
512 		return (1);
513 
514 	case TYPE_A:
515 		while ((c = getc(instr)) != EOF) {
516 			if (c == '\r') {
517 				if (ferror (outstr))
518 					return (1);
519 				if ((c = getc(instr)) != '\n')
520 					putc ('\r', outstr);
521 				if (c == '\0')
522 					continue;
523 			}
524 			putc (c, outstr);
525 		}
526 		if (ferror (instr) || ferror (outstr))
527 			return (1);
528 		return (0);
529 	}
530 	fatal("Unknown type in receive_data.");
531 	/*NOTREACHED*/
532 }
533 
534 fatal(s)
535 	char *s;
536 {
537 	reply(451, "Error in server: %s\n", s);
538 	reply(221, "Closing connection due to server error.");
539 	exit(0);
540 }
541 
542 reply(n, s, args)
543 	int n;
544 	char *s;
545 {
546 
547 	printf("%d ", n);
548 	_doprnt(s, &args, stdout);
549 	printf("\r\n");
550 	fflush(stdout);
551 	if (debug) {
552 		fprintf(stderr, "<--- %d ", n);
553 		_doprnt(s, &args, stderr);
554 		fprintf(stderr, "\n");
555 		fflush(stderr);
556 	}
557 }
558 
559 lreply(n, s, args)
560 	int n;
561 	char *s;
562 {
563 	printf("%d-", n);
564 	_doprnt(s, &args, stdout);
565 	printf("\r\n");
566 	fflush(stdout);
567 	if (debug) {
568 		fprintf(stderr, "<--- %d-", n);
569 		_doprnt(s, &args, stderr);
570 		fprintf(stderr, "\n");
571 	}
572 }
573 
574 replystr(s)
575 	char *s;
576 {
577 	printf("%s\r\n", s);
578 	fflush(stdout);
579 	if (debug)
580 		fprintf(stderr, "<--- %s\n", s);
581 }
582 
583 ack(s)
584 	char *s;
585 {
586 	reply(200, "%s command okay.", s);
587 }
588 
589 nack(s)
590 	char *s;
591 {
592 	reply(502, "%s command not implemented.", s);
593 }
594 
595 yyerror()
596 {
597 	reply(500, "Command not understood.");
598 }
599 
600 delete(name)
601 	char *name;
602 {
603 	struct stat st;
604 
605 	if (stat(name, &st) < 0) {
606 		reply(550, "%s: %s.", name, sys_errlist[errno]);
607 		return;
608 	}
609 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
610 		if (rmdir(name) < 0) {
611 			reply(550, "%s: %s.", name, sys_errlist[errno]);
612 			return;
613 		}
614 		goto done;
615 	}
616 	if (unlink(name) < 0) {
617 		reply(550, "%s: %s.", name, sys_errlist[errno]);
618 		return;
619 	}
620 done:
621 	ack("DELE");
622 }
623 
624 cwd(path)
625 	char *path;
626 {
627 
628 	if (chdir(path) < 0) {
629 		reply(550, "%s: %s.", path, sys_errlist[errno]);
630 		return;
631 	}
632 	ack("CWD");
633 }
634 
635 makedir(name)
636 	char *name;
637 {
638 	struct stat st;
639 	int dochown = stat(name, &st) < 0;
640 
641 	if (mkdir(name, 0777) < 0) {
642 		reply(550, "%s: %s.", name, sys_errlist[errno]);
643 		return;
644 	}
645 	if (dochown)
646 		(void) chown(name, pw->pw_uid, -1);
647 	ack("MKDIR");
648 }
649 
650 removedir(name)
651 	char *name;
652 {
653 
654 	if (rmdir(name) < 0) {
655 		reply(550, "%s: %s.", name, sys_errlist[errno]);
656 		return;
657 	}
658 	ack("RMDIR");
659 }
660 
661 pwd()
662 {
663 	char path[MAXPATHLEN + 1];
664 
665 	if (getwd(path) == NULL) {
666 		reply(451, "%s.", path);
667 		return;
668 	}
669 	reply(251, "\"%s\" is current directory.", path);
670 }
671 
672 char *
673 renamefrom(name)
674 	char *name;
675 {
676 	struct stat st;
677 
678 	if (stat(name, &st) < 0) {
679 		reply(550, "%s: %s.", name, sys_errlist[errno]);
680 		return ((char *)0);
681 	}
682 	reply(350, "File exists, ready for destination name");
683 	return (name);
684 }
685 
686 renamecmd(from, to)
687 	char *from, *to;
688 {
689 
690 	if (rename(from, to) < 0) {
691 		reply(550, "rename: %s.", sys_errlist[errno]);
692 		return;
693 	}
694 	ack("RNTO");
695 }
696 
697 /*
698  * Convert network-format internet address
699  * to base 256 d.d.d.d representation.
700  */
701 char *
702 ntoa(in)
703 	struct in_addr in;
704 {
705 	static char b[18];
706 	register char *p;
707 
708 	p = (char *)&in;
709 #define	UC(b)	(((int)b)&0xff)
710 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
711 	return (b);
712 }
713 
714 dolog(sin)
715 	struct sockaddr_in *sin;
716 {
717 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
718 		sizeof (struct in_addr), AF_INET);
719 	char *remotehost;
720 	time_t t;
721 
722 	if (hp)
723 		remotehost = hp->h_name;
724 	else
725 		remotehost = inet_ntoa(sin->sin_addr);
726 	t = time(0);
727 	fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
728 	fflush(stderr);
729 }
730 
731 /*
732  * Special version of popen which avoids
733  * call to shell.  This insures noone may
734  * create a pipe to a hidden program as a side
735  * effect of a list or dir command.
736  */
737 #define	tst(a,b)	(*mode == 'r'? (b) : (a))
738 #define	RDR	0
739 #define	WTR	1
740 static	int popen_pid[5];
741 
742 static char *
743 nextarg(cpp)
744 	char *cpp;
745 {
746 	register char *cp = cpp;
747 
748 	if (cp == 0)
749 		return (cp);
750 	while (*cp && *cp != ' ' && *cp != '\t')
751 		cp++;
752 	if (*cp == ' ' || *cp == '\t') {
753 		*cp++ = '\0';
754 		while (*cp == ' ' || *cp == '\t')
755 			cp++;
756 	}
757 	if (cp == cpp)
758 		return ((char *)0);
759 	return (cp);
760 }
761 
762 FILE *
763 popen(cmd, mode)
764 	char *cmd, *mode;
765 {
766 	int p[2], ac, gac;
767 	register myside, hisside, pid;
768 	char *av[20], *gav[512];
769 	register char *cp;
770 
771 	if (pipe(p) < 0)
772 		return (NULL);
773 	cp = cmd, ac = 0;
774 	/* break up string into pieces */
775 	do {
776 		av[ac++] = cp;
777 		cp = nextarg(cp);
778 	} while (cp && *cp && ac < 20);
779 	av[ac] = (char *)0;
780 	gav[0] = av[0];
781 	/* glob each piece */
782 	for (gac = ac = 1; av[ac] != NULL; ac++) {
783 		char **pop;
784 		extern char **glob();
785 
786 		pop = glob(av[ac]);
787 		if (pop) {
788 			av[ac] = (char *)pop;		/* save to free later */
789 			while (*pop && gac < 512)
790 				gav[gac++] = *pop++;
791 		}
792 	}
793 	gav[gac] = (char *)0;
794 	myside = tst(p[WTR], p[RDR]);
795 	hisside = tst(p[RDR], p[WTR]);
796 	if ((pid = fork()) == 0) {
797 		/* myside and hisside reverse roles in child */
798 		close(myside);
799 		dup2(hisside, tst(0, 1));
800 		close(hisside);
801 		execv(gav[0], gav);
802 		_exit(1);
803 	}
804 	for (ac = 1; av[ac] != NULL; ac++)
805 		blkfree((char **)av[ac]);
806 	if (pid == -1)
807 		return (NULL);
808 	popen_pid[myside] = pid;
809 	close(hisside);
810 	return (fdopen(myside, mode));
811 }
812 
813 pclose(ptr)
814 	FILE *ptr;
815 {
816 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
817 	int status;
818 
819 	f = fileno(ptr);
820 	fclose(ptr);
821 	istat = signal(SIGINT, SIG_IGN);
822 	qstat = signal(SIGQUIT, SIG_IGN);
823 	hstat = signal(SIGHUP, SIG_IGN);
824 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
825 		;
826 	if (r == -1)
827 		status = -1;
828 	signal(SIGINT, istat);
829 	signal(SIGQUIT, qstat);
830 	signal(SIGHUP, hstat);
831 	return (status);
832 }
833 
834 /*
835  * Check user requesting login priviledges.
836  * Disallow anyone mentioned in the file FTPUSERS
837  * to allow people such as uucp to be avoided.
838  */
839 checkuser(name)
840 	register char *name;
841 {
842 	char line[BUFSIZ], *index();
843 	FILE *fd;
844 	int found = 0;
845 
846 	fd = fopen(FTPUSERS, "r");
847 	if (fd == NULL)
848 		return (1);
849 	while (fgets(line, sizeof (line), fd) != NULL) {
850 		register char *cp = index(line, '\n');
851 
852 		if (cp)
853 			*cp = '\0';
854 		if (strcmp(line, name) == 0) {
855 			found++;
856 			break;
857 		}
858 	}
859 	fclose(fd);
860 	return (!found);
861 }
862