Subject: NFS problem; virtual Servers

From: Jens Hamisch <[email-masked]>
Date: Sat, 17 Aug 1996 13:57:26 +0200 (MET DST)

Hello,

there has been a NFS problem with the wu-ftpd-2.4.2-beta11. The problem was,
that ftpd tried to do the following in case of a STORE command:

     fd = open( 'destinaton file', ....)
     setuid( 0);
     fchown( fd, uid, gid);
     setuid( ftp);

     .... write the file contents

     close( fd);

This will only work for none-NFS file systems. In cae of NFS, the FTPD will
get a 'EACCESS' at the close command, because it runs as user 'ftp' and tries
to write and close a file owned by the user 'uid' on the remode NFS file
system. That'll only work on none-NFS systems, because the writing process
still owns the file after changing the ownership.

I've fixed this by moving the fchown part behind the close statement. This
works out fine even for UFS and NFS file systems.

Also I've applied another patch to the wu-ftpd virtual server capability. Some
of the changes I've made have been posted already last week. They are part of
the patch, which comes with this mail.

I've inroduced 2 new command to the ftpaccess file. The first is

    virtual <address> pass <filename>

which assigns a pass-file to the virtual server running at the specified IP
address. This file names a list of user-ids being allowed to access the vir-
tual server. The file format may be a simple list (yes, in my last patch this
would produce a core dump ... now it is fixed ;-) of user IDs with one user
name per line, or a copy of the local /etc/passwd file. In case of using the
passwd file, everything behind the first ':' may be removed to increase the
security of your system.

The second one is a

    virtual <address> upload <root-dir> <dirglob> <yes|no> <owner> <group> ...

command, which implements upload restrictions that apply only for a single
virtual server.

The patch may be applied to the wu-ftpd-2.4.2-beta11 package by

    1.)  Save the patch to a single file (e.g. 'patch-ftpd') and remove
	 everything, which is added by the mail transfer

    2.)  Unpack your wu-ftpd-2.4.2-beta11 packge

    3.)  cd wu-ftpd-2.4.2-beta11

    4.)  patch < patch-ftpd   or whatever the filename of your pathfile is

    5.)  compile wu-ftpd as usual

The following files are affected by the patch:

    src/ftpd.c
    src/extensions.c
    src/newvers.sh
    doc/ftpaccess.5
    ./VIRTUAL.FTP.SUPPPORT

Maybe the new functionality will be part of the next wu-ftpd (academic) distri-
butions. On request, I may put the entire patched ftpd-distribution on our
ftp-server.
In any case, I would like to get your feedback if there are security incidents,
comilation problems or somewhat else.

cu on the bitstream
jens

---- snip -----------------------------------------------------------

--- ./doc/ftpaccess.5.orig	Sat Aug 17 10:49:58 1996
+++ ./doc/ftpaccess.5	Sat Aug 17 11:14:22 1996
@@ -296,24 +296,49 @@
 
 The external program ftpshut(8) can be used to automate the
 process of generating this file.
+.SH Extended Access Capabilities for virtual servers
+The capabilitiy to run virtual FTP servers prerequisites some 
+options to be enabled at compile time of the FTP server. Please
+refer the installation instructions in order to enable your
+FTP server for running virtual adresses.
+You need to assign a single IP address for each of your virtual
+FTP servers. Many operating systems (such as Solaris) offer the
+opportunity to do this by adding virtual addresses to your primary
+network interface using the ifconfig command. On SunOS, you will
+have to install the (public domain) vif package first. All addresses
+refered in the following belong to these virtual IP addresses and
+are to be specified in a numeric dotted form as 'a.b.c.d'.
 .TP 0.5i
-.B virtual <address> <root|banner|logfile> <path>
-Enables the virtual ftp server capabilities. The <address> is the ip address
-of the virtual server. The second arguement specifies that the <path> is either
-the path to the 
-.B root
-of the filesystem for this virtual server, the
-.B banner
-presented to the user when connecting to this virtual server, or the
-.B logfile 
-where transfers are recorded for this virtual server. If the
-.B logfile
-is not specified the default logfile will be used. 
-All  other message files and permissions as well as any
-other settings in this file apply to all virtual servers. 
+.B virtual <address> root <path>
+Specifies the root path of your virtual FTP server running at
+IP address <address>.
+.TP 0.5i
+.B virtual <address> banner <path>
+Specifies a banner file to be displayed for this specific virtual
+FTP server.
+.TP 0.5i
+.B virtual <address> logfile <path>
+Defines a path, where to put the logfiles for the virtual server.
+.TP 0.5i
+.B virtual <address> pass <path>
+This defines a (password-) file which restricts the users being allowed
+to access the virtual server at <address>. The file named as third
+parameter of this command may either a copy of your password file or
+simply a list of user names. No authentication is done using this file,
+so if you'd like to copy your password file, you're free to remove
+everything which is security sensitive in this file (e.g. the encrypted
+password). If you don't specify a virtual pass phrase, any user known
+on your system is allowed to access the server. If you do, don't forget
+to add the 'ftp' and the 'anonymous' user in order to anable anonymous
+ftp support for your virtual server.
+.TP 0.5i
+.B virtual <address> upload <root-dir> <dirglob> <yes|no> <owner> <group> <mode> ["dirs"|"nodirs"]
+This is a special upload command valid for your virtual server. The
+parameters and their meaning of the command are the same as for the normal
+upload command described later in this manpage.
 
 NOTE: Your operating system may not support this feature. It has been tested
