From sakamoto@on-sky.net  Fri Nov 15 20:51:38 2002
Return-Path: <sakamoto@on-sky.net>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id DDC0737B401
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 15 Nov 2002 20:51:37 -0800 (PST)
Received: from stars.on-sky.net (stars.on-sky.net [211.6.119.236])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 0DA3743E77
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 15 Nov 2002 20:51:37 -0800 (PST)
	(envelope-from sakamoto@on-sky.net)
Received: from stars.on-sky.net (localhost [127.0.0.1])
	by stars.on-sky.net (8.12.6/8.12.6) with ESMTP id gAG4pP2V005881
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 16 Nov 2002 13:51:25 +0900 (JST)
	(envelope-from sakamoto@stars.on-sky.net)
Received: (from sakamoto@localhost)
	by stars.on-sky.net (8.12.6/8.12.6/Submit) id gAG4pOQY005880;
	Sat, 16 Nov 2002 13:51:24 +0900 (JST)
	(envelope-from sakamoto)
Message-Id: <200211160451.gAG4pOQY005880@stars.on-sky.net>
Date: Sat, 16 Nov 2002 13:51:24 +0900 (JST)
From: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
Reply-To: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject:
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         45327
>Category:       bin
>Synopsis:       [patch] ftpd extention for arbitrary chroot directory
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    yar
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Nov 15 21:00:02 PST 2002
>Closed-Date:    Tue Feb 11 06:54:39 PST 2003
>Last-Modified:  Tue Feb 11 06:54:39 PST 2003
>Originator:     Hideki SAKAMOTO
>Release:        FreeBSD 4.7-RELEASE-p2 i386(RELENG_4_7 at Nov 15 2002)
>Organization:
>Environment:

 System: FreeBSD xxxxxxxxxxxxxxxx 4.7-RELEASE-p2 FreeBSD 4.7-RELEASE-p2 #1: Fri Nov 15 01:42:56 JST 2002 sakamoto@xxxxxxxx:/usr/obj/usr/src/sys/XXXX i386

