external password check

This commit is contained in:
Roland Gruber 2018-04-10 21:32:26 +02:00
parent 78bfff3bb8
commit fa67431df3
4 changed files with 61 additions and 2 deletions

View File

@ -171,6 +171,8 @@ $helpArray = array (
"Text" => _('Specifies if the password must not contain the user name.')), "Text" => _('Specifies if the password must not contain the user name.')),
"248" => array ("Headline" => _('Password must not contain part of user/first/last name'), "248" => array ("Headline" => _('Password must not contain part of user/first/last name'),
"Text" => _('Specifies if the password must not contain 3 or more characters of the user/first/last name.')), "Text" => _('Specifies if the password must not contain 3 or more characters of the user/first/last name.')),
"249" => array ("Headline" => _('External password check'),
"Text" => _('Please specify the URL (e.g. "https://api.pwnedpasswords.com/range/{SHA1PREFIX}") of your external password check and the severity if the check fails.')),
"250" => array ("Headline" => _("Filter"), "250" => array ("Headline" => _("Filter"),
"Text" => _("Here you can input simple filter expressions (e.g. 'value' or 'v*'). The filter is case-sensitive.")), "Text" => _("Here you can input simple filter expressions (e.g. 'value' or 'v*'). The filter is case-sensitive.")),
"260" => array ("Headline" => _("Additional LDAP filter"), "260" => array ("Headline" => _("Additional LDAP filter"),

View File

@ -1,10 +1,9 @@
<?php <?php
use \LAM\LIB\TWO_FACTOR\TwoFactorProviderService; use \LAM\LIB\TWO_FACTOR\TwoFactorProviderService;
/* /*
$Id$
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2017 Roland Gruber Copyright (C) 2003 - 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -2287,6 +2286,9 @@ class LAMCfgMain {
/** password may contain more than 2 characters of user/first/last name */ /** password may contain more than 2 characters of user/first/last name */
public $passwordMustNotContain3Chars = 'false'; public $passwordMustNotContain3Chars = 'false';
/** external URL for password checking (e.g. https://domain.com/url/{SHA1}) */
public $externalPwdCheckUrl = null;
/** path to config file */ /** path to config file */
private $conffile; private $conffile;
@ -2311,6 +2313,7 @@ class LAMCfgMain {
"passwordMinUpper", "passwordMinLower", "passwordMinNumeric", "passwordMinUpper", "passwordMinLower", "passwordMinNumeric",
"passwordMinClasses", "passwordMinSymbol", 'checkedRulesCount', "passwordMinClasses", "passwordMinSymbol", 'checkedRulesCount',
'passwordMustNotContainUser', 'passwordMustNotContain3Chars', 'passwordMustNotContainUser', 'passwordMustNotContain3Chars',
'externalPwdCheckUrl',
"mailEOL", 'errorReporting', 'encryptSession', 'allowedHostsSelfService', "mailEOL", 'errorReporting', 'encryptSession', 'allowedHostsSelfService',
'license' 'license'
); );
@ -2405,6 +2408,7 @@ class LAMCfgMain {
if (!in_array("checkedRulesCount", $saved)) array_push($file_array, "\n\n# Password: checked rules\n" . "checkedRulesCount: " . $this->checkedRulesCount); if (!in_array("checkedRulesCount", $saved)) array_push($file_array, "\n\n# Password: checked rules\n" . "checkedRulesCount: " . $this->checkedRulesCount);
if (!in_array("passwordMustNotContain3Chars", $saved)) array_push($file_array, "\n\n# Password: must not contain part of user name\n" . "passwordMustNotContain3Chars: " . $this->passwordMustNotContain3Chars); if (!in_array("passwordMustNotContain3Chars", $saved)) array_push($file_array, "\n\n# Password: must not contain part of user name\n" . "passwordMustNotContain3Chars: " . $this->passwordMustNotContain3Chars);
if (!in_array("passwordMustNotContainUser", $saved)) array_push($file_array, "\n\n# Password: must not contain user name\n" . "passwordMustNotContainUser: " . $this->passwordMustNotContainUser); if (!in_array("passwordMustNotContainUser", $saved)) array_push($file_array, "\n\n# Password: must not contain user name\n" . "passwordMustNotContainUser: " . $this->passwordMustNotContainUser);
if (!in_array("externalPwdCheckUrl", $saved)) array_push($file_array, "\n\n" . "externalPwdCheckUrl: " . $this->externalPwdCheckUrl);
if (!in_array("mailEOL", $saved)) array_push($file_array, "\n\n# Email format (default/unix)\n" . "mailEOL: " . $this->mailEOL); if (!in_array("mailEOL", $saved)) array_push($file_array, "\n\n# Email format (default/unix)\n" . "mailEOL: " . $this->mailEOL);
if (!in_array("errorReporting", $saved)) array_push($file_array, "\n\n# PHP error reporting (default/system)\n" . "errorReporting: " . $this->errorReporting); if (!in_array("errorReporting", $saved)) array_push($file_array, "\n\n# PHP error reporting (default/system)\n" . "errorReporting: " . $this->errorReporting);
if (!in_array("license", $saved)) array_push($file_array, "\n\n# License\n" . "license: " . $this->license); if (!in_array("license", $saved)) array_push($file_array, "\n\n# License\n" . "license: " . $this->license);

View File

@ -491,6 +491,49 @@ function checkPasswordStrength($password, $userName, $otherUserAttrs) {
} }
} }
} }
// check external password service
if (!checkPwdWithExternalPasswordService($cfg, $password)) {
return _('Your selected password is known to be insecure.');
}
return true;
}
/**
* Checks the password against the external password service.
*
* @param LAMCfgMain $cfg main configuration
* @param string $password password
* @return boolean password accepted as secure
*/
function checkPwdWithExternalPasswordService($cfg, $password) {
if (!function_exists('curl_init') || empty($cfg->externalPwdCheckUrl)) {
return true;
}
$sha1 = sha1($password);
$sha1Prefix = substr($sha1, 0, 5);
$sha1Suffix = substr($sha1, 5);
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$url = $cfg->externalPwdCheckUrl;
$url = str_replace('{SHA1PREFIX}', $sha1Prefix, $url);
curl_setopt($curl, CURLOPT_URL, $url);
$results = curl_exec($curl);
$code = curl_errno($curl);
if ($code) {
logNewMessage(LOG_ERR, 'Error calling the external password service at ' . $url
. '. ' . curl_error($curl));
return true;
}
curl_close($curl);
if (empty($results)) {
return true;
}
$results = explode("\n", $results);
foreach ($results as $result) {
if (stripos($result, $sha1Suffix . ':') !== false) {
return false;
}
}
return true; return true;
} }

