xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 13152)
1 #ifndef lint
2 static char sccsid[] = "@(#)ftpd.c	4.21 (Berkeley) 06/15/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 			(void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr));
186 			gethostname(hostname, sizeof (hostname));
187 			reply(220, "%s FTP server (%s) ready.",
188 				hostname, version);
189 			for (;;) {
190 				setjmp(errcatch);
191 				yyparse();
192 			}
193 		}
194 		close(ctrl);
195 	}
196 }
197 
198 reapchild()
199 {
200 	union wait status;
201 
202 	while (wait3(&status, WNOHANG, 0) > 0)
203 		;
204 }
205 
206 lostconn()
207 {
208 
209 	fatal("Connection closed.");
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 	seteuid(pw->pw_uid);
247 	/*
248 	 * Save everything so globbing doesn't
249 	 * clobber the fields.
250 	 */
251 	save = *pw;
252 	save.pw_name = savestr(pw->pw_name);
253 	save.pw_passwd = savestr(pw->pw_passwd);
254 	save.pw_comment = savestr(pw->pw_comment);
255 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
256 	save.pw_dir = savestr(pw->pw_dir);
257 	save.pw_shell = savestr(pw->pw_shell);
258 	pw = &save;
259 	home = pw->pw_dir;		/* home dir for globbing */
260 	return;
261 bad:
262 	seteuid(0);
263 	pw = NULL;
264 }
265 
266 char *
267 savestr(s)
268 	char *s;
269 {
270 	char *malloc();
271 	char *new = malloc(strlen(s) + 1);
272 
273 	if (new != NULL)
274 		strcpy(new, s);
275 	return (new);
276 }
277 
278 retrieve(cmd, name)
279 	char *cmd, *name;
280 {
281 	FILE *fin, *dout;
282 	struct stat st;
283 	int (*closefunc)();
284 
285 	if (cmd == 0) {
286 #ifdef notdef
287 		/* no remote command execution -- it's a security hole */
288 		if (*name == '|')
289 			fin = popen(name + 1, "r"), closefunc = pclose;
290 		else
291 #endif
292 			fin = fopen(name, "r"), closefunc = fclose;
293 	} else {
294 		char line[BUFSIZ];
295 
296 		sprintf(line, cmd, name), name = line;
297 		fin = popen(line, "r"), closefunc = pclose;
298 	}
299 	if (fin == NULL) {
300 		if (errno != 0)
301 			reply(550, "%s: %s.", name, sys_errlist[errno]);
302 		return;
303 	}
304 	st.st_size = 0;
305 	if (cmd == 0 &&
306 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
307 		reply(550, "%s: not a plain file.", name);
308 		goto done;
309 	}
310 	dout = dataconn(name, st.st_size, "w");
311 	if (dout == NULL)
312 		goto done;
313 	if (send_data(fin, dout) || ferror(dout))
314 		reply(550, "%s: %s.", name, sys_errlist[errno]);
315 	else
316 		reply(226, "Transfer complete.");
317 	fclose(dout), data = -1;
318 done:
319 	(*closefunc)(fin);
320 }
321 
322 store(name, mode)
323 	char *name, *mode;
324 {
325 	FILE *fout, *din;
326 	int (*closefunc)(), dochown = 0;
327 
328 #ifdef notdef
329 	/* no remote command execution -- it's a security hole */
330 	if (name[0] == '|')
331 		fout = popen(&name[1], "w"), closefunc = pclose;
332 	else
333 #endif
334 	{
335 		struct stat st;
336 
337 		if (stat(name, &st) < 0)
338 			dochown++;
339 		fout = fopen(name, mode), closefunc = fclose;
340 	}
341 	if (fout == NULL) {
342 		reply(550, "%s: %s.", name, sys_errlist[errno]);
343 		return;
344 	}
345 	din = dataconn(name, (off_t)-1, "r");
346 	if (din == NULL)
347 		goto done;
348 	if (receive_data(din, fout) || ferror(fout))
349 		reply(550, "%s: %s.", name, sys_errlist[errno]);
350 	else
351 		reply(226, "Transfer complete.");
352 	fclose(din), data = -1;
353 done:
354 	if (dochown)
355 		(void) chown(name, pw->pw_uid, -1);
356 	(*closefunc)(fout);
357 }
358 
359 FILE *
360 getdatasock(mode)
361 	char *mode;
362 {
363 	int s, linger;
364 
365 	if (data >= 0)
366 		return (fdopen(data, mode));
367 	s = socket(AF_INET, SOCK_STREAM, 0, 0);
368 	if (s < 0)
369 		return (NULL);
370 	seteuid(0);
371 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
372 		goto bad;
373 	/* anchor socket to avoid multi-homing problems */
374 	data_source.sin_family = AF_INET;
375 	data_source.sin_addr = ctrl_addr.sin_addr;
376 	if (bind(s, &data_source, sizeof (data_source), 0) < 0)
377 		goto bad;
378 	linger = 60;			/* value ignored by system */
379 	(void) setsockopt(s, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger));
380 	seteuid(pw->pw_uid);
381 	return (fdopen(s, mode));
382 bad:
383 	seteuid(pw->pw_uid);
384 	close(s);
385 	return (NULL);
386 }
387 
388 FILE *
389 dataconn(name, size, mode)
390 	char *name;
391 	off_t size;
392 	char *mode;
393 {
394 	char sizebuf[32];
395 	FILE *file;
396 	int retry = 0;
397 
398 	if (size >= 0)
399 		sprintf (sizebuf, " (%ld bytes)", size);
400 	else
401 		(void) strcpy(sizebuf, "");
402 	if (data >= 0) {
403 		reply(125, "Using existing data connection for %s%s.",
404 		    name, sizebuf);
405 		usedefault = 1;
406 		return (fdopen(data, mode));
407 	}
408 	if (usedefault)
409 		data_dest = his_addr;
410 	usedefault = 1;
411 	file = getdatasock(mode);
412 	if (file == NULL) {
413 		reply(425, "Can't create data socket (%s,%d): %s.",
414 		    ntoa(data_source.sin_addr),
415 		    ntohs(data_source.sin_port),
416 		    sys_errlist[errno]);
417 		return (NULL);
418 	}
419 	reply(150, "Opening data connection for %s (%s,%d)%s.",
420 	    name, ntoa(data_dest.sin_addr.s_addr),
421 	    ntohs(data_dest.sin_port), sizebuf);
422 	data = fileno(file);
423 	while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
424 		if (errno == EADDRINUSE && retry < swaitmax) {
425 			sleep(swaitint);
426 			retry += swaitint;
427 			continue;
428 		}
429 		reply(425, "Can't build data connection: %s.",
430 		    sys_errlist[errno]);
431 		(void) fclose(file);
432 		data = -1;
433 		return (NULL);
434 	}
435 	return (file);
436 }
437 
438 /*
439  * Tranfer the contents of "instr" to
440  * "outstr" peer using the appropriate
441  * encapulation of the date subject
442  * to Mode, Structure, and Type.
443  *
444  * NB: Form isn't handled.
445  */
446 send_data(instr, outstr)
447 	FILE *instr, *outstr;
448 {
449 	register int c;
450 	int netfd, filefd, cnt;
451 	char buf[BUFSIZ];
452 
453 	switch (type) {
454 
455 	case TYPE_A:
456 		while ((c = getc(instr)) != EOF) {
457 			if (c == '\n') {
458 				if (ferror (outstr))
459 					return (1);
460 				putc('\r', outstr);
461 			}
462 			putc(c, outstr);
463 			if (c == '\r')
464 				putc ('\0', outstr);
465 		}
466 		if (ferror (instr) || ferror (outstr))
467 			return (1);
468 		return (0);
469 
470 	case TYPE_I:
471 	case TYPE_L:
472 		netfd = fileno(outstr);
473 		filefd = fileno(instr);
474 
475 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
476 			if (write(netfd, buf, cnt) < 0)
477 				return (1);
478 		return (cnt < 0);
479 	}
480 	reply(504,"Unimplemented TYPE %d in send_data", type);
481 	return (1);
482 }
483 
484 /*
485  * Transfer data from peer to
486  * "outstr" using the appropriate
487  * encapulation of the data subject
488  * to Mode, Structure, and Type.
489  *
490  * N.B.: Form isn't handled.
491  */
492 receive_data(instr, outstr)
493 	FILE *instr, *outstr;
494 {
495 	register int c;
496 	int cnt;
497 	char buf[BUFSIZ];
498 
499 
500 	switch (type) {
501 
502 	case TYPE_I:
503 	case TYPE_L:
504 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
505 			if (write(fileno(outstr), buf, cnt) < 0)
506 				return (1);
507 		return (cnt < 0);
508 
509 	case TYPE_E:
510 		reply(504, "TYPE E not implemented.");
511 		return (1);
512 
513 	case TYPE_A:
514 		while ((c = getc(instr)) != EOF) {
515 			if (c == '\r') {
516 				if (ferror (outstr))
517 					return (1);
518 				if ((c = getc(instr)) != '\n')
519 					putc ('\r', outstr);
520 				if (c == '\0')
521 					continue;
522 			}
523 			putc (c, outstr);
524 		}
525 		if (ferror (instr) || ferror (outstr))
526 			return (1);
527 		return (0);
528 	}
529 	fatal("Unknown type in receive_data.");
530 	/*NOTREACHED*/
531 }
532 
533 fatal(s)
534 	char *s;
535 {
536 	reply(451, "Error in server: %s\n", s);
537 	reply(221, "Closing connection due to server error.");
538 	exit(0);
539 }
540 
541 reply(n, s, args)
542 	int n;
543 	char *s;
544 {
545 
546 	printf("%d ", n);
547 	_doprnt(s, &args, stdout);
548 	printf("\r\n");
549 	fflush(stdout);
550 	if (debug) {
551 		fprintf(stderr, "<--- %d ", n);
552 		_doprnt(s, &args, stderr);
553 		fprintf(stderr, "\n");
554 		fflush(stderr);
555 	}
556 }
557 
558 lreply(n, s, args)
559 	int n;
560 	char *s;
561 {
562 	printf("%d-", n);
563 	_doprnt(s, &args, stdout);
564 	printf("\r\n");
565 	fflush(stdout);
566 	if (debug) {
567 		fprintf(stderr, "<--- %d-", n);
568 		_doprnt(s, &args, stderr);
569 		fprintf(stderr, "\n");
570 	}
571 }
572 
573 replystr(s)
574 	char *s;
575 {
576 	printf("%s\r\n", s);
577 	fflush(stdout);
578 	if (debug)
579 		fprintf(stderr, "<--- %s\n", s);
580 }
581 
582 ack(s)
583 	char *s;
584 {
585 	reply(200, "%s command okay.", s);
586 }
587 
588 nack(s)
589 	char *s;
590 {
591 	reply(502, "%s command not implemented.", s);
592 }
593 
594 yyerror()
595 {
596 	reply(500, "Command not understood.");
597 }
598 
599 delete(name)
600 	char *name;
601 {
602 	struct stat st;
603 
604 	if (stat(name, &st) < 0) {
605 		reply(550, "%s: %s.", name, sys_errlist[errno]);
606 		return;
607 	}
608 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
609 		if (rmdir(name) < 0) {
610 			reply(550, "%s: %s.", name, sys_errlist[errno]);
611 			return;
612 		}
613 		goto done;
614 	}
615 	if (unlink(name) < 0) {
616 		reply(550, "%s: %s.", name, sys_errlist[errno]);
617 		return;
618 	}
619 done:
620 	ack("DELE");
621 }
622 
623 cwd(path)
624 	char *path;
625 {
626 
627 	if (chdir(path) < 0) {
628 		reply(550, "%s: %s.", path, sys_errlist[errno]);
629 		return;
630 	}
631 	ack("CWD");
632 }
633 
634 makedir(name)
635 	char *name;
636 {
637 	struct stat st;
638 	int dochown = stat(name, &st) < 0;
639 
640 	if (mkdir(name, 0777) < 0) {
641 		reply(550, "%s: %s.", name, sys_errlist[errno]);
642 		return;
643 	}
644 	if (dochown)
645 		(void) chown(name, pw->pw_uid, -1);
646 	ack("MKDIR");
647 }
648 
649 removedir(name)
650 	char *name;
651 {
652 
653 	if (rmdir(name) < 0) {
654 		reply(550, "%s: %s.", name, sys_errlist[errno]);
655 		return;
656 	}
657 	ack("RMDIR");
658 }
659 
660 pwd()
661 {
662 	char path[MAXPATHLEN + 1];
663 
664 	if (getwd(path) == NULL) {
665 		reply(451, "%s.", path);
666 		return;
667 	}
668 	reply(251, "\"%s\" is current directory.", path);
669 }
670 
671 char *
672 renamefrom(name)
673 	char *name;
674 {
675 	struct stat st;
676 
677 	if (stat(name, &st) < 0) {
678 		reply(550, "%s: %s.", name, sys_errlist[errno]);
679 		return ((char *)0);
680 	}
681 	reply(350, "File exists, ready for destination name");
682 	return (name);
683 }
684 
685 renamecmd(from, to)
686 	char *from, *to;
687 {
688 
689 	if (rename(from, to) < 0) {
690 		reply(550, "rename: %s.", sys_errlist[errno]);
691 		return;
692 	}
693 	ack("RNTO");
694 }
695 
696 /*
697  * Convert network-format internet address
698  * to base 256 d.d.d.d representation.
699  */
700 char *
701 ntoa(in)
702 	struct in_addr in;
703 {
704 	static char b[18];
705 	register char *p;
706 
707 	p = (char *)&in;
708 #define	UC(b)	(((int)b)&0xff)
709 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
710 	return (b);
711 }
712 
713 dolog(sin)
714 	struct sockaddr_in *sin;
715 {
716 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
717 		sizeof (struct in_addr), AF_INET);
718 	char *remotehost;
719 	time_t t;
720 
721 	if (hp)
722 		remotehost = hp->h_name;
723 	else
724 		remotehost = inet_ntoa(sin->sin_addr);
725 	t = time(0);
726 	fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
727 	fflush(stderr);
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;
766 	register myside, hisside, pid;
767 	char *av[512];
768 	char **pop, **popargs = NULL;
769 	extern char **glob();
770 	register char *cp;
771 
772 	if (pipe(p) < 0)
773 		return (NULL);
774 	cp = cmd, ac = 0;
775 	do {
776 		av[ac++] = cp;
777 		cp = nextarg(cp);
778 	} while (cp && *cp);
779 	av[ac] = (char *)0;
780 	if (ac > 1) {
781 		popargs = glob(&av[1]);
782 		if (popargs == NULL)
783 			return (NULL);
784 		for (ac = 1, pop = popargs; *pop;)
785 			av[ac++] = *pop++;
786 	}
787 	av[ac] = (char *)0;
788 	myside = tst(p[WTR], p[RDR]);
789 	hisside = tst(p[RDR], p[WTR]);
790 	if ((pid = fork()) == 0) {
791 		/* myside and hisside reverse roles in child */
792 		close(myside);
793 		dup2(hisside, tst(0, 1));
794 		close(hisside);
795 		execv(av[0], av);
796 		_exit(1);
797 	}
798 	if (popargs != NULL)
799 		blkfree(popargs);
800 	if (pid == -1)
801 		return (NULL);
802 	popen_pid[myside] = pid;
803 	close(hisside);
804 	return (fdopen(myside, mode));
805 }
806 
807 pclose(ptr)
808 	FILE *ptr;
809 {
810 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
811 	int status;
812 
813 	f = fileno(ptr);
814 	fclose(ptr);
815 	istat = signal(SIGINT, SIG_IGN);
816 	qstat = signal(SIGQUIT, SIG_IGN);
817 	hstat = signal(SIGHUP, SIG_IGN);
818 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
819 		;
820 	if (r == -1)
821 		status = -1;
822 	signal(SIGINT, istat);
823 	signal(SIGQUIT, qstat);
824 	signal(SIGHUP, hstat);
825 	return (status);
826 }
827 
828 /*
829  * Check user requesting login priviledges.
830  * Disallow anyone mentioned in the file FTPUSERS
831  * to allow people such as uucp to be avoided.
832  */
833 checkuser(name)
834 	register char *name;
835 {
836 	char line[BUFSIZ], *index();
837 	FILE *fd;
838 	int found = 0;
839 
840 	fd = fopen(FTPUSERS, "r");
841 	if (fd == NULL)
842 		return (1);
843 	while (fgets(line, sizeof (line), fd) != NULL) {
844 		register char *cp = index(line, '\n');
845 
846 		if (cp)
847 			*cp = '\0';
848 		if (strcmp(line, name) == 0) {
849 			found++;
850 			break;
851 		}
852 	}
853 	fclose(fd);
854 	return (!found);
855 }
856