Subject: virtual ftp patch

From: Scott Barker <[email-masked]>
Date: Tue, 10 Dec 1996 13:36:35 -0700 (MST)

I made some modifications to your virtual ftpd support that may interest
you. I have cc'd this note to [email-masked], in case it interests
the wu-ftpd developers as well.

Primarily, I allowed for hostnames as well as ip numbers in the ftpaccess
file. I also had the code use the first instance of each "virtual" command in
the ftpaccess file, rather than parsing the whole acl list and using the last
instance.

Also, I removed the code for what must have been the "old way" of handling
virtual domains, since it's not documented anyway.

I added a feature of my own (RESTRICTED_VIRTUAL_ADMIN) which allows remote
administration of a virtual ftp site by a single admin account, which is
restricted to the chroot() environment, and need not have a valid shell (thus
allowing for /bin/false shells to restrict the remote admin to ftp only).

Here's the patch:

diff -u -P -r wu-ftpd-2.4.2-beta-11.orig/src/ftpd.c wu-ftpd-2.4.2-beta-11/src/ftpd.c
--- wu-ftpd-2.4.2-beta-11.orig/src/ftpd.c	Sun Apr 14 22:51:11 1996
+++ wu-ftpd-2.4.2-beta-11/src/ftpd.c	Tue Dec 10 13:07:23 1996
@@ -213,6 +213,7 @@
 int virtual_mode=0;
 struct sockaddr_in virtual_addr;
 struct sockaddr_in *virtual_ptr;
+char virtual_hostname[MAXHOSTNAMELEN];
 char virtual_root[MAXPATHLEN];
 char virtual_banner[MAXPATHLEN];
 char virtual_logfile[MAXPATHLEN];
@@ -378,7 +379,11 @@
 
 #ifdef VIRTUAL
       int virtual_len;
-      int set;
+      int set_root, set_banner, set_logfile;
+      struct hostent *virtual_hostent;
+      int virtual_addr_idx;
+      int found_entry;
+      struct in_addr virtual_in_addr;
 #endif
 
 #ifdef FACILITY
@@ -623,53 +628,57 @@
 #ifdef VIRTUAL
     virtual_len = sizeof(virtual_addr);
     if (getsockname(0, (struct sockaddr *) &virtual_addr,
-		    &virtual_len) < 0) {
-      virtual_mode = 0;
-    }
-    else {
-      virtual_ptr = (struct sockaddr_in *) &virtual_addr;
-      set = 0;
-      entry = (struct aclmember *) NULL;
-      while (!set && getaclentry("virtual", &entry)) {
-	if (!strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr))) {
-	  if (ARG1 != NULL && ARG2 != NULL) {
-	    virtual_mode = 1;
-	    strncpy(virtual_root, ARG1, MAXPATHLEN);
-	    strncpy(virtual_banner, ARG2, MAXPATHLEN);
-	    syslog(LOG_ERR, "VirtualFTP Connect to: %s",
-		   inet_ntoa(virtual_ptr->sin_addr));
-	  }
-	}
-      }
-    }
-#endif
-
-
-#ifdef VIRTUAL
-    virtual_len = sizeof(virtual_addr);
-    if (getsockname(0, (struct sockaddr *) &virtual_addr,
                    &virtual_len) < 0) {
       virtual_mode = 0;
     }
     else {
       virtual_ptr = (struct sockaddr_in *) &virtual_addr;
-      set = 0;
+      set_root = 0;
+      set_banner = 0;
+      set_logfile = 0;
       entry = (struct aclmember *) NULL;
-      while (!set && getaclentry("virtual", &entry)) {
-       if (!strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr))) {
-         if(!strcmp(ARG1, "root")) {
-           syslog(LOG_NOTICE, "VirtualFTP Connect to: %s",
-                  inet_ntoa(virtual_ptr->sin_addr));
-           virtual_mode = 1;
-           strncpy(virtual_root, ARG2, MAXPATHLEN);
-         }
-         if(!strcmp(ARG1, "banner")) 
-           strncpy(virtual_banner, ARG2, MAXPATHLEN);
-         if(!strcmp(ARG1, "logfile")) 
-           strncpy(virtual_logfile, ARG2, MAXPATHLEN);
-       }
+      while ((!set_root || !set_banner || !set_logfile) &&
+             getaclentry("virtual", &entry)) {
+        if (strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr))) {
+          if (!(virtual_hostent = gethostbyname(ARG0)) ||
+              virtual_hostent->h_addrtype != AF_INET)
+            continue;
+          found_entry = 0;
+          for (virtual_addr_idx = 0;
+               virtual_hostent->h_addr_list[virtual_addr_idx];
+               virtual_addr_idx++) {
+            memcpy(&virtual_in_addr,
+                   virtual_hostent->h_addr_list[virtual_addr_idx],
+                   sizeof(struct in_addr));
+            if (virtual_in_addr.s_addr == virtual_ptr->sin_addr.s_addr) {
+              found_entry = 1;
+              break;
+            }
+          }
+          if (!found_entry)
+            continue;
+        }
+
+        if(!strcmp(ARG1, "root") && !set_root) {
+          virtual_mode = 1;
+          strncpy(virtual_hostname, ARG0, MAXHOSTNAMELEN);
+          set_root = 1;
+          strncpy(virtual_root, ARG2, MAXPATHLEN);
+        }
+        if(!strcmp(ARG1, "banner") && !set_banner) {
+          set_banner = 1;
+          strncpy(virtual_banner, ARG2, MAXPATHLEN);
+        }
+        if(!strcmp(ARG1, "logfile") && !set_logfile) {
+          set_logfile = 1;
+          strncpy(virtual_logfile, ARG2, MAXPATHLEN);
+        }
       }
     }