-on BSD/OS, Solaris 2.X and Linux.
+on BSD/OS, SunOS 4.1.3, Solaris 2.X and Linux.
 
 .SH Permission Capabilities
 .TP 0.5i
--- ./src/ftpd.c.orig	Sun Aug 11 11:36:08 1996
+++ ./src/ftpd.c	Sat Aug 17 10:39:06 1996
@@ -216,6 +216,7 @@
 char virtual_root[MAXPATHLEN];
 char virtual_banner[MAXPATHLEN];
 char virtual_logfile[MAXPATHLEN];
+char virtual_passfile[MAXPATHLEN];
 #endif
 
 int data;
@@ -379,6 +380,7 @@
 #ifdef VIRTUAL
       int virtual_len;
       int set;
+      virtual_passfile[0] = '\0';
 #endif
 
 #ifdef FACILITY
@@ -620,6 +622,7 @@
     authenticate();
     conv_init();
 
+#if 0
 #ifdef VIRTUAL
     virtual_len = sizeof(virtual_addr);
     if (getsockname(0, (struct sockaddr *) &virtual_addr,
@@ -643,6 +646,7 @@
       }
     }
 #endif
+#endif

 #ifdef VIRTUAL
@@ -667,6 +671,8 @@
            strncpy(virtual_banner, ARG2, MAXPATHLEN);
          if(!strcmp(ARG1, "logfile")) 
            strncpy(virtual_logfile, ARG2, MAXPATHLEN);
+         if(!strcmp(ARG1, "pass")) 
+           strncpy(virtual_passfile, ARG2, MAXPATHLEN);
        }
       }
     }
@@ -841,6 +847,38 @@
         return (p);
 #endif  /* M_UNIX */
 
+#ifdef VIRTUAL
+    if(virtual_mode) {
+	if( *virtual_passfile) {
+	    FILE *vpasswdf;
+	    if((FILE *) NULL != (vpasswdf = fopen( virtual_passfile, "r"))) {
+		char *vpassline, vpassbuf[ BUFSIZ];
+		char *vpasstok;
+		int found = 0;
+		while( !feof( vpasswdf) && !found) {
+		    vpassline = fgets( vpassbuf, BUFSIZ, vpasswdf);
+		    vpasstok = strtok( vpassline, ":");
+		    if( !vpasstok) vpasstok = vpassline;
+		    if( !strcmp( vpasstok, name))
+			found = 1;
+		}
+		fclose( vpasswdf);
+		if( !found) {
+		    syslog(LOG_NOTICE,
+			"VirtualFTP LOGIN REFUSED (%s not in %s)",
+			name, virtual_passfile);
+		    return((struct passwd *) NULL);
+		}
+	    } else {
+		syslog(LOG_NOTICE,
+		    "VirtualFTP LOGIN REFUSED (passfile %s not found)",
+		    virtual_passfile); 
+		return((struct passwd *) NULL);
+	    }
+	}
+    }
+#endif
+
     if (save.pw_name)   free(save.pw_name);
     if (save.pw_gecos)  free(save.pw_gecos);
     if (save.pw_dir)    free(save.pw_dir);
@@ -1840,7 +1878,6 @@
 {
     FILE *fout, *din;
     struct stat st;
-    int (*closefunc) ();
 #ifdef __STDC__
     char *gunique(char *local);
 #else
@@ -1927,18 +1964,6 @@
         perror_reply(553, name);
         return;
     }
-    /* if we have a uid and gid, then use them. */
-
-    if (valid > 0) {
-        oldid = geteuid();
-        (void) seteuid((uid_t) 0);
-        if ((fchown(fdout, uid, gid)) < 0) {
-            (void) seteuid(oldid);
-            perror_reply(550, "fchown");
-            return;
-        }
-        (void) seteuid(oldid);
-    }
 #endif /* UPLOAD */
 
     if (restart_point)
