Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 4 Jan 2001 12:14:41 -0500 (EST)
From:      Daniel Hagan <dhagan@colltech.com>
To:        freebsd-audit@freebsd.org
Cc:        freebsd-security@freebsd.org
Subject:   Re: ftpd and anonymous setup (modified ftpd)
Message-ID:  <200101041714.f04HEfB75136@athena.cs.vt.edu>

next in thread | raw e-mail | index | archive | help
Here's a quick patch that includes the chroot/cwd patch mentioned earlier 
and a login.conf capability to set a session to read-only.

[Apologies if you receive this twice, I think it got bounced at freebsd.org]

Daniel


Index: ftpcmd.y
===================================================================
RCS file: /raid/ncvs/src/libexec/ftpd/ftpcmd.y,v
retrieving revision 1.19
diff -u -r1.19 ftpcmd.y
--- ftpcmd.y	2000/12/16 19:19:19	1.19
+++ ftpcmd.y	2001/01/04 15:55:42
@@ -92,6 +92,8 @@
 extern  char tmpline[];
 extern	int readonly;
 extern	int noepsv;
+extern	int dochroot;
+extern	char *cd_dir, *chroot_dir;
 
 off_t	restart_point;
 
@@ -505,8 +507,11 @@
 	| CWD check_login CRLF
 		{
 			if ($2) {
-				if (guest)
-					cwd("/");
+				if (guest || dochroot)
+					if (cd_dir != NULL) 
+						cwd(cd_dir);
+					else
+						cwd("/");
 				else
 					cwd(pw->pw_dir);
 			}
Index: ftpd.8
===================================================================
RCS file: /raid/ncvs/src/libexec/ftpd/ftpd.8,v
retrieving revision 1.36
diff -u -r1.36 ftpd.8
--- ftpd.8	2000/12/18 08:33:25	1.36
+++ ftpd.8	2001/01/04 16:58:49
@@ -158,6 +158,10 @@
 .It Fl r
 Put server in read-only mode.
 All commands which may modify the local filesystem are disabled.
+Read-only mode may be set on a per account basis in
+.Xr login.conf 5
+with the boolean capability "ftp-readonly".  Once set in a session
+it cannot be cleared (i.e. by USER).
 .It Fl E
 Disable the EPSV command.
 This is useful for servers behind older firewalls.
@@ -311,13 +315,14 @@
 or the user is a member of a group with a group entry in this file,
 i.e. one prefixed with
 .Ql \&@ ,
-the session's root will be changed to the user's login directory by
+the session's root will be changed to the user's login directory (up to the first /./) by
 .Xr chroot 2
 as for an
 .Dq anonymous
 or
 .Dq ftp
 account (see next item).
+The user is placed into the directory that remainds after stripping the former from the user's login directory.
 This facility may also be triggered by enabling the boolean "ftp-chroot"
 capability in
 .Xr login.conf 5 .
Index: ftpd.c
===================================================================
RCS file: /raid/ncvs/src/libexec/ftpd/ftpd.c,v
retrieving revision 1.72
diff -u -r1.72 ftpd.c
--- ftpd.c	2000/12/20 03:34:54	1.72
+++ ftpd.c	2001/01/04 17:00:42
@@ -140,6 +140,7 @@
 int	anon_only = 0;    /* Only anonymous ftp allowed */
 int	guest;
 int	dochroot;
+char   *cd_dir = NULL, *chroot_dir = NULL;
 int	stats;
 int	statfd = -1;
 int	type;
@@ -188,6 +189,9 @@
 
 char	*pid_file = NULL;
 
+/* WARNING: FTP_CHROOT_SEPARATOR *MUST* end in / */
+#define FTP_CHROOT_SEPARATOR   "/./"
+
 /*
  * Timeout intervals for retrying connections
  * to hosts that don't accept PORT cmds.  This
@@ -251,6 +255,7 @@
 static char	*sgetsave __P((char *));
 static void	 reapchild __P((int));
 static void      logxfer __P((char *, long, long));
+static void      get_chroot_and_cd_dirs __P((char *, char **, char **));
 
 static char *
 curdir()
@@ -1038,6 +1043,13 @@
 	logged_in = 0;
 	guest = 0;
 	dochroot = 0;
+	/* 
+	 * do not reset readonly to 0 b/c once session is ro, we leave it
+	 * that way for security's sake.
+	 */
+	free(chroot_dir);
+	free(cd_dir);
+	chroot_dir = cd_dir = NULL;
 }
 
 #if !defined(NOPAM)
