tbrehm
2013-02-22 526b997c9891a796b152cdbab8e329b356b1f596
commit | author | age
396f0e 1 <?php
T 2
3 /*
4 Copyright (c) 2007, Till Brehm, projektfarm Gmbh
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9
10     * Redistributions of source code must retain the above copyright notice,
11       this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright notice,
13       this list of conditions and the following disclaimer in the documentation
14       and/or other materials provided with the distribution.
15     * Neither the name of ISPConfig nor the names of its contributors
16       may be used to endorse or promote products derived from this software without
17       specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 class shelluser_jailkit_plugin {
32     
33     //* $plugin_name and $class_name have to be the same then the name of this class
34     var $plugin_name = 'shelluser_jailkit_plugin';
35     var $class_name = 'shelluser_jailkit_plugin';
36     
37     //* This function is called during ispconfig installation to determine
38     //  if a symlink shall be created for this plugin.
39     function onInstall() {
40         global $conf;
41         
42         if($conf['services']['web'] == true) {
43             return true;
44         } else {
45             return false;
46         }
47         
48     }
49     
50         
51     /*
52          This function is called when the plugin is loaded
53     */
54     
55     function onLoad() {
56         global $app;
57         
58         /*
59         Register for the events
60         */
61         
62         $app->plugins->registerEvent('shell_user_insert',$this->plugin_name,'insert');
63         $app->plugins->registerEvent('shell_user_update',$this->plugin_name,'update');
64         $app->plugins->registerEvent('shell_user_delete',$this->plugin_name,'delete');
65
66         
67     }
68     
69     //* This function is called, when a shell user is inserted in the database
70     function insert($event_name,$data) {
71         global $app, $conf;
72         
73         $app->uses('system');
ff6a68 74         $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$data['new']['parent_domain_id']);
396f0e 75         
T 76         if($app->system->is_user($data['new']['username'])) {
77         
78             /**
79              * Setup Jailkit Chroot System If Enabled 
80              */
81             if ($data['new']['chroot'] == "jailkit")
82             {
4bd960 83                 
4b9329 84                 
396f0e 85                 // load the server configuration options
T 86                 $app->uses("getconf");
87                 $this->data = $data;
88                 $this->app = $app;
89                 $this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
8db8f3 90                 
07bdbd 91                 $this->_update_website_security_level();
4bd960 92                 
T 93                 $app->system->web_folder_protection($web['document_root'],false);
07bdbd 94             
T 95                 $this->_setup_jailkit_chroot();
96                 
11deb6 97                 $this->_add_jailkit_user();
T 98                 
8cf78b 99                 //* call the ssh-rsa update function
00a055 100                 $this->_setup_ssh_rsa();
L 101                 
ff6a68 102                 //$command .= 'usermod -s /usr/sbin/jk_chrootsh -U '.escapeshellcmd($data['new']['username']);
T 103                 //exec($command);
104                 $app->system->usermod($data['new']['username'], 0, 0, '', '/usr/sbin/jk_chrootsh', '', '');
105                 
106                 //* Unlock user
526b99 107                 $command = 'usermod -U '.escapeshellcmd($data['new']['username']).' 2>/dev/null';
11deb6 108                 exec($command);
T 109                 
07bdbd 110                 $this->_update_website_security_level();
4b9329 111                 $app->system->web_folder_protection($web['document_root'],true);
396f0e 112             }
T 113         
114             $app->log("Jailkit Plugin -> insert username:".$data['new']['username'],LOGLEVEL_DEBUG);
115             
116         } else {
117             $app->log("Jailkit Plugin -> insert username:".$data['new']['username']." skipped, the user does not exist.",LOGLEVEL_WARN);
118         }
119         
120     }
121     
122     //* This function is called, when a shell user is updated in the database
123     function update($event_name,$data) {
124         global $app, $conf;
125         
126         $app->uses('system');
ff6a68 127         $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$data['new']['parent_domain_id']);
396f0e 128         
T 129         if($app->system->is_user($data['new']['username'])) {
130         
131             /**
132              * Setup Jailkit Chroot System If Enabled 
133              */
134             if ($data['new']['chroot'] == "jailkit")
135             {
4b9329 136                 
396f0e 137                 // load the server configuration options
T 138                 $app->uses("getconf");
139                 $this->data = $data;
140                 $this->app = $app;
141                 $this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
8db8f3 142                 
T 143                 $this->_update_website_security_level();
4bd960 144                 
T 145                 $app->system->web_folder_protection($web['document_root'],false);
396f0e 146             
T 147                 $this->_setup_jailkit_chroot();
148                 $this->_add_jailkit_user();
00a055 149                 
8cf78b 150                 //* call the ssh-rsa update function
00a055 151                 $this->_setup_ssh_rsa();
07bdbd 152                 
T 153                 $this->_update_website_security_level();
4b9329 154                 
T 155                 $app->system->web_folder_protection($web['document_root'],true);
396f0e 156             }
T 157         
158             $app->log("Jailkit Plugin -> update username:".$data['new']['username'],LOGLEVEL_DEBUG);
159             
160         } else {
161             $app->log("Jailkit Plugin -> update username:".$data['new']['username']." skipped, the user does not exist.",LOGLEVEL_WARN);
162         }
163         
164     }
165     
166     //* This function is called, when a shell user is deleted in the database
167     /**
168      * TODO: Remove chroot user home and from the chroot passwd file
169      */ 
170     function delete($event_name,$data) {
171         global $app, $conf;
172         
173         $app->uses('system');
174         
ff6a68 175         $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$data['old']['parent_domain_id']);
4b9329 176         
396f0e 177         if ($data['old']['chroot'] == "jailkit")
T 178         {
179             $app->uses("getconf");
180             $this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit');
181             
182             $jailkit_chroot_userhome = $this->_get_home_dir($data['old']['username']);
183             
184             //commented out proved to be dangerous on config errors
185             //exec('rm -rf '.$data['old']['dir'].$jailkit_chroot_userhome);
186             
4b9329 187             $app->system->web_folder_protection($web['document_root'],false);
T 188             
396f0e 189             if(@is_dir($data['old']['dir'].$jailkit_chroot_userhome)) {
8cf78b 190                 $command = 'userdel -f';
526b99 191                 $command .= ' '.escapeshellcmd($data['old']['username']).' &> /dev/null';
396f0e 192                 exec($command);
T 193                 $app->log("Jailkit Plugin -> delete chroot home:".$data['old']['dir'].$jailkit_chroot_userhome,LOGLEVEL_DEBUG);
194             }
07bdbd 195             
4b9329 196             $app->system->web_folder_protection($web['document_root'],true);
T 197             
396f0e 198         }
T 199         
200         $app->log("Jailkit Plugin -> delete username:".$data['old']['username'],LOGLEVEL_DEBUG);
201         
202         
203     }
204     
205     function _setup_jailkit_chroot()
206     {
4bd960 207             global $app;
T 208             
396f0e 209             //check if the chroot environment is created yet if not create it with a list of program sections from the config
T 210             if (!is_dir($this->data['new']['dir'].'/etc/jailkit'))
211             {
212                 $command = '/usr/local/ispconfig/server/scripts/create_jailkit_chroot.sh';
213                 $command .= ' '.escapeshellcmd($this->data['new']['dir']);
214                 $command .= ' \''.$this->jailkit_config['jailkit_chroot_app_sections'].'\'';
526b99 215                 exec($command.' 2>/dev/null');
396f0e 216                 
T 217                 $this->app->log("Added jailkit chroot with command: ".$command,LOGLEVEL_DEBUG);
218                 
219                 $this->_add_jailkit_programs();
220                 
221                 //add bash.bashrc script
222                 //we need to collect the domain name to be used as the HOSTNAME in the bashrc script
223                 $web = $this->app->db->queryOneRecord("SELECT domain FROM web_domain WHERE domain_id = ".intval($this->data['new']["parent_domain_id"]));
224                 
225                 $this->app->load('tpl');
226         
227                 $tpl = new tpl();
228                 $tpl->newTemplate("bash.bashrc.master");
229                 
230                 $tpl->setVar('jailkit_chroot',true);
231                 $tpl->setVar('domain',$web['domain']);
232                 $tpl->setVar('home_dir',$this->_get_home_dir(""));
233                 
234                 $bashrc = escapeshellcmd($this->data['new']['dir']).'/etc/bash.bashrc';
94d2cf 235                 if(@is_file($bashrc) || @is_link($bashrc)) unlink($bashrc);
396f0e 236                 
T 237                 file_put_contents($bashrc,$tpl->grab());
238                 unset($tpl);
239                 
94d2cf 240                 $this->app->log("Added bashrc script : ".$bashrc,LOGLEVEL_DEBUG);
396f0e 241                 
T 242                 $tpl = new tpl();
243                 $tpl->newTemplate("motd.master");
244                 
245                 $tpl->setVar('domain',$web['domain']);
246                 
247                 $motd = escapeshellcmd($this->data['new']['dir']).'/var/run/motd';
94d2cf 248                 if(@is_file($motd) || @is_link($motd)) unlink($motd);
396f0e 249                 
4bd960 250                 $app->system->file_put_contents($motd,$tpl->grab());
396f0e 251                 
T 252             }
253     }
254     
255     function _add_jailkit_programs()
256     {
257         //copy over further programs and its libraries
258         $command = '/usr/local/ispconfig/server/scripts/create_jailkit_programs.sh';
259         $command .= ' '.escapeshellcmd($this->data['new']['dir']);
260         $command .= ' \''.$this->jailkit_config['jailkit_chroot_app_programs'].'\'';
526b99 261         exec($command.' 2>/dev/null');
396f0e 262         
T 263         $this->app->log("Added programs to jailkit chroot with command: ".$command,LOGLEVEL_DEBUG);
264     }
265     
266     function _get_home_dir($username)
267     {
8e725d 268         return str_replace("[username]",escapeshellcmd($username),$this->jailkit_config['jailkit_chroot_home']);
396f0e 269     }
T 270     
271     function _add_jailkit_user()
272     {
8cf78b 273             global $app;
T 274             
396f0e 275             //add the user to the chroot
T 276             $jailkit_chroot_userhome = $this->_get_home_dir($this->data['new']['username']);
277             $jailkit_chroot_puserhome = $this->_get_home_dir($this->data['new']['puser']);
278             
8e725d 279             if(!is_dir($this->data['new']['dir'].'/etc')) mkdir($this->data['new']['dir'].'/etc', 0755);
J 280             if(!is_file($this->data['new']['dir'].'/etc/passwd')) touch($this->data['new']['dir'].'/etc/passwd', 0755);
e47d46 281             
396f0e 282             // IMPORTANT!
T 283             // ALWAYS create the user. Even if the user was created before
284             // if we check if the user exists, then a update (no shell -> jailkit) will not work
285             // and the user has FULL ACCESS to the root of the server!
286             $command = '/usr/local/ispconfig/server/scripts/create_jailkit_user.sh';
287             $command .= ' '.escapeshellcmd($this->data['new']['username']);
288             $command .= ' '.escapeshellcmd($this->data['new']['dir']);
289             $command .= ' '.$jailkit_chroot_userhome;
290             $command .= ' '.escapeshellcmd($this->data['new']['shell']);
291             $command .= ' '.$this->data['new']['puser'];
292             $command .= ' '.$jailkit_chroot_puserhome;
526b99 293             exec($command.' 2>/dev/null');
8cf78b 294             
T 295             //* Change the homedir of the shell user and parent user
296             //* We have to do this manually as the usermod command fails 
297             //* when the user is logged in or a command is running under that user
ff6a68 298             /*
8cf78b 299             $passwd_file_array = file('/etc/passwd');
T 300             $passwd_out = '';
301             if(is_array($passwd_file_array)) {
302                 foreach($passwd_file_array as $line) {
303                     $line = trim($line);
304                     $parts = explode(':',$line);
305                     if($parts[0] == $this->data['new']['username']) {
306                         $parts[5] = escapeshellcmd($this->data['new']['dir'].'/.'.$jailkit_chroot_userhome);
307                         $parts[6] = escapeshellcmd('/usr/sbin/jk_chrootsh');
308                         $new_line = implode(':',$parts);
309                         copy('/etc/passwd','/etc/passwd~');
310                         chmod('/etc/passwd~',0600);
311                         $app->uses('system');
312                         $app->system->replaceLine('/etc/passwd',$line,$new_line,1,0);
313                     }
314                 }
ff6a68 315             }*/
8cf78b 316             
ff6a68 317             $app->system->usermod($this->data['new']['username'], 0, 0, $this->data['new']['dir'].'/.'.$jailkit_chroot_userhome, '/usr/sbin/jk_chrootsh');
T 318             $app->system->usermod($this->data['new']['puser'], 0, 0, $this->data['new']['dir'].'/.'.$jailkit_chroot_userhome, '/usr/sbin/jk_chrootsh');
396f0e 319                 
T 320             $this->app->log("Added jailkit user to chroot with command: ".$command,LOGLEVEL_DEBUG);
edf806 321                         
T 322             if(!is_dir($this->data['new']['dir'].$jailkit_chroot_userhome)) mkdir(escapeshellcmd($this->data['new']['dir'].$jailkit_chroot_userhome), 0755, true);
4bd960 323             $app->system->chown(escapeshellcmd($this->data['new']['dir'].$jailkit_chroot_userhome), $this->data['new']['username']);
T 324             $app->system->chgrp(escapeshellcmd($this->data['new']['dir'].$jailkit_chroot_userhome), $this->data['new']['pgroup']);
396f0e 325                 
T 326             $this->app->log("Added created jailkit user home in : ".$this->data['new']['dir'].$jailkit_chroot_userhome,LOGLEVEL_DEBUG);
327             
edf806 328             if(!is_dir($this->data['new']['dir'].$jailkit_chroot_puserhome)) mkdir(escapeshellcmd($this->data['new']['dir'].$jailkit_chroot_puserhome), 0755, true);
4bd960 329             $app->system->chown(escapeshellcmd($this->data['new']['dir'].$jailkit_chroot_puserhome), $this->data['new']['puser']);
T 330             $app->system->chgrp(escapeshellcmd($this->data['new']['dir'].$jailkit_chroot_puserhome), $this->data['new']['pgroup']);
396f0e 331                 
8cf78b 332             $this->app->log("Added jailkit parent user home in : ".$this->data['new']['dir'].$jailkit_chroot_puserhome,LOGLEVEL_DEBUG);
00a055 333             
8cf78b 334
396f0e 335     }
T 336     
8db8f3 337     //* Update the website root directory permissions depending on the security level
T 338     function _update_website_security_level() {
339         global $app,$conf;
340         
341         // load the server configuration options
342         $app->uses("getconf");
343         $web_config = $app->getconf->get_server_config($conf["server_id"], 'web');
344         
345         // Get the parent website of this shell user
346         $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$this->data['new']['parent_domain_id']);
347         
348         //* If the security level is set to high
ff6a68 349         if($web_config['security_level'] == 20 && is_array($web)) {
T 350             $app->system->web_folder_protection($web["document_root"],false);
351             $app->system->chmod($web["document_root"],0755);
352             $app->system->chown($web["document_root"],'root');
353             $app->system->chgrp($web["document_root"],'root');
354             $app->system->web_folder_protection($web["document_root"],true);
8db8f3 355         }
T 356         
357     }
358     
07bdbd 359     //* Wrapper for exec function for easier debugging
T 360     private function _exec($command) {
361         global $app;
8e725d 362         $app->log('exec: '.$command,LOGLEVEL_DEBUG);
07bdbd 363         exec($command);
T 364     }
396f0e 365
00a055 366     private function _setup_ssh_rsa() {
8ab3cd 367         global $app;
00a055 368         $this->app->log("ssh-rsa setup shelluser_jailkit",LOGLEVEL_DEBUG); 
L 369         // Get the client ID, username, and the key
27c623 370         $domain_data = $this->app->db->queryOneRecord('SELECT sys_groupid FROM web_domain WHERE web_domain.domain_id = '.intval($this->data['new']['parent_domain_id']));
L 371         $sys_group_data = $this->app->db->queryOneRecord('SELECT * FROM sys_group WHERE sys_group.groupid = '.intval($domain_data['sys_groupid']));
00a055 372         $id = intval($sys_group_data['client_id']);
L 373         $username= $sys_group_data['name'];
27c623 374         $client_data = $this->app->db->queryOneRecord('SELECT * FROM client WHERE client.client_id = '.$id);
00a055 375         $userkey = $client_data['ssh_rsa'];
L 376         unset($domain_data);
377         unset($client_data);
378         
379         // ssh-rsa authentication variables
8ab3cd 380         $sshrsa = $this->data['new']['ssh_rsa'];
00a055 381         $usrdir = escapeshellcmd($this->data['new']['dir']).'/'.$this->_get_home_dir($this->data['new']['username']);
8ab3cd 382         $sshdir = $usrdir.'/.ssh';
T 383         $sshkeys= $usrdir.'/.ssh/authorized_keys';
384             
385         $app->uses('file');
386         $sshrsa = $app->file->unix_nl($sshrsa);
387         $sshrsa = $app->file->remove_blank_lines($sshrsa,0);
00a055 388         
L 389         // If this user has no key yet, generate a pair
8ab3cd 390         if ($userkey == '' && $id > 0){
00a055 391             //Generate ssh-rsa-keys
L 392             exec('ssh-keygen -t rsa -C '.$username.'-rsa-key-'.time().' -f /tmp/id_rsa -N ""');
8ab3cd 393             
T 394             // use the public key that has been generated
4bd960 395             $userkey = $app->system->file_get_contents('/tmp/id_rsa.pub');
8ab3cd 396             
00a055 397             // save keypair in client table
4bd960 398             $this->app->db->query("UPDATE client SET created_at = ".time().", id_rsa = '".$app->db->quote($app->system->file_get_contents('/tmp/id_rsa'))."', ssh_rsa = '".$app->db->quote($userkey)."' WHERE client_id = ".$id);
8ab3cd 399
4bd960 400             $app->system->unlink('/tmp/id_rsa');
T 401             $app->system->unlink('/tmp/id_rsa.pub');
00a055 402             $this->app->log("ssh-rsa keypair generated for ".$username,LOGLEVEL_DEBUG);
L 403         };
404         
8ab3cd 405         if (!file_exists($sshkeys)){
00a055 406             // add root's key
8ab3cd 407             $app->file->mkdirs($sshdir, '0755');
4bd960 408             if(is_file('/root/.ssh/authorized_keys')) $app->system->file_put_contents($sshkeys, $app->system->file_get_contents('/root/.ssh/authorized_keys'));
00a055 409         
8ab3cd 410             // Remove duplicate keys
26c0fc 411             $existing_keys = @file($sshkeys);
8ab3cd 412             $new_keys = explode("\n", $userkey);
26c0fc 413             $final_keys_arr = @array_merge($existing_keys, $new_keys);
8ab3cd 414             $new_final_keys_arr = array();
T 415             if(is_array($final_keys_arr) && !empty($final_keys_arr)){
416                 foreach($final_keys_arr as $key => $val){
417                     $new_final_keys_arr[$key] = trim($val);
418                 }
419             }
420             $final_keys = implode("\n", array_flip(array_flip($new_final_keys_arr)));
421             
00a055 422             // add the user's key
8ab3cd 423             file_put_contents($sshkeys, $final_keys);
T 424             $app->file->remove_blank_lines($sshkeys);
00a055 425             $this->app->log("ssh-rsa authorisation keyfile created in ".$sshkeys,LOGLEVEL_DEBUG);
L 426         }
8cf78b 427         //* Get the keys
T 428         $existing_keys = file($sshkeys);
429         $new_keys = explode("\n", $sshrsa);
430         $old_keys = explode("\n",$this->data['old']['ssh_rsa']);
8ab3cd 431             
8cf78b 432         //* Remove all old keys
T 433         if(is_array($old_keys)) {
434             foreach($old_keys as $key => $val) {
435                 $k = array_search(trim($val),$existing_keys);
436                 unset($existing_keys[$k]);
437             }
00a055 438         }
8cf78b 439             
T 440         //* merge the remaining keys and the ones fom the ispconfig database.
441         if(is_array($new_keys)) {
442             $final_keys_arr = array_merge($existing_keys, $new_keys);
443         } else {
444             $final_keys_arr = $existing_keys;
445         }
446             
447         $new_final_keys_arr = array();
448         if(is_array($final_keys_arr) && !empty($final_keys_arr)){
449             foreach($final_keys_arr as $key => $val){
450                 $new_final_keys_arr[$key] = trim($val);
451             }
452         }
453         $final_keys = implode("\n", array_flip(array_flip($new_final_keys_arr)));
454             
455         // add the custom key 
4bd960 456         $app->system->file_put_contents($sshkeys, $final_keys);
8cf78b 457         $app->file->remove_blank_lines($sshkeys);
T 458         $this->app->log("ssh-rsa key updated in ".$sshkeys,LOGLEVEL_DEBUG);
459         
00a055 460         // set proper file permissions
8cf78b 461         exec("chown -R ".escapeshellcmd($this->data['new']['puser']).":".escapeshellcmd($this->data['new']['pgroup'])." ".$sshdir);
T 462         exec("chmod 700 ".$sshdir);
00a055 463         exec("chmod 600 '$sshkeys'");
L 464         
465     }
396f0e 466 } // end class
T 467
8e725d 468 ?>