@@ -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 );
@@ -2191,4 +2239,43 @@ public function create_jailkit_chroot($home_dir, $app_sections = array()) {
21912239 return true ;
21922240 }
21932241
2242+
2243+ public function pipe_exec ($ cmd , $ stdin , &$ retval = null , &$ stderr = null ) {
2244+ $ descriptors = array (
2245+ 0 => array ('pipe ' , 'r ' ),
2246+ 1 => array ('pipe ' , 'w ' ),
2247+ 2 => array ('pipe ' , 'w ' )
2248+ );
2249+
2250+ $ result = '' ;
2251+ $ pipes = null ;
2252+ $ proc = proc_open ($ cmd , $ descriptors , $ pipes );
2253+ if (is_resource ($ proc )) {
2254+ fwrite ($ pipes [0 ], $ stdin );
2255+ fclose ($ pipes [0 ]);
2256+
2257+ $ result = stream_get_contents ($ pipes [1 ]);
2258+ $ stderr = stream_get_contents ($ pipes [2 ]);
2259+ fclose ($ pipes [1 ]);
2260+ fclose ($ pipes [2 ]);
2261+
2262+ $ retval = proc_close ($ proc );
2263+
2264+ return $ result ;
2265+ } else {
2266+ return false ;
2267+ }
2268+ }
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+ }
21942281}
0 commit comments