Subject: security/4061: /etc/security check of /etc/ftpusers is incomplete and doesn't support full syntax
To: None <gnats-bugs@gnats.netbsd.org>
From: None <jbernard@tater.mines.edu>
List: netbsd-bugs
Date: 08/29/1997 18:42:45
>Number:         4061
>Category:       security
>Synopsis:       /etc/security check of /etc/ftpusers is incomplete and doesn't support full syntax
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Fri Aug 29 17:50:01 1997
>Last-Modified:
>Originator:     Jim Bernard
>Organization:
	speaking for myself
>Release:        August 29, 1997
>Environment:
System: NetBSD zoo 1.2G NetBSD 1.2G (ZOO) #0: Sat Jul 19 12:48:58 MDT 1997 jim@zoo:/jaz/home/local/compile/sys/arch/i386/compile/ZOO i386


>Description:
	The portion of /etc/security that checks /etc/ftpusers to verify that
	root and uucp users are denied access has two problems:
	  * It doesn't check for all root users (which it should, since the
	    OS is distributed with two root users in /etc/master.passwd).
	    Thus, on a default system, user "toor" may be granted ftp access.
	  * It is out of sync with the current syntax of the /etc/ftpusers
	    file as implemented in ftpd.  (PR 4046 contains a fix for this,
	    but the fix does not maintain backward compatibility with the
	    old syntax, which is still supported by ftpd.  A fully compatible
	    fix is provided below.)
>How-To-Repeat:
	* Try to gain ftp access as "toor" on a system with both "root" and
	  "toor" password entries and find that it works.
	* Read the /etc/security script and note the incompatibility with
	  the syntax described in ftpd(8).
>Fix:
	Since the general problem of implementing in a shell script the
	syntax supported by ftpd for /etc/ftpusers is difficult, and since
	it is important to track possible future changes in that syntax,
	I chose to use ftpd itself to do the checking.  The patches to ftpd
	implement a new option (-c user) that simply runs the same routine
	already used to check ftpusers for real connections.  This feature
	is then used in new /etc/security script code to check ftpusers.
	Also, there's a patch to add "toor" to ftpusers in the distribution.

	While making the changes to ftpd, I noted a couple of problems there:
	  * The getopt return code was checked against EOF (not -1)
	  * If the -a option is given without an argument, the session will
	    run chrooted to the standard directory (~ftp) rather than failing.
	    This could have unintended security-related consequences.
	These are fixed as well in the patch below.

	Here are the specific changes:

ftpd.c:
	- Moved arg-list processing up to top of main (otherwise there's
	  no way to get a check-access-and-exit option to work, since
	  there is no connection to set up).

	- Added "-c user" option to check access for "user" and exit
	  after suitable notification; /etc/ftpusers is checked, using
	  the same routine called in normal use, so the check is guaranteed
	  to be compatible with and in sync with the working code.

	- Added explicit check for option -a in case its argument is
	  omitted, so the program will exit, rather than running
	  chrooted to the wrong directory.

	- Test getopt return against -1, not EOF.

ftpd.8:
	- Added documentation for -c option.

/etc/security:
	- Check _all_ uid=0 users (e.g., both "root" and "toor")

	- Use new ftpd -c feature to check /etc/ftpusers with full
	  compatibility with the syntax implemented in ftpd

	- Print a heading if any problems are found--no output otherwise

/etc/ftpusers:
	- Added entry for "toor" (which is in the distributed master.passwd)


