Skip to content

Commit bb38acd

Browse files
committed
WIP: jail cleanup and updates
1 parent ca5966f commit bb38acd

File tree

4 files changed

+199
-37
lines changed

4 files changed

+199
-37
lines changed

server/lib/classes/system.inc.php

Lines changed: 172 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,22 @@ function move($file1, $file2) {
941941
return $return_var == 0 ? true : false;
942942
}
943943

944+
function rmdir($dir, $recursive=false) {
945+
$dir = rtrim($dir, '/');
946+
if (is_dir($dir)) {
947+
$objects = scandir($dir);
948+
foreach ($objects as $object) {
949+
if ($object != "." && $object != ".." && $recursive) {
950+
if (filetype($dir.'/'.$object) == 'dir')
951+
$this->rmdir($dir.'/'.$object, $recursive);
952+
else unlink ($dir.'/'.$object);
953+
}
954+
}
955+
reset($objects);
956+
rmdir($dir);
957+
}
958+
}
959+
944960
function touch($file, $allow_symlink = false){
945961
global $app;
946962
if($allow_symlink == false && @file_exists($file) && $this->checkpath($file) == false) {
@@ -2223,61 +2239,192 @@ public function create_jailkit_user($username, $home_dir, $user_home_dir, $shell
22232239
return true;
22242240
}
22252241

2226-
public function create_jailkit_programs($home_dir, $programs = array()) {
2242+
public function create_jailkit_chroot($home_dir, $app_sections = array(), $options = array()) {
2243+
if(!is_dir($home_dir)) {
2244+
$app->log("Jail directory does not exist: $homedir", LOGLEVEL_WARN);
2245+
return false;
2246+
}
2247+
if(empty($app_sections)) {
2248+
return true;
2249+
} elseif(is_string($app_sections)) {
2250+
$app_sections = preg_split('/[\s,]+/', $app_sections);
2251+
}
2252+
2253+
// Change ownership of the chroot directory to root
2254+
$this->chown($home_dir, 'root');
2255+
$this->chgrp($home_dir, 'root');
2256+
2257+
$program_args = '';
2258+
foreach ($options as $opt) {
2259+
switch ($opt) {
2260+
case '-k|hardlink':
2261+
$program_args .= ' -k';
2262+
break;
2263+
case '-f|force':
2264+
$program_args .= ' -f';
2265+
break;
2266+
}
2267+
}
2268+
# /etc/jailkit/jk_init.ini is the default path, probably not needed?
2269+
$program_args .= ' -c /etc/jailkit/jk_init.ini -j ?';
2270+
foreach($app_sections as $app_section) {
2271+
# should check that section exists with jk_init --list ?
2272+
$program_args .= ' ' . escapeshellarg($app_section);
2273+
}
2274+
2275+
// Initialize the chroot into the specified directory with the specified applications
2276+
$cmd = 'jk_init' . $program_args;
2277+
$this->exec_safe($cmd, $home_dir);
2278+
2279+
// Create the temp directory
2280+
if(!is_dir($home_dir . '/tmp')) {
2281+
$this->mkdirpath($home_dir . '/tmp', 0770);
2282+
} else {
2283+
$this->chmod($home_dir . '/tmp', 0770, true);
2284+
}
2285+
2286+
// Fix permissions of the root firectory
2287+
$this->chmod($home_dir . '/bin', 0755, true); // was chmod g-w $CHROOT_HOMEDIR/bin
2288+
2289+
return true;
2290+
}
2291+
2292+
public function create_jailkit_programs($home_dir, $programs = array(), $options = array()) {
2293+
if(!is_dir($home_dir)) {
2294+
$app->log("Jail directory does not exist: $homedir", LOGLEVEL_WARN);
2295+
return false;
2296+
}
22272297
if(empty($programs)) {
22282298
return true;
22292299
} elseif(is_string($programs)) {
22302300
$programs = preg_split('/[\s,]+/', $programs);
22312301
}
2302+
2303+
# prohibit ill-advised copying paths known to be sensitive/problematic
2304+
# (easy to bypass if needed, eg. use /./etc)
2305+
$blacklisted_paths_regex = array(
2306+
'|^/$|',
2307+
'|^/proc(/.*)?$|',
2308+
'|^/sys(/.*)?$|',
2309+
'|^/etc/?$|',
2310+
'|^/dev/?$|',
2311+
'|^/tmp/?$|',
2312+
'|^/run/?$|',
2313+
'|^/boot/?$|',
2314+
);
2315+
22322316
$program_args = '';
2317+
foreach ($options as $opt) {
2318+
switch ($opt) {
2319+
case '-k|hardlink':
2320+
$program_args .= ' -k';
2321+
break;
2322+
case '-f|force':
2323+
$program_args .= ' -f';
2324+
break;
2325+
}
2326+
}
2327+
$program_args .= ' -j ?';
2328+
2329+
$bad_paths = array();
22332330
foreach($programs as $prog) {
2234-
$program_args .= ' ' . escapeshellarg($prog);
2331+
foreach ($blacklisted_paths_regex as $re) {
2332+
if (preg_match($re, $prog, $matches)) {
2333+
$bad_paths[] = $matches[0];
2334+
}
2335+
}
2336+
if (count($bad_paths) > 0) {
2337+
$app->log("Prohibited path not added to jail $homedir: " . implode(", ", $bad_paths), LOGLEVEL_WARN);
2338+
} else {
2339+
$program_args .= ' ' . escapeshellarg($prog);
2340+
}
22352341
}
22362342

2237-
$cmd = 'jk_cp -j ?' . $program_args;
2238-
$this->exec_safe($cmd, $home_dir);
2343+
if (count($programs) > count($bad_paths)) {
2344+
$cmd = 'jk_cp' . $program_args;
2345+
$this->exec_safe($cmd, $home_dir);
2346+
}
22392347

22402348
return true;
22412349
}
22422350

2243-
public function create_jailkit_chroot($home_dir, $app_sections = array()) {
2244-
if(empty($app_sections)) {
2245-
return true;
2246-
} elseif(is_string($app_sections)) {
2247-
$app_sections = preg_split('/[\s,]+/', $app_sections);
2351+
public function update_jailkit_chroot($home_dir, $sections = array(), $programs = array(), $options = array()) {
2352+
if(!is_dir($home_dir)) {
2353+
$app->log("Jail directory does not exist: $homedir", LOGLEVEL_WARN);
2354+
return false;
2355+
}
2356+
2357+
$opts = array('force');
2358+
foreach ($options as $opt) {
2359+
switch ($opt) {
2360+
case '-k|hardlink':
2361+
$opts[] = 'hardlink';
2362+
break;
2363+
}
22482364
}
22492365

22502366
// Change ownership of the chroot directory to root
22512367
$this->chown($home_dir, 'root');
22522368
$this->chgrp($home_dir, 'root');
22532369

2254-
$app_args = '';
2255-
foreach($app_sections as $app_section) {
2256-
$app_args .= ' ' . escapeshellarg($app_section);
2370+
$jailkit_directories = array(
2371+
'bin',
2372+
'dev',
2373+
'etc',
2374+
'lib',
2375+
'lib32',
2376+
'lib64',
2377+
'opt',
2378+
'sys',
2379+
'usr',
2380+
'var',
2381+
);
2382+
2383+
foreach ($jailkit_directories as $dir) {
2384+
$root_dir = '/'.$dir;
2385+
$jail_dir = rtrim($home_dir, '/') . '/'.$dir;
2386+
2387+
if (!is_dir($jail_dir)) {
2388+
continue;
2389+
}
2390+
2391+
// if directory exists in jail but not in root, remove it
2392+
if (is_dir($jail_dir) && !is_dir($root_dir)) {
2393+
$this->rmdir($jail_dir, true);
2394+
continue;
2395+
}
2396+
2397+
// remove dangling symlinks
2398+
$app->log("TODO: search for and remove dangling symlinks", LOGLEVEL_DEBUG);
2399+
2400+
// search for and remove hardlinked
2401+
if (!in_array($opts, 'hardlink') && !in_array($options, 'allow_hardlink')) {
2402+
$app->log("TODO: search for and remove hardlinked files", LOGLEVEL_DEBUG);
2403+
// search for and save list of files
2404+
}
22572405
}
22582406

2259-
// Initialize the chroot into the specified directory with the specified applications
2260-
$cmd = 'jk_init -f -c /etc/jailkit/jk_init.ini -j ?' . $app_args;
2261-
$this->exec_safe($cmd, $home_dir);
2407+
// reinstall jailkit sections and programs
2408+
if(!(empty($sections) && empty($programs))) {
2409+
$this->create_jailkit_chroot($home_dir, $sections, $opts);
2410+
$this->create_jailkit_programs($home_dir, $programs, $opts);
2411+
}
22622412

22632413
// Create the temp directory
22642414
if(!is_dir($home_dir . '/tmp')) {
2265-
$this->mkdirpath($home_dir . '/tmp', 0777);
2415+
$this->mkdirpath($home_dir . '/tmp', 0770);
22662416
} else {
2267-
$this->chmod($home_dir . '/tmp', 0777, true);
2417+
$this->chmod($home_dir . '/tmp', 0770, true);
2418+
}
2419+
2420+
// search for and remove hardlinked
2421+
if (!in_array($opts, 'hardlink') && !in_array($options, 'allow_hardlink')) {
2422+
$app->log("TODO: search for hardlinked files now missing and add back", LOGLEVEL_DEBUG);
22682423
}
22692424

22702425
// Fix permissions of the root firectory
22712426
$this->chmod($home_dir . '/bin', 0755, true); // was chmod g-w $CHROOT_HOMEDIR/bin
22722427

2273-
// mysql needs the socket in the chrooted environment
2274-
$this->mkdirpath($home_dir . '/var/run/mysqld');
2275-
2276-
// ln /var/run/mysqld/mysqld.sock $CHROOT_HOMEDIR/var/run/mysqld/mysqld.sock
2277-
if(!file_exists("/var/run/mysqld/mysqld.sock")) {
2278-
$this->exec_safe('ln ? ?', '/var/run/mysqld/mysqld.sock', $home_dir . '/var/run/mysqld/mysqld.sock');
2279-
}
2280-
22812428
return true;
22822429
}
22832430

server/plugins-available/apache2_plugin.inc.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ function update($event_name, $data) {
779779
if($data['new']['stats_type'] != '' && !is_dir($data['new']['document_root'].'/' . $web_folder . '/stats')) $app->system->mkdirpath($data['new']['document_root'].'/' . $web_folder . '/stats');
780780
if(!is_dir($data['new']['document_root'].'/ssl')) $app->system->mkdirpath($data['new']['document_root'].'/ssl');
781781
if(!is_dir($data['new']['document_root'].'/cgi-bin')) $app->system->mkdirpath($data['new']['document_root'].'/cgi-bin');
782-
if(!is_dir($data['new']['document_root'].'/tmp')) $app->system->mkdirpath($data['new']['document_root'].'/tmp');
782+
if(!is_dir($data['new']['document_root'].'/tmp')) $app->system->mkdirpath($data['new']['document_root'].'/tmp', 0770);
783783
if(!is_dir($data['new']['document_root'].'/webdav')) $app->system->mkdirpath($data['new']['document_root'].'/webdav');
784784

785785
if(!is_dir($data['new']['document_root'].'/.ssh')) {

server/plugins-available/shelluser_base_plugin.inc.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ function update($event_name, $data) {
233233
$app->system->web_folder_protection($web['document_root'], false);
234234

235235
if($homedir != $homedir_old){
236-
$app->system->web_folder_protection($web['document_root'], false);
237236
// Rename dir, in case the new directory exists already.
238237
if(is_dir($homedir)) {
239238
$app->log("New Homedir exists, renaming it to ".$homedir.'_bak', LOGLEVEL_DEBUG);
@@ -245,10 +244,8 @@ function update($event_name, $data) {
245244
$app->file->mkdirs($homedir, '0750');
246245
$app->system->chown($homedir,$data['new']['puser']);
247246
$app->system->chgrp($homedir,$data['new']['pgroup']);
248-
$app->system->web_folder_protection($web['document_root'], true);
249247
} else {
250248
if(!is_dir($homedir)){
251-
$app->system->web_folder_protection($web['document_root'], false);
252249
if(!is_dir($data['new']['dir'].'/home')){
253250
$app->file->mkdirs($data['new']['dir'].'/home', '0755');
254251
$app->system->chown($data['new']['dir'].'/home','root');
@@ -257,7 +254,6 @@ function update($event_name, $data) {
257254
$app->file->mkdirs($homedir, '0750');
258255
$app->system->chown($homedir,$data['new']['puser']);
259256
$app->system->chgrp($homedir,$data['new']['pgroup']);
260-
$app->system->web_folder_protection($web['document_root'], true);
261257
}
262258
}
263259
$app->system->usermod($data['old']['username'], 0, $app->system->getgid($data['new']['pgroup']), $homedir, $data['new']['shell'], $data['new']['password'], $data['new']['username']);
@@ -361,7 +357,7 @@ function delete($event_name, $data) {
361357

362358
// We delete only non jailkit users, jailkit users will be deleted by the jailkit plugin.
363359
if ($data['old']['chroot'] != "jailkit") {
364-
// if this web uses PHP-FPM, that PPH-FPM service must be stopped before we can delete this user
360+
// if this web uses PHP-FPM, that PHP-FPM service must be stopped before we can delete this user
365361
if($web['php'] == 'php-fpm'){
366362
if($web['server_php_id'] != 0){
367363
$default_php_fpm = false;

server/plugins-available/shelluser_jailkit_plugin.inc.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ function insert($event_name, $data) {
109109
$this->data = $data;
110110
$this->app = $app;
111111
$this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
112+
foreach (array('jailkit_chroot_app_sections', 'jailkit_chroot_app_programs', 'jailkit_do_not_remove_paths') as $section) {
113+
if (isset($web[$section]) && $web[$section] != '' ) {
114+
$this->jailkit_config[$section] = $web[$section];
115+
}
116+
}
112117

113118
$this->_update_website_security_level();
114119

@@ -158,8 +163,6 @@ function update($event_name, $data) {
158163
return false;
159164
}
160165

161-
$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ?", $data['new']['parent_domain_id']);
162-
163166
if(!$app->system->is_allowed_user($data['new']['username'], false, false)
164167
|| !$app->system->is_allowed_user($data['new']['puser'], true, true)
165168
|| !$app->system->is_allowed_group($data['new']['pgroup'], true, true)) {
@@ -168,6 +171,8 @@ function update($event_name, $data) {
168171
}
169172

170173
if($app->system->is_user($data['new']['puser'])) {
174+
$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ?", $data['new']['parent_domain_id']);
175+
171176
// Get the UID of the parent user
172177
$uid = intval($app->system->getuid($data['new']['puser']));
173178
if($uid > $this->min_uid) {
@@ -186,6 +191,11 @@ function update($event_name, $data) {
186191
$this->data = $data;
187192
$this->app = $app;
188193
$this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
194+
foreach (array('jailkit_chroot_app_sections', 'jailkit_chroot_app_programs', 'jailkit_do_not_remove_paths') as $section) {
195+
if (isset($web[$section]) && $web[$section] != '' ) {
196+
$this->jailkit_config[$section] = $web[$section];
197+
}
198+
}
189199

190200
$this->_update_website_security_level();
191201

@@ -231,12 +241,17 @@ function delete($event_name, $data) {
231241
return false;
232242
}
233243

234-
$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ?", $data['old']['parent_domain_id']);
235-
236244
if ($data['old']['chroot'] == "jailkit")
237245
{
246+
$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ?", $data['old']['parent_domain_id']);
247+
238248
$app->uses("getconf");
239249
$this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
250+
foreach (array('jailkit_chroot_app_sections', 'jailkit_chroot_app_programs', 'jailkit_do_not_remove_paths') as $section) {
251+
if (isset($web[$section]) && $web[$section] != '' ) {
252+
$this->jailkit_config[$section] = $web[$section];
253+
}
254+
}
240255

241256
$jailkit_chroot_userhome = $this->_get_home_dir($data['old']['username']);
242257

@@ -248,15 +263,19 @@ function delete($event_name, $data) {
248263
$app->system->exec_safe($command, $data['old']['username'], $data['old']['username']);
249264

250265
// Remove the jailed user from passwd and shadow file inside the jail
251-
$app->system->removeLine($data['old']['dir'].'/etc/passwd', $data['old']['username']);
252-
$app->system->removeLine($data['old']['dir'].'/etc/shadow', $data['old']['username']);
266+
$app->system->removeLine($data['old']['dir'].'/etc/passwd', $data['old']['username'].':');
267+
$app->system->removeLine($data['old']['dir'].'/etc/shadow', $data['old']['username'].':');
253268

254269
if(@is_dir($data['old']['dir'].$jailkit_chroot_userhome)) {
255270
$this->_delete_homedir($data['old']['dir'].$jailkit_chroot_userhome,$userid,$data['old']['parent_domain_id']);
256271

257272
$app->log("Jailkit Plugin -> delete chroot home:".$data['old']['dir'].$jailkit_chroot_userhome, LOGLEVEL_DEBUG);
258273
}
259274

275+
if (isset($web['delete_unused_jailkit']) && $web['delete_unused_jailkit']) {
276+
$app->system->delete_jailkit_if_unused($web['domain_id']);
277+
}
278+
260279
$app->system->web_folder_protection($web['document_root'], true);
261280

262281
}

0 commit comments

Comments
 (0)