Subject: bin/4769: enhance ftpchroot like ftpusers
To: None <gnats-bugs@gnats.netbsd.org>
From: Ty Sarna <tsarna@endicor.com>
List: netbsd-bugs
Date: 01/03/1998 19:35:26
>Number:         4769
>Category:       bin
>Synopsis:       enhance ftpchroot like ftpusers
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Jan  3 17:50:01 1998
>Last-Modified:
>Originator:     Ty Sarna
>Organization:
	Endicor Technologies, Inc., San Antonio, Texas
>Release:        1.3_BETA-971217
>Environment:
	
	1.3_BETA-971217, (on i386, but doesn't matter)

>Description:
	Patches generalize checkaccess()'s functionality into checkuser(),
	Which can then be used both for checkaccess() (/etc/ftpusers)
	and /etc/ftpchroot (and possibly other things, in the future).

	Upshot: /etc/ftpchroot then implements the wildcard matching and
	other features of the new ftpusers syntax, in a backward
	compatible manner.
>How-To-Repeat:
	N/A
>Fix:
--- ftpd.c.orig	Mon Dec 29 15:16:33 1997
+++ ftpd.c	Sat Jan  3 18:34:15 1998
@@ -100,6 +100,11 @@
 #include <varargs.h>
 #endif
 
+#ifndef TRUE
+#define TRUE	1
+#define FALSE	0
+#endif
+
 static char version[] = "Version 7.02";
 
 extern	off_t restart_point;
@@ -184,7 +189,7 @@
 
 static void	 ack __P((char *));
 static void	 myoob __P((int));
-static int	 checkuser __P((char *, char *));
+static int	 checkuser __P((char *, char *, int def, int nofile));
 static int	 checkaccess __P((char *));
 static FILE	*dataconn __P((char *, off_t, char *));
 static void	 dolog __P((struct sockaddr_in *));
@@ -500,66 +505,36 @@
 		sleep((unsigned) login_attempts);
 }
 
-/*
- * Check if a user is in the file "fname"
- */
-static int
-checkuser(fname, name)
-	char *fname;
-	char *name;
-{
-	FILE *fd;
-	int found = 0;
-	char *p, line[BUFSIZ];
-
-	if ((fd = fopen(fname, "r")) != NULL) {
-		while (fgets(line, sizeof(line), fd) != NULL)
-			if ((p = strchr(line, '\n')) != NULL) {
-				*p = '\0';
-				if (line[0] == '#')
-					continue;
-				if (strcmp(line, name) == 0) {
-					found = 1;
-					break;
-				}
-			}
-		(void) fclose(fd);
-	}
-	return (found);
-}
-
-/*
- * Determine whether a user has access, based on information in 
- * _PATH_FTPUSERS. Each line is a shell-style glob followed by
- * `allow' or `deny' (with deny being the default if anything but
- * `allow', or nothing at all, is specified).
+/* Determine wether something is to happen (allow access, chroot)
+ * for a user. Each line is a shell-style glob followed by
+ * `yes' or `no'.
  *
- * Each glob is matched against the username in turn, and the first
- * match found is used. If no match is found, access is allowed.
+ * For backward compatability, `allow' and `deny' are synonymns 
+ * for `yes' and `no', respectively.
  *
- * Any line starting with `#' is considered a comment and ignored.
+ * Each glob is matched against the username in turn, and the first
+ * match found is used. If no match is found, the result is the
+ * argument `def'. If a match is found but without and explicit
+ * `yes'/`no', the result is the opposite of def.
  *
- * This is probably not the best way to do this, but it preserves
- * the old semantics where if a user was listed in the file he was
- * denied, otherwise he was allowed.
+ * If the file doesn't exist at all, the result is the argument
+ * `nofile'
  *
- * There is one change in the semantics, however; ftpd will now `fail
- * safe' and deny all access if there's no /etc/ftpusers file.
+ * Any line starting with `#' is considered a comment and ignored.
  *
- * Return 1 if the user is denied, or 0 if he is allowed.
+ * Returns FALSE for no/deny, or TRUE for yes/allow.
  */
 static int
