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