@@ -1950,7 +1975,6 @@
     fout = fopen(name, mode);
 #endif /* UPLOAD */
 
-    closefunc = fclose;
     if (fout == NULL) {
         perror_reply(553, name);
         return;
@@ -2057,8 +2081,24 @@
     }
     data = -1;
     pdata = -1;
+
   done:
-    (*closefunc) (fout);
+    fclose( fout);
+
+#ifdef UPLOAD
+    /* if we have a uid and gid, then use them. */
+
+    if (valid > 0) {
+        oldid = geteuid();
+        (void) seteuid((uid_t) 0);
+        if ((chown(name, uid, gid)) < 0) {
+            (void) seteuid(oldid);
+            perror_reply(550, "chown");
+            return;
+        }
+        (void) seteuid(oldid);
+    }
+#endif
 }
 
 FILE *
--- ./src/newvers.sh.orig	Sun Aug 11 15:49:09 1996
+++ ./src/newvers.sh	Sat Aug 17 10:36:14 1996
@@ -21,7 +21,7 @@
 if [ ! -r edit ]; then echo 0 > edit; fi
 touch edit
 awk '	{	edit = $1 + 1; }\
-END	{	printf "char version[] = \"Version wu-2.4.2-academ[BETA-11](%d) ", edit > "vers.c";\
+END	{	printf "char version[] = \"Version wu-2.4.2-academ[BETA-11.v2](%d) ", edit > "vers.c";\
 #LANG=
 #LC_TIME=
 		printf "%d\n", edit > "edit"; }' < edit
--- ./src/extensions.c.orig	Mon Apr 15 05:51:05 1996
+++ ./src/extensions.c	Sat Aug 17 10:00:14 1996
@@ -50,6 +50,11 @@
 #include <sys/stat.h>
 #include <sys/file.h>
 #include <sys/param.h>
+#ifdef VIRTUAL
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
 
 #if defined(HAVE_STATVFS)
 #include <sys/statvfs.h>
@@ -118,6 +123,12 @@
 
 int show_fullinfo;
 
+#ifdef VIRTUAL
+extern int virtual_mode;
+extern struct sockaddr_in *virtual_ptr;
+extern char virtual_root[];
+#endif
+
 #ifdef __STDC__
 check_newer(char *path, struct stat *st, int flag)
 #else
@@ -797,23 +808,53 @@
     struct group *grent;
     char buf[BUFSIZ];
 
-    while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
-        if (ARG3 && ARG4) {
-            pwent = getpwnam(ARG3);
-            grent = getgrnam(ARG4);
-
-            if (pwent)  sprintf(buf, "%d", pwent->pw_uid);
-            else        sprintf(buf, "%d", 0);
-            ARG3 = (char *) malloc(strlen(buf) + 1);
-            strcpy(ARG3, buf);
-
-            if (grent)  sprintf(buf, "%d", grent->gr_gid);
-            else        sprintf(buf, "%d", 0);
-            ARG4 = (char *) malloc(strlen(buf) + 1);
-            strcpy(ARG4, buf);
-	    endgrent();
-        }
+#ifdef VIRTUAL
+    if( virtual_mode) {
+
+	while( getaclentry("virtual", &entry)) {
+	    if( !strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr)) &&
+	        !strcmp(ARG1, "upload") && ARG2 && ARG3 && ARG4) {
+
+		if (ARG5 && ARG6) {
+		    pwent = getpwnam(ARG5);
+		    grent = getgrnam(ARG6);
+
+		    if (pwent)  sprintf(buf, "%d", pwent->pw_uid);
+		    else        sprintf(buf, "%d", 0);
+		    ARG5 = (char *) malloc(strlen(buf) + 1);
+		    strcpy(ARG5, buf);
+
+		    if (grent)  sprintf(buf, "%d", grent->gr_gid);
+		    else        sprintf(buf, "%d", 0);
+		    ARG6 = (char *) malloc(strlen(buf) + 1);
+		    strcpy(ARG6, buf);
+		    endgrent();
+		}
+	    }
+	}
+
+    } else {
+#endif
+	while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
+	    if (ARG3 && ARG4) {
+		pwent = getpwnam(ARG3);
+		grent = getgrnam(ARG4);
+
+		if (pwent)  sprintf(buf, "%d", pwent->pw_uid);
+		else        sprintf(buf, "%d", 0);
+		ARG3 = (char *) malloc(strlen(buf) + 1);
+		strcpy(ARG3, buf);
+
+		if (grent)  sprintf(buf, "%d", grent->gr_gid);
+		else        sprintf(buf, "%d", 0);
+		ARG4 = (char *) malloc(strlen(buf) + 1);
+		strcpy(ARG4, buf);
+		endgrent();
+	    }
+	}
+#ifdef VIRTUAL
     }
