From fab7432197fa5d47e6be3daae141065ca0d9e41a Mon Sep 17 00:00:00 2001
From: Jesse Norell <jesse@kci.net>
Date: Thu, 15 Oct 2020 16:16:15 -0600
Subject: [PATCH] fix jk_update regex and more symlink cleanup

---
 server/lib/classes/system.inc.php | 68 +++++++++++++++++++++++++++++--
 1 file changed, 64 insertions(+), 4 deletions(-)

diff --git a/server/lib/classes/system.inc.php b/server/lib/classes/system.inc.php
index 32afb9943c..45eb9d213f 100644
--- a/server/lib/classes/system.inc.php
+++ b/server/lib/classes/system.inc.php
@@ -1031,6 +1031,61 @@ class system{
 		}
 	}
 
+	function remove_recursive_symlinks($path, $chroot_basedir='', $recursive=false) {
+		global $app;
+
+		if ($path != '/') {
+			$path = rtrim($path, '/');
+		}
+		if (strlen($chroot_basedir) > 0) {
+			if (!is_dir($chroot_basedir)) {
+				$app->log("remove_recursive_symlink: invalid chroot basedir: $chroot_basedir", LOGLEVEL_DEBUG);
+				return false;
+			}
+			if (!(substr($path, 0, strlen($chroot_basedir)) === $chroot_basedir)) {
+				$app->log("remove_recursive_symlink: path $path is not below chroot basedir $chroot_basedir", LOGLEVEL_DEBUG);
+				return false;
+			}
+			if ($chroot_basedir != '/') {
+				$chroot_basedir = rtrim($chroot_basedir, '/');
+			}
+		}
+		if (is_dir($path)) {
+			$objects = array_diff(scandir($path), array('.', '..'));
+			foreach ($objects as $object) {
+				if (is_dir("$path/$object") && $recursive) {
+					$this->remove_recursive_symlinks("$path/$object", $chroot_basedir, $recursive);
+				} elseif (is_link("$path/$object")) {
+					$realpath = realpath("$path/$object");
+					if (strlen($chroot_basedir) > 0 ) {
+						$root_path = substr("$path/$object", strlen($chroot_basedir));
+						if ($root_path && $realpath == $root_path) {
+							$app->log("removing recursive symlink $path/$object", LOGLEVEL_DEBUG);
+							unlink ("$path/$object");
+						}
+					}
+					if ($realpath = "" || $realpath == "$path/$object") {
+						$app->log("removing recursive symlink $path/$object", LOGLEVEL_DEBUG);
+						unlink ("$path/$object");
+					}
+				}
+			}
+		} elseif (is_link("$path")) {
+			$realpath = realpath($path);
+			if (strlen($chroot_basedir) > 0 ) {
+				$root_path = substr($path, strlen($chroot_basedir));
+				if ($root_path && $realpath == $root_path) {
+					$app->log("removing recursive symlink $path", LOGLEVEL_DEBUG);
+					unlink ($path);
+				}
+			}
+			if ($realpath = "" || $realpath == $path) {
+				$app->log("removing recursive symlink $path", LOGLEVEL_DEBUG);
+				unlink ($path);
+			}
+		}
+	}
+
 	function checkpath($path) {
 		$path = trim($path);
 		//* We allow only absolute paths
@@ -2485,6 +2540,7 @@ $app->log("update_jailkit_chroot called for $home_dir with options ".print_r($op
 			}
 
 			$this->remove_broken_symlinks($jail_dir, true);
+			$this->remove_recursive_symlinks($jail_dir, $home_dir, true);
 
 			// save list of hardlinked files
 			if (!(in_array('hardlink', $opts) || in_array('allow_hardlink', $options))) {
@@ -2531,18 +2587,22 @@ $app->log('jk_update returned: '.print_r($this->_last_exec_out, true), LOGLEVEL_
 		foreach ($this->_last_exec_out as $line) {
 			# jk_update sample output:
 			# skip /var/www/clients/client1/web1/opt/
-			if (substr( $line, 0, 4 ) === "skip") {
+			# removing outdated file /var/www/clients/client15/web19/usr/bin/host
+			# removing deprecated directory /var/www/clients/client15/web19/usr/lib/x86_64-linux-gnu/libtasn1.so.6.5.3
+			# Creating symlink /var/www/clients/client15/web19/lib/x86_64-linux-gnu/libicudata.so.65 to libicudata.so.65.1
+			# Copying /usr/bin/mysql to /var/www/clients/client15/web19/usr/bin/mysql
+			if (preg_match('@^(skip|removing (outdated|deprecated)|Creating|Copying)@', $line)) {
 				continue;
 			}
 
 			# jk_update sample output:
 			# ERROR: failed to remove deprecated directory /var/www/clients/client1/web10/usr/lib/x86_64-linux-gnu/libGeoIP.so.1.6.9
-			if (preg_match('@^(?:[^ ]+){6}(?:.+)('.preg_quote($home_dir, '@').'.+)@', $line, $matches)) {
+			if (preg_match('@^(?:[^ ]+ ){6}(?:.+)('.preg_quote($home_dir, '@').'.+)@', $line, $matches)) {
 				# remove deprecated files that jk_update failed to remove
-				if (is_file($matches[1])) {
+				if (is_file($matches[1]) || is_link($matches[1])) {
 $app->log("update_jailkit_chroot: removing deprecated file which jk_update failed to remove:  ".$matches[1], LOGLEVEL_DEBUG);
 					unlink($matches[1]);
-				} elseif (is_dir($matches[1])) {
+				} elseif (is_dir($matches[1]) && !is_link($matches[1])) {
 $app->log("update_jailkit_chroot: removing deprecated directory which jk_update failed to remove:  ".$matches[1], LOGLEVEL_DEBUG);
 					$this->rmdir($matches[1], true);
 				} else {
-- 
GitLab