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