From 126290c8e8d4a4bbba21f08b2f5dee4578d9224d Mon Sep 17 00:00:00 2001 From: Marius Burkard <m.burkard@pixcept.de> Date: Fri, 4 Oct 2019 12:05:35 +0200 Subject: [PATCH] - added possibility to execute mkdirpath, file_put_contents and file_get_contents as unprivileged user, partly implements #5417 --- server/lib/classes/system.inc.php | 78 +++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/server/lib/classes/system.inc.php b/server/lib/classes/system.inc.php index 525bd7bda1..3db57c510f 100644 --- a/server/lib/classes/system.inc.php +++ b/server/lib/classes/system.inc.php @@ -834,23 +834,55 @@ class system{ } } - function file_put_contents($filename, $data, $allow_symlink = false) { + function file_put_contents($filename, $data, $allow_symlink = false, $run_as_user = null) { global $app; if($allow_symlink == false && $this->checkpath($filename) == false) { $app->log("Action aborted, file is a symlink: $filename", LOGLEVEL_WARN); return false; } - if(file_exists($filename)) unlink($filename); - return file_put_contents($filename, $data); + if($run_as_user !== null && $run_as_user !== 'root') { + if(!$this->check_run_as_user($run_as_user)) { + $app->log("Action aborted, invalid run-as-user: $run_as_user", LOGLEVEL_WARN); + return false; + } + if(file_exists($filename)) { + $cmd = $this->get_sudo_command('rm ' . escapeshellarg($filename), $run_as_user); + $this->exec_safe($cmd); + } + $cmd = $this->get_sudo_command('cat - > ' . escapeshellarg($filename), $run_as_user); + $retval = null; + $stderr = ''; + $this->pipe_exec($cmd, $data, $retval, $stderr); + if($retval > 0) { + $app->log("Safe file_put_contents failed: $stderr", LOGLEVEL_WARN); + return false; + } else { + $size = filesize($filename); + return $size; + } + } else { + if(file_exists($filename)) unlink($filename); + return file_put_contents($filename, $data); + } } - function file_get_contents($filename, $allow_symlink = false) { + function file_get_contents($filename, $allow_symlink = false, $run_as_user = null) { global $app; if($allow_symlink == false && $this->checkpath($filename) == false) { $app->log("Action aborted, file is a symlink: $filename", LOGLEVEL_WARN); return false; } - return file_get_contents($filename, $data); + + if($run_as_user !== null && $run_as_user !== 'root') { + if(!$this->check_run_as_user($run_as_user)) { + $app->log("Action aborted, invalid run-as-user: $run_as_user", LOGLEVEL_WARN); + return false; + } + $cmd = $this->get_sudo_command('cat ' . escapeshellarg($filename), $run_as_user) . ' 2>/dev/null'; + return $this->system_safe($cmd); + } else { + return file_get_contents($filename); + } } function rename($filename, $new_filename, $allow_symlink = false) { @@ -862,13 +894,29 @@ class system{ return rename($filename, $new_filename); } - function mkdir($dirname, $allow_symlink = false, $mode = 0777, $recursive = false) { + function mkdir($dirname, $allow_symlink = false, $mode = 0777, $recursive = false, $run_as_user = null) { global $app; if($allow_symlink == false && $this->checkpath($dirname) == false) { $app->log("Action aborted, file is a symlink: $dirname", LOGLEVEL_WARN); return false; } - if(@mkdir($dirname, $mode, $recursive)) { + if($run_as_user !== null && !$this->check_run_as_user($run_as_user)) { + $app->log("Action aborted, invalid run-as-user: $run_as_user", LOGLEVEL_WARN); + return false; + } + $success = false; + if($run_as_user !== null && $run_as_user !== 'root') { + $cmd = $this->get_sudo_command('mkdir ' . ($recursive ? '-p ' : '') . escapeshellarg($dirname), $run_as_user) . ' >/dev/null 2>&1'; + $this->exec_safe($cmd); + if($this->last_exec_retcode() != 0) { + $success = false; + } else { + $success = true; + } + } else { + $success = @mkdir($dirname, $mode, $recursive); + } + if($success) { return true; } else { $app->log("mkdir failed: $dirname", LOGLEVEL_DEBUG); @@ -1677,14 +1725,14 @@ class system{ } //* Function to create directory paths and chown them to a user and group - function mkdirpath($path, $mode = 0755, $user = '', $group = '') { + function mkdirpath($path, $mode = 0755, $user = '', $group = '', $run_as_user = null) { $path_parts = explode('/', $path); $new_path = ''; if(is_array($path_parts)) { foreach($path_parts as $part) { $new_path .= '/'.$part; if(!@is_dir($new_path)) { - $this->mkdir($new_path); + $this->mkdir($new_path, false, 0777, false, $run_as_user); $this->chmod($new_path, $mode); if($user != '') $this->chown($new_path, $user); if($group != '') $this->chgrp($new_path, $group); @@ -2218,4 +2266,16 @@ class system{ return false; } } + + private function get_sudo_command($cmd, $run_as_user) { + return 'sudo -u ' . escapeshellarg($run_as_user) . ' sh -c ' . escapeshellarg($cmd); + } + + private function check_run_as_user($username) { + if(preg_match('/^[a-zA-Z0-9_\-]+$/', $username)) { + return true; + } else{ + return false; + } + } } -- GitLab