Subject: Virtual FTP and restricting user access

From: Bill Nugent <[email-masked]>
Date: Mon, 13 Oct 1997 15:50:28 -0400

Howdy,

I've just upgraded to 2.4.2-beta-15 from 2.4(29) and the improvements are 
great!  Good work.

In setting up the virtual FTP servers it became clear that there is no 
way to restrict a guest user (or anonymous) from logging in on any given 
virtual server.  In my application, this could be problematic down the 
road when the ftp server is broken up onto different machines because 
some guest users may have developed sloppy habits (e.g., using 
ftp.sales.example.com instead of ftp.dev.example.com).

I am attaching a patch which I came up with to restrict who can log onto 
a particular virtual server as either a guest or anonymous user by adding 
a new config line to ftpaccess.  For example:

virtual 10.10.10.10 user joe nancy
# Multiple lines okay
virtual 10.10.10.11 user joe walter
virtual 10.10.10.11 user frank
# No guest or anonymous users
virtual 10.10.10.12 user 
# ftp and anonoymous fine but not guests
virtual 10.10.10.13 user anonymous ftp

In doing this coding, I noticed multiple calls to inet_ntoa() with the 
same argument in ftpd.c.  The first two sections of the patch eliminate 
the repeatitive calls in addition to saving the virtual IP address for 
use by the virtual user hack in the last patch section.

Use this code AT YOUR OWN RISK.  I do not warrant this code at all.  No 
rights reserved.  It works for me under Redhat 4.2 i386.

To add this functionality: apply the patch and add '#define VIRTUAL_USER' 
to src/config.h to enable the patch (or to CFLAGS in the Makefile).  
Virtual servers must also be enabled.

Looking forward to feedback.

	Thank you,
	Bill

--- ftpd.c      Mon Oct 13 10:08:58 1997
+++ ftpd.c.virtual-user Mon Oct 13 15:12:11 1997
@@ -222,6 +222,7 @@
 int virtual_mode=0;
 char virtual_root[MAXPATHLEN];
 char virtual_banner[MAXPATHLEN];
+char virtual_ip_addr[32];  /* Cut down on repeated conversions...*/
 #endif

 int data;
@@ -748,14 +749,17 @@
     virtual_len = sizeof(virtual_addr);
     if (getsockname(0, (struct sockaddr *) &virtual_addr, &virtual_len) 
== 0) {
         virtual_ptr = (struct sockaddr_in *) &virtual_addr;
+       /* Only do the inet_ntoa() call once */
+       strncpy(virtual_ip_addr, inet_ntoa(virtual_ptr->sin_addr),
+               sizeof(virtual_ip_addr));
         entry = (struct aclmember *) NULL;
         while (getaclentry("virtual", &entry)) {
             if (!ARG0 || !ARG1 || !ARG2)
                 continue;
-            if (!strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr))) {
+            if (!strcmp(ARG0, virtual_ip_addr)) {
                 if(!strcmp(ARG1, "root")) {
                    syslog(LOG_NOTICE, "VirtualFTP Connect to: %s",
-                           inet_ntoa(virtual_ptr->sin_addr));
+                           virtual_ip_addr);
                     virtual_mode = 1;
                     strncpy(virtual_root, ARG2, MAXPATHLEN);
                    /* reset hostname to this virtual name */
@@ -1238,6 +1242,39 @@
 /* fall here if username okay in any case */
 #endif /* ANON_ONLY */

+#if defined(VIRTUAL) && defined(VIRTUAL_USER)
+    else {
+       /* If a valid 'virtual nnn.nnn.nnn.nnn user USERS' line exists  */
+       /* obey it!                                                     */
+       struct aclmember *entry = NULL;
+       int which = 0;
+
+        while (getaclentry("virtual", &entry)) {
+           /* Weed out everything that doesn't match */
+            if (!ARG0 || !ARG1 ||
+               strcmp(ARG0, virtual_ip_addr) || strcmp(ARG1, "user")) {
+                continue;
+           }
+           for (which = 2; (which < MAXARGS) && ARG[which]; which++) {
+               if (!strcmp(ARG[which], name))
+                   goto MATCH;
+           }
+       }
+       /* Allow login if there are no 'virtual i.j.k.l user' entries   */
+       /* for this IP address                                          */
+       if (which != 0) {
+           reply(530, "User %s unknown.", name);
+           syslog(LOG_NOTICE,
+              "FTP LOGIN REFUSED: VirtualFTP user %s not allowed access 
to %s",
+             name, virtual_ip_addr);
+           return;
+       }
+      MATCH:
+       syslog(LOG_DEBUG, "VirtualFTP user %s match on %s",
+              name, virtual_ip_addr);
+    }
+#endif /* defined(VIRTUAL) && defined(VIRTUAL_USER) */
+
     if ((pw = sgetpwnam(name)) != NULL) {
 #ifndef USE_PAM /* PAM should be doing these checks, not ftpd */
         if ((shell = pw->pw_shell) == NULL || *shell == 0)