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_base_plugin {
32     
33     var $plugin_name = 'shelluser_base_plugin';
34     var $class_name = 'shelluser_base_plugin';
35     var $min_uid = 499;
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     
70     function insert($event_name,$data) {
71         global $app, $conf;
72         
73         $app->uses('system');
74         
b67344 75         //* Check if the resulting path is inside the docroot
T 76         $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($data['new']['parent_domain_id']));
77         if(substr(realpath($data['new']['dir']),0,strlen($web['document_root'])) != $web['document_root']) {
78             $app->log('Directory of the shell user is outside of website docroot.',LOGLEVEL_WARN);
79             return false;
80         }
81         
396f0e 82         if($app->system->is_user($data['new']['puser'])) {
4b9329 83             
T 84             //* Remove webfolder protection
85             $app->system->web_folder_protection($web['document_root'],false);
86             
396f0e 87             // Get the UID of the parent user
T 88             $uid = intval($app->system->getuid($data['new']['puser']));
89             if($uid > $this->min_uid) {
90                 $command = 'useradd';
e47d46 91                 $command .= ' -d '.escapeshellcmd($data['new']['dir']);
T 92                 $command .= ' -g '.escapeshellcmd($data['new']['pgroup']);
93                 $command .= ' -o '; // non unique
94                 if($data['new']['password'] != '') $command .= ' -p '.escapeshellcmd($data['new']['password']);
95                 $command .= ' -s '.escapeshellcmd($data['new']['shell']);
96                 $command .= ' -u '.escapeshellcmd($uid);
396f0e 97                 $command .= ' '.escapeshellcmd($data['new']['username']);
T 98             
99                 exec($command);
e47d46 100                 $app->log("Executed command: ".$command,LOGLEVEL_DEBUG);
396f0e 101                 $app->log("Added shelluser: ".$data['new']['username'],LOGLEVEL_DEBUG);
08c588 102                                 
L 103                 // call the ssh-rsa update function
104                 $app->uses("getconf");
105                 $this->data = $data;
106                 $this->app = $app;
107                 $this->_setup_ssh_rsa();
396f0e 108                 
12e119 109                 //* Create .bash_history file
4bd960 110                 $app->system->touch(escapeshellcmd($data['new']['dir']).'/.bash_history');
T 111                 $app->system->chmod(escapeshellcmd($data['new']['dir']).'/.bash_history', 0755);
112                 $app->system->chown(escapeshellcmd($data['new']['dir']).'/.bash_history', $data['new']['username']);
113                 $app->system->chgrp(escapeshellcmd($data['new']['dir']).'/.bash_history', $data['new']['pgroup']);
12e119 114                 
396f0e 115                 //* Disable shell user temporarily if we use jailkit
T 116                 if($data['new']['chroot'] == 'jailkit') {
526b99 117                     $command = 'usermod -s /bin/false -L '.escapeshellcmd($data['new']['username']).' 2>/dev/null';
396f0e 118                     exec($command);
T 119                     $app->log("Disabling shelluser temporarily: ".$command,LOGLEVEL_DEBUG);
120                 }
4b9329 121                 
T 122                 //* Add webfolder protection again
123                 $app->system->web_folder_protection($web['document_root'],true);
396f0e 124             
T 125             } else {
126                 $app->log("UID = $uid for shelluser:".$data['new']['username']." not allowed.",LOGLEVEL_ERROR);
127             }
128         } else {
8e725d 129             $app->log("Skipping insertion of user:".$data['new']['username'].", parent user ".$data['new']['puser']." does not exist.",LOGLEVEL_WARN);
396f0e 130         }
T 131     }
132     
133     function update($event_name,$data) {
134         global $app, $conf;
135         
136         $app->uses('system');
137         
b67344 138         //* Check if the resulting path is inside the docroot
T 139         $web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($data['new']['parent_domain_id']));
140         if(substr(realpath($data['new']['dir']),0,strlen($web['document_root'])) != $web['document_root']) {
141             $app->log('Directory of the shell user is outside of website docroot.',LOGLEVEL_WARN);
142             return false;
143         }
144         
396f0e 145         if($app->system->is_user($data['new']['puser'])) {
T 146             // Get the UID of the parent user
147             $uid = intval($app->system->getuid($data['new']['puser']));
148             if($uid > $this->min_uid) {
149                 // Check if the user that we want to update exists, if not, we insert it
150                 if($app->system->is_user($data['old']['username'])) {
ff6a68 151                     /*
396f0e 152                     $command = 'usermod';
T 153                     $command .= ' --home '.escapeshellcmd($data['new']['dir']);
154                     $command .= ' --gid '.escapeshellcmd($data['new']['pgroup']);
155                     // $command .= ' --non-unique ';
156                     $command .= ' --password '.escapeshellcmd($data['new']['password']);
157                     if($data['new']['chroot'] != 'jailkit') $command .= ' --shell '.escapeshellcmd($data['new']['shell']);
158                     // $command .= ' --uid '.escapeshellcmd($uid);
159                     $command .= ' --login '.escapeshellcmd($data['new']['username']);
160                     $command .= ' '.escapeshellcmd($data['old']['username']);
161             
162                     exec($command);
e47d46 163                     $app->log("Executed command: $command ",LOGLEVEL_DEBUG);
ff6a68 164                     */
T 165                     $groupinfo = posix_getgrnam($data['new']['pgroup']);
166                     $app->system->usermod($data['old']['username'],0, $groupinfo[gid], $data['new']['dir'], $data['new']['shell'], $data['new']['password'], $data['new']['username']);
396f0e 167                     $app->log("Updated shelluser: ".$data['old']['username'],LOGLEVEL_DEBUG);
08c588 168                                     
L 169                     // call the ssh-rsa update function
170                     $app->uses("getconf");
171                     $this->data = $data;
172                     $this->app = $app;
173                     $this->_setup_ssh_rsa();
12e119 174                     
T 175                     //* Create .bash_history file
176                     if(!is_file($data['new']['dir']).'/.bash_history') {
4bd960 177                         $app->system->touch(escapeshellcmd($data['new']['dir']).'/.bash_history');
T 178                         $app->system->chmod(escapeshellcmd($data['new']['dir']).'/.bash_history', 0755);
179                         $app->system->chown(escapeshellcmd($data['new']['dir']).'/.bash_history',escapeshellcmd($data['new']['username']));
180                         $app->system->chgrp(escapeshellcmd($data['new']['dir']).'/.bash_history',escapeshellcmd($data['new']['pgroup']));
12e119 181                     }
T 182                     
396f0e 183                 } else {
T 184                     // The user does not exist, so we insert it now
185                     $this->insert($event_name,$data);
186                 }
187             } else {
188                 $app->log("UID = $uid for shelluser:".$data['new']['username']." not allowed.",LOGLEVEL_ERROR);
189             }
190         } else {
8e725d 191             $app->log("Skipping update for user:".$data['new']['username'].", parent user ".$data['new']['puser']." does not exist.",LOGLEVEL_WARN);
396f0e 192         }
T 193     }
194     
195     function delete($event_name,$data) {
196         global $app, $conf;
197         
198         $app->uses('system');
199         
200         if($app->system->is_user($data['old']['username'])) {
201             // Get the UID of the user
202             $userid = intval($app->system->getuid($data['old']['username']));
203             if($userid > $this->min_uid) {
ac32a4 204                 // We delete only non jailkit users, jailkit users will be deleted by the jailkit plugin.
T 205                 if ($data['old']['chroot'] != "jailkit") {
206                     $command = 'userdel -f';
526b99 207                     $command .= ' '.escapeshellcmd($data['old']['username']).' &> /dev/null';
ac32a4 208                     exec($command);
T 209                     $app->log("Deleted shelluser: ".$data['old']['username'],LOGLEVEL_DEBUG);
210                 }
396f0e 211             
T 212             } else {
213                 $app->log("UID = $userid for shelluser:".$data['old']['username']." not allowed.",LOGLEVEL_ERROR);
214             }
215         } else {
216             $app->log("User:".$data['new']['username']." does not exist in in /etc/passwd, skipping delete.",LOGLEVEL_WARN);
217         }
218         
219     }
220     
00a055 221     private function _setup_ssh_rsa() {
8ab3cd 222         global $app;
00a055 223         $this->app->log("ssh-rsa setup shelluser_base",LOGLEVEL_DEBUG);
L 224         // Get the client ID, username, and the key
27c623 225         $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 226         $sys_group_data = $this->app->db->queryOneRecord('SELECT * FROM sys_group WHERE sys_group.groupid = '.intval($domain_data['sys_groupid']));
00a055 227         $id = intval($sys_group_data['client_id']);
L 228         $username= $sys_group_data['name'];
27c623 229         $client_data = $this->app->db->queryOneRecord('SELECT * FROM client WHERE client.client_id = '.$id);
00a055 230         $userkey = $client_data['ssh_rsa'];
L 231         unset($domain_data);
232         unset($client_data);
233         
234         // ssh-rsa authentication variables
8ab3cd 235         $sshrsa = $this->data['new']['ssh_rsa'];
00a055 236         $usrdir = escapeshellcmd($this->data['new']['dir']);
L 237         $sshdir = $usrdir.'/.ssh';
238         $sshkeys= $usrdir.'/.ssh/authorized_keys';
239         
8ab3cd 240         $app->uses('file');
T 241         $sshrsa = $app->file->unix_nl($sshrsa);
242         $sshrsa = $app->file->remove_blank_lines($sshrsa,0);
243         
00a055 244         // If this user has no key yet, generate a pair
8ab3cd 245         if ($userkey == '' && $id > 0){
00a055 246             //Generate ssh-rsa-keys
L 247             exec('ssh-keygen -t rsa -C '.$username.'-rsa-key-'.time().' -f /tmp/id_rsa -N ""');
8ab3cd 248             
T 249             // use the public key that has been generated
4bd960 250             $userkey = $app->system->file_get_contents('/tmp/id_rsa.pub');
8ab3cd 251             
00a055 252             // save keypair in client table
4bd960 253             $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 254             
4bd960 255             $app->system->unlink('/tmp/id_rsa');
T 256             $app->system->unlink('/tmp/id_rsa.pub');
00a055 257             $this->app->log("ssh-rsa keypair generated for ".$username,LOGLEVEL_DEBUG);
L 258         };
8ab3cd 259
T 260         if (!file_exists($sshkeys)){
00a055 261             // add root's key
8cf78b 262             $app->file->mkdirs($sshdir, '0700');
4bd960 263             if(is_file('/root/.ssh/authorized_keys')) $app->system->file_put_contents($sshkeys, $app->system->file_get_contents('/root/.ssh/authorized_keys'));
00a055 264         
8ab3cd 265             // Remove duplicate keys
8cf78b 266             $existing_keys = @file($sshkeys);
8ab3cd 267             $new_keys = explode("\n", $userkey);
8cf78b 268             $final_keys_arr = @array_merge($existing_keys, $new_keys);
8ab3cd 269             $new_final_keys_arr = array();
T 270             if(is_array($final_keys_arr) && !empty($final_keys_arr)){
271                 foreach($final_keys_arr as $key => $val){
272                     $new_final_keys_arr[$key] = trim($val);
273                 }
274             }
275             $final_keys = implode("\n", array_flip(array_flip($new_final_keys_arr)));
276             
00a055 277             // add the user's key
4bd960 278             $app->system->file_put_contents($sshkeys, $final_keys);
8ab3cd 279             $app->file->remove_blank_lines($sshkeys);
00a055 280             $this->app->log("ssh-rsa authorisation keyfile created in ".$sshkeys,LOGLEVEL_DEBUG);
L 281         }
8ab3cd 282             
8cf78b 283         //* Get the keys
T 284         $existing_keys = file($sshkeys);
285         $new_keys = explode("\n", $sshrsa);
286         $old_keys = explode("\n",$this->data['old']['ssh_rsa']);
287             
288         //* Remove all old keys
289         if(is_array($old_keys)) {
290             foreach($old_keys as $key => $val) {
291                 $k = array_search(trim($val),$existing_keys);
292                 unset($existing_keys[$k]);
293             }
00a055 294         }
8cf78b 295             
T 296         //* merge the remaining keys and the ones fom the ispconfig database.
297         if(is_array($new_keys)) {
298             $final_keys_arr = array_merge($existing_keys, $new_keys);
299         } else {
300             $final_keys_arr = $existing_keys;
301         }
302             
303         $new_final_keys_arr = array();
304         if(is_array($final_keys_arr) && !empty($final_keys_arr)){
305             foreach($final_keys_arr as $key => $val){
306                 $new_final_keys_arr[$key] = trim($val);
307             }
308         }
309         $final_keys = implode("\n", array_flip(array_flip($new_final_keys_arr)));
310             
311         // add the custom key 
4bd960 312         $app->system->file_put_contents($sshkeys, $final_keys);
8cf78b 313         $app->file->remove_blank_lines($sshkeys);
T 314         $this->app->log("ssh-rsa key updated in ".$sshkeys,LOGLEVEL_DEBUG);
315         
00a055 316         // set proper file permissions
8cf78b 317         exec("chown -R ".escapeshellcmd($this->data['new']['puser']).":".escapeshellcmd($this->data['new']['pgroup'])." ".$sshdir);
00a055 318         exec("chmod 600 '$sshkeys'");
L 319         
08c588 320     }
396f0e 321     
T 322
323 } // end class
324
8e725d 325 ?>