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