Index: proftpd-dfsg-1.3.5/modules/mod_auth.c
===================================================================
--- proftpd-dfsg-1.3.5.orig/modules/mod_auth.c
+++ proftpd-dfsg-1.3.5/modules/mod_auth.c
@@ -690,9 +690,66 @@ static char *get_default_chdir(pool *p,
   return dir;
 }
 
-/* Determine if the user (non-anon) needs a default root dir other than /.
- */
+static int is_symlink_path(pool *p, const char *path, size_t pathlen) {
+  int res, xerrno = 0;
+  struct stat st;
+  char *ptr;
 
+  if (pathlen == 0) {
+    return 0;
+  }
+
+  pr_fs_clear_cache();
+  res = pr_fsio_lstat(path, &st);
+  if (res < 0) {
+    xerrno = errno;
+
+    pr_log_pri(PR_LOG_WARNING, "error: unable to check %s: %s", path,
+      strerror(xerrno));
+
+    errno = xerrno;
+    return -1;
+  }
+
+  if (S_ISLNK(st.st_mode)) {
+    errno = EPERM;
+    return -1;
+  }
+
+  /* To handle the case where a component further up the path might be a
+   * symlink (which lstat(2) will NOT handle), we walk the path backwards,
+   * calling ourselves recursively.
+   */
+
+  ptr = strrchr(path, '/');
+  if (ptr != NULL) {
+    char *new_path;
+    size_t new_pathlen;
+
+    pr_signals_handle();
+
+    new_pathlen = ptr - path;
+
+    /* Make sure our pointer actually changed position. */
+    if (new_pathlen == pathlen) {
+      return 0;
+    }
+
+    new_path = pstrndup(p, path, new_pathlen);
+
+    pr_log_debug(DEBUG10,
+      "AllowChrootSymlink: path '%s' not a symlink, checking '%s'", path,
+      new_path);
+    res = is_symlink_path(p, new_path, new_pathlen);
+    if (res < 0) {
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+/* Determine if the user (non-anon) needs a default root dir other than /. */
 static int get_default_root(pool *p, int allow_symlinks, char **root) {
   config_rec *c = NULL;
   char *dir = NULL;
@@ -735,7 +792,6 @@ static int get_default_root(pool *p, int
 
       if (allow_symlinks == FALSE) {
         char *path, target_path[PR_TUNABLE_PATH_MAX + 1];
-        struct stat st;
         size_t pathlen;
 
         /* First, deal with any possible interpolation.  dir_realpath() will
@@ -766,22 +822,13 @@ static int get_default_root(pool *p, int
           path[pathlen-1] = '\0';
         }
 
-        pr_fs_clear_cache();
-        res = pr_fsio_lstat(path, &st);
+        res = is_symlink_path(p, path, pathlen);
         if (res < 0) {
-          xerrno = errno;
-
-          pr_log_pri(PR_LOG_WARNING, "error: unable to check %s: %s", path,
-            strerror(xerrno));
-
-          errno = xerrno;
-          return -1;
-        }
+          if (errno == EPERM) {
+            pr_log_pri(PR_LOG_WARNING, "error: DefaultRoot %s is a symlink "
+              "(denied by AllowChrootSymlinks config)", path);
+          }
 
-        if (S_ISLNK(st.st_mode)) {
-          pr_log_pri(PR_LOG_WARNING,
-            "error: DefaultRoot %s is a symlink (denied by AllowChrootSymlinks "
-            "config)", path);
           errno = EPERM;
           return -1;
         }