>Description:
 	This patch will available to set arbitrary chroot directory in
 	/etc/ftpchroot.
 
 	example of /etc/ftpchroot :
 
 	webuser		public_html		# relative from $HOME
 	webmanager	/usr/local/www/data	# absolute dir
 
 How-To-Repeat:
 
 Fix:
 *** ftpd.c.orig	Sat Nov 16 12:45:41 2002
 --- ftpd.c	Sat Nov 16 13:21:00 2002
 ***************
 *** 261,266 ****
 --- 261,267 ----
   static void	 reapchild __P((int));
   static void      logxfer __P((char *, off_t, time_t));
   static char	*doublequote __P((char *));
 + static char	*getchrdir __P((char *));
   
   static char *
   curdir()
 ***************
 *** 1138,1143 ****
 --- 1139,1199 ----
   }
   
   /*
 +  * Return chroot directory for the user. If not found, return NULL.
 +  */
 + char *
 + getchrdir(name)
 + 	char *name;
 + {
 + 	FILE *fd;
 + 	int found = 0;
 + 	size_t len;
 + 	char *p, *mp, *line, *rval = NULL;
 + 
 + 	if ((fd = fopen(_PATH_FTPCHROOT, "r")) != NULL) {
 + 		while (!found && (line = fgetln(fd, &len)) != NULL) {
 + 			/* skip comments and group name */
 +                         if (line[0] == '#' || line[0] == '@')
 +                                 continue;
 +                         if (line[len - 1] == '\n') {
 +                                 line[len - 1] = '\0';
 +                                 mp = NULL;
 +                         } else {
 +                                 if ((mp = malloc(len + 1)) == NULL)
 +                                         fatalerror("Ran out of memory.");
 +                                 memcpy(mp, line, len);
 +                                 mp[len] = '\0';
 +                                 line = mp;
 +                         }
 +                         /* avoid possible leading and trailing whitespace */
 +                         p = strtok(line, " \t");
 +                         /* skip empty lines */
 +                         if (p == NULL)
 +                                 goto nextline;
 + 			/*
 + 			 * Just check for username match
 + 			 */
 + 			if (strcmp(p, name) == 0) {
 + 				found = 1;
 + 				/*
 + 				 * Get chroot directory ently
 + 				 */
 + 				p = strtok(NULL, " \t");
 + 				if (p != NULL)
 + 					rval = strdup(p);
 + 			}
 + nextline:
 + 			if (mp)
 + 				free(mp);
 + 		}
 + 		(void) fclose(fd);
 + 	}
 + 	return (rval);
 + }
 + 
 + 			
 + 
 + /*
    * Terminate login as previous user, if any, resetting state;
    * used when USER command is given or login fails.
    */
 ***************
 *** 1300,1305 ****
 --- 1356,1362 ----
   #ifdef	LOGIN_CAP
   	login_cap_t *lc = NULL;
   #endif
 + 	char *p, *chrdir;
   
   	if (logged_in || askpasswd == 0) {
   		reply(503, "Login with USER first.");
 ***************
 *** 1422,1428 ****
   			goto bad;
   		}
   	} else if (dochroot) {
 ! 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
   			reply(550, "Can't change root.");
   			goto bad;
   		}
 --- 1479,1500 ----
   			goto bad;
   		}
   	} else if (dochroot) {
 ! 		p = getchrdir(pw->pw_name);
 ! 		if (p == NULL) {
 ! 			chrdir = pw->pw_dir;
 ! 		} else if (p[0] == '/') {
 ! 			/*
 ! 			 * Absolute path
 ! 			 */
 ! 			chrdir = p;
 ! 		} else {
 ! 			/*
 ! 			 * Relative path from user's home directory
 ! 			 */
 ! 			chrdir = (char *)malloc(strlen(pw->pw_dir)+strlen(p)+2);
 ! 			sprintf(chrdir, "%s/%s", pw->pw_dir, p);
 ! 		}
 ! 		if (chroot(chrdir) < 0 || chdir("/") < 0) {
   			reply(550, "Can't change root.");
   			goto bad;
   		}
 
 
 
>How-To-Repeat:
>Fix:
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: gnats-admin->freebsd-bugs 
Responsible-Changed-By: tom 
Responsible-Changed-When: Tue Nov 19 06:52:46 PST 2002 
Responsible-Changed-Why:  
Misfiled patch to ftpd.c 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45327 
Responsible-Changed-From-To: freebsd-bugs->yar 
Responsible-Changed-By: dwmalone 
Responsible-Changed-When: Wed Nov 20 03:00:38 PST 2002 
Responsible-Changed-Why:  
Yar is probably in a good position to look at ftpd patches. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45327 

From: Yar Tikhiy <yar@FreeBSD.org>
To: freebsd-gnats-submit@FreeBSD.org, sakamoto@hlla.is.tsukuba.ac.jp
Cc:  
Subject: Re: pending/45327: 
Date: Wed, 20 Nov 2002 19:18:08 +0300

 While the proposed feature looks good to me, I have a few critical
 remarks on it.
 
 1) Why to read /etc/ftpchroot twice in a row?  It would be better
 to see if a user is subject to chroot'ing and to fetch his chroot
 directory at once.  I'd rather move the "absolute vs. relative" code
 there as well for consistency.
 
 2) I see no reason for ignoring group lines in ftpchroot.  That would
 be an unnecessary bug.
 
 3) The ftpd(8) man page should describe the new ftpchroot field.
 
 Thanks.
 
 -- 
 Yar
Class-Changed-From-To: sw-bug->change-request 
Class-Changed-By: keramida 
Class-Changed-When: Wed Nov 20 14:16:25 PST 2002 
Class-Changed-Why:  
Refile this under the bin/ category, drop its priority a bit and 
edit header to remove fields that were bogus. 