-checkaccess(name)
-	char *name;
+checkuser(fname, name, def, nofile)
+	char *fname, *name;
+	int def, nofile;
 {
-#define ALLOWED		0
-#define	NOT_ALLOWED	1
 	FILE *fd;
-	int retval = ALLOWED;
+	int retval = def;
 	char *glob, *perm, line[BUFSIZ];
 
-	if ((fd = fopen(conffilename(_PATH_FTPUSERS), "r")) == NULL)
-		return NOT_ALLOWED;
+	if ((fd = fopen(conffilename(fname), "r")) == NULL)
+		return nofile;
 
 	while (fgets(line, sizeof(line), fd) != NULL)  {
 		glob = strtok(line, " \t\n");
@@ -567,18 +542,33 @@
 			continue;
 		perm = strtok(NULL, " \t\n");
 		if (fnmatch(glob, name, 0) == 0)  {
-			if (perm != NULL && strcmp(perm, "allow") == 0)
-				retval = ALLOWED;
+			if (perm != NULL && 
+			(  (strcmp(perm, "allow") == 0)
+			|| (strcmp(perm, "yes")   == 0)) )
+				retval = TRUE;
+			else if (perm != NULL && 
+			(  (strcmp(perm, "deny") == 0)
+			|| (strcmp(perm, "no")   == 0)) )
+				retval = FALSE;
 			else
-				retval = NOT_ALLOWED;
+				retval = !def;
 			break;
 		}
 	}
 	(void) fclose(fd);
 	return (retval);
 }
-#undef	ALLOWED
-#undef	NOT_ALLOWED
+
+/*
+ * Check if a user is allowed access. Default is no if /etc/ftpusers
+ * doesn't exist, yes if exists but user not listed.
+ */
+static int
+checkaccess(name)
+	char *name;
+{
+	return !checkuser(_PATH_FTPUSERS, name, TRUE, FALSE);
+}
 
 /*
  * Terminate login as previous user, if any, resetting state;
@@ -705,7 +695,7 @@
 	logwtmp(ttyline, pw->pw_name, remotehost);
 	logged_in = 1;
 
-	dochroot = checkuser(conffilename(_PATH_FTPCHROOT), pw->pw_name);
+	dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name, FALSE, FALSE);
 
 	/* parse ftpd.conf, setting up various parameters */
 	if (guest)
--- ftpd.8.orig	Sat Jan  3 18:53:15 1998
+++ ftpd.8	Sat Jan  3 19:03:18 1998
@@ -220,8 +220,9 @@
 shell is assumed to be
 .Pa /bin/sh . )
 .It
-If the user name appears in the file
+If directed by the file
 .Pa /etc/ftpchroot
+(see below)
 the session's root will be changed to the user's login directory by
 .Xr chroot 2
 as for an
@@ -264,16 +265,24 @@
 .Dq #
 or a glob pattern that uses the same syntax as /bin/sh,
 optionally followed by whitespace and
-.Dq allow
+.Dq allow ,
+.Dq yes ,
+.Dq deny ,
 or
-.Dq deny .
+.Dq no .
 Each glob pattern is compared in turn against the username
 until a match is found.  If the word following the matched glob
 pattern is
 .Dq allow
-the user is granted access; if the word is
-anything else, or nothing at all, the user is denied access.
-(No further comparisons are attempted after the first successful match.)
+or
+.Dq yes
+the user is granted access; if the word is 
+.Dq deny
+or
+.Dq no ,
+or if the word is missing,
+the user is denied access.
+No further comparisons are attempted after the first successful match.
 If no match is found, the user is granted access.
 This syntax is backward-compatable with the old syntax.
 .Pp
@@ -289,6 +298,29 @@
 to
 .Pa /etc/ftpusers
 in order to allow guest logins.
+.Ss /etc/ftpchroot
+The file
+.Pa /etc/ftpchroot
+is used to determine which users will have their session's root changed
+to the user's home directory.
+If the file does not exist, the root change is not performed.
+If it does exist, each line is a a comment starting with
+.Dq #
+or a glob pattern that uses the same syntax as /bin/sh,
+optionally followed by whitespace and
+.Dq yes
+or
+.Dq no .
+Each glob pattern is compared in turn against the username
+until a match is found.  If the word following the matched glob
+pattern is
+.Dq yes
+or there is no following word, the root is changed.
+If the word is 
+.Dq no ,
+or if no match is found, the root is not changed.
+No further comparisons are attempted after the first successful match.
+This syntax is backward-compatable with the old syntax.
 .Ss /etc/ftpd.conf
 The file
 .Pa /etc/ftpd.conf
	
>Audit-Trail:
>Unformatted: