Software Package:       
	wuftpd

Release/Version:
	2.4

Retrieved from:
	ftp.uu.net:networking/archival/ftp/wuarchive-ftpd

Bug reports:
	This software package is maintained by both BSDI and the
	software contributor.  Please send any bug reports to both
	support@BSDI.COM and bryan@fegmania.wustl.edu.

Comments:

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Modifications to this version made by BSDI:

This is not a complete list of the modifications -- there
are actually a bunch of them because ftpd was written for
a backwards fnmatch() function, and we've also added support
for BSDI style authentication, alternate ftpaccess files for
virtual hosting, fixed up the man pages, etc.

The file ftpd/auth.c is BSDI specific

diff -c -r2.1 ftpd/extensions.c
*** ftpd/extensions.c	1995/02/03 06:40:39	2.1
--- ftpd/extensions.c	1996/11/20 18:06:37
***************
*** 715,721 ****
  
            /* is it in the allowed character set? */
  #if defined(REGEXEC)
!           if (regcomp(&regexbuf, ARG2, REG_EXTENDED|REG_ICASE) != 0) {
                reply(553, "REGEX error");
  #elif defined(REGEX)
            if ((sp = regcmp(ARG2, (char *) 0)) == NULL) {
--- 715,721 ----
  
            /* is it in the allowed character set? */
  #if defined(REGEXEC)
!           if (regcomp(&regexbuf, ARG2, REG_EXTENDED) != 0) {
                reply(553, "REGEX error");
  #elif defined(REGEX)
            if ((sp = regcmp(ARG2, (char *) 0)) == NULL) {
***************
*** 749,756 ****
                /* ARGj == entry->arg[j] */
                if (entry->arg[j]) {
  #if defined(REGEXEC)
!                   if (regcomp(&regexbuf, entry->arg[j], 
!                         REG_EXTENDED|REG_ICASE) !=0) {
                        reply(553, "REGEX error");
  #elif defined(REGEX)
                    if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) {
--- 749,755 ----
                /* ARGj == entry->arg[j] */
                if (entry->arg[j]) {
  #if defined(REGEXEC)
!                   if (regcomp(&regexbuf, entry->arg[j], REG_EXTENDED) !=0) {
                        reply(553, "REGEX error");
  #elif defined(REGEX)
                    if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) {

diff -c -r1.4 ftpd.c
*** ftpd.c	1994/11/29 22:06:39	1.4
--- ftpd.c	1996/11/20 22:19:56
***************
*** 1,4 ****
! /*	BSDI	*/
  
  /* Copyright (c) 1985, 1988, 1990 Regents of the University of California.
   * All rights reserved.
--- 1,4 ----
! /*	BSDI	BSDI_CONTRIB,v 2.3 1996/11/20 22:22:06 prb Exp	*/
  
  /* Copyright (c) 1985, 1988, 1990 Regents of the University of California.
   * All rights reserved.
***************
*** 150,155 ****
--- 150,156 ----
  struct sockaddr_in data_dest;
  struct sockaddr_in his_addr;
  struct sockaddr_in pasv_addr;
+ struct sockaddr_in my_addr;
  
  int data;
  jmp_buf errcatch,
***************
*** 261,266 ****
--- 262,269 ----
      int tos;
  #endif
      char *cp;
+     struct hostent *hp;
+     char *access_file = _PATH_FTPACCESS;
  
  #ifdef SecureWare
      setluid(1);                         /* make sure there is a valid luid */
***************
*** 310,320 ****
--- 313,344 ----
      LastArgv = envp[-1] + strlen(envp[-1]);
  #endif /* SETPROCTITLE */
  
+ #ifdef HAVE_SYSINFO
+     sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
+ #else
+     (void) gethostname(hostname, sizeof (hostname));
+ #endif
+ 
      argc--, argv++;
      while (argc > 0 && *argv[0] == '-') {
          for (cp = &argv[0][1]; *cp; cp++)
              switch (*cp) {
  
+ 	    case 'm':
+ 		addrlen = sizeof(his_addr);
+ 		if (getsockname(0, (struct sockaddr *) &my_addr, &addrlen) < 0){
+ 		    syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
+ #ifndef DEBUG
+ 		    exit(1);
+ #endif
+ 		}
+ 		hp = gethostbyaddr((char *)&my_addr.sin_addr,
+ 		    sizeof (struct in_addr), my_addr.sin_family);
+ 
+ 		if (hp)
+ 		    (void) strncpy(hostname, hp->h_name, sizeof (hostname));
+ 		break;
+ 
              case 'a':
                  use_accessfile = 1;
                  break;
***************
*** 323,328 ****
--- 347,356 ----
                  use_accessfile = 0;
                  break;
  
+     	    case 'F':
+ 		access_file = ++cp;
+                 goto nextopt;
+ 
              case 'v':
                  debug = 1;
                  break;
***************
*** 413,419 ****
  #ifdef SIGBUS
      (void) signal(SIGBUS, randomsig);
  #endif
! #ifdef SIGSEGV
      (void) signal(SIGSEGV, randomsig);
  #endif
  #ifdef SIGSYS
--- 441,447 ----
  #ifdef SIGBUS
      (void) signal(SIGBUS, randomsig);
  #endif
! #ifdef SIGSEGV_no
      (void) signal(SIGSEGV, randomsig);
  #endif
  #ifdef SIGSYS
***************
*** 490,502 ****
      mode = MODE_S;
      tmpline[0] = '\0';
  
! #ifdef HAVE_SYSINFO
!     sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
! #else
!     (void) gethostname(hostname, sizeof (hostname));
! #endif
! 
!     access_init();
      authenticate();
      conv_init();
  
--- 518,524 ----
      mode = MODE_S;
      tmpline[0] = '\0';
  
!     access_init(access_file);
      authenticate();
      conv_init();
  
***************
*** 689,698 ****
   * and uucp to be avoided. */
  user(char *name)
  {
!     register char *cp;
      char *shell;
      char *getusershell();
      int   why = 0;
  
  #ifdef HOST_ACCESS                     /* 19-Mar-93    BM              */
      if (!rhost_ok(name, remotehost, remoteaddr))
--- 711,723 ----
   * and uucp to be avoided. */
  user(char *name)
  {
!     char *cp;
      char *shell;
      char *getusershell();
      int   why = 0;
+     char *auth;
+     extern int ext_auth;
+     extern char *start_auth();
  
  #ifdef HOST_ACCESS                     /* 19-Mar-93    BM              */
      if (!rhost_ok(name, remotehost, remoteaddr))
***************
*** 706,711 ****
--- 731,738 ----
  #endif
  
  #ifdef LOG_FAILED                       /* 06-Nov-92    EHK             */
+     if (auth = strchr(name, ':'))
+         *auth++ = 0;
      strncpy(the_user, name, MAXUSERNAMELEN - 1);
  #endif
  
***************
*** 879,885 ****
      } else
          acl_setfunctions();
  
!     reply(331, "Password required for %s.", name);
      askpasswd = 1;
      /* Delay before reading passwd after first failed attempt to slow down
       * passwd-guessing programs. */
--- 906,923 ----
      } else
          acl_setfunctions();
  
!     if ((cp = start_auth(auth, name, pw)) != NULL) {
! 	char *s;
! 
!         for (;;) {
!             s = strsep(&cp, "\n");
!             if (cp == NULL || *cp == '\0')
!                 break;
!             lreply(331, s);
!         }
!         reply(331, s);
!     } else 
! 	reply(331, "Password required for %s.", name);
      askpasswd = 1;
      /* Delay before reading passwd after first failed attempt to slow down
       * passwd-guessing programs. */
***************
*** 968,973 ****
--- 1006,1015 ----
  
  pass(char *passwd)
  {
+     struct timeval tp;
+     extern int ext_auth;
+     extern char *check_auth();
+ 
      char *xpasswd,
       *salt;
  
***************
*** 992,1039 ****
      if (!anonymous) {           /* "ftp" is only account allowed no password */
          if (*passwd == '-')
              passwd++;
! #ifdef SHADOW_PASSWORD
!         if (pw) {
!            struct spwd *spw = getspnam( pw->pw_name );
!            if( !spw ) { pw->pw_passwd = ""; }
!            else { pw->pw_passwd = spw->sp_pwdp; }
!         }
! #endif
  
!         *guestpw = NULL;
!         if (pw == NULL)
!             salt = "xx";
!         else
!             salt = pw->pw_passwd;
  #ifdef KERBEROS
!         xpasswd = crypt16(passwd, salt);
  #else
!         xpasswd = crypt(passwd, salt);
  #endif
  
  #ifdef ULTRIX_AUTH
!         if ((numfails = ultrix_check_pass(passwd, xpasswd)) < 0) {
  #else
!         /* The strcmp does not catch null passwords! */
!         if (pw == NULL || *pw->pw_passwd == '\0' ||
!             strcmp(xpasswd, pw->pw_passwd)) {
  #endif
!             reply(530, "Login incorrect.");
  
  #ifdef LOG_FAILED                       /* 27-Apr-93    EHK/BM             */
!             syslog(LOG_INFO, "failed login from %s [%s], %s",
!                               remotehost, remoteaddr, the_user);
  #endif
!             acl_remove();
  
!             pw = NULL;
              if (++login_attempts >= lgi_failure_threshold) {
                  syslog(LOG_NOTICE, "repeated login failures from %s [%s]",
                         remotehost, remoteaddr);
                  exit(0);
              }
              return;
!         }
      } else {
          char *pwin,
           *pwout = guestpw;
--- 1034,1114 ----
      if (!anonymous) {           /* "ftp" is only account allowed no password */
          if (*passwd == '-')
              passwd++;
!         if (ext_auth) {
!             if (salt = check_auth(the_user, passwd)) {
!                 reply(530, salt);
  
! #ifdef LOG_FAILED                       /* 27-Apr-93    EHK/BM             */
!                 syslog(LOG_INFO, "failed login from %s [%s], %s",
!                                   remotehost, remoteaddr, the_user);
! #endif
!                 pw = NULL;
!             }
!         } else {
! #ifdef SHADOW_PASSWORD
! 	    if (pw) {
! 	       struct spwd *spw = getspnam( pw->pw_name );
! 	       if( !spw ) { pw->pw_passwd = ""; }
! 	       else { pw->pw_passwd = spw->sp_pwdp; }
! 	    }
! #endif
! 	    *guestpw = NULL;
! 	    if (pw == NULL)
! 		salt = "xx";
! 	    else
! 		salt = pw->pw_passwd;
  #ifdef KERBEROS
! 	    xpasswd = crypt16(passwd, salt);
  #else
! 	    xpasswd = crypt(passwd, salt);
  #endif
  
  #ifdef ULTRIX_AUTH
! 	    if ((numfails = ultrix_check_pass(passwd, xpasswd)) < 0) {
  #else
! 	    /* The strcmp does not catch null passwords! */
! 	    if (pw == NULL || *pw->pw_passwd == '\0' ||
! 		strcmp(xpasswd, pw->pw_passwd)) {
  #endif
! 		reply(530, "Login incorrect.");
  
  #ifdef LOG_FAILED                       /* 27-Apr-93    EHK/BM             */
! 		syslog(LOG_INFO, "failed login from %s [%s], %s",
! 				  remotehost, remoteaddr, the_user);
  #endif
! 		pw = NULL;
! 	    }
! 	}
! 
! 	if (pw) {
! 	    if (pw->pw_change || pw->pw_expire)
! 		(void)gettimeofday(&tp, (struct timezone *)NULL);
! 	    if (pw->pw_change && tp.tv_sec >= pw->pw_change) {
! 		reply(530, "Password has expired.");
! #ifdef LOG_FAILED
! 		syslog(LOG_INFO, "expired password from %s [%s], %s",
! 		    remotehost, remoteaddr, the_user);
! #endif
! 		pw = NULL;
! 	    } else if (pw->pw_expire && tp.tv_sec >= pw->pw_expire) {
! 		reply(530, "Account has expired.");
! #ifdef LOG_FAILED
! 		syslog(LOG_INFO, "expired account from %s [%s], %s",
! 		    remotehost, remoteaddr, the_user);
! #endif
! 		pw = NULL;
! 	    }
! 	}
  
! 	if (pw == NULL) {
!             acl_remove();
              if (++login_attempts >= lgi_failure_threshold) {
                  syslog(LOG_NOTICE, "repeated login failures from %s [%s]",
                         remotehost, remoteaddr);
                  exit(0);
              }
              return;
! 	}
      } else {
          char *pwin,
           *pwout = guestpw;
***************
*** 1110,1115 ****
--- 1185,1192 ----
          /* We MUST do a chdir() after the chroot. Otherwise the old current
           * directory will be accessible as "." outside the new root! */
          if (anonymous) {
+             if (getaclentry("spooldir", &entry) && ARG0)
+ 		pw->pw_dir = ARG0;
              if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
                  reply(550, "Can't set guest privileges.");
                  goto bad;
***************
*** 1404,1412 ****
      if (dout == NULL)
          goto done;
  #ifdef HAVE_ST_BLKSIZE
!     send_data(fin, dout, st.st_blksize);
  #else
!     send_data(fin, dout, BUFSIZ);
  #endif
      (void) fclose(dout);
  
--- 1481,1489 ----
      if (dout == NULL)
          goto done;
  #ifdef HAVE_ST_BLKSIZE
!     send_data(fin, dout, (int)st.st_blksize);
  #else
!     send_data(fin, dout, (int)BUFSIZ);
  #endif
      (void) fclose(dout);
  
***************
*** 1419,1429 ****
  
          if (!xfertime)
              xfertime++;
!         realpath(logname ? logname : name, namebuf);
          for (loop = 0; namebuf[loop]; loop++)
              if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
                  namebuf[loop] = '_';
!         sprintf(msg, "%.24s %d %s %d %s %c %s %c %c %s ftp %d %s\n",
                  ctime(&curtime),
                  xfertime,
                  remotehost,
--- 1496,1511 ----
  
          if (!xfertime)
              xfertime++;
!         if (realpath(logname ? logname : name, namebuf) == NULL)
! 		/*
! 		 * if realpath() fails then we don't know where
! 		 * we are, so at least give the file name in the log.
! 		 */
! 		strcpy(namebuf, name);
          for (loop = 0; namebuf[loop]; loop++)
              if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
                  namebuf[loop] = '_';
!         sprintf(msg, "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s\n",
                  ctime(&curtime),
                  xfertime,
                  remotehost,
***************
*** 1611,1617 ****
          for (loop = 0; namebuf[loop]; loop++)
              if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
                  namebuf[loop] = '_';
!         sprintf(msg, "%.24s %d %s %d %s %c %s %c %c %s ftp %d %s\n",
                  ctime(&curtime),
                  xfertime,
                  remotehost,
--- 1693,1699 ----
          for (loop = 0; namebuf[loop]; loop++)
              if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
                  namebuf[loop] = '_';
!         sprintf(msg, "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s\n",
                  ctime(&curtime),
                  xfertime,
                  remotehost,
***************
*** 1693,1713 ****
      char sizebuf[32];
      FILE *file;
      int retry = 0;
! #ifdef IPTOS_LOWDELAY
      int tos;
  #endif
  
      file_size = size;
      byte_count = 0;
!     if (size != (off_t) - 1)
!         (void) sprintf(sizebuf, " (%ld bytes)", size);
      else
          (void) strcpy(sizebuf, "");
      if (pdata >= 0) {
          struct sockaddr_in from;
          int s,
            fromlen = sizeof(from);
  
          s = accept(pdata, (struct sockaddr *) &from, &fromlen);
          if (s < 0) {
              reply(425, "Can't open data connection.");
--- 1775,1822 ----
      char sizebuf[32];
      FILE *file;
      int retry = 0;
! #ifdef IPTOS_THROUGHPUT
      int tos;
  #endif
  
      file_size = size;
      byte_count = 0;
!     if (size != (off_t) -1)
!         (void) sprintf(sizebuf, " (%qd bytes)", size);
      else
          (void) strcpy(sizebuf, "");
      if (pdata >= 0) {
          struct sockaddr_in from;
          int s,
            fromlen = sizeof(from);
+ 	fd_set rfds;
+ 	struct timeval tv;
  
+ 	/* Do not blindly call accept(), since peer might never connect. */
+ 	FD_ZERO(&rfds);
+ 	tv.tv_sec = timeout;
+ 	tv.tv_usec = 0;
+ 	for (;;) {
+ 	    FD_SET(pdata, &rfds);
+ 	    s = select(pdata + 1, &rfds, NULL, NULL, &tv);
+ 	    if (s > 0)
+ 		break;
+ 	    if (s == 0) {
+ 		reply(421, "Timeout, can't open data connection.");
+ 		(void) close(pdata);
+ 		pdata = -1;
+ 		return (NULL);
+ 	    }
+ 	    if (errno != EINTR) {
+ 		reply(425, "Can't open data connection.");
+ 		(void) close(pdata);
+ 		pdata = -1;
+ 		return (NULL);
+ 	    }
+ 	    /* Should reduce tv.tv_sec here by the amount of time we were
+ 	     * asleep in select, but not worth the effort, and someday the
+ 	     * kernel will do it for us. */
+ 	}
          s = accept(pdata, (struct sockaddr *) &from, &fromlen);
          if (s < 0) {
              reply(425, "Can't open data connection.");
***************
*** 1717,1724 ****
          }
          (void) close(pdata);
          pdata = s;
! #ifdef IPTOS_LOWDELAY
!         tos = IPTOS_LOWDELAY;
          (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
                            sizeof(int));
  
--- 1826,1833 ----
          }
          (void) close(pdata);
          pdata = s;
! #ifdef IPTOS_THROUGHPUT
!         tos = IPTOS_THROUGHPUT;
          (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
                            sizeof(int));
  
***************
*** 1765,1771 ****
   * encapsulation of the data subject to Mode, Structure, and Type.
   *
   * NB: Form isn't handled. */
! send_data(FILE *instr, FILE *outstr, off_t blksize)
  {
      register int c,
        cnt;
--- 1874,1880 ----
   * encapsulation of the data subject to Mode, Structure, and Type.
   *
   * NB: Form isn't handled. */
! send_data(FILE *instr, FILE *outstr, int blksize)
  {
      register int c,
        cnt;
***************
*** 2050,2065 ****
      vprintf(fmt, ap);
      printf("\r\n");
      (void) fflush(stdout);
  
      if (debug) {
          char buf[BUFSIZ];
          (void) vsprintf(buf, fmt, ap);
  
          syslog(LOG_DEBUG, "<--- %d ", n);
          syslog(LOG_DEBUG, buf);
      }
- 
-     va_end(ap);
  }
  
  /* VARARGS2 */
--- 2159,2179 ----
      vprintf(fmt, ap);
      printf("\r\n");
      (void) fflush(stdout);
+     va_end(ap);
  
      if (debug) {
          char buf[BUFSIZ];
+ 	va_start(ap);
+ 	/* first argument is always the return code */
+ 	n = va_arg(ap, int);
+ 	/* second argument is always fmt string     */
+ 	fmt = va_arg(ap, char *);
          (void) vsprintf(buf, fmt, ap);
+ 	va_end(ap);
  
          syslog(LOG_DEBUG, "<--- %d ", n);
          syslog(LOG_DEBUG, buf);
      }
  }
  
  /* VARARGS2 */
***************
*** 2082,2097 ****
      vprintf(fmt, ap);
      printf("\r\n");
      (void) fflush(stdout);
  
      if (debug) {
          char buf[BUFSIZ];
          (void) vsprintf(buf, fmt, ap);
  
          syslog(LOG_DEBUG, "<--- %d- ", n);
          syslog(LOG_DEBUG, buf);
      }
  
-     va_end(ap);
  }
  
  #else
--- 2196,2217 ----
      vprintf(fmt, ap);
      printf("\r\n");
      (void) fflush(stdout);
+     va_end(ap);
  
      if (debug) {
          char buf[BUFSIZ];
+ 	va_start(ap);
+ 	/* first argument is always the return code */
+ 	n = va_arg(ap, int);
+ 	/* second argument is always fmt string     */
+ 	fmt = va_arg(ap, char *);
          (void) vsprintf(buf, fmt, ap);
+ 	va_end(ap);
  
          syslog(LOG_DEBUG, "<--- %d- ", n);
          syslog(LOG_DEBUG, buf);
      }
  
  }
  
  #else
***************
*** 2448,2458 ****
          longjmp(urgcatch, 1);
      }
      if (strcmp(cp, "STAT\r\n") == 0) {
!         if (file_size != (off_t) - 1)
!             reply(213, "Status: %lu of %lu bytes transferred",
                    byte_count, file_size);
          else
!             reply(213, "Status: %lu bytes transferred", byte_count);
      }
  }
  
--- 2568,2578 ----
          longjmp(urgcatch, 1);
      }
      if (strcmp(cp, "STAT\r\n") == 0) {
!         if (file_size != (off_t) -1)
!             reply(213, "Status: %qd of %qd bytes transferred",
                    byte_count, file_size);
          else
!             reply(213, "Status: %qd bytes transferred", byte_count);
      }
  }
  
***************
*** 2466,2471 ****
--- 2586,2597 ----
      register char *p,
       *a;
  
+     if (!logged_in) {
+ 	syslog(LOG_NOTICE, "passive but not logged in");
+ 	reply(503, "Login with USER first.");
+ 	return;
+     }
+ 
      pdata = socket(AF_INET, SOCK_STREAM, 0);
      if (pdata < 0) {
          perror_reply(425, "Can't open passive connection");
***************
*** 2599,2605 ****
          }
          if ((st.st_mode & S_IFMT) == S_IFREG) {
              if (dout == NULL) {
!                 dout = dataconn("file list", (off_t) - 1, "w");
                  if (dout == NULL)
                      return;
                  transflag++;
--- 2725,2731 ----
          }
          if ((st.st_mode & S_IFMT) == S_IFREG) {
              if (dout == NULL) {
!                 dout = dataconn("file list", (off_t) -1, "w");
                  if (dout == NULL)
                      return;
                  transflag++;
***************
*** 2639,2645 ****
              if (simple || (stat(nbuf, &st) == 0 &&
                             (st.st_mode & S_IFMT) == S_IFREG)) {
                  if (dout == NULL) {
!                     dout = dataconn("file list", (off_t) - 1,
                                      "w");
                      if (dout == NULL)
                          return;
--- 2765,2771 ----
              if (simple || (stat(nbuf, &st) == 0 &&
                             (st.st_mode & S_IFMT) == S_IFREG)) {
                  if (dout == NULL) {
!                     dout = dataconn("file list", (off_t) -1,
                                      "w");
                      if (dout == NULL)
                          return;
