Skip to content

Commit 126290c

Browse files
author
Marius Burkard
committed
- added possibility to execute mkdirpath, file_put_contents and file_get_contents as unprivileged user, partly implements #5417
1 parent 9d5bde2 commit 126290c

File tree

1 file changed

+69
-9
lines changed

1 file changed

+69
-9
lines changed

server/lib/classes/system.inc.php

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -834,23 +834,55 @@ function chmod($file, $mode, $allow_symlink = false) {
834834
}
835835
}
836836

837-
function file_put_contents($filename, $data, $allow_symlink = false) {
837+
function file_put_contents($filename, $data, $allow_symlink = false, $run_as_user = null) {
838838
global $app;
839839
if($allow_symlink == false && $this->checkpath($filename) == false) {
840840
$app->log("Action aborted, file is a symlink: $filename", LOGLEVEL_WARN);
841841
return false;
842842
}
843-
if(file_exists($filename)) unlink($filename);
844-
return file_put_contents($filename, $data);
843+
if($run_as_user !== null && $run_as_user !== 'root') {
844+
if(!$this->check_run_as_user($run_as_user)) {
845+
$app->log("Action aborted, invalid run-as-user: $run_as_user", LOGLEVEL_WARN);
846+
return false;
847+
}
848+
if(file_exists($filename)) {
849+
$cmd = $this->get_sudo_command('rm ' . escapeshellarg($filename), $run_as_user);
850+
$this->exec_safe($cmd);
851+
}
852+
$cmd = $this->get_sudo_command('cat - > ' . escapeshellarg($filename), $run_as_user);
853+
$retval = null;
854+
$stderr = '';
855+
$this->pipe_exec($cmd, $data, $retval, $stderr);
856+
if($retval > 0) {
857+
$app->log("Safe file_put_contents failed: $stderr", LOGLEVEL_WARN);
858+
return false;
859+
} else {
860+
$size = filesize($filename);
861+
return $size;
862+
}
863+
} else {
864+
if(file_exists($filename)) unlink($filename);
865+
return file_put_contents($filename, $data);
866+
}
845867
}
846868

847-
function file_get_contents($filename, $allow_symlink = false) {
869+
function file_get_contents($filename, $allow_symlink = false, $run_as_user = null) {
848870
global $app;
849871
if($allow_symlink == false && $this->checkpath($filename) == false) {
850872
$app->log("Action aborted, file is a symlink: $filename", LOGLEVEL_WARN);
851873
return false;
852874
}
853-
return file_get_contents($filename, $data);
875+
876+
if($run_as_user !== null && $run_as_user !== 'root') {
877+
if(!$this->check_run_as_user($run_as_user)) {
878+
$app->log("Action aborted, invalid run-as-user: $run_as_user", LOGLEVEL_WARN);
879+
return false;
880+
}
881+
$cmd = $this->get_sudo_command('cat ' . escapeshellarg($filename), $run_as_user) . ' 2>/dev/null';
882+
return $this->system_safe($cmd);
883+
} else {
884+
return file_get_contents($filename);
885+
}
854886
}
855887

856888
function rename($filename, $new_filename, $allow_symlink = false) {
@@ -862,13 +894,29 @@ function rename($filename, $new_filename, $allow_symlink = false) {
862894
return rename($filename, $new_filename);
863895
}
864896

865-
function mkdir($dirname, $allow_symlink = false, $mode = 0777, $recursive = false) {
897+
function mkdir($dirname, $allow_symlink = false, $mode = 0777, $recursive = false, $run_as_user = null) {
866898
global $app;
867899
if($allow_symlink == false && $this->checkpath($dirname) == false) {
868900
$app->log("Action aborted, file is a symlink: $dirname", LOGLEVEL_WARN);
869901
return false;
870902
}
871-
if(@mkdir($dirname, $mode, $recursive)) {
903+
if($run_as_user !== null && !$this->check_run_as_user($run_as_user)) {
904+
$app->log("Action aborted, invalid run-as-user: $run_as_user", LOGLEVEL_WARN);
905+
return false;
906+
}
907+
$success = false;
908+
if($run_as_user !== null && $run_as_user !== 'root') {
909+
$cmd = $this->get_sudo_command('mkdir ' . ($recursive ? '-p ' : '') . escapeshellarg($dirname), $run_as_user) . ' >/dev/null 2>&1';
910+
$this->exec_safe($cmd);
911+
if($this->last_exec_retcode() != 0) {
912+
$success = false;
913+
} else {
914+
$success = true;
915+
}
916+
} else {
917+
$success = @mkdir($dirname, $mode, $recursive);
918+
}
919+
if($success) {
872920
return true;
873921
} else {
874922
$app->log("mkdir failed: $dirname", LOGLEVEL_DEBUG);
@@ -1677,14 +1725,14 @@ function maildirmake($maildir_path, $user = '', $subfolder = '', $group = '') {
16771725
}
16781726

16791727
//* Function to create directory paths and chown them to a user and group
1680-
function mkdirpath($path, $mode = 0755, $user = '', $group = '') {
1728+
function mkdirpath($path, $mode = 0755, $user = '', $group = '', $run_as_user = null) {
16811729
$path_parts = explode('/', $path);
16821730
$new_path = '';
16831731
if(is_array($path_parts)) {
16841732
foreach($path_parts as $part) {
16851733
$new_path .= '/'.$part;
16861734
if(!@is_dir($new_path)) {
1687-
$this->mkdir($new_path);
1735+
$this->mkdir($new_path, false, 0777, false, $run_as_user);
16881736
$this->chmod($new_path, $mode);
16891737
if($user != '') $this->chown($new_path, $user);
16901738
if($group != '') $this->chgrp($new_path, $group);
@@ -2218,4 +2266,16 @@ public function pipe_exec($cmd, $stdin, &$retval = null, &$stderr = null) {
22182266
return false;
22192267
}
22202268
}
2269+
2270+
private function get_sudo_command($cmd, $run_as_user) {
2271+
return 'sudo -u ' . escapeshellarg($run_as_user) . ' sh -c ' . escapeshellarg($cmd);
2272+
}
2273+
2274+
private function check_run_as_user($username) {
2275+
if(preg_match('/^[a-zA-Z0-9_\-]+$/', $username)) {
2276+
return true;
2277+
} else{
2278+
return false;
2279+
}
2280+
}
22212281
}

0 commit comments

Comments
 (0)