Subject: Securing Anonymous FTP Uploads
To: None <tech-security@NetBSD.ORG>
From: Curt Sampson <cjs@portal.ca>
List: tech-security
Date: 03/28/1997 00:12:03
The following is a set of diffs I'm proposing to apply to our FTP
daemon in order to make anonymous uploads more secure (i.e., less
open to abuse).

It does the following:

* Sets the default umask for anonymous users to 707, thus clearing
  out all but group read/write/execute access on uploaded files.

* Disables the umask, chmod, delete and rmdir commands for anonymous
  users.

The idea is this:

Directories in which files may be uploaded are owned by ftp, and
have owner permissions of wx or rwx. This allows anonymous users
to change to that directory, upload files to it, and optionally
see the contents of that directory. 

Uploaded files will be owned by ftp and that directory's group.
The ftp user will not be able to read the contents of or overwrite
uploaded files. Nor will they be able to remove files or directories,
or change the permissions on files or directories.

Users who are members of the group that owns the directory will be
able to do all the standard file management stuff in that directory.
Users who are not members of the group will not be able to read
the files uploaded, and may have fewer permissions, depending on
the permission bits the sysadmin sets on the directory.

If the site prefers a more `open' policy, it can recompile ftpd
with GUEST_CMASK set to something else. This would be pretty foolish
if the machine were on the Internet, however, since this would open
it to being an exchange site for pirated software.

I would also add a section to the ftpd manual page explaining all
of this.

Does anyone see any problem with this?

cjs

Curt Sampson    cjs@portal.ca		Info at http://www.portal.ca/
Internet Portal Services, Inc.	
Vancouver, BC   (604) 257-9400		De gustibus, aut bene aut nihil.

---------- Forwarded message ----------
Date: Thu, 27 Mar 1997 23:58:18 -0800 (PST)
From: Curt Sampson <cjs@gnostic.cynic.net>
To: cjs@portal.ca
Subject: ftp diffs

Index: ftpd.c
===================================================================
RCS file: /usr2/CVSRoot/netbsd/src/libexec/ftpd/ftpd.c,v
retrieving revision 1.1.1.3
diff -u -r1.1.1.3 ftpd.c
--- ftpd.c	1997/02/09 06:34:09	1.1.1.3
+++ ftpd.c	1997/03/28 07:58:04
@@ -126,6 +126,9 @@
 #undef CMASK
 #define CMASK 027
 #endif
+#if !defined(GUEST_CMASK)
+#define GUEST_CMASK 0707
+#endif
 int	defumask = CMASK;		/* default umask value */
 char	tmpline[7];
 char	hostname[MAXHOSTNAMELEN];
@@ -665,7 +668,10 @@
 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
 			    remotehost, pw->pw_name);
 	}
-	(void) umask(defumask);
+	if (guest) 
+	    (void) umask(GUEST_CMASK);
+	else
+	    (void) umask(defumask);
 	return;
 bad:
 	/* Forget all about it... */
Index: ftpcmd.y
===================================================================
RCS file: /usr2/CVSRoot/netbsd/src/libexec/ftpd/ftpcmd.y,v
retrieving revision 1.1.1.3
diff -u -r1.1.1.3 ftpcmd.y
--- ftpcmd.y	1996/04/10 11:27:25	1.1.1.3
+++ ftpcmd.y	1997/03/28 07:58:13
@@ -292,10 +292,15 @@
 		}
 	| DELE check_login SP pathname CRLF
 		{
-			if ($2 && $4 != NULL)
-				delete($4);
-			if ($4 != NULL)
-				free($4);
+			if (guest)  {
+			    reply(502,
+			        "Anonymous users may not use this command.");
+			} else {
+			    if ($2 && $4 != NULL)
+				    delete($4);
+			    if ($4 != NULL)
+				    free($4);
+			}
 		}
 	| RNTO SP pathname CRLF
 		{
@@ -356,10 +361,15 @@
 		}
 	| RMD check_login SP pathname CRLF
 		{
-			if ($2 && $4 != NULL)
-				removedir($4);
-			if ($4 != NULL)
-				free($4);
+			if (guest)  {
+			    reply(502,
+			        "Anonymous users may not use this command.");
+			} else {
+			    if ($2 && $4 != NULL)
+				    removedir($4);
+			    if ($4 != NULL)
+				    free($4);
+			}
 		}
 	| PWD check_login CRLF
 		{
@@ -394,7 +404,10 @@
 			int oldmask;
 
 			if ($4) {
-				if (($6 == -1) || ($6 > 0777)) {
+				if (guest)
+				    reply(502,
+				"Anonymous users may not use this command.");
+				else if (($6 == -1) || ($6 > 0777)) {
 					reply(501, "Bad UMASK value");
 				} else {
 					oldmask = umask($6);
@@ -407,7 +420,10 @@
 	| SITE SP CHMOD check_login SP octal_number SP pathname CRLF
 		{
 			if ($4 && ($8 != NULL)) {
-				if ($6 > 0777)
+				if (guest)
+				    reply(502,
+				"Anonymous users may not use this command.");
+				else if ($6 > 0777)
 					reply(501,
 				"CHMOD: Mode value must be between 0 and 0777");
 				else if (chmod($8, $6) < 0)