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