View File

@ -179,6 +179,12 @@ if (isset($_POST['submitFormData'])) {
$cfg->checkedRulesCount = $_POST['passwordRulesCount']; $cfg->checkedRulesCount = $_POST['passwordRulesCount'];
$cfg->passwordMustNotContain3Chars = isset($_POST['passwordMustNotContain3Chars']) && ($_POST['passwordMustNotContain3Chars'] == 'on') ? 'true' : 'false'; $cfg->passwordMustNotContain3Chars = isset($_POST['passwordMustNotContain3Chars']) && ($_POST['passwordMustNotContain3Chars'] == 'on') ? 'true' : 'false';
$cfg->passwordMustNotContainUser = isset($_POST['passwordMustNotContainUser']) && ($_POST['passwordMustNotContainUser'] == 'on') ? 'true' : 'false'; $cfg->passwordMustNotContainUser = isset($_POST['passwordMustNotContainUser']) && ($_POST['passwordMustNotContainUser'] == 'on') ? 'true' : 'false';
if (function_exists('curl_init')) {
$cfg->externalPwdCheckUrl = $_POST['externalPwdCheckUrl'];
if (!empty($cfg->externalPwdCheckUrl) && (strpos($cfg->externalPwdCheckUrl, '{SHA1PREFIX}') === false)) {
$errors[] = _('The URL for the external password check is invalid.');
}
}
if (isset($_POST['sslCaCertUpload'])) { if (isset($_POST['sslCaCertUpload'])) {
if (!isset($_FILES['sslCaCert']) || ($_FILES['sslCaCert']['size'] == 0)) { if (!isset($_FILES['sslCaCert']) || ($_FILES['sslCaCert']['size'] == 0)) {
$errors[] = _('No file selected.'); $errors[] = _('No file selected.');
@ -378,6 +384,10 @@ $passwordMustNotContainUser = ($cfg->passwordMustNotContainUser === 'true') ? tr
$row->add(new htmlResponsiveInputCheckbox('passwordMustNotContainUser',$passwordMustNotContainUser , _('Password must not contain user name'), '247'), 12); $row->add(new htmlResponsiveInputCheckbox('passwordMustNotContainUser',$passwordMustNotContainUser , _('Password must not contain user name'), '247'), 12);
$passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true') ? true : false; $passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true') ? true : false;
$row->add(new htmlResponsiveInputCheckbox('passwordMustNotContain3Chars', $passwordMustNotContain3Chars, _('Password must not contain part of user/first/last name'), '248'), 12); $row->add(new htmlResponsiveInputCheckbox('passwordMustNotContain3Chars', $passwordMustNotContain3Chars, _('Password must not contain part of user/first/last name'), '248'), 12);
if (function_exists('curl_init')) {
$row->addVerticalSpacer('1rem');
$row->add(new htmlResponsiveInputField(_('External password check'), 'externalPwdCheckUrl', $cfg->externalPwdCheckUrl, '249'), 12);
}
// logging // logging
$row->add(new htmlSubTitle(_("Logging")), 12); $row->add(new htmlSubTitle(_("Logging")), 12);