--- ftpd.c-dist	Wed Aug 27 05:18:37 1997
+++ ftpd.c	Fri Aug 29 06:59:39 1997
@@ -219,42 +219,23 @@
 {
 	int addrlen, ch, on = 1, tos;
 	char *cp, line[LINE_MAX];
 	FILE *fd;
 
-	/*
-	 * LOG_NDELAY sets up the logging connection immediately,
-	 * necessary for anonymous ftp's that chroot and can't do it later.
-	 */
-	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
-	addrlen = sizeof(his_addr);
-	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
-		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
-		exit(1);
-	}
-	addrlen = sizeof(ctrl_addr);
-	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
-		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
-		exit(1);
-	}
-#ifdef IP_TOS
-	tos = IPTOS_LOWDELAY;
-	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
-		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
-#endif
-	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
-	debug = 0;
-
-	/* set this here so klogin can use it... */
-	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
-
-	while ((ch = getopt(argc, argv, "a:dlt:T:u:v")) != EOF) {
+	while ((ch = getopt(argc, argv, "a:c:dlt:T:u:v")) != -1) {
 		switch (ch) {
 		case 'a':
 			anondir = optarg;
 			break;
 
+		case 'c':
+			printf("/etc/ftpusers %s access by %s\n",
+				(checkaccess(optarg)) ? "denies" : "allows",
+				optarg);
+			exit(0);
+			break;
+
 		case 'd':
 		case 'v':		/* deprecated */
 			debug = 1;
 			break;
 
@@ -268,14 +249,42 @@
 			warnx("-%c has been deprecated in favour of ftpd.conf",
 			    ch);
 			break;
 
 		default:
+			if (optopt == 'a' || optopt == 'c') exit(1);
 			warnx("unknown flag -%c ignored", optopt);
 			break;
 		}
 	}
+
+	/*
+	 * LOG_NDELAY sets up the logging connection immediately,
+	 * necessary for anonymous ftp's that chroot and can't do it later.
+	 */
+	openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
+	addrlen = sizeof(his_addr);
+	if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
+		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
+		exit(1);
+	}
+	addrlen = sizeof(ctrl_addr);
+	if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
+		exit(1);
+	}
+#ifdef IP_TOS
+	tos = IPTOS_LOWDELAY;
+	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+	debug = 0;
+
+	/* set this here so klogin can use it... */
+	(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
+
 	(void) freopen(_PATH_DEVNULL, "w", stderr);
 	(void) signal(SIGPIPE, lostconn);
 	(void) signal(SIGCHLD, SIG_IGN);
 	if ((long)signal(SIGURG, myoob) < 0)
 		syslog(LOG_ERR, "signal: %m");
--- ftpd.8-dist	Sat Jun 14 05:20:30 1997
+++ ftpd.8	Thu Aug 28 21:04:32 1997
@@ -42,10 +42,11 @@
 Internet File Transfer Protocol server
 .Sh SYNOPSIS
 .Nm
 .Op Fl dl
 .Op Fl a Ar anondir
+.Op Fl c Ar user
 .Sh DESCRIPTION
 .Nm
 is the
 Internet File Transfer Protocol
 server process.  The server uses the
@@ -61,10 +62,16 @@
 .It Fl a
 Define the directory to
 .Xr chroot 2
 into for anonymous logins.
 Default is the home directory for the ftp user.
+.It Fl c
+Check whether the specified user would be granted access under
+the restrictions given in
+.Pa /etc/ftpusers
+and exit without attempting a connection.  This can be useful for
+testing configurations.
 .It Fl d
 Debugging information is written to the syslog using LOG_FTP.
 .It Fl l
 Each successful and failed
 .Xr ftp 1
--- security-dist	Fri Aug 22 05:36:17 1997
+++ security	Fri Aug 29 17:58:32 1997
@@ -245,18 +245,24 @@
 		fi
 	fi
 fi
 
 # Root and uucp should both be in /etc/ftpusers.
-# XXX This should be updated to support the new format...
 if [ "$check_ftpusers" = YES ]; then
-	list="root uucp"
+	cp /dev/null $OUTPUT
+	root_users=`awk -F: '$3 == 0 { print $1 }' $MP`
+	list="$root_users uucp"
 	for i in $list; do
-		if ! egrep "^$i$" /etc/ftpusers > /dev/null ; then
-			printf "\n$i is not listed in /etc/ftpusers file.\n"
+		msg=`/usr/libexec/ftpd -c $i`
+		if echo $msg | grep -q "allows access"; then
+			printf "\t$msg.\n" >> $OUTPUT
 		fi
 	done
+	if [ -s $OUTPUT ]; then
+		printf "\nChecking the /etc/ftpusers configuration:\n"
+		cat $OUTPUT
+	fi
 fi
 
 # Uudecode should not be in the /etc/aliases file.
 if [ "$check_aliases" = YES ]; then
 	if egrep '^[^#]*(uudecode|decode).*\|' /etc/aliases; then
--- ftpusers-dist	Sun Apr  6 05:08:11 1997
+++ ftpusers	Fri Aug 29 18:17:51 1997
@@ -3,5 +3,6 @@
 # list of users denied (or allowed) ftp access
 # read by ftpd(8).
 root	deny
+toor	deny
 uucp	deny
 *	allow
>Audit-Trail:
>Unformatted: