From d4bea5ad13f425b03b9a50362b3f54c47bee528f Mon Sep 17 00:00:00 2001 From: manusfreedom Date: Mon, 15 Jun 2026 16:06:30 +0200 Subject: [PATCH] `Path::isOwner()` hardcodes `/tmp` instead of using `sys_get_temp_dir()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Bug Report: `Path::isOwner()` hardcodes `/tmp` instead of using `sys_get_temp_dir()` **Component:** `libraries/vendor/joomla/filesystem/src/Path.php` **Method:** `Path::isOwner()` **Affects:** Joomla 4.x / 5.x > **Note:** The content of this bug report and the proposed fix were AI-assisted (generated with Claude). The root cause, reproduction steps, and the process of identifying the issue were found through real investigation and debugging — only the write-up itself was generated with AI assistance. --- ## Summary `Path::isOwner()` hardcodes `/tmp` as the first candidate for a writable temporary directory, instead of using PHP's `sys_get_temp_dir()`. This causes failures on systems where the PHP temporary directory is configured differently — such as environments with AppArmor, SELinux, `open_basedir` restrictions, or `PrivateTmp=true` in systemd — where `/tmp` may be inaccessible to the php-fpm process. --- ## Steps to Reproduce 1. Configure php-fpm with a custom temporary directory (e.g. `sys_temp_dir = /var/lib/php/tmp` in `php.ini` or pool config) 2. Restrict access to `/tmp` via AppArmor, SELinux, or `open_basedir` 3. Save Global Configuration in Joomla administrator 4. `Path::isOwner()` is called, attempts `is_writable('/tmp')`, fails, falls through to `.` (current directory) or `session.save_path` 5. AppArmor logs `DENIED operation="mknod"` on `/tmp/` 6. The UI displays: `Joomla\Filesystem\File::delete: Failed deleting ` --- ## Root Cause ```php // Current — hardcoded /tmp, ignores PHP configuration $dir = is_writable('/tmp') ? '/tmp' : false; ``` PHP provides `sys_get_temp_dir()` precisely to abstract the platform/configuration-specific temp directory. It correctly reflects: - `sys_temp_dir` from `php.ini` - `upload_tmp_dir` fallback - Pool-level `php_value[sys_temp_dir]` overrides - OS-level temp dir on Windows (`%TEMP%`) and non-standard Linux setups --- ## Proposed Fix ```php // Fixed — respects PHP configuration $dir = is_writable(sys_get_temp_dir()) ? sys_get_temp_dir() : false; ``` Full corrected method: ```php /** * Method to determine if script owns the path. * * @param string $path Path to check ownership. * * @return boolean True if the php script owns the path passed. * * @since 1.0 */ public static function isOwner($path) { $tmp = md5(random_bytes(16)); $ssp = ini_get('session.save_path'); // Use PHP's configured temp dir instead of hardcoded /tmp $dir = is_writable(sys_get_temp_dir()) ? sys_get_temp_dir() : false; $dir = !$dir && is_writable('.') ? '.' : $dir; $dir = !$dir && is_writable($ssp) ? $ssp : $dir; if ($dir) { $test = $dir . '/' . $tmp; $blank = ''; File::write($test, $blank, false); $return = fileowner($test) === fileowner($path); File::delete($test); return $return; } return false; } ``` --- ## Impact | Area | Description | |---|---| | **Security** | Hardcoding `/tmp` forces administrators to grant php-fpm access to the system `/tmp`, undermining AppArmor/SELinux confinement and `PrivateTmp=true` isolation | | **Correctness** | Ignores explicit PHP configuration (`sys_temp_dir`, pool-level overrides) | | **Portability** | Breaks on Windows and non-standard Linux setups where `/tmp` does not exist | --- ## Environment - Joomla 4.x / 5.x - php-fpm with AppArmor profile (`PrivateTmp=true` or custom `sys_temp_dir`) - File: `libraries/vendor/joomla/filesystem/src/Path.php` --- src/Path.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Path.php b/src/Path.php index 4d1f26b6..bcaadef7 100644 --- a/src/Path.php +++ b/src/Path.php @@ -235,7 +235,7 @@ public static function isOwner($path) $ssp = ini_get('session.save_path'); // Try to find a writable directory - $dir = is_writable('/tmp') ? '/tmp' : false; + $dir = is_writable(sys_get_temp_dir()) ? sys_get_temp_dir() : false; $dir = !$dir && is_writable('.') ? '.' : $dir; $dir = !$dir && is_writable($ssp) ? $ssp : $dir;