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