SHA256
1
0
forked from pool/proftpd
proftpd/proftpd-AllowChrootSymlinks.patch

126 lines
3.6 KiB
Diff

commit ecff21e0d0e84f35c299ef91d7fda088e516d4ed
Author: TJ Saunders <tj@castaglia.org>
Date: Mon Mar 6 08:31:29 2017 -0800
Backporting recursive handling of DefaultRoot path, when AllowChrootSymlinks
is off, to 1.3.5 branch. (CVE-2017-7418)
diff --git a/modules/mod_auth.c b/modules/mod_auth.c
index 386576162..410215979 100644
--- a/modules/mod_auth.c
+++ b/modules/mod_auth.c
@@ -2,7 +2,7 @@
* ProFTPD - FTP server daemon
* Copyright (c) 1997, 1998 Public Flood Software
* Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
- * Copyright (c) 2001-2016 The ProFTPD Project team
+ * Copyright (c) 2001-2017 The ProFTPD Project team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -688,9 +688,66 @@ static char *get_default_chdir(pool *p, xaset_t *conf) {
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;
@@ -733,7 +790,6 @@ static int get_default_root(pool *p, int allow_symlinks, char **root) {
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
@@ -764,22 +820,13 @@ static int get_default_root(pool *p, int allow_symlinks, char **root) {
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;
}