To the original submitter: 
Please don't remove the leading '>' character from the fields of 
the form send-pr(1) presents you with.  Thanks for submitting this 
problem report :) 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45327 

From: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
To: Yar Tikhiy <yar@FreeBSD.org>
Cc: freebsd-gnats-submit@FreeBSD.org, sakamoto@hlla.is.tsukuba.ac.jp
Subject: Re: pending/45327: 
Date: Thu, 21 Nov 2002 12:33:14 +0900

 Hi,
 
 At Wed, 20 Nov 2002 19:18:08 +0300,
 Yar Tikhiy wrote:
 > While the proposed feature looks good to me, 
 
 Thank you.
 
 > While the proposed feature looks good to me, I have a few critical
 > remarks on it.
 > 
 > 1) Why to read /etc/ftpchroot twice in a row?  It would be better
 > to see if a user is subject to chroot'ing and to fetch his chroot
 > directory at once.  I'd rather move the "absolute vs. relative" code
 > there as well for consistency.
 
 I agree the point of "read /etc/ftpchroot just once", but checkuser()
 function is used to check /etc/ftpusers too. Thus I thought It's not
 good to change checkuser() function to return chroot path in any way
 and I made special function to do that.
 
 (tick-tack ...)
 
 Oh! I understand. Your feedback is; (for example)
 
         dochroot =
 #ifdef  LOGIN_CAP       /* Allow login.conf configuration as well */
                 login_getcapbool(lc, "ftp-chroot", 0) ||
 #endif
                 ((p = getchrdir(pw->pw_name)) != null);
 
 It looks good to me.
 
 > 2) I see no reason for ignoring group lines in ftpchroot.  That would
 > be an unnecessary bug.
 >
 > 3) The ftpd(8) man page should describe the new ftpchroot field.
 
 That's right. I neglected. I'd like to try 3) but I'm not good at
 English. So if it's bad, please neglect.
 
    5.   If the user name appears in the file /etc/ftpchroot, or the
         user is a member of a group with a group entry in this file,
         i.e. one prefixed with `@', the session's root will be changed
         to the user's login directory by chroot(2) as for an
         ``anonymous'' or ``ftp'' account (see next item).  
 +	Additionally, if the effectual directory name appears in the
 +	second field of /etc/ftpchroot, the session's root will be
 +	changed to that directory(see example below). 	This facil-
         ity may also be triggered by enabling the boolean "ftp-chroot"
         capability in login.conf(5).  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 anony-
         mous account.
 
  	example of /etc/ftpchroot :
 
  	webuser		public_html		# relative from $HOME dir
  	webmanager	/usr/local/www/data	# absolute dir
 
 
 Thanks
 
 ---
   Hideki Sakamoto
   e-mail: sakamoto@hlla.is.tsukuba.ac.jp
 

From: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
To: Yar Tikhiy <yar@FreeBSD.org>
Cc: freebsd-gnats-submit@FreeBSD.org, sakamoto@hlla.is.tsukuba.ac.jp
Subject: Re: pending/45327: 
Date: Thu, 21 Nov 2002 13:23:49 +0900

 Hi,
 
 At Thu, 21 Nov 2002 12:33:14 +0900,
 Hideki SAKAMOTO wrote:
 > Oh! I understand. Your feedback is; (for example)
 > 
 >         dochroot =
 > #ifdef  LOGIN_CAP       /* Allow login.conf configuration as well */
 >                 login_getcapbool(lc, "ftp-chroot", 0) ||
 > #endif
 >                 ((p = getchrdir(pw->pw_name)) != null);
 > 
 > It looks good to me.
 
 I remember this simple approvement is bad. Because if only the user
 name appears in /etc/ftpchroot, dochroot will be '0'.
 
 /etc/ftpchroot must allow:
 
 uname1				# no chroot directory ently(chroot to $HOME)
 uname2	path/to/relative/dir	# relative directory ently
 uname3	/path/to/absolute/dir	# absolute directory ently
 
 Thanks.
 
 -- 
   Hideki Sakamoto
   e-mail: sakamoto@hlla.is.tsukuba.ac.jp
 

From: Yar Tikhiy <yar@FreeBSD.org>
To: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
Cc: freebsd-gnats-submit@FreeBSD.org
Subject: Re: pending/45327:
Date: Thu, 21 Nov 2002 11:46:35 +0300

 Hi,
 
 On Thu, Nov 21, 2002 at 01:23:49PM +0900, Hideki SAKAMOTO wrote:
 > 
 > At Thu, 21 Nov 2002 12:33:14 +0900, Hideki SAKAMOTO wrote:
 > > Oh! I understand. Your feedback is; (for example)
 > > 
 > >         dochroot =
 > > #ifdef  LOGIN_CAP       /* Allow login.conf configuration as well */
 > >                 login_getcapbool(lc, "ftp-chroot", 0) ||
 > > #endif
 > >                 ((p = getchrdir(pw->pw_name)) != null);
 > > 
 > > It looks good to me.
 > 
 > I remember this simple approvement is bad. Because if only the user
 > name appears in /etc/ftpchroot, dochroot will be '0'.
 > 
 > /etc/ftpchroot must allow:
 > 
 > uname1				# no chroot directory ently(chroot to $HOME)
 > uname2	path/to/relative/dir	# relative directory ently
 > uname3	/path/to/absolute/dir	# absolute directory ently
 > 
 > Thanks.
 
 Just make getchrdir() accept "struct passwd *" and pass "pw" to it
 so getchrdir() can return user's home directory if the ftpchroot
 line for the user has the old format.
 
 -- 
 Yar

From: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
To: Yar Tikhiy <yar@FreeBSD.org>
Cc: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
Subject: Re: pending/45327:
Date: Tue, 21 Jan 2003 20:21:46 +0900

 Hi,
 
 My name is Hideki Sakamoto. I send PR bin/45327 last year. Long time
 has passed since I received your last message. If it's not too late,
 please review my new patch. It's an extension patch of /etc/ftpchroot
 to set arbitrary chroot directory.
 
 At Thu, 21 Nov 2002 11:46:35 +0300,
 Yar Tikhiy wrote:
 > 
 > Hi,
 > 
 > On Thu, Nov 21, 2002 at 01:23:49PM +0900, Hideki SAKAMOTO wrote:
 > > 
 > > At Thu, 21 Nov 2002 12:33:14 +0900, Hideki SAKAMOTO wrote:
 > > > Oh! I understand. Your feedback is; (for example)
 > > > 
 > > >         dochroot =
 > > > #ifdef  LOGIN_CAP       /* Allow login.conf configuration as well */
 > > >                 login_getcapbool(lc, "ftp-chroot", 0) ||
 > > > #endif
 > > >                 ((p = getchrdir(pw->pw_name)) != null);
 > > > 
 > > > It looks good to me.
 > > 
 > > I remember this simple approvement is bad. Because if only the user
 > > name appears in /etc/ftpchroot, dochroot will be '0'.
 > > 
 > > /etc/ftpchroot must allow:
 > > 
 > > uname1				# no chroot directory ently(chroot to $HOME)
 > > uname2	path/to/relative/dir	# relative directory ently
 > > uname3	/path/to/absolute/dir	# absolute directory ently
 > > 
 > > Thanks.
 > 
 > Just make getchrdir() accept "struct passwd *" and pass "pw" to it
 > so getchrdir() can return user's home directory if the ftpchroot
 > line for the user has the old format.
 
 Since variable "pw" is global and its used in other
 function(e.g. checkuser()), I don't pass "pw" as argument in this
 patch.
 
 * I made this patch on 4.7-RELEASE-p2 system(RELENG_4_7 CVSup).
 
 * Its work well even if no directory description in /etc/ftpchroot and
   no name entry in /etc/ftpchroot but set "ftp-chroot" in login.conf(5).
   If "ftp-chroot" is set in the login.conf database, ftpd will chroot to
   user's home directory.
 
 * There is no need for "fname" argument in checkuser() function
    because it always called with _PATH_FTPUSERS_ now.
 
 * Change the name of checkuser() function to direct one.
 
 * I worry about checkftpchroot() is almost the copy of
   checkftpusers(). 
   Other implementation I thought are:
 	a. declare char *chrootdir as global variable.
  	b. check fname in checkuser() and set chrootdir if the
 	   fname is equal to "_PATH_FTPCHROOT_"
   or
 	Just move "absolute vs. relative" code at the next line of 
 	checkuser(_PATH_FTPCHROOT_, ... ) in pass(). I think
 	strtok(NULL, " \t") may work there.
   I don't adopt these implementation because I think first one makes
   checkuser() to behave two different role in one function, and second
   one is difficult to understand what is in the strtok() buffer.
 
 
 Thanks.
 
 *** ftpd.c.org	Tue Jan 21 16:21:42 2003
 --- ftpd.c	Tue Jan 21 18:50:48 2003
 ***************
 *** 244,250 ****
   static void	 ack __P((char *));
   static void	 sigurg __P((int));
   static void	 myoob __P((void));
 ! static int	 checkuser __P((char *, char *, int));
   static FILE	*dataconn __P((char *, off_t, char *));
   static void	 dolog __P((struct sockaddr *));
   static char	*curdir __P((void));
 --- 244,251 ----
   static void	 ack __P((char *));
   static void	 sigurg __P((int));
   static void	 myoob __P((void));
 ! static int	 checkftpusers __P((char *, int));
 ! static int	 checkftpchroot __P((char *, char **));
   static FILE	*dataconn __P((char *, off_t, char *));
   static void	 dolog __P((struct sockaddr *));
   static char	*curdir __P((void));
 ***************
 *** 1008,1015 ****
   
   	guest = 0;
   	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
 ! 		if (checkuser(_PATH_FTPUSERS, "ftp", 0) ||
 ! 		    checkuser(_PATH_FTPUSERS, "anonymous", 0))
   			reply(530, "User %s access denied.", name);
   #ifdef VIRTUAL_HOSTING
   		else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
 --- 1009,1016 ----
   
   	guest = 0;
   	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
 ! 		if (checkftpusers("ftp", 0) ||
 ! 		    checkftpusers("anonymous", 0))
   			reply(530, "User %s access denied.", name);
   #ifdef VIRTUAL_HOSTING
   		else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
 ***************
 *** 1040,1046 ****
   				break;
   		endusershell();
   
 ! 		if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) {
   			reply(530, "User %s access denied.", name);
   			if (logging)
   				syslog(LOG_NOTICE,
 --- 1041,1047 ----
   				break;
   		endusershell();
   
 ! 		if (cp == NULL || checkftpusers(name, 1)) {
   			reply(530, "User %s access denied.", name);
   			if (logging)
   				syslog(LOG_NOTICE,
 ***************
 *** 1068,1078 ****
   }
   
   /*
 !  * Check if a user is in the file "fname"
    */
   static int
 ! checkuser(fname, name, pwset)
 ! 	char *fname;
   	char *name;
   	int pwset;
   {
 --- 1069,1078 ----
   }
   
   /*
 !  * Check if a user is in the _PATH_FTPUSERS
    */
   static int
 ! checkftpusers(name, pwset)
   	char *name;
   	int pwset;
   {
 ***************
 *** 1081,1087 ****
   	size_t len;
   	char *line, *mp, *p;
   
 ! 	if ((fd = fopen(fname, "r")) != NULL) {
   		while (!found && (line = fgetln(fd, &len)) != NULL) {
   			/* skip comments */
   			if (line[0] == '#')
 --- 1081,1087 ----
   	size_t len;
   	char *line, *mp, *p;
   
 ! 	if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
   		while (!found && (line = fgetln(fd, &len)) != NULL) {
   			/* skip comments */
   			if (line[0] == '#')
 ***************
 *** 1138,1143 ****
 --- 1138,1240 ----
   }
   
   /*
 +  * Check if a user is in the file _PATH_FTPCHROOT & set chroot directory name
 +  */
 + static int
 + checkftpchroot(name, chrootdir)
 + 	char *name;
 + 	char **chrootdir;
 + {
 + 	FILE *fd;
 + 	int found = 0;
 + 	size_t len;
 + 	char *line, *mp, *p;
 + 
 + 	if ((fd = fopen(_PATH_FTPCHROOT, "r")) != NULL) {
 + 		while (!found && (line = fgetln(fd, &len)) != NULL) {
 + 			/* skil comments */
 + 			if (line[0] == '#')
 + 				continue;
 + 			if (line[len - 1] == '\n') {
 + 				line[len - 1] = '\0';
 + 				mp = NULL;
 + 			} else {
 + 				if ((mp = malloc(len + 1)) == NULL)
 + 					fatalerror("Ran out of memory.");
 + 				memcpy(mp, line, len);
 + 				mp[len] = '\0';
 + 				line = mp;
 + 			}
 + 			/* avoid possible leading and trailing whitespace */
 + 			p = strtok(line, " \t");
 + 			/* skip empty lines */
 + 			if (p == NULL)
 + 				goto nextline;
 + 			/*
 + 			 * if first chr is '@', check group membership
 + 			 */
 + 			if (p[0] == '@') {
 + 				int i = 0;
 + 				struct group *grp;
 + 
 + 				if ((grp = getgrnam(p+1)) == NULL)
 + 					goto nextline;
 + 				/*
 + 				 * Check user's default group
 + 				 */
 + 				if (grp->gr_gid == pw->pw_gid)
 + 					found = 1;
 + 				/*
 + 				 * Check supplementary groups
 + 				 */
 + 				while (!found && grp->gr_mem[i])
 + 					found = strcmp(name,
 + 						grp->gr_mem[i++])
 + 						== 0;
 + 			}
 + 			/*
 + 			 * Otherwise, just check for username match
 + 			 */
 + 			else
 + 				found = strcmp(p, name) == 0;
 + nextline:
 + 			if (mp)
 + 				free(mp);
 + 		}
 + 		(void) fclose(fd);
 + 		/*
 + 		 * set chrootdir
 + 		 */
 + 		if (found == 1) {
 + 			p = strtok(NULL, " \t");
 + 			if (p == NULL) {
 + 				/*
 + 				 * no chroot entry
 + 				 */
 + 				*chrootdir = strdup(pw->pw_dir);
 + 			} else if (p[0] == '/') {
 + 				/*
 + 				 * Absolute path
 + 				 */
 + 				*chrootdir = strdup(p);
 + 			} else {
 + 				/*
 + 				 * Relative path from user's home directory
 + 				 */
 + 				*chrootdir = (char *)malloc(strlen(pw->pw_dir)+strlen(p)+2);
 + 				sprintf(*chrootdir, "%s/%s", pw->pw_dir, p);
 + 			}
 + 		} else {
 + 			/*
 + 			 * for the boolean "ftp-chroot" in login.conf(5)
 + 			 */
 + 			*chrootdir = strdup(pw->pw_dir);
 + 		}
 + 	}
 + 	return (found);
 + }
 + 
 + /*
    * Terminate login as previous user, if any, resetting state;
    * used when USER command is given or login fails.
    */
 ***************
 *** 1300,1305 ****
 --- 1397,1403 ----
   #ifdef	LOGIN_CAP
   	login_cap_t *lc = NULL;
   #endif
 + 	char **chrootdir;
   
   	if (logged_in || askpasswd == 0) {
   		reply(503, "Login with USER first.");
 ***************
 *** 1406,1416 ****
   #endif
   			stats = 0;
   
 ! 	dochroot =
   #ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
 ! 		login_getcapbool(lc, "ftp-chroot", 0) ||
   #endif
 ! 		checkuser(_PATH_FTPCHROOT, pw->pw_name, 1);
   	if (guest) {
   		/*
   		 * We MUST do a chdir() after the chroot. Otherwise
 --- 1504,1514 ----
   #endif
   			stats = 0;
   
 ! 	dochroot = checkftpchroot(pw->pw_name, chrootdir)
   #ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
 ! 		 || login_getcapbool(lc, "ftp-chroot", 0)
   #endif
 ! 		;
   	if (guest) {
   		/*
   		 * We MUST do a chdir() after the chroot. Otherwise
 ***************
 *** 1422,1428 ****
   			goto bad;
   		}
   	} else if (dochroot) {
 ! 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
   			reply(550, "Can't change root.");
   			goto bad;
   		}
 --- 1520,1526 ----
   			goto bad;
   		}
   	} else if (dochroot) {
 ! 		if (chroot(*chrootdir) < 0 || chdir("/") < 0) {
   			reply(550, "Can't change root.");
   			goto bad;
   		}

From: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
To: Yar Tikhiy <yar@FreeBSD.org>
Cc: Hideki SAKAMOTO <sakamoto@hlla.is.tsukuba.ac.jp>
Subject: Re: pending/45327:
Date: Wed, 22 Jan 2003 13:00:21 +0900

 Hi,
 
 I think of non-redundant patch. It looks better to me.
 
 Thanks.
 
 *** ftpd.c.org	Tue Jan 21 16:21:42 2003
 --- ftpd.c	Wed Jan 22 12:38:10 2003
 ***************
 *** 244,250 ****
   static void	 ack __P((char *));
   static void	 sigurg __P((int));
   static void	 myoob __P((void));
 ! static int	 checkuser __P((char *, char *, int));
   static FILE	*dataconn __P((char *, off_t, char *));
   static void	 dolog __P((struct sockaddr *));
   static char	*curdir __P((void));
 --- 244,251 ----
   static void	 ack __P((char *));
   static void	 sigurg __P((int));
   static void	 myoob __P((void));
 ! static int	 checkuser __P((char *, char *, int, char **));
 ! static int	 checkftpchroot __P((char *, char **));
   static FILE	*dataconn __P((char *, off_t, char *));
   static void	 dolog __P((struct sockaddr *));
   static char	*curdir __P((void));
 ***************
 *** 1008,1015 ****
   
   	guest = 0;
   	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
 ! 		if (checkuser(_PATH_FTPUSERS, "ftp", 0) ||
 ! 		    checkuser(_PATH_FTPUSERS, "anonymous", 0))
   			reply(530, "User %s access denied.", name);
   #ifdef VIRTUAL_HOSTING
   		else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
 --- 1009,1016 ----
   
   	guest = 0;
   	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
 ! 		if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL) ||
 ! 		    checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL))
   			reply(530, "User %s access denied.", name);
   #ifdef VIRTUAL_HOSTING
   		else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) {
 ***************
 *** 1040,1046 ****
   				break;
   		endusershell();
   
 ! 		if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1)) {
   			reply(530, "User %s access denied.", name);
   			if (logging)
   				syslog(LOG_NOTICE,
 --- 1041,1047 ----
   				break;
   		endusershell();
   
 ! 		if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1, NULL)) {
   			reply(530, "User %s access denied.", name);
   			if (logging)
   				syslog(LOG_NOTICE,
 ***************
 *** 1071,1080 ****
    * Check if a user is in the file "fname"
    */
   static int
 ! checkuser(fname, name, pwset)
   	char *fname;
   	char *name;
   	int pwset;
   {
   	FILE *fd;
   	int found = 0;
 --- 1072,1082 ----
    * Check if a user is in the file "fname"
    */
   static int
 ! checkuser(fname, name, pwset, rval_p)
   	char *fname;
   	char *name;
   	int pwset;
 + 	char **rval_p;
   {
   	FILE *fd;
   	int found = 0;
 ***************
 *** 1133,1138 ****
 --- 1135,1182 ----
   				free(mp);
   		}
   		(void) fclose(fd);
 + 		if (found == 1 && rval_p != NULL) {
 + 			p = strtok(NULL, " \t");
 + 			if (p != NULL)
 + 				*rval_p = strdup(p);
 + 		}
 + 	}
 + 	return (found);
 + }
 + 
 + /*
 +  * Check if a user is in the file _PATH_FTPCHROOT & set chroot directory name
 +  */
 + static int
 + checkftpchroot(name, chrootdir_p)
 + 	char *name;
 + 	char **chrootdir_p;
 + {
 + 	int found = 0;
 + 	char *p = NULL;
 + 
 + 	found = checkuser(_PATH_FTPCHROOT, name, 1, &p);
 + 
 + 	/*
 + 	 * set chrootdir
 + 	 */
 + 	if (p == NULL) {
 + 		/*
 + 		 * Found:	old format compatibility
 + 		 * Not found:	to use with login.conf configuration
 + 		 */
 + 		*chrootdir_p = strdup(pw->pw_dir);
 + 	} else if (p[0] == '/') {
 + 		/*
 + 		 * Absolute path
 + 		 */
 + 		*chrootdir_p = strdup(p);
 + 	} else {
 + 		/*
 + 		 * Relative path from user's home directory
 + 		 */
 + 		*chrootdir_p = (char *)malloc(strlen(pw->pw_dir)+strlen(p)+2);
 + 		sprintf(*chrootdir_p, "%s/%s", pw->pw_dir, p);
   	}
   	return (found);
   }
 ***************
 *** 1300,1305 ****
 --- 1344,1350 ----
   #ifdef	LOGIN_CAP
   	login_cap_t *lc = NULL;
   #endif
 + 	char *chrootdir = NULL;
   
   	if (logged_in || askpasswd == 0) {
   		reply(503, "Login with USER first.");
 ***************
 *** 1406,1416 ****
   #endif
   			stats = 0;
   
 ! 	dochroot =
   #ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
 ! 		login_getcapbool(lc, "ftp-chroot", 0) ||
   #endif
 ! 		checkuser(_PATH_FTPCHROOT, pw->pw_name, 1);
   	if (guest) {
   		/*
   		 * We MUST do a chdir() after the chroot. Otherwise
 --- 1451,1465 ----
   #endif
   			stats = 0;
   
 ! 	/*
 ! 	 * Keep this checking order because checkftpchroot() sets the name of 
 ! 	 * chroot directory for login.conf configuration.
 !          */
 ! 	dochroot = checkftpchroot(pw->pw_name, &chrootdir)
   #ifdef	LOGIN_CAP	/* Allow login.conf configuration as well */
 ! 		|| login_getcapbool(lc, "ftp-chroot", 0)
   #endif
 ! 	;
   	if (guest) {
   		/*
   		 * We MUST do a chdir() after the chroot. Otherwise
 ***************
 *** 1422,1428 ****
   			goto bad;
   		}
   	} else if (dochroot) {
 ! 		if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
   			reply(550, "Can't change root.");
   			goto bad;
   		}
 --- 1471,1477 ----
   			goto bad;
   		}
   	} else if (dochroot) {
 ! 		if (chroot(chrootdir) < 0 || chdir("/") < 0) {
   			reply(550, "Can't change root.");
   			goto bad;
   		}
 
State-Changed-From-To: open->patched 
State-Changed-By: yar 
State-Changed-When: Mon Jan 27 03:39:41 PST 2003 
State-Changed-Why:  
The proposed feature has been added to CURRENT's ftpd(8). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45327 
State-Changed-From-To: patched->closed 
State-Changed-By: yar 
State-Changed-When: Tue Feb 11 06:53:32 PST 2003 
State-Changed-Why:  
This feature has been merged to STABLE as well.  Thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45327 
>Unformatted:
