Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 17 Oct 2000 15:59:18 -0700 (PDT)
From:      agifford@infowest.com
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   bin/22066: Patch to add feature to FreeBSD's ftpd when used with chroot
Message-ID:  <20001017225918.519C737B4C5@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         22066
>Category:       bin
>Synopsis:       Patch to add feature to FreeBSD's ftpd when used with chroot
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 17 16:00:00 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     Aaron D. Gifford
>Release:        FreeBSD-4.1.1-STABLE
>Organization:
>Environment:
FreeBSD my.freebsd.box 4.1.1-STABLE FreeBSD 4.1.1-STABLE #0: Thu Oct 12 11:58:29 MDT 2000     root@my.freebsd.box:/usr/src/sys/compile/LOCAL  i386
>Description:
Hello,

When using the ftpd daemon with users who are chrooted (see the ftpd man page, authentication rule #5), if a user's home directory is /users/joe, ftpd behaves exactly as I expect and changes to that directory.  However, in certain cases, the following feature would be preferable:

If I use a set-up like this:

  /users/joe       - directory in which I want user joe to be chrooted
  /users/joe/bin   - where chroot-accessable binaries are kept
  /users/joe/etc   - chroot-accesable system stuff (like group) so
                     that ls and other tools behave normally in the
                     chrooted environment
  /users/joe/web   - this is the ONLY subdirectory that user joe
                     owns (all others are root.wheel) and has write
                     privileges to - it is where joe can upload
                     web pages to

With the above setup, it would be nice to be able to automatically
AFTER the chroot to /users/joe do a cwd() to /web so that joe will
begin his FTP session within his writable subdirectory.  The
wu-ftpd daemon has this ability by setting a user's home directory to:

  /users/joe/./web

By adding a "/./" to the home directory, the wu-ftpd daemon knows where
the chroot portion of the home directory ends and where the cwd()
portion begins.  Unfortunatly, wu-ftpd is not an option.  FreeBSD's
ftpd is far superior.

The patch below adds this feature to FreeBSD's ftpd daemon.  I hope that you seriously consider adding this to to FreeBSD.  It is quite handy and I expect that there are many others who would enjoy this added ability.

The only drawback that I can see to adding this feature (and I must mention it to be honest) is in cases where existing installations use a "/./" in home directories for some other purpose where changing the existing chroot behavior of the FTP daemon would require them to restructure.  Installations where this would be a problem are very likely extremely rare or nonexistent.  So while I mention it, I don't think it would really be a problem.

I've been using these very same modifications to support this new chroot feature on a web server where users are chrooted to their own subdirectories for security purposes.  It has been running without problems for months on a system with about 12,000 users.

Sincerely,
Aaron Gifford
<agifford@infowest.com>

P.S. If the patch included doesn't quite come out right (I'm pasting it using a web browser), email me and I'll email a copy to you.
>How-To-Repeat:
This is a feature request.
>Fix:
Patch to ftpd follows:

+++ libexec/ftpd/ftpd.c Tue Oct  3 10:18:24 2000
@@ -250,6 +250,63 @@
 static void     reapchild __P((int));
 static void      logxfer __P((char *, long, long));

+/*
+ * Two new subroutines to let ftpd chroot to a home directory in the
+ * format /home/dir/./here/or/there where the chroot will be done
+ * on chroot("/home/dir/") followed by a chdir ("/here/or/there")
+ * (Much like wu-ftpd's similar feature) and do it safely.
+ */
+static char *
+chrootdir(dir)
+       char *dir;
+{
+       static char cdir[MAXPATHLEN+1];
+       int len = 0;
+
+       if (!dir) {
+               cdir[0] = '/';
+               cdir[1] = '\0';
+               return cdir;
+       }
+       while(*dir && len < MAXPATHLEN) {
+               if (*dir == '/' && *(dir+1) == '.' && *(dir+2) == '/') {
+                       cdir[len++] = *dir;
+                       cdir[len] = '\0';
+                       return cdir;
+               }
+               cdir[len++] = *dir++;
+       }
+       cdir[len] = '\0';
+       return cdir;
+}
+
+static char *
+homedir(dir)
+       char *dir;
+{
+       static char hdir[MAXPATHLEN+1];
+       int len = 0;
+
+       if (!dir) {
+               hdir[0] = '/';
+               hdir[1] = '\0';
+               return hdir;
+       }
+       while (*dir && !(*dir == '/' && *(dir+1) == '.' && *(dir+2) == '/'))
+               dir++;
+       if (!*dir) {
+               hdir[0] = '/';
+               hdir[1] = '\0';
+               return hdir;
+       }
+       dir += 2;
+       while (*dir && len < MAXPATHLEN)
+               hdir[len++] = *dir++;
+       hdir[len] = '\0';
+       return hdir;
+}
+/* End of chroot feature subroutine additions */
+
 static char *
 curdir()
 {
@@ -260,7 +317,7 @@
        if (path[1] != '\0')            /* special case for root dir. */
                strcat(path, "/");
        /* For guest account, skip / since it's chrooted */
-       return (guest ? path+1 : path);
+       return ((dochroot || guest) ? path+1 : path);
 }

 int
@@ -1285,12 +1342,12 @@
                 * the old current directory will be accessible as "."
                 * outside the new root!
                 */
-               if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+               if (chroot(chrootdir(pw->pw_dir)) < 0 || chdir(homedir(pw->pw_dir)) < 0) {
                        reply(550, "Can't set guest privileges.");
                        goto bad;
                }
        } else if (dochroot) {
-               if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+               if (chroot(chrootdir(pw->pw_dir)) < 0 || chdir(homedir(pw->pw_dir)) < 0) {
                        reply(550, "Can't change root.");
                        goto bad;
                }


>Release-Note:
>Audit-Trail:
>Unformatted:


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




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