BSD 4_1c_2 release
[unix-history] / usr / src / ucb / netser / ftp / ftpd.c
CommitLineData
c6d3d85f 1#ifndef lint
e804469b 2static char sccsid[] = "@(#)ftpd.c 4.16 (Berkeley) 2/21/83";
c6d3d85f
SL
3#endif
4
5/*
6 * FTP server.
7 */
aa159ba8 8#include <sys/param.h>
c6d3d85f
SL
9#include <sys/stat.h>
10#include <sys/ioctl.h>
11#include <sys/socket.h>
12
13#include <netinet/in.h>
14
15#include <stdio.h>
16#include <signal.h>
17#include <wait.h>
18#include <pwd.h>
19#include <setjmp.h>
20#include <netdb.h>
28e19fb7 21#include <errno.h>
c6d3d85f
SL
22
23#include "ftp.h"
24
0597ed04
SL
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
c6d3d85f
SL
32extern int errno;
33extern char *sys_errlist[];
34extern char *crypt();
35extern char version[];
36extern char *home; /* pointer to home directory for glob */
37extern FILE *popen(), *fopen();
38extern int pclose(), fclose();
39
40struct sockaddr_in ctrl_addr;
41struct sockaddr_in data_source;
42struct sockaddr_in data_dest;
43struct sockaddr_in his_addr;
44
45struct hostent *hp;
46
47int data;
48jmp_buf errcatch;
49int logged_in;
50struct passwd *pw;
51int debug;
52int logging = 1;
53int guest;
54int type;
55int form;
56int stru; /* avoid C keyword */
57int mode;
8643b66e 58int usedefault = 1; /* for data transfers */
c6d3d85f
SL
59char hostname[32];
60char *remotehost;
8643b66e 61struct servent *sp;
c6d3d85f
SL
62
63int lostconn();
3cb22fa5 64int reapchild();
c6d3d85f
SL
65FILE *getdatasock(), *dataconn();
66char *ntoa();
67
68main(argc, argv)
69 int argc;
70 char *argv[];
71{
72 int ctrl, s, options = 0;
c6d3d85f
SL
73 char *cp;
74
75 sp = getservbyname("ftp", "tcp");
76 if (sp == 0) {
d33c618b 77 fprintf(stderr, "ftpd: ftp/tcp: unknown service\n");
c6d3d85f
SL
78 exit(1);
79 }
80 ctrl_addr.sin_port = sp->s_port;
81 data_source.sin_port = htons(ntohs(sp->s_port) - 1);
82 signal(SIGPIPE, lostconn);
83 debug = 0;
84 argc--, argv++;
85 while (argc > 0 && *argv[0] == '-') {
86 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
87
88 case 'd':
89 debug = 1;
90 options |= SO_DEBUG;
91 break;
92
93 default:
94 fprintf(stderr, "Unknown flag -%c ignored.\n", cp);
95 break;
96 }
97 argc--, argv++;
98 }
99#ifndef DEBUG
100 if (fork())
101 exit(0);
102 for (s = 0; s < 10; s++)
6fcbb8a1 103 (void) close(s);
d33c618b 104 (void) open("/", 0);
c6d3d85f 105 (void) dup2(0, 1);
d33c618b 106 (void) dup2(0, 2);
c6d3d85f
SL
107 { int tt = open("/dev/tty", 2);
108 if (tt > 0) {
109 ioctl(tt, TIOCNOTTY, 0);
110 close(tt);
111 }
112 }
113#endif
aa159ba8 114 while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
c6d3d85f
SL
115 perror("ftpd: socket");
116 sleep(5);
117 }
3cb22fa5
SL
118 if (options & SO_DEBUG)
119 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
120 perror("ftpd: setsockopt (SO_DEBUG)");
121#ifdef notdef
122 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
123 perror("ftpd: setsockopt (SO_KEEPALIVE)");
124#endif
c6d3d85f
SL
125 while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
126 perror("ftpd: bind");
127 sleep(5);
128 }
6ac93e85 129 sigset(SIGCHLD, reapchild);
aa159ba8 130 listen(s, 10);
c6d3d85f
SL
131 for (;;) {
132 int hisaddrlen = sizeof (his_addr);
133
134 ctrl = accept(s, &his_addr, &hisaddrlen, 0);
135 if (ctrl < 0) {
3cb22fa5
SL
136 if (errno == EINTR)
137 continue;
c6d3d85f 138 perror("ftpd: accept");
c6d3d85f
SL
139 continue;
140 }
c6d3d85f 141 if (fork() == 0) {
d33c618b 142 signal (SIGCHLD, SIG_IGN);
c6d3d85f
SL
143 if (logging)
144 dolog(&his_addr);
145 close(s);
146 dup2(ctrl, 0), close(ctrl), dup2(0, 1);
147 /* do telnet option negotiation here */
aa159ba8
SL
148 /*
149 * Set up default state
150 */
c6d3d85f
SL
151 logged_in = 0;
152 data = -1;
aa159ba8
SL
153 type = TYPE_A;
154 form = FORM_N;
155 stru = STRU_F;
156 mode = MODE_S;
c6d3d85f
SL
157 gethostname(hostname, sizeof (hostname));
158 reply(220, "%s FTP server (%s) ready.",
159 hostname, version);
160 for (;;) {
161 setjmp(errcatch);
162 yyparse();
163 }
164 }
165 close(ctrl);
c6d3d85f
SL
166 }
167}
168
3cb22fa5
SL
169reapchild()
170{
171 union wait status;
172
173 while (wait3(&status, WNOHANG, 0) > 0)
174 ;
175}
176
c6d3d85f
SL
177lostconn()
178{
179
180 fatal("Connection closed.");
181}
182
183pass(passwd)
184 char *passwd;
185{
aa159ba8
SL
186 char *xpasswd, *savestr();
187 static struct passwd save;
c6d3d85f
SL
188
189 if (logged_in || pw == NULL) {
190 reply(503, "Login with USER first.");
191 return;
192 }
193 if (!guest) { /* "ftp" is only account allowed no password */
194 xpasswd = crypt(passwd, pw->pw_passwd);
195 if (strcmp(xpasswd, pw->pw_passwd) != 0) {
196 reply(530, "Login incorrect.");
197 pw = NULL;
198 return;
199 }
200 }
aa159ba8 201 setegid(pw->pw_gid);
c6d3d85f
SL
202 initgroups(pw->pw_name, pw->pw_gid);
203 if (chdir(pw->pw_dir)) {
204 reply(550, "User %s: can't change directory to $s.",
205 pw->pw_name, pw->pw_dir);
aa159ba8 206 goto bad;
c6d3d85f 207 }
aa159ba8 208 if (guest && chroot(pw->pw_dir) < 0) {
c6d3d85f 209 reply(550, "Can't set guest privileges.");
aa159ba8 210 goto bad;
c6d3d85f
SL
211 }
212 if (!guest)
213 reply(230, "User %s logged in.", pw->pw_name);
214 else
215 reply(230, "Guest login ok, access restrictions apply.");
216 logged_in = 1;
aa159ba8
SL
217 seteuid(pw->pw_uid);
218 /*
219 * Save everything so globbing doesn't
220 * clobber the fields.
221 */
222 save = *pw;
223 save.pw_name = savestr(pw->pw_name);
224 save.pw_passwd = savestr(pw->pw_passwd);
225 save.pw_comment = savestr(pw->pw_comment);
226 save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
227 save.pw_dir = savestr(pw->pw_dir);
228 save.pw_shell = savestr(pw->pw_shell);
229 pw = &save;
230 home = pw->pw_dir; /* home dir for globbing */
231 return;
232bad:
233 seteuid(0);
234 pw = NULL;
235}
236
237char *
238savestr(s)
239 char *s;
240{
241 char *malloc();
242 char *new = malloc(strlen(s) + 1);
243
244 if (new != NULL)
245 strcpy(new, s);
e804469b 246 return(new);
c6d3d85f
SL
247}
248
249retrieve(cmd, name)
250 char *cmd, *name;
251{
252 FILE *fin, *dout;
253 struct stat st;
254 int (*closefunc)();
255
256 if (cmd == 0) {
6fcbb8a1
SL
257#ifdef notdef
258 /* no remote command execution -- it's a security hole */
c6d3d85f
SL
259 if (*name == '!')
260 fin = popen(name + 1, "r"), closefunc = pclose;
261 else
6fcbb8a1 262#endif
c6d3d85f
SL
263 fin = fopen(name, "r"), closefunc = fclose;
264 } else {
265 char line[BUFSIZ];
266
5c81b68e 267 sprintf(line, cmd, name), name = line;
c6d3d85f
SL
268 fin = popen(line, "r"), closefunc = pclose;
269 }
270 if (fin == NULL) {
271 reply(550, "%s: %s.", name, sys_errlist[errno]);
272 return;
273 }
274 st.st_size = 0;
275 if (cmd == 0 &&
276 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
277 reply(550, "%s: not a plain file.", name);
278 goto done;
279 }
280 dout = dataconn(name, st.st_size, "w");
281 if (dout == NULL)
282 goto done;
aa159ba8 283 if (send_data(fin, dout) || ferror(dout))
c6d3d85f
SL
284 reply(550, "%s: %s.", name, sys_errlist[errno]);
285 else
286 reply(226, "Transfer complete.");
aa159ba8 287 fclose(dout), data = -1;
c6d3d85f
SL
288done:
289 (*closefunc)(fin);
290}
291
292store(name, mode)
293 char *name, *mode;
294{
295 FILE *fout, *din;
aa159ba8 296 int (*closefunc)(), dochown = 0;
c6d3d85f 297
6fcbb8a1
SL
298#ifdef notdef
299 /* no remote command execution -- it's a security hole */
c6d3d85f
SL
300 if (name[0] == '!')
301 fout = popen(&name[1], "w"), closefunc = pclose;
6fcbb8a1
SL
302 else
303#endif
304 {
aa159ba8
SL
305 struct stat st;
306
307 if (stat(name, &st) < 0)
308 dochown++;
c6d3d85f 309 fout = fopen(name, mode), closefunc = fclose;
aa159ba8 310 }
c6d3d85f
SL
311 if (fout == NULL) {
312 reply(550, "%s: %s.", name, sys_errlist[errno]);
313 return;
314 }
315 din = dataconn(name, -1, "r");
316 if (din == NULL)
317 goto done;
aa159ba8 318 if (receive_data(din, fout) || ferror(fout))
c6d3d85f
SL
319 reply(550, "%s: %s.", name, sys_errlist[errno]);
320 else
321 reply(226, "Transfer complete.");
322 fclose(din), data = -1;
323done:
aa159ba8
SL
324 if (dochown)
325 (void) chown(name, pw->pw_uid, -1);
c6d3d85f
SL
326 (*closefunc)(fout);
327}
328
329FILE *
330getdatasock(mode)
331 char *mode;
332{
58846728 333 int s;
c6d3d85f
SL
334
335 if (data >= 0)
336 return (fdopen(data, mode));
58846728
SL
337 s = socket(AF_INET, SOCK_STREAM, 0, 0);
338 if (s < 0)
c6d3d85f 339 return (NULL);
c6d3d85f 340 seteuid(0);
58846728
SL
341 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
342 goto bad;
343 if (bind(s, &data_source, sizeof (data_source), 0) < 0)
344 goto bad;
0a11c3ae 345 seteuid(pw->pw_uid);
c6d3d85f 346 return (fdopen(s, mode));
58846728
SL
347bad:
348 seteuid(pw->pw_uid);
349 close(s);
350 return (NULL);
c6d3d85f
SL
351}
352
353FILE *
354dataconn(name, size, mode)
355 char *name;
356 int size;
357 char *mode;
358{
359 char sizebuf[32];
360 FILE *file;
361
362 if (size >= 0)
363 sprintf(sizebuf, " (%d bytes)", size);
364 else
365 (void) strcpy(sizebuf, "");
366 if (data >= 0) {
367 reply(125, "Using existing data connection for %s%s.",
368 name, sizebuf);
8643b66e 369 usedefault = 1;
c6d3d85f
SL
370 return (fdopen(data, mode));
371 }
afa6f79b 372 if (usedefault)
5c81b68e 373 data_dest = his_addr;
5c81b68e 374 usedefault = 1;
c6d3d85f
SL
375 file = getdatasock(mode);
376 if (file == NULL) {
377 reply(425, "Can't create data socket (%s,%d): %s.",
378 ntoa(data_source.sin_addr),
379 ntohs(data_source.sin_port),
380 sys_errlist[errno]);
381 return (NULL);
382 }
58846728
SL
383 reply(150, "Opening data connection for %s (%s,%d)%s.",
384 name, ntoa(data_dest.sin_addr.s_addr),
385 ntohs(data_dest.sin_port), sizebuf);
c6d3d85f
SL
386 data = fileno(file);
387 if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
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 */
405send_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) {
d33c618b
SL
416 if (c == '\n') {
417 if (ferror (outstr))
418 return (1);
c6d3d85f 419 putc('\r', outstr);
d33c618b
SL
420 }
421 putc(c, outstr);
422 if (c == '\r')
423 putc ('\0', outstr);
c6d3d85f 424 }
d33c618b
SL
425 if (ferror (instr) || ferror (outstr))
426 return (1);
c6d3d85f
SL
427 return (0);
428
429 case TYPE_I:
430 case TYPE_L:
431 netfd = fileno(outstr);
432 filefd = fileno(instr);
433
aa159ba8 434 while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
c6d3d85f
SL
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 */
451receive_data(instr, outstr)
452 FILE *instr, *outstr;
453{
454 register int c;
d33c618b 455 int cnt;
c6d3d85f
SL
456 char buf[BUFSIZ];
457
458
459 switch (type) {
460
461 case TYPE_I:
462 case TYPE_L:
2e4e0338
SL
463 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
464 if (write(fileno(outstr), buf, cnt) < 0)
c6d3d85f
SL
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:
c6d3d85f 473 while ((c = getc(instr)) != EOF) {
c6d3d85f 474 if (c == '\r') {
d33c618b
SL
475 if (ferror (outstr))
476 return (1);
477 if ((c = getc(instr)) != '\n')
478 putc ('\r', outstr);
479 if (c == '\0')
480 continue;
c6d3d85f 481 }
d33c618b 482 putc (c, outstr);
c6d3d85f 483 }
d33c618b
SL
484 if (ferror (instr) || ferror (outstr))
485 return (1);
c6d3d85f
SL
486 return (0);
487 }
488 fatal("Unknown type in receive_data.");
489 /*NOTREACHED*/
490}
491
492fatal(s)
493 char *s;
494{
495 reply(451, "Error in server: %s\n", s);
496 reply(221, "Closing connection due to server error.");
497 exit(0);
498}
499
500reply(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
517lreply(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
532replystr(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
541ack(s)
542 char *s;
543{
544 reply(200, "%s command okay.", s);
545}
546
547nack(s)
548 char *s;
549{
550 reply(502, "%s command not implemented.", s);
551}
552
553yyerror()
554{
555 reply(500, "Command not understood.");
556}
557
558delete(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 }
578done:
579 ack("DELE");
580}
581
582cwd(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
aa159ba8 593makedir(name)
c6d3d85f
SL
594 char *name;
595{
aa159ba8
SL
596 struct stat st;
597 int dochown = stat(name, &st) < 0;
c6d3d85f
SL
598
599 if (mkdir(name, 0777) < 0) {
600 reply(550, "%s: %s.", name, sys_errlist[errno]);
601 return;
602 }
aa159ba8
SL
603 if (dochown)
604 (void) chown(name, pw->pw_uid, -1);
c6d3d85f
SL
605 ack("MKDIR");
606}
607
aa159ba8 608removedir(name)
c6d3d85f
SL
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
aa159ba8 619pwd()
c6d3d85f 620{
aa159ba8 621 char path[MAXPATHLEN + 1];
c6d3d85f
SL
622 char *p;
623
624 if (getwd(path) == NULL) {
625 reply(451, "%s.", path);
626 return;
627 }
628 reply(251, "\"%s\" is current directory.", path);
629}
630
631char *
632renamefrom(name)
633 char *name;
634{
635 struct stat st;
636
637 if (stat(name, &st) < 0) {
638 reply(550, "%s: %s.", name, sys_errlist[errno]);
639 return ((char *)0);
640 }
aa159ba8 641 reply(350, "File exists, ready for destination name");
c6d3d85f
SL
642 return (name);
643}
644
645renamecmd(from, to)
646 char *from, *to;
647{
648
649 if (rename(from, to) < 0) {
650 reply(550, "rename: %s.", sys_errlist[errno]);
651 return;
652 }
653 ack("RNTO");
654}
655
656int guest;
657/*
658 * Test pathname for guest-user safety.
659 */
660inappropriate_request(name)
661 char *name;
662{
663 int bogus = 0, depth = 0, length = strlen(name);
664 char *p, *s;
665
666 if (!guest)
667 return (0);
668 if (name[0] == '/' || name[0] == '|')
669 bogus = 1;
670 for (p = name; p < name+length;) {
671 s = p; /* start of token */
672 while ( *p && *p!= '/')
673 p++;
674 *p = 0;
675 if (strcmp(s, "..") == 0)
676 depth -= 1; /* backing up */
677 else if (strcmp(s, ".") == 0)
678 depth += 0; /* no change */
679 else
680 depth += 1; /* descending */
681 if (depth < 0) {
682 bogus = 1;
683 break;
684 }
685 }
686 if (bogus)
687 reply(553, "%s: pathname disallowed guest users", name);
688 return (bogus);
689}
690
691/*
692 * Convert network-format internet address
693 * to base 256 d.d.d.d representation.
694 */
695char *
696ntoa(in)
697 struct in_addr in;
698{
699 static char b[18];
700 register char *p;
701
c6d3d85f
SL
702 p = (char *)&in;
703#define UC(b) (((int)b)&0xff)
704 sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
705 return (b);
706}
707
708dolog(sin)
709 struct sockaddr_in *sin;
710{
711 struct hostent *hp = gethostbyaddr(&sin->sin_addr,
712 sizeof (struct in_addr), AF_INET);
713 char *remotehost;
714 time_t t;
715
716 if (hp)
717 remotehost = hp->h_name;
718 else
719 remotehost = "UNKNOWNHOST";
720 t = time(0);
aa159ba8 721 fprintf(stderr,"FTP: connection from %s at %s", remotehost, ctime(&t));
c6d3d85f
SL
722 fflush(stderr);
723}
0597ed04
SL
724
725/*
726 * Special version of popen which avoids
727 * call to shell. This insures noone may
728 * create a pipe to a hidden program as a side
729 * effect of a list or dir command.
730 */
731#define tst(a,b) (*mode == 'r'? (b) : (a))
732#define RDR 0
733#define WTR 1
734static int popen_pid[5];
735
736static char *
737nextarg(cpp)
738 char *cpp;
739{
740 register char *cp = cpp;
741
742 if (cp == 0)
743 return (cp);
744 while (*cp && *cp != ' ' && *cp != '\t')
745 cp++;
746 if (*cp == ' ' || *cp == '\t') {
747 *cp++ = '\0';
748 while (*cp == ' ' || *cp == '\t')
749 cp++;
750 }
751 if (cp == cpp)
752 return ((char *)0);
753 return (cp);
754}
755
756FILE *
757popen(cmd, mode)
758 char *cmd, *mode;
759{
760 int p[2], ac;
761 register myside, hisside, pid;
762 char *av[10];
763 register char *cp;
764
765 if (pipe(p) < 0)
766 return (NULL);
767 cp = cmd, ac = 0;
768 do {
769 av[ac++] = cp;
770 cp = nextarg(cp);
771 } while (cp && *cp);
772 av[ac] = (char *)0;
773 myside = tst(p[WTR], p[RDR]);
774 hisside = tst(p[RDR], p[WTR]);
775 if ((pid = fork()) == 0) {
776 /* myside and hisside reverse roles in child */
777 close(myside);
778 dup2(hisside, tst(0, 1));
779 close(hisside);
780 execv(av[0], av);
781 _exit(1);
782 }
783 if (pid == -1)
784 return (NULL);
785 popen_pid[myside] = pid;
786 close(hisside);
787 return (fdopen(myside, mode));
788}
789
790pclose(ptr)
791 FILE *ptr;
792{
793 register f, r, (*hstat)(), (*istat)(), (*qstat)();
794 int status;
795
796 f = fileno(ptr);
797 fclose(ptr);
798 istat = signal(SIGINT, SIG_IGN);
799 qstat = signal(SIGQUIT, SIG_IGN);
800 hstat = signal(SIGHUP, SIG_IGN);
801 while ((r = wait(&status)) != popen_pid[f] && r != -1)
802 ;
803 if (r == -1)
804 status = -1;
805 signal(SIGINT, istat);
806 signal(SIGQUIT, qstat);
807 signal(SIGHUP, hstat);
808 return (status);
809}
810
811/*
812 * Check user requesting login priviledges.
813 * Disallow anyone mentioned in the file FTPUSERS
814 * to allow people such as uucp to be avoided.
815 */
816checkuser(name)
817 register char *name;
818{
819 char line[BUFSIZ], *index();
820 FILE *fd;
821 int found = 0;
822
823 fd = fopen(FTPUSERS, "r");
824 if (fd == NULL)
825 return (1);
826 while (fgets(line, sizeof (line), fd) != NULL) {
827 register char *cp = index(line, '\n');
828
829 if (cp)
830 *cp = '\0';
831 if (strcmp(line, name) == 0) {
832 found++;
833 break;
834 }
835 }
836 fclose(fd);
837 return (!found);
838}