+#endif
 }
 
 int
@@ -960,27 +1001,64 @@
     return(-1);
   }
   i = match_value;  /* i is -1 here */
-  while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
- /* CERT suggested fix (change && to || ) */
-    if ( (!strcmp(ARG0, pw->pw_dir))  ||
-		   ((i = path_compare(ARG1, cwdir)) >= match_value) ) {
-          match_value = i;
-          ap2 = ARG2;
-          if (ARG3)  ap3 = ARG3;
-          else       ap3 = NULL;
-          if (ARG4)  ap4 = ARG4;
-          else       ap4 = NULL;
-          if (ARG6)  ap6 = ARG6;
-          else       ap6 = NULL;
-      }
-    if ( ((ap2 && !strcasecmp(ap2, "no")) && 
-	  (ap3 && strcasecmp(ap3, "dirs"))) || 
-	 (ap3 && !strcasecmp(ap3, "nodirs")) ||
+
+#ifdef VIRTUAL
+    if( virtual_mode) {
+
+	while( getaclentry("virtual", &entry)) {
+	    if( !strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr)) &&
+	        !strcmp(ARG1, "upload") && ARG2 && ARG3 && ARG4) {
+
+		if ( (!strcmp(ARG2, virtual_root))  ||
+		    ((i = path_compare(ARG3, cwdir)) >= match_value) ) {
+		    match_value = i;
+		    ap2 = ARG4;
+		    if (ARG5)  ap3 = ARG5;
+		    else       ap3 = NULL;
+		    if (ARG6)  ap4 = ARG6;
+		    else       ap4 = NULL;
+		    if (ARG8)  ap6 = ARG8;
+		    else       ap6 = NULL;
+		}
+	    }
+	    if (((ap2 && !strcasecmp(ap2, "no")) && 
+		(ap3 && strcasecmp(ap3, "dirs"))) || 
+		(ap3 && !strcasecmp(ap3, "nodirs")) ||
 		(ap6 && !strcasecmp(ap6, "nodirs")) ) {
-      reply(530, "%s: Permission denied.  (Mkdir)", name);
-      return(0);
+		reply(530, "%s: Permission denied.  (Mkdir)", name);
+		return(0);
+	    }
+	}
+
+    } else {
+#endif
+
+	while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
+	    /* CERT suggested fix (change && to || ) */
+	    if ( (!strcmp(ARG0, pw->pw_dir))  ||
+		((i = path_compare(ARG1, cwdir)) >= match_value) ) {
+		match_value = i;
+		ap2 = ARG2;
+		if (ARG3)  ap3 = ARG3;
+		else       ap3 = NULL;
+		if (ARG4)  ap4 = ARG4;
+		else       ap4 = NULL;
+		if (ARG6)  ap6 = ARG6;
+		else       ap6 = NULL;
+	    }
+	    if (((ap2 && !strcasecmp(ap2, "no")) && 
+		(ap3 && strcasecmp(ap3, "dirs"))) || 
+		(ap3 && !strcasecmp(ap3, "nodirs")) ||
+		(ap6 && !strcasecmp(ap6, "nodirs")) ) {
+		reply(530, "%s: Permission denied.  (Mkdir)", name);
+		return(0);
+	    }
+	}
+
+#ifdef VIRTUAL
     }
