Bug 22066

Summary: Patch to add feature to FreeBSD's ftpd when used with chroot
Product: Base System Reporter: agifford
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.1.1-STABLE   
Hardware: Any   
OS: Any   

Description agifford 2000-10-18 00:00:00 UTC
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.

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;
                }
How-To-Repeat: This is a feature request.
Comment 1 dhagan 2001-01-04 21:52:54 UTC
This should probably be closed in preference to bin/23944.
Comment 2 dd freebsd_committer freebsd_triage 2001-07-22 16:49:34 UTC
State Changed
From-To: open->closed

duplicate of 23944; thanks dhagan@colltech.com