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