-  }
+#endif
+
   if (ap3)
      *uid = atoi(ap3);    /* the uid  */
   if (ap4) {
@@ -1036,20 +1114,49 @@
       entry = NULL;
       match_value = -1;
       i = match_value;
-      while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
-          if ( (!strcmp(ARG0, pw->pw_dir)) &&
-		       ((i = path_compare(ARG1, cwdir)) >= match_value) ) {
-              match_value = i;
-              ap1 = ARG1;
-              ap2 = ARG2;
-              if (ARG3) ap3 = ARG3;
-              else      ap3 = NULL;
-              if (ARG4) ap4 = ARG4;
-              else      ap4 = NULL;
-              if (ARG5) ap5 = ARG5;
-              else      ap5 = NULL;
-          }
+
+#ifdef VIRTUAL
+    if( virtual_mode) {
+
+	while( getaclentry("virtual", &entry)) {
+	    if( !strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr)) &&
+	        !strcmp(ARG1, "upload") && ARG2 && ARG3 && ARG4) {
+
+		if ( (!strcmp(ARG2, virtual_root))  &&
+		    ((i = path_compare(ARG3, cwdir)) >= match_value) ) {
+		    match_value = i;
+		    ap1 = ARG3;
+		    ap2 = ARG4;
+		    if (ARG5) ap3 = ARG5;
+		    else      ap3 = NULL;
+		    if (ARG6) ap4 = ARG6;
+		    else      ap4 = NULL;
+		    if (ARG7) ap5 = ARG7;
+		    else      ap5 = NULL;
+		}
+	    }
+	}
+
+    } else {
+#endif
+	while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
+	    if( (!strcmp(ARG0, pw->pw_dir)) &&
+		((i = path_compare(ARG1, cwdir)) >= match_value) ) {
+		match_value = i;
+		ap1 = ARG1;
+		ap2 = ARG2;
+		if (ARG3) ap3 = ARG3;
+		else      ap3 = NULL;
+		if (ARG4) ap4 = ARG4;
+		else      ap4 = NULL;
+		if (ARG5) ap5 = ARG5;
+		else      ap5 = NULL;
+	    }
 	}
+
+#ifdef VIRTUAL
+    }
+#endif
 
       if (ap3 && ( (!strcasecmp("dirs",ap3)) || (!strcasecmp("nodirs", ap3)) ))
         ap3 = NULL;
--- ./VIRTUAL.FTP.SUPPPORT.orig	Sat Aug 17 10:41:38 1996
+++ ./VIRTUAL.FTP.SUPPPORT	Sat Aug 17 10:49:33 1996
@@ -1,10 +1,8 @@
-Date: Fri, 26 May 1995 21:33:23 -0400 (EDT)
-From: Brian Kramer <[email-masked]>
-To: [email-masked]
-Subject: Virtual FTP Servers
-
 [Modifications to provide for discrete xferlogs for each server provided by
 Marc G. Fournier <[email-masked]> -- sob.]
+
+[Modifications to provide for virtual uploading and user restrictions
+provided by Jens Hamisch <[email-masked]>]
  
 I'm attaching a patch for wu-ftpd 2.4 to allow virtual ftp servers to be 
 setup.  Basically so a user ftping to ftp1.domain.com gets one ftp banner 
@@ -31,25 +29,26 @@
 virtual 10.10.10.10 root    /var/ftp/virtual/ftp-serv 
 virtual 10.10.10.10 banner  /var/ftp/virtual/ftp-serv/banner.msg
 virtual 10.10.10.10 logfile /var/log/ftp/virtual/ftp-serv/xferlog
+virtual 10.10.10.10 pass    /subsys/wwwstaff/var/etc/ftp.pass
+virtual 10.10.10.10 upload  /subsys/wwwstaff/home/ftp /incoming yes uid gid mode perms
+virtual 10.10.10.10 upload  /subsys/wwwstaff/home/ftp /bin no

 The first arg is the ip address of the virtual server.
-The second arg is either "root", "banner" or "logfile" (without the quotes)
-for that virtual server.
+The second arg is either "root", "banner", "pass", "upload" or "logfile"
+(without the quotes) for that virtual server.
 The third arg is the file system location for the item specified in the 
-second arg.
+second arg in case of "root", "banner", "pass" or "logfile".
+In case of "upload" the semantics of the arguments is exactly the same
+as for the original "upload" command. 
+The filename specified with "pass" specifies a list of users to be allowed
+to access this virtual server. Never forget to add the "ftp" and "anonymous"
+accounts to this file. The file structure may be just a list of names (one
+per line) or a copy of your local /etc/passwd file. You may want to remove
+al sensitive data (like the encrypted password) from this file by the means
+of increasing security of your system. [jens]
 
 Note: all the other message files, etc, and permissions and other settings
 in the ftpaccess file apply to all virtual servers.
-
-$Id: VIRTUAL.FTP.SUPPPORT,v 1.2 1996/03/15 06:17:33 sob Exp $
-
-
-
-
-
-
-
-

---- snap -----------------------------------------------------------

-- 

--------------------------------------------------------------------------------
     /
 +##+|##+   STRAWBERRY                  Jens Hamisch
+v#+v v##+  EDV-Systeme GmbH            Managing director
/ v    v\v
| . .  . |  Brauneckweg 2               Car (Voice):  (+49 172) 81 04 162
|     .  |  D-82549 Koenigsdorf         Voice:        (+49 8179) 5278 Q, Fax
 | .     |                              Email:        [email-masked]
 \   .  /   Tel./Fax: (+49 8179) 5278                 [email-masked]
  \____/    [email-masked]   Home (Email): [email-masked]