Newer
Older
exec($mount_cmd);
sleep(1);
if (!$this->is_mounted($backup_dir)) $mounted = false;
}
} else $mounted = false;
Florian Schaal
committed
if (!$mounted) {
//* send email to admin that backup directory could not be mounted
$global_config = $app->getconf->get_global_config('mail');
if($global_config['admin_mail'] != ''){
$subject = 'Backup directory '.$backup_dir.' could not be mounted';
$message = "Backup directory ".$backup_dir." could not be mounted.\n\nThe command\n\n".$mount_cmd."\n\nfailed.";
mail($global_config['admin_mail'], $subject, $message);
}
}
return $mounted;
}
function umount_backup_dir($backup_dir, $mount_cmd = '/usr/local/ispconfig/server/scripts/backup_dir_umount.sh'){
Florian Schaal
committed
global $app, $conf;
if ( is_file($mount_cmd) &&
is_executable($mount_cmd) &&
fileowner($mount_cmd) === 0
) {
if ($this->is_mounted($backup_dir)){
exec($mount_cmd);
sleep(1);
$unmounted = $this->is_mounted($backup_dir) == 0 ? true : false;
if(!$unmounted) {
//* send email to admin that backup directory could not be unmounted
$global_config = $app->getconf->get_global_config('mail');
if($global_config['admin_mail'] != ''){
$subject = 'Backup directory '.$backup_dir.' could not be unmounted';
$message = "Backup directory ".$backup_dir." could not be unmounted.\n\nThe command\n\n".$mount_cmd."\n\nfailed.";
mail($global_config['admin_mail'], $subject, $message);
}
}
Florian Schaal
committed
}
}
return $unmounted;
Florian Schaal
committed
function _getinitcommand($servicename, $action, $init_script_directory = '', $check_service) {
global $conf;
// upstart
if(is_executable('/sbin/initctl')){
exec('/sbin/initctl version 2>/dev/null | /bin/grep -q upstart', $retval['output'], $retval['retval']);
if(intval($retval['retval']) == 0) return 'service '.$servicename.' '.$action;
}
Florian Schaal
committed
// systemd
if(is_executable('/bin/systemd') || is_executable('/usr/bin/systemctl')){
Florian Schaal
committed
if ($check_service) {
$this->exec_safe("systemctl is-enabled ? 2>&1", $servicename);
$ret_val = $this->last_exec_retcode();
Florian Schaal
committed
}
if ($ret_val == 0 || !$check_service) {
return 'systemctl '.$action.' '.$servicename.'.service';
}
Florian Schaal
committed
// sysvinit
if($init_script_directory == '') $init_script_directory = $conf['init_scripts'];
if(substr($init_script_directory, -1) === '/') $init_script_directory = substr($init_script_directory, 0, -1);
Florian Schaal
committed
if($check_service && is_executable($init_script_directory.'/'.$servicename)) {
return $init_script_directory.'/'.$servicename.' '.$action;
}
if (!$check_service) {
return $init_script_directory.'/'.$servicename.' '.$action;
}
Florian Schaal
committed
function getinitcommand($servicename, $action, $init_script_directory = '', $check_service=false) {
if (is_array($servicename)) {
foreach($servicename as $service) {
$out = $this->_getinitcommand($service, $action, $init_script_directory, true);
if ($out != '') return $out;
}
} else {
return $this->_getinitcommand($servicename, $action, $init_script_directory, $check_service);
}
}
function getapacheversion($get_minor = false) {
global $app;
$cmd = '';
if($this->is_installed('apache2ctl')) $cmd = 'apache2ctl -v';
elseif($this->is_installed('apachectl')) $cmd = 'apachectl -v';
else {
$app->log("Could not check apache version, apachectl not found.", LOGLEVEL_DEBUG);
exec($cmd, $output, $return_var);
if($return_var != 0 || !$output[0]) {
$app->log("Could not check apache version, apachectl did not return any data.", LOGLEVEL_WARN);
return '2.2';
}
if(preg_match('/version:\s*Apache\/(\d+)(\.(\d+)(\.(\d+))*)?(\D|$)/i', $output[0], $matches)) {
return $matches[1] . (isset($matches[3]) ? '.' . $matches[3] : '') . (isset($matches[5]) && $get_minor == true ? '.' . $matches[5] : '');
} else {
$app->log("Could not check apache version, did not find version string in apachectl output.", LOGLEVEL_WARN);
return '2.2';
}
}
function getapachemodules() {
global $app;
$cmd = '';
if($this->is_installed('apache2ctl')) $cmd = 'apache2ctl -t -D DUMP_MODULES';
elseif($this->is_installed('apachectl')) $cmd = 'apachectl -t -D DUMP_MODULES';
else {
$app->log("Could not check apache modules, apachectl not found.", LOGLEVEL_WARN);
return array();
}
exec($cmd . ' 2>/dev/null', $output, $return_var);
if($return_var != 0 || !$output[0]) {
$app->log("Could not check apache modules, apachectl did not return any data.", LOGLEVEL_WARN);
return array();
}
$modules = array();
for($i = 0; $i < count($output); $i++) {
if(preg_match('/^\s*(\w+)\s+\((shared|static)\)\s*$/', $output[$i], $matches)) {
$modules[] = $matches[1];
}
}
return $modules;
}
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
//* ISPConfig mail function
public function mail($to, $subject, $text, $from, $filepath = '', $filetype = 'application/pdf', $filename = '', $cc = '', $bcc = '', $from_name = '') {
global $app, $conf;
if($conf['demo_mode'] == true) $app->error("Mail sending disabled in demo mode.");
$app->uses('getconf,ispcmail');
$mail_config = $app->getconf->get_global_config('mail');
if($mail_config['smtp_enabled'] == 'y') {
$mail_config['use_smtp'] = true;
$app->ispcmail->setOptions($mail_config);
}
$app->ispcmail->setSender($from, $from_name);
$app->ispcmail->setSubject($subject);
$app->ispcmail->setMailText($text);
if($filepath != '') {
if(!file_exists($filepath)) $app->error("Mail attachement does not exist ".$filepath);
$app->ispcmail->readAttachFile($filepath);
}
if($cc != '') $app->ispcmail->setHeader('Cc', $cc);
if($bcc != '') $app->ispcmail->setHeader('Bcc', $bcc);
$app->ispcmail->send($to);
$app->ispcmail->finish();
return true;
}
public function is_allowed_user($username, $check_id = true, $restrict_names = false) {
global $app;
$name_blacklist = array('root','ispconfig','vmail','getmail');
if(in_array($username,$name_blacklist)) return false;
if(preg_match('/^[a-zA-Z0-9\.\-_]{1,32}$/', $username) == false) return false;
if($check_id && intval($this->getuid($username)) < $this->min_uid) return false;
if($restrict_names == true && preg_match('/^web\d+$/', $username) == false) return false;
public function is_allowed_group($groupname, $check_id = true, $restrict_names = false) {
$name_blacklist = array('root','ispconfig','vmail','getmail');
if(in_array($groupname,$name_blacklist)) return false;
if(preg_match('/^[a-zA-Z0-9\.\-_]{1,32}$/', $groupname) == false) return false;
if($check_id && intval($this->getgid($groupname)) < $this->min_gid) return false;
if($restrict_names == true && preg_match('/^client\d+$/', $groupname) == false) return false;
public function last_exec_out() {
return $this->_last_exec_out;
}
public function last_exec_retcode() {
return $this->_last_exec_retcode;
}
public function exec_safe($cmd) {
$args = func_get_args();
$arg_count = func_num_args();
Marius Burkard
committed
if($arg_count != substr_count($cmd, '?') + 1) {
trigger_error('Placeholder count not matching argument list.', E_USER_WARNING);
return false;
}
array_shift($args);
$pos = 0;
$a = 0;
foreach($args as $value) {
$a++;
$pos = strpos($cmd, '?', $pos);
if($pos === false) {
break;
}
$value = escapeshellarg($value);
$cmd = substr_replace($cmd, $value, $pos, 1);
$pos += strlen($value);
}
}
$this->_last_exec_out = null;
$this->_last_exec_retcode = null;
$ret = exec($cmd, $this->_last_exec_out, $this->_last_exec_retcode);
$app->log("safe_exec cmd: " . $cmd . " - return code: " . $this->_last_exec_retcode, LOGLEVEL_DEBUG);
public function system_safe($cmd) {
call_user_func_array(array($this, 'exec_safe'), func_get_args());
return implode("\n", $this->_last_exec_out);
}
public function create_jailkit_user($username, $home_dir, $user_home_dir, $shell = '/bin/bash', $p_user = null, $p_user_home_dir = null) {
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
$app->log("create_jailkit_user: invalid home_dir: $home_dir", LOGLEVEL_WARN);
return false;
}
// Check if USERHOMEDIR already exists
if(!is_dir($home_dir . '/.' . $user_home_dir)) {
$this->mkdirpath($home_dir . '/.' . $user_home_dir, 0755, $username);
}
// Reconfigure the chroot home directory for the user
$cmd = 'usermod --home=? ? 2>/dev/null';
$this->exec_safe($cmd, $home_dir . '/.' . $user_home_dir, $username);
// Add the chroot user
$cmd = 'jk_jailuser -n -s ? -j ? ?';
$this->exec_safe($cmd, $shell, $home_dir, $username);
// We have to reconfigure the chroot home directory for the parent user
if($p_user !== null) {
$cmd = 'usermod --home=? ? 2>/dev/null';
$this->exec_safe($cmd, $home_dir . '/.' . $p_user_home_dir, $p_user);
}
return true;
}
public function create_jailkit_chroot($home_dir, $app_sections = array(), $options = array()) {
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
$app->log("create_jailkit_chroot: invalid home_dir: $home_dir", LOGLEVEL_WARN);
return false;
}
$app->log("create_jailkit_chroot: jail directory does not exist: $home_dir", LOGLEVEL_WARN);
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
return false;
}
if(empty($app_sections)) {
return true;
} elseif(is_string($app_sections)) {
$app_sections = preg_split('/[\s,]+/', $app_sections);
}
// Change ownership of the chroot directory to root
$this->chown($home_dir, 'root');
$this->chgrp($home_dir, 'root');
$program_args = '';
foreach ($options as $opt) {
switch ($opt) {
case '-k|hardlink':
$program_args .= ' -k';
break;
case '-f|force':
$program_args .= ' -f';
break;
}
}
# /etc/jailkit/jk_init.ini is the default path, probably not needed?
$program_args .= ' -c /etc/jailkit/jk_init.ini -j ?';
foreach($app_sections as $app_section) {
# should check that section exists with jk_init --list ?
$program_args .= ' ' . escapeshellarg($app_section);
}
// Initialize the chroot into the specified directory with the specified applications
$cmd = 'jk_init' . $program_args;
$this->exec_safe($cmd, $home_dir);
// Create the temp directory
if(!is_dir($home_dir . '/tmp')) {
$this->mkdirpath($home_dir . '/tmp', 0770);
} else {
$this->chmod($home_dir . '/tmp', 0770, true);
}
// Fix permissions of the root firectory
$this->chmod($home_dir . '/bin', 0755, true); // was chmod g-w $CHROOT_HOMEDIR/bin
return true;
}
public function create_jailkit_programs($home_dir, $programs = array(), $options = array()) {
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
$app->log("create_jailkit_programs: invalid home_dir: $home_dir", LOGLEVEL_WARN);
return false;
}
$app->log("create_jailkit_programs: jail directory does not exist: $home_dir", LOGLEVEL_WARN);
if(empty($programs)) {
return true;
} elseif(is_string($programs)) {
$programs = preg_split('/[\s,]+/', $programs);
# prohibit ill-advised copying paths known to be sensitive/problematic
# (easy to bypass if needed, eg. use /./etc)
$blacklisted_paths_regex = array(
'@^/$@',
'@^/proc(/.*)?$@',
'@^/sys(/.*)?$@',
'@^/etc/?$@',
'@^/dev/?$@',
'@^/tmp/?$@',
'@^/run/?$@',
'@^/boot/?$@',
'@^/var(/?|/backups?/?)?$@',
$program_args = '';
foreach ($options as $opt) {
switch ($opt) {
case '-k|hardlink':
$program_args .= ' -k';
break;
case '-f|force':
$program_args .= ' -f';
break;
}
}
$program_args .= ' -j ?';
$bad_paths = array();
foreach($programs as $prog) {
foreach ($blacklisted_paths_regex as $re) {
if (preg_match($re, $prog, $matches)) {
$bad_paths[] = $matches[0];
}
}
if (count($bad_paths) > 0) {
$app->log("Prohibited path not added to jail $home_dir: " . implode(", ", $bad_paths), LOGLEVEL_WARN);
} else {
$program_args .= ' ' . escapeshellarg($prog);
}
if (count($programs) > count($bad_paths)) {
$cmd = 'jk_cp' . $program_args;
$this->exec_safe($cmd, $home_dir);
}
return true;
}
public function update_jailkit_chroot($home_dir, $sections = array(), $programs = array(), $options = array()) {
global $app;
$app->uses('ini_parser');
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
$app->log("update_jailkit_chroot: invalid home_dir: $home_dir", LOGLEVEL_WARN);
return false;
}
$app->log("update_jailkit_chroot: jail directory does not exist: $home_dir", LOGLEVEL_WARN);
foreach ($options as $opt) {
switch ($opt) {
case '-k|hardlink':
$opts[] = 'hardlink';
break;
case '-f|force':
$opts[] = 'force';
break;
// Change ownership of the chroot directory to root
$this->chown($home_dir, 'root');
$this->chgrp($home_dir, 'root');
$jailkit_directories = array(
'bin',
'dev',
'etc',
'lib',
'lib32',
'lib64',
'opt',
'sys',
'usr',
'var',
);
$skips = '';
$multiple_links = array();
foreach ($jailkit_directories as $dir) {
$root_dir = '/'.$dir;
$jail_dir = rtrim($home_dir, '/') . '/'.$dir;
if (!is_dir($jail_dir)) {
continue;
}
// if directory exists in jail but not in root, remove it
if (is_dir($jail_dir) && !is_dir($root_dir)) {
$this->rmdir($jail_dir, true);
if (!in_array('hardlink', $opts) && !in_array('allow_hardlink', $options)) {
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
$find_multiple_links = function ( $path ) use ( &$find_multiple_links ) {
$found = array();
if (is_dir($path)) {
$objects = array_diff(scandir($path), array('.', '..'));
foreach ($objects as $object) {
$ret = $find_multiple_links( "$path/$object" );
if (count($ret) > 0) {
$found = array_merge($found, $ret);
}
}
} else {
$stat = stat($path);
if ($stat['nlink'] > 1) {
$found[$path] = $path;
}
}
return $found;
};
$ret = $find_multiple_links( $jail_dir );
if (count($ret) > 0) {
$multiple_links = array_merge($multiple_links, $ret);
}
}
// remove broken symlinks a second time after hardlink cleanup
$this->remove_broken_symlinks($dir, true);
}
$cmd = 'jk_update --jail='.escapeshellarg($home_dir) . $skips;
exec($cmd, $out, $ret);
foreach ($out as $line) {
if (substr( $line, 0, 4 ) === "skip") {
if (preg_match('@^(? [^ ]+){6}(.+)'.preg_quote($home_dir, '@').'$@', $line, $matches)) {
# remove deprecated files that jk_update failed to remove
if (is_file($matches[1])) {
$app->log("removing deprecated file which jk_update failed to remove: ".$matches[1], LOGLEVEL_DEBUG);
unlink($matches[1]);
} elseif (is_dir($matches[1])) {
$app->log("removing deprecated directory which jk_update failed to remove: ".$matches[1], LOGLEVEL_DEBUG);
$this->rmdir($matches[1], true);
}
# unhandled error
$app->log("jk_update error for jail $home_dir: ".$matches[1], LOGLEVEL_DEBUG);
// reinstall jailkit sections and programs
if(!(empty($sections) && empty($programs))) {
$this->create_jailkit_chroot($home_dir, $sections, $opts);
$this->create_jailkit_programs($home_dir, $programs, $opts);
}
// Create the temp directory
if(!is_dir($home_dir . '/tmp')) {
$this->mkdirpath($home_dir . '/tmp', 0770);
$this->chmod($home_dir . '/tmp', 0770, true);
}
// search for any hardlinked files which are now missing
if (!in_array('hardlink', $opts) && !in_array('allow_hardlink', $options)) {
foreach ($multiple_links as $file) {
if (!is_file($file)) {
// strip $home_dir from $file
if (substr($file, 0, strlen(rtrim($home_dir, '/'))) == strlen(rtrim($home_dir, '/'))) {
$file = substr($file, strlen(rtrim($home_dir, '/')));
}
if (is_file($file)) { // file exists in root
$app->log("file with multiple links still missing, running jk_cp to restore: $file", LOGLEVEL_DEBUG);
$cmd = 'jk_cp -j ? ' . escapeshellarg($file);
$this->exec_safe($cmd, $home_dir);
} else {
// not necessarily an error
$app->log("previously hardlinked file was not found to restore: $file", LOGLEVEL_DEBUG);
}
}
}
}
// Fix permissions of the root firectory
$this->chmod($home_dir . '/bin', 0755, true); // was chmod g-w $CHROOT_HOMEDIR/bin
// remove non-existent jails from /etc/jailkit/jk_socketd.ini
if (is_file('/etc/jailkit/jk_socketd.ini')) {
$rewrite = false;
$jk_socketd_ini = $app->ini_parser->parse_ini_file('/etc/jailkit/jk_socketd.ini');
foreach ($jk_socketd_ini as $log => $settings) {
if ($jail != $log && !is_dir($jail)) {
unset($jk_socketd_ini[$log]);
$rewrite=true;
}
}
if ($rewrite) {
$app->log('update_jailkit_chroot: writing /etc/jailkit/jk_socketd.ini', LOGLEVEL_DEBUG);
$app->ini_parse->write_ini_file($jk_socketd_ini, '/etc/jailkit/jk_socketd.ini');
}
}
return true;
}
public function delete_jailkit_chroot($home_dir) {
global $app;
$app->uses('ini_parser');
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
$app->log("delete_jailkit_chroot: invalid home_dir: $home_dir", LOGLEVEL_WARN);
return false;
}
$app->log("delete_jailkit_chroot: jail directory does not exist: $home_dir", LOGLEVEL_DEBUG);
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
return false;
}
$jailkit_directories = array(
'bin',
'dev',
'etc',
'lib',
'lib32',
'lib64',
'opt',
'sys',
'usr',
'var',
);
$removed = '';
foreach ($jailkit_directories as $dir) {
$jail_dir = rtrim($home_dir, '/') . '/'.$dir;
if (is_dir($jail_dir)) {
$this->rmdir($jail_dir, true);
$removed .= ' /'.$dir;
}
}
$app->log("delete_jailkit_chroot: removed from jail $home_dir: $removed", LOGLEVEL_DEBUG);
// handle etc and home special
$home = rtrim($home_dir, '/') . '/home';
@rmdir($home); # ok to fail if non-empty
$private = rtrim($home_dir, '/') . '/private';
if (is_dir($home) && is_dir($private)) {
$archive = $private.'/home-'.date('c');
rename($home, $archive);
}
// remove $home_dir from /etc/jailkit/jk_socketd.ini
if (is_file('/etc/jailkit/jk_socketd.ini')) {
$jk_socketd_ini = $app->ini_parser->parse_ini_file('/etc/jailkit/jk_socketd.ini');
$log = $home . '/dev/log';
if (isset($jk_socketd_ini[$log])) {
unset($jk_socketd_ini[$log]);
$app->log('delete_jailkit_chroot: writing /etc/jailkit/jk_socketd.ini', LOGLEVEL_DEBUG);
$app->ini_parse->write_ini_file($jk_socketd_ini, '/etc/jailkit/jk_socketd.ini');
}
}
Marius Burkard
committed
public function pipe_exec($cmd, $stdin, &$retval = null, &$stderr = null) {
$descriptors = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w')
);
Marius Burkard
committed
$result = '';
$pipes = null;
$proc = proc_open($cmd, $descriptors, $pipes);
if(is_resource($proc)) {
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
Marius Burkard
committed
$result = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
Marius Burkard
committed
$retval = proc_close($proc);
Marius Burkard
committed
return $result;
} else {
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;
}
}