@@ -1291,19 +1303,24 @@
 		login_getcapbool(lc, "ftp-chroot", 0) ||
 #endif
 		checkuser(_PATH_FTPCHROOT, pw->pw_name, 1);
-	if (guest) {
+#ifdef	LOGIN_CAP	/* Check for ftp-readonly */
+	if (readonly = 0)
+		readonly = login_getcapbool(lc, "ftp-readonly", 0);
+#endif
+	if (guest || dochroot) {
 		/*
 		 * We MUST do a chdir() after the chroot. Otherwise
 		 * the old current directory will be accessible as "."
 		 * outside the new root!
 		 */
-		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
-			reply(550, "Can't set guest privileges.");
-			goto bad;
-		}
-	} else if (dochroot) {
-		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
-			reply(550, "Can't change root.");
+		get_chroot_and_cd_dirs(pw->pw_dir, &chroot_dir, &cd_dir);
+		/*
+		 * Do not free chroot_dir & cd_dir b/c they are used in
+		 * processing CWD commands from client.  They should be 
+		 * free'd during a user logout.
+		 */
+		if (chroot(chroot_dir) < 0 || chdir(cd_dir) < 0) {
+			reply(550, guest ? "Can't set guest privileges." : "Can't change root.");
 			goto bad;
 		}
 	} else if (chdir(pw->pw_dir) < 0) {
@@ -2802,5 +2819,50 @@
 			ctime(&now)+4, ident, remotehost,
 			path, name, size, now - start + (now == start));
 		write(statfd, buf, strlen(buf));
+       }
+}
+
+/*
+ * Make a pointer to the chroot dir and another to the cd dir.
+ * The first is all the path up to the first FTP_CHROOT_SEPARATOR.
+ * The later is the remaining chars, not including the FTP_CHROOT_SEPARATOR,
+ * but prepending a '/', if FTP_CHROOT_SEPARATOR is found.
+ * Otherwise, return user_home_dir as chroot_dir and "/" as cd_dir.
+ */
+static void
+get_chroot_and_cd_dirs(user_home_dir, chroot_dir, cd_dir)
+       char *user_home_dir;
+       char **chroot_dir;
+       char **cd_dir;
+{
+       char *p;
+
+       /* Make a pointer to first character of string FTP_CHROOT_SEPARATOR
+          inside user_home_dir. */
+       p = (char *) strstr(user_home_dir, FTP_CHROOT_SEPARATOR);
+       if (p == NULL) {
+                /*
+                 * There is not FTP_CHROOT_SEPARATOR string inside
+                 * user_home_dir. Return user_home_dir as chroot_dir,
+                 * and "/" as cd_dir.
+                 */
+                if ((*chroot_dir = (char *) strdup(user_home_dir)) == NULL)
+                       fatal("Ran out of memory.");
+                if ((*cd_dir = (char *) strdup("/")) == NULL)
+                       fatal("Ran out of memory.");
+       } else {
+                /*
+                 * Use strlen(user_home_dir) as maximun length for
+                 * both cd_dir and chroot_dir, as both are substrings of
+                 * user_home_dir.
+                 */
+                if ((*chroot_dir = malloc(strlen(user_home_dir))) == NULL)
+                       fatal("Ran out of memory.");
+                if ((*cd_dir = malloc(strlen(user_home_dir))) == NULL)
+                       fatal("Ran out of memory.");
+                (void) strncpy(*chroot_dir, user_home_dir, p-user_home_dir);
+                /* Skip FTP_CHROOT_SEPARATOR (except the last /). */
+                p += strlen(FTP_CHROOT_SEPARATOR)-1;
+                (void) strncpy(*cd_dir, p, strlen(p));
 	}
 }


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200101041714.f04HEfB75136>