diff --git a/lam/HISTORY b/lam/HISTORY index c644fed6..70f2f3af 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -1,5 +1,6 @@ September 2016 - Windows: allow to show effective members of a group + - Lamdaemon: support SSH key authentication - LAM Pro: -> Group of names/members + roles: allow to show effective members of a group -> Cron jobs: diff --git a/lam/help/help.inc b/lam/help/help.inc index 91fcfdbc..af132335 100644 --- a/lam/help/help.inc +++ b/lam/help/help.inc @@ -211,6 +211,12 @@ $helpArray = array ( "Text" => _('Default method to output a random password.')), '283' => array ("Headline" => _('Force password change by default'), "Text" => _('Enforce password change on next login by default.')), + '284' => array ("Headline" => _('User name'), + "Text" => _('User name for SSH connection to lamdaemon server. If empty the user name of the person who is logged into LAM will be used.')), + '285' => array ("Headline" => _('SSH key file'), + "Text" => _('Path to SSH key file to connect to lamdaemon server. If empty then password authentication with the person\'s password who is logged into LAM will be used.')), + '286' => array ("Headline" => _('SSH key password'), + "Text" => _('Password to unlock SSH key file.')), // 300 - 399 // profile editor, file upload "301" => array ("Headline" => _("RDN identifier"), diff --git a/lam/lib/config.inc b/lam/lib/config.inc index 43137f86..c29705f6 100644 --- a/lam/lib/config.inc +++ b/lam/lib/config.inc @@ -475,6 +475,21 @@ class LAMConfig { */ private $scriptServer; + /** + * user name for lamdaemon + */ + private $scriptUserName; + + /** + * File name of SSH key for lamdaemon. + */ + private $scriptSSHKey; + + /** + * Password for lamdaemon SSH key. + */ + private $scriptSSHKeyPassword; + /** LDAP cache timeout */ private $cachetimeout; @@ -560,7 +575,8 @@ class LAMConfig { 'lamProMailText', 'lamProMailIsHTML', 'lamProMailAllowAlternateAddress', 'httpAuthentication', 'loginSearchDN', 'loginSearchPassword', 'timeZone', 'jobsBindUser', 'jobsBindPassword', 'jobsDatabase', 'jobToken', 'jobs', 'jobsDBHost', 'jobsDBPort', 'jobsDBUser', 'jobsDBPassword', 'jobsDBName', 'pwdResetAllowSpecificPassword', - 'pwdResetAllowScreenPassword', 'pwdResetForcePasswordChange', 'pwdResetDefaultPasswordOutput' + 'pwdResetAllowScreenPassword', 'pwdResetForcePasswordChange', 'pwdResetDefaultPasswordOutput', + 'scriptUserName', 'scriptSSHKey', 'scriptSSHKeyPassword' ); @@ -753,6 +769,9 @@ class LAMConfig { if (!in_array("scriptPath", $saved)) array_push($file_array, "\n\n# Path to external Script\n" . "scriptPath: " . $this->scriptPath . "\n"); if (!in_array("scriptServer", $saved)) array_push($file_array, "\n\n# Servers of external script\n" . "scriptServer: " . $this->scriptServer . "\n"); if (!in_array("scriptRights", $saved)) array_push($file_array, "\n\n# Access rights for home directories\n" . "scriptRights: " . $this->scriptRights . "\n"); + if (!in_array("scriptUserName", $saved)) array_push($file_array, "\n" . "scriptUserName: " . $this->scriptUserName . "\n"); + if (!in_array("scriptSSHKey", $saved)) array_push($file_array, "\n" . "scriptSSHKey: " . $this->scriptSSHKey . "\n"); + if (!in_array("scriptSSHKeyPassword", $saved)) array_push($file_array, "\n" . "scriptSSHKeyPassword: " . $this->scriptSSHKeyPassword . "\n"); if (!in_array("cachetimeout", $saved)) array_push($file_array, "\n\n# Number of minutes LAM caches LDAP searches.\n" . "cacheTimeout: " . $this->cachetimeout . "\n"); if (!in_array("searchLimit", $saved)) array_push($file_array, "\n\n# LDAP search limit.\n" . "searchLimit: " . $this->searchLimit . "\n"); if (!in_array("activeTypes", $saved)) array_push($file_array, "\n\n# List of active account types.\n" . "activeTypes: " . $this->activeTypes . "\n"); @@ -1271,6 +1290,60 @@ class LAMConfig { } } + /** + * Returns the path to lamdamon SSH key. + * + * @return string key path + */ + public function getScriptSSHKey() { + return $this->scriptSSHKey; + } + + /** + * Sets the path to lamdamon SSH key. + * + * @param string $value key path + */ + public function setScriptSSHKey($value) { + $this->scriptSSHKey = $value; + } + + /** + * Returns the password for the lamdamon SSH key. + * + * @return string password + */ + public function getScriptSSHKeyPassword() { + return $this->scriptSSHKeyPassword; + } + + /** + * Sets the password for the lamdamon SSH key. + * + * @param string $value password + */ + public function setScriptSSHKeyPassword($value) { + $this->scriptSSHKeyPassword = $value; + } + + /** + * Returns the lamdaemon user name. + * + * @return string user name + */ + public function getScriptUserName() { + return $this->scriptUserName; + } + + /** + * Sets the lamdaemon user name. + * + * @param string $value user name + */ + public function setScriptUserName($value) { + $this->scriptUserName = $value; + } + /** * Returns the LDAP cache timeout in minutes * diff --git a/lam/lib/lamdaemon.inc b/lam/lib/lamdaemon.inc index 597ca218..5ef5d780 100644 --- a/lam/lib/lamdaemon.inc +++ b/lam/lib/lamdaemon.inc @@ -3,7 +3,7 @@ $Id$ This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2004 - 2011 Roland Gruber + Copyright (C) 2004 - 2016 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,40 +46,83 @@ function lamdaemon($command, $server) { // add phpseclib to include path set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/3rdParty/phpseclib'); include_once('Net/SSH2.php'); - // get username and password of the current lam-admin - $credentials = $_SESSION['ldap']->decrypt_login(); + try { + $handle = lamConnectSSH($server); + } + catch (Exception $e) { + return array("ERROR," . $e->getMessage() . "," . $server); + } + $output = $handle->exec("sudo " . $_SESSION['config']->get_scriptPath() . ' ' . escapeshellarg($command)); + return array($output); +} + +/** + * Connects to the given SSH server. + * + * @param String $server server name (e.g. localhost or localhost,1234) + * @return object handle + */ +function lamConnectSSH($server) { + // add phpseclib to include path + set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/3rdParty/phpseclib'); + include_once('Net/SSH2.php'); + include_once('Crypt/RSA.php'); $serverNameParts = explode(",", $server); + $handle = false; if (sizeof($serverNameParts) > 1) { - $handle = new Net_SSH2($serverNameParts[0], $serverNameParts[1]); + $handle = @new Net_SSH2($serverNameParts[0], $serverNameParts[1]); } else { - $handle = new Net_SSH2($server); + $handle = @new Net_SSH2($server); } - if ($handle) { + if (!$handle) { + throw new Exception(_("Unable to connect to remote server!")); + } + lamLoginSSH($handle); + return $handle; +} + +/** + * Performs a login to the provided SSH handle. + * + * @param handle $handle SSH handle + * @throws Exception login failed + */ +function lamLoginSSH($handle) { + $username = $_SESSION['config']->getScriptUserName(); + $credentials = $_SESSION['ldap']->decrypt_login(); + if (empty($username)) { + // get user name from current LAM user $sr = @ldap_read($_SESSION['ldap']->server(), $credentials[0], "objectClass=posixAccount", array('uid'), 0, 0, 0, LDAP_DEREF_NEVER); - if (!$sr) { - $return = array("ERROR," . _("Your LAM admin user must be a valid Unix account to work with lamdaemon!") . ","); - return $return; + if ($sr) { + $entry = @ldap_get_entries($_SESSION['ldap']->server(), $sr); + $username = $entry[0]['uid'][0]; } - $entry = @ldap_get_entries($_SESSION['ldap']->server(), $sr); - if (!isset($entry[0]['uid'][0])) { - $return = array("ERROR," . _("Your LAM admin user must be a valid Unix account to work with lamdaemon!") . ","); - return $return; - } - $userName = $entry[0]['uid'][0]; - if ($handle->login($userName, $credentials[1])) { - $output = $handle->exec("sudo " . $_SESSION['config']->get_scriptPath() . ' ' . escapeshellarg($command)); - $return = array($output); - return $return; - } - else { - $return = array("ERROR," . _('Unable to connect to remote server!') . "," . $server); - return $return; + if (empty($username)) { + throw new Exception(sprintf(_("Your LAM admin user (%s) must be a valid Unix account to work with lamdaemon!"), $credentials[0])); } } - else { - $return = array("ERROR," . _('Unable to connect to remote server!') . "," . $server); - return $return; + $password = $credentials[1]; + if (!empty($_SESSION['config']->getScriptSSHKey())) { + // use key authentication + $keyPath = $_SESSION['config']->getScriptSSHKey(); + if (!file_exists($keyPath) || !is_readable($keyPath)) { + throw new Exception(sprintf(_("Unable to read %s."), htmlspecialchars($keyPath))); + } + $key = file_get_contents($keyPath); + $rsa = new Crypt_RSA(); + $keyPassword = $_SESSION['config']->getScriptSSHKeyPassword(); + if (!empty($keyPassword)) { + $rsa->setPassword($keyPassword); + } + if (!$rsa->loadKey($key)) { + throw new Exception(sprintf(_("Unable to load key %s."), htmlspecialchars($keyPath))); + } + $password = $rsa; + } + $login = @$handle->login($username, $password); + if (!$login) { + throw new Exception(_("Unable to login to remote server!")); } } diff --git a/lam/templates/config/confmain.php b/lam/templates/config/confmain.php index fff0b234..914f195f 100644 --- a/lam/templates/config/confmain.php +++ b/lam/templates/config/confmain.php @@ -340,6 +340,13 @@ $container->addElement(new htmlSpacer(null, '10px'), true); $lamdaemonSettingsContent = new htmlTable(); $lamdaemonSettingsContent->addElement(new htmlTableExtendedInputField(_("Server list"), 'scriptservers', $conf->get_scriptServers(), '218'), true); $lamdaemonSettingsContent->addElement(new htmlTableExtendedInputField(_("Path to external script"), 'scriptpath', $conf->get_scriptPath(), '210'), true); + +$lamdaemonSettingsContent->addElement(new htmlTableExtendedInputField(_('User name'), 'scriptuser', $conf->getScriptUserName(), '284'), true); +$lamdaemonSettingsContent->addElement(new htmlTableExtendedInputField(_('SSH key file'), 'scriptkey', $conf->getScriptSSHKey(), '285'), true); +$sshKeyPassword = new htmlTableExtendedInputField(_('SSH key password'), 'scriptkeypassword', $conf->getScriptSSHKeyPassword(), '286'); +$sshKeyPassword->setIsPassword(true); +$lamdaemonSettingsContent->addElement($sshKeyPassword, true); + $lamdaemonSettingsContent->addElement(new htmlSpacer(null, '5px'), true); $lamdaemonSettingsContent->addElement(new htmlOutputText(_("Rights for the home directory"))); $chmod = $conf->get_scriptRights(); @@ -688,6 +695,9 @@ function checkInput() { if (!$conf->set_scriptrights($chmod)) { $errors[] = array("ERROR", _("Script rights are invalid!")); } + $conf->setScriptUserName($_POST['scriptuser']); + $conf->setScriptSSHKey($_POST['scriptkey']); + $conf->setScriptSSHKeyPassword($_POST['scriptkeypassword']); // tool settings $tools = getTools(); $toolSettings = array(); diff --git a/lam/templates/tests/lamdaemonTest.php b/lam/templates/tests/lamdaemonTest.php index ca2e8abb..2da7b8ec 100644 --- a/lam/templates/tests/lamdaemonTest.php +++ b/lam/templates/tests/lamdaemonTest.php @@ -3,18 +3,18 @@ $Id$ This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) - Copyright (C) 2006 - 2015 Roland Gruber + Copyright (C) 2006 - 2016 Roland Gruber This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -83,12 +83,12 @@ else if ((sizeof($servers) > 0) && isset($servers[0]) && ($servers[0] != '')) { $serverSelect = new htmlSelect('server', $serverOptions); $serverSelect->setHasDescriptiveElements(true); $container->addElement($serverSelect, true); - + $container->addElement(new htmlOutputText(_("Check quotas"))); $container->addElement(new htmlInputCheckbox('checkQuotas', false), true); - + $container->addElement(new htmlSpacer(null, '10px'), true); - + $okButton = new htmlButton('runTest', _("Ok")); $okButton->colspan = 2; $container->addElement($okButton); @@ -174,7 +174,7 @@ function lamRunLamdaemonTestSuite($serverName, $serverTitle, $testQuota, $contai $LAMDAEMON_PROTOCOL_VERSION = '5'; $okImage = "../../graphics/pass.png"; $failImage = "../../graphics/fail.png"; - + flush(); $stopTest = false; $spacer = new htmlSpacer('10px', null); @@ -210,29 +210,34 @@ function lamRunLamdaemonTestSuite($serverName, $serverTitle, $testQuota, $contai flush(); // check Unix account of LAM admin + $credentials = $_SESSION['ldap']->decrypt_login(); if (!$stopTest) { - $container->addElement(new htmlOutputText(_("Unix account"))); - $container->addElement($spacer); - $credentials = $_SESSION['ldap']->decrypt_login(); - $unixOk = false; - $sr = @ldap_read($_SESSION['ldap']->server(), $credentials[0], "objectClass=posixAccount", array('uid'), 0, 0, 0, LDAP_DEREF_NEVER); - if ($sr) { - $entry = @ldap_get_entries($_SESSION['ldap']->server(), $sr); - $userName = $entry[0]['uid'][0]; - if ($userName) { - $unixOk = true; + if (empty($_SESSION['config']->getScriptUserName())) { + $container->addElement(new htmlOutputText(_("Unix account"))); + $container->addElement($spacer); + $unixOk = false; + $sr = @ldap_read($_SESSION['ldap']->server(), $credentials[0], "objectClass=posixAccount", array('uid'), 0, 0, 0, LDAP_DEREF_NEVER); + if ($sr) { + $entry = @ldap_get_entries($_SESSION['ldap']->server(), $sr); + $userName = $entry[0]['uid'][0]; + if ($userName) { + $unixOk = true; + } + } + if ($unixOk) { + $container->addElement(new htmlImage($okImage)); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(sprintf(_("Using %s to connect to remote server."), $userName)), true); + } + else { + $container->addElement(new htmlImage($failImage)); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(sprintf(_("Your LAM admin user (%s) must be a valid Unix account to work with lamdaemon!"), $credentials[0])), true); + $stopTest = true; } } - if ($unixOk) { - $container->addElement(new htmlImage($okImage)); - $container->addElement($spacer); - $container->addElement(new htmlOutputText(sprintf(_("Using %s to connect to remote server."), $userName)), true); - } else { - $container->addElement(new htmlImage($failImage)); - $container->addElement($spacer); - $container->addElement(new htmlOutputText(sprintf(_("Your LAM admin user (%s) must be a valid Unix account to work with lamdaemon!"), $credentials[0])), true); - $stopTest = true; + $userName = $_SESSION['config']->getScriptUserName(); } } @@ -244,45 +249,37 @@ function lamRunLamdaemonTestSuite($serverName, $serverTitle, $testQuota, $contai $container->addElement($spacer); flush(); $sshOk = false; - $handle = lamTestConnectSSH($serverName); - if ($handle) { - if ($handle->login($userName, $credentials[1])) { - $sshOk = true; - } - } - if ($sshOk) { + try { + $handle = lamConnectSSH($serverName); $container->addElement(new htmlImage($okImage)); $container->addElement($spacer); - $container->addElement(new htmlOutputText(_("SSH connection could be established.")), true); + $container->addElement(new htmlOutputText(_("SSH connection established.")), true); } - else { + catch (Exception $e) { $container->addElement(new htmlImage($failImage)); $container->addElement($spacer); - $container->addElement(new htmlOutputText(_("Unable to connect to remote server!")), true); + $container->addElement(new htmlOutputText($e->getMessage()), true); $stopTest = true; } } flush(); - + if (!$stopTest) { $stopTest = lamTestLamdaemon("+" . $SPLIT_DELIMITER . "test" . $SPLIT_DELIMITER . "basic", $stopTest, $handle, _("Execute lamdaemon"), $container); } - + if (!$stopTest) { $stopTest = lamTestLamdaemon("+" . $SPLIT_DELIMITER . "test" . $SPLIT_DELIMITER . "version" . $SPLIT_DELIMITER . $LAMDAEMON_PROTOCOL_VERSION, $stopTest, $handle, _("Lamdaemon version"), $container); } - + if (!$stopTest) { - $handle = lamTestConnectSSH($serverName); - @$handle->login($userName, $credentials[1]); + $handle = lamConnectSSH($serverName); $stopTest = lamTestLamdaemon("+" . $SPLIT_DELIMITER . "test" . $SPLIT_DELIMITER . "nss" . $SPLIT_DELIMITER . "$userName", $stopTest, $handle, _("Lamdaemon: check NSS LDAP"), $container); if (!$stopTest && $testQuota) { - $handle = lamTestConnectSSH($serverName); - @$handle->login($userName, $credentials[1]); + $handle = lamConnectSSH($serverName); $stopTest = lamTestLamdaemon("+" . $SPLIT_DELIMITER . "test" . $SPLIT_DELIMITER . "quota", $stopTest, $handle, _("Lamdaemon: Quota module installed"), $container); - $handle = lamTestConnectSSH($serverName); - @$handle->login($userName, $credentials[1]); + $handle = lamConnectSSH($serverName); $stopTest = lamTestLamdaemon("+" . $SPLIT_DELIMITER . "quota" . $SPLIT_DELIMITER . "get" . $SPLIT_DELIMITER . "user", $stopTest, $handle, _("Lamdaemon: read quotas"), $container); } } @@ -293,23 +290,4 @@ function lamRunLamdaemonTestSuite($serverName, $serverTitle, $testQuota, $contai $container->addElement($endMessage); } -/** - * Connects to the given SSH server. - * - * @param String $server server name (e.g. localhost or localhost,1234) - * @return object handle - */ -function lamTestConnectSSH($server) { - // add phpseclib to include path - set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/../../lib/3rdParty/phpseclib'); - include_once('Net/SSH2.php'); - $serverNameParts = explode(",", $server); - if (sizeof($serverNameParts) > 1) { - return @new Net_SSH2($serverNameParts[0], $serverNameParts[1]); - } - else { - return @new Net_SSH2($server); - } -} - ?> diff --git a/lam/tests/conf-main-test.php b/lam/tests/conf-main-test.php deleted file mode 100644 index 6fb30824..00000000 --- a/lam/tests/conf-main-test.php +++ /dev/null @@ -1,68 +0,0 @@ -