Newer
Older
) {
if (!$this->is_mounted($backup_dir)){
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;
}
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
//* 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);
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) {
$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);
if(!is_dir($home_dir . '/tmp')) {
$this->mkdirpath($home_dir . '/tmp', 0770);
} else {
$this->chmod($home_dir . '/tmp', 0770, true);
}
if(!is_dir($home_dir . '/var/run')) {
$this->mkdirpath($home_dir . '/var/run', 0755);
} else {
$this->chmod($home_dir . '/var/run', 0755, true);
}
if(!is_dir($home_dir . '/var/tmp')) {
$this->mkdirpath($home_dir . '/var/tmp', 0770);
} else {
$this->chmod($home_dir . '/var/tmp', 0770, true);
}
$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) {
$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()) {
$app->log("update_jailkit_chroot called for $home_dir with options ".print_r($options, true), LOGLEVEL_DEBUG);
// 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);
$jk_update_args = '';
$jk_cp_args = '';
foreach ($options as $opt) {
switch ($opt) {
$jk_update_args .= ' -k';
$jk_cp_args .= ' -k';
// 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);
$this->remove_broken_symlinks($jail_dir, true);
if (!(in_array('hardlink', $opts) || in_array('allow_hardlink', $options))) {
$app->log("update_jailkit_chroot: searching for hardlinks in $jail_dir", LOGLEVEL_DEBUG);
$find_multiple_links = function ( $path ) use ( &$find_multiple_links ) {
$found = array();
$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);
}
}
} elseif (is_file($path)) {
$stat = lstat($path);
if ($stat['nlink'] > 1) {
$found[$path] = $path;
}
}
return $found;
};
if (count($ret) > 0) {
$multiple_links = array_merge($multiple_links, $ret);
}
// remove broken symlinks a second time after hardlink cleanup
$this->remove_broken_symlinks($jail_dir, true);
}
else { $app->log("update_jailkit_chroot: NOT searching for hardlinks in $jail_dir, options: ".print_r($options, true), LOGLEVEL_DEBUG); }
foreach ($multiple_links as $file) {
$app->log("update_jailkit_chroot: removing hardlinked file: $file", LOGLEVEL_DEBUG);
unlink($file);
}
$cmd = 'jk_update --jail=?' . $jk_update_args . $skips;
$this->exec_safe($cmd, $home_dir);
$app->log('jk_update returned: '.print_r($this->_last_exec_out, true), LOGLEVEL_DEBUG);
# handle jk_update output
# jk_update sample output:
# skip /var/www/clients/client1/web1/opt/
if (substr( $line, 0, 4 ) === "skip") {
# jk_update sample output:
# ERROR: failed to remove deprecated directory /var/www/clients/client1/web10/usr/lib/x86_64-linux-gnu/libGeoIP.so.1.6.9
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("update_jailkit_chroot: removing deprecated file which jk_update failed to remove: ".$matches[1], LOGLEVEL_DEBUG);
unlink($matches[1]);
} elseif (is_dir($matches[1])) {
$app->log("update_jailkit_chroot: removing deprecated directory which jk_update failed to remove: ".$matches[1], LOGLEVEL_DEBUG);
} else {
# unhandled error
//$app->log("jk_update error for jail $home_dir: ".$matches[1], LOGLEVEL_DEBUG);
// at least for 3.2 beta, lets gather some of this info:
$app->log("jk_update error for jail $home_dir, feel free to pass to ispconfig developers: ".print_r( $matches, true), LOGLEVEL_DEBUG);
# any other ERROR or WARNING
# sample so far:
# ERROR: /usr/bin/nano does not exist
# WARNING: section [whatever] does not exist in /etc/jailkit/jk_init.ini
} elseif (preg_match('/^(WARNING|ERROR)/', $line, $matches)) {
$app->log("jk_update: $line", 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);
}
if(!is_dir($home_dir . '/tmp')) {
$this->mkdirpath($home_dir . '/tmp', 0770);
$this->chmod($home_dir . '/tmp', 0770, true);
}
if(!is_dir($home_dir . '/var/run')) {
$this->mkdirpath($home_dir . '/var/run', 0755);
} else {
$this->chmod($home_dir . '/var/run', 0755, true);
}
if(!is_dir($home_dir . '/var/tmp')) {
$this->mkdirpath($home_dir . '/var/tmp', 0770);
} else {
$this->chmod($home_dir . '/var/tmp', 0770, true);
}
// TODO: Set /usr/bin/php symlink to php version of the website.
//
// Currently server_php does not have a field for the cli path;
// we can guess/determing according to OS-specific conventions or add that field.
// Then symlink /usr/bin/php (or correct OS-specific path) to that location.
// 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, '/'))) == rtrim($home_dir, '/')) {
$file = substr($file, strlen(rtrim($home_dir, '/')));
}
if (is_file($file)) { // file exists in root
$app->log("update_jailkit_chroot: previously hardlinked file still missing, running jk_cp to restore: $file", LOGLEVEL_DEBUG);
$cmd = 'jk_cp -j ? ' . $jk_cp_args . ' ' . escapeshellarg($file);
$this->exec_safe($cmd, $home_dir);
} else {
// not necessarily an error
$app->log("update_jailkit_chroot: previously hardlinked file was not restored and is no longer present in system: $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);
return false;
}
$jailkit_directories = array(
'bin',
'dev',
'etc',
'lib',
'lib32',
'lib64',
'opt',
'sys',
'usr',
'var',
'run', # not used by jailkit, but added for cleanup
);
$removed = '';
foreach ($jailkit_directories as $dir) {
$jail_dir = rtrim($home_dir, '/') . '/'.$dir;
if (is_link($jail_dir)) {
unlink($jail_dir);
$removed .= ' /'.$dir;
} elseif (is_dir($jail_dir)) {
$this->rmdir($jail_dir, true);
$removed .= ' /'.$dir;
}
}
$app->log("delete_jailkit_chroot: removed from jail $home_dir: $removed", LOGLEVEL_DEBUG);
$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;
}
}