+
+    if (virtual_mode)
+      syslog(LOG_NOTICE, "VirtualFTP Connect to: %s (%s)", virtual_hostname,
+             inet_ntoa(virtual_ptr->sin_addr));
 #endif
 
     if (is_shutdown(1) != 0) {
@@ -1088,6 +1097,18 @@
 #endif /* ANON_ONLY */
 
     if ((pw = sgetpwnam(name)) != NULL) {
+#ifdef RESTRICTED_VIRTUAL_ADMIN
+      if (virtual_mode) {
+        if (strcmp(pw->pw_dir, virtual_root) {
+          reply(530, "User %s access denied...", name);
+          syslog(LOG_NOTICE,
+                 "FTP LOGIN REFUSED (unauthorized access) FROM %s [%s], %s",
+                 remotehost, remoteaddr, name);
+          pw = (struct passwd *) NULL;
+          return;
+        }
+      } else {
+#endif
         if ((shell = pw->pw_shell) == NULL || *shell == 0)
             shell = _PATH_BSHELL;
         while ((cp = getusershell()) != NULL)
@@ -1107,6 +1128,9 @@
         /* after they log in successfully                                  */
 	if (use_accessfile)		/* see above.  _H*/
             guest = acl_guestgroup(pw);
+#ifdef RESTRICTED_VIRTUAL_ADMIN
+      }
+#endif
     }
     if (access_ok(530) < 1) {
         reply(530, "User %s access denied....", name);

-- 
Scott Barker
Linux Consultant
[email-masked]
http://www.cuug.ab.ca:8001/~barkers/   (under construction)

[ I try to reply to all e-mail within 3 days. If you don't   ]
[ get a response by then, I probably didn't get your e-mail. ]
[ Unsolicited commercial and junk e-mail will be proof-read for US$100 ]

"The strongest man in the world is he who stands alone."
   - Ibsen

---

From: Scott Barker <[email-masked]>
Date: Wed, 11 Dec 1996 12:37:13 -0700 (MST)

Oops. Sent the wrong patch. Here's the right one.

I added a feature of my own (RESTRICTED_VIRTUAL_ADMIN) which allows remote
administration of a virtual ftp site by a single admin account, which is
restricted by chroot() to it's own account, and need not have a valid shell
(thus allowing for /bin/false shells to restrict the remote admin to ftp
only, and prevent the remote admin from accessessing anything but their own
virtual site via ftp).

Here's the patch:

+++ wu-ftpd-2.4.2-beta-11/src/ftpd.c	Wed Dec 11 12:24:01 1996
 
 #ifdef VIRTUAL
       int virtual_len;
-      int set;
 
 #ifdef FACILITY
@@ -623,53 +628,57 @@
 #ifdef VIRTUAL

     if ((pw = sgetpwnam(name)) != NULL) {
+#ifdef RESTRICTED_VIRTUAL_ADMIN
+      if (virtual_mode) {
+        if (strcmp(pw->pw_dir, virtual_root)) {
+          reply(530, "User %s access denied...", name);
+          syslog(LOG_NOTICE,
+                 "FTP LOGIN REFUSED (unauthorized access) FROM %s [%s], %s",
+                 remotehost, remoteaddr, name);
+          pw = (struct passwd *) NULL;
+          return;
+        }
+      } else {
+#endif
         if ((shell = pw->pw_shell) == NULL || *shell == 0)
             shell = _PATH_BSHELL;
         while ((cp = getusershell()) != NULL)
@@ -1107,6 +1128,9 @@
         /* after they log in successfully                                  */
 	if (use_accessfile)		/* see above.  _H*/
             guest = acl_guestgroup(pw);
+#ifdef RESTRICTED_VIRTUAL_ADMIN
+      }
+#endif
     }
     if (access_ok(530) < 1) {
         reply(530, "User %s access denied....", name);
@@ -1460,6 +1484,16 @@
             }
         }
     }
+
+#ifdef RESTRICTED_VIRTUAL_ADMIN
+    if (!anonymous && !guest && virtual_mode) {
+      if (chroot(virtual_root) < 0 || chdir("/") < 0) {
+        reply(550, "Can't set admin privileges.");
+        goto bad;
+      }
+    }
+#endif
+
 #ifdef AIX
     {
        /* AIX 3 lossage.  Don't ask.  It's undocumented.  */
@@ -1488,7 +1522,11 @@
         goto bad;
     }
 #endif
+#ifdef RESTRICTED_VIRTUAL_ADMIN
+     if (!anonymous && !guest && !virtual_mode) {
+#else
      if (!anonymous && !guest) {
+#endif
         if (chdir(pw->pw_dir) < 0) {
             if (chdir("/") < 0) {
                 reply(530, "User %s: can't change directory to %s.",
@@ -1497,8 +1535,7 @@
             } else
                 lreply(230, "No directory! Logging in with home=/");
 	  }
-      }
-
+     }
 
     /* * following two lines were inside the next scope... */

"It has yet to be proven that intelligence has any survival value."
   - Arthur C. Clarke