Subject: chroot'ed ftp accounts (patches for /usr/libexec/ftpd)
To: None <current-users@sun-lamp.cs.berkeley.edu>
From: None <Jarle.F.Greipsland@idt.unit.no>
List: current-users
Date: 04/06/1994 12:14:33
Hi, 
what follows is some patches to the ftp daemon that will allow other
accounts than the anonymous one to be chroot'ed.  I'm not sure if what I've
done is the proper way to do things, but it seems to work and it might be
useful to others.

The problem:  You want to distribute one (or rather several) set of files
by ftp. 
1) You want password protection (per set) for some rudimentary access
restriction. 
2) No unintentional access to other files than those in a given set put up
for ftp.

Anonymous ftp will give you 2, but not 1.  Also, anonymous ftp only allows
for one set of files put up for ftp (~ftp/*).

A non-anonymous ftp account (with /dev/null as shell) will give you 1, but
not 2.  ftp sessions into this non-anonymous account will be able to roam
freely in your filesystems.

Solution: Maintain a list, similar to /etc/ftpusers, of accounts, that are
to be chroot'ed when ftp'ed into.  The accounts will still have to be set
up as for an anonymous account, i.e. with bin/ls and etc/passwd
substitutes.

If this somehow isn't a proper modification to ftpd, please dump the
patches in /dev/null.  Also, if I somehow have managed to blast myself an
wide-open security hole by doing this, please let me know. :-)

					-jarle

PS. Sorry, the diffs are against NetBSD-current medio March, but I think
the should still apply. DS.
----
"This isn't right.  This isn't even wrong."
			-- Wolfgang Pauli, on a paper submitted by a physicist
			   colleague.

================================================================
*** 1.1	1994/04/05 15:43:21
--- pathnames.h	1994/04/05 15:43:55
***************
*** 38,39 ****
--- 38,40 ----
  
  #define	_PATH_FTPUSERS	"/etc/ftpusers"
+ #define	_PATH_FTPCHROOT "/etc/ftpchroot"

================================================================
*** 1.1	1994/04/05 15:42:31
--- ftpd.c	1994/04/05 17:13:37
***************
*** 40,44 ****
  #ifndef lint
  /*static char sccsid[] = "from: @(#)ftpd.c	5.40 (Berkeley) 7/2/91";*/
! static char rcsid[] = "$Id: ftpd.c,v 1.1 1994/04/05 15:42:31 jarle Exp $";
  #endif /* not lint */
  
--- 40,44 ----
  #ifndef lint
  /*static char sccsid[] = "from: @(#)ftpd.c	5.40 (Berkeley) 7/2/91";*/
! static char rcsid[] = "$Id: ftpd.c,v 1.5 1994/04/05 17:13:25 jarle Exp jarle $";
  #endif /* not lint */
  
***************
*** 108,111 ****
--- 108,112 ----
  int	logging;
  int	guest;
+ int	dochroot;
  int	type;
  int	form;
***************
*** 353,356 ****
--- 354,360 ----
  			reply(530, "Can't change user from guest login.");
  			return;
+ 		} else if (dochroot) {
+ 			reply(530, "Can't change user from chroot user.");
+ 			return;
  		}
  		end_login();
***************
*** 359,363 ****
  	guest = 0;
  	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
! 		if (checkuser("ftp") || checkuser("anonymous"))
  			reply(530, "User %s access denied.", name);
  		else if ((pw = sgetpwnam("ftp")) != NULL) {
--- 363,368 ----
  	guest = 0;
  	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
! 		if (checkuser(_PATH_FTPUSERS, "ftp") ||
! 		    checkuser(_PATH_FTPUSERS, "anonymous"))
  			reply(530, "User %s access denied.", name);
  		else if ((pw = sgetpwnam("ftp")) != NULL) {
***************
*** 376,380 ****
  				break;
  		endusershell();
! 		if (cp == NULL || checkuser(name)) {
  			reply(530, "User %s access denied.", name);
  			if (logging)
--- 381,385 ----
  				break;
  		endusershell();
! 		if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) {
  			reply(530, "User %s access denied.", name);
  			if (logging)
***************
*** 397,403 ****
  
  /*
!  * Check if a user is in the file _PATH_FTPUSERS
   */
! checkuser(name)
  	char *name;
  {
--- 402,409 ----
  
  /*
!  * Check if a user is in the file "fname"
   */
! checkuser(fname, name)
! 	char *fname;
  	char *name;
  {
***************
*** 406,410 ****
  	char line[BUFSIZ];
  
! 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
  		while (fgets(line, sizeof(line), fd) != NULL)
  			if ((p = index(line, '\n')) != NULL) {
--- 412,416 ----
  	char line[BUFSIZ];
  
! 	if ((fd = fopen(fname, "r")) != NULL) {
  		while (fgets(line, sizeof(line), fd) != NULL)
  			if ((p = index(line, '\n')) != NULL) {
***************
*** 433,436 ****
--- 439,443 ----
  	logged_in = 0;
  	guest = 0;
+ 	dochroot = 0;
  }
  
***************
*** 474,477 ****
--- 481,485 ----
  	logged_in = 1;
  
+ 	dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name);
  	if (guest) {
  		/*
***************
*** 482,485 ****
--- 490,498 ----
  		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.");
  			goto bad;
  		}

================================================================
*** 1.1	1994/04/05 18:07:01
--- ftpd.8	1994/04/05 18:16:18
***************
*** 31,35 ****
  .\"
  .\"     from: @(#)ftpd.8	6.9 (Berkeley) 3/16/91
! .\"	$Id: ftpd.8,v 1.1 1994/04/05 18:07:01 jarle Exp $
  .\"
  .Dd March 16, 1991
--- 31,35 ----
  .\"
  .\"     from: @(#)ftpd.8	6.9 (Berkeley) 3/16/91
! .\"	$Id: ftpd.8,v 1.2 1994/04/05 18:15:34 jarle Exp jarle $
  .\"
  .Dd March 16, 1991
***************
*** 169,173 ****
  .Pp
  .Nm Ftpd
! authenticates users according to three rules. 
  .Pp
  .Bl -enum -offset indent
--- 169,173 ----
  .Pp
  .Nm Ftpd
! authenticates users according to five rules. 
  .Pp
  .Bl -enum -offset indent
***************
*** 184,187 ****
--- 184,200 ----
  The user must have a standard shell returned by 
  .Xr getusershell 3 .
+ .It
+ If the user name appears in the file
+ .Pa /etc/ftpchroot
+ the session's root will be changed to the user's login directory by
+ .Xr chroot 2
+ as for an
+ .Dq anonymous
+ or
+ .Dq ftp
+ account (see next item).  However, the user must still supply a password.
+ This feature is intended as a compromise between a fully anonymous account 
+ and a fully privileged account.  The account should also be set up as for an
+ anonymous account.
  .It
  If the user name is


------------------------------------------------------------------------------