Skip to content

Commit

Permalink
use local password policy feature from ltb-common (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
David Coutadeur committed Sep 16, 2024
1 parent 3ee36eb commit e1d6a0e
Show file tree
Hide file tree
Showing 13 changed files with 301 additions and 30 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ docs/_build
/htdocs/vendor/bootstrap/
composer.lock
tests/.phpunit.result.cache
htdocs/js/ppolicy.js
htdocs/css/ppolicy.css
templates/policy.tpl
8 changes: 6 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"datatables.net/datatables.net-bs5": "2.0.8",
"datatables.net/datatables.net-buttons-bs5": "3.0.2",
"fortawesome/font-awesome": "v6.5.2",
"ltb-project/ltb-common": "dev-main",
"ltb-project/ltb-common": "dev-36-add-password-policy",
"twbs/bootstrap": "v5.3.2"
},
"scripts": {
Expand Down Expand Up @@ -32,7 +32,11 @@
"rm -rf htdocs/vendor/font-awesome/*",
"cp -R vendor/fortawesome/font-awesome/css htdocs/vendor/font-awesome",
"cp -R vendor/fortawesome/font-awesome/webfonts htdocs/vendor/font-awesome",
"rm -rf vendor/fortawesome/font-awesome"
"rm -rf vendor/fortawesome/font-awesome",

"cp -f vendor/ltb-project/ltb-common/src/ppolicy/html/policy.tpl templates/policy.tpl",
"cp -f vendor/ltb-project/ltb-common/src/ppolicy/js/ppolicy.js htdocs/js/ppolicy.js",
"cp -f vendor/ltb-project/ltb-common/src/ppolicy/css/ppolicy.css htdocs/css/ppolicy.css"
]
},
"require-dev": {
Expand Down
47 changes: 47 additions & 0 deletions conf/config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,53 @@
$use_searchidle = true;
$idledays = 60;


# Local password policy
# This is applied before directory password policy
# Minimal length
$pwd_min_length = 0;
# Maximal length
$pwd_max_length = 0;
# Minimal lower characters
$pwd_min_lower = 0;
# Minimal upper characters
$pwd_min_upper = 0;
# Minimal digit characters
$pwd_min_digit = 0;
# Minimal special characters
$pwd_min_special = 0;
# Definition of special characters
$pwd_special_chars = "^a-zA-Z0-9";
# Forbidden characters
#$pwd_forbidden_chars = "@%";
# Check that password is different than login
$pwd_diff_login = true;
# Forbidden words which must not appear in the password
$pwd_forbidden_words = array();
# Forbidden ldap fields
# Respective values of the user's entry must not appear in the password
# example: $pwd_forbidden_ldap_fields = array('cn', 'givenName', 'sn', 'mail');
$pwd_forbidden_ldap_fields = array();
# Complexity: number of different class of character required
$pwd_complexity = 0;
# use pwnedpasswords api v2 to securely check if the password has been on a leak
$use_pwnedpasswords = false;
# show password entropy bar (require php zxcvbn module)
$pwd_display_entropy = false;
# enforce password entropy check
$pwd_check_entropy = false;
# minimum entropy level required (when $pwd_check_entropy enabled)
$pwd_min_entropy = 3;
# Show policy constraints message:
# always
# never
# onerror
$pwd_show_policy = "never";
# Position of password policy constraints message:
# above - the form
# below - the form
$pwd_show_policy_pos = "above";

## Mail
# LDAP mail attribute
$mail_attributes = array( "mail", "gosaMailAlternateAddress", "proxyAddresses" );
Expand Down
16 changes: 16 additions & 0 deletions htdocs/checkentropy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

require_once '../vendor/autoload.php';

// new password sent in the url, base64 encoded
$newpass = htmlspecialchars($_POST["password"]);
$entropy_response = \Ltb\Ppolicy::checkEntropyJSON($newpass);
if ($debug) {
error_log("checkEntropy: ".$entropy_response);
}

print $entropy_response;
exit(0);

?>

41 changes: 36 additions & 5 deletions htdocs/display.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,26 @@
}

# Lock
$pwdLockout = strtolower($ppolicy_entry[0]['pwdlockout'][0]) == "true" ? true : false;
$pwdLockoutDuration = $ppolicy_entry[0]['pwdlockoutduration'][0];
$pwdAccountLockedTime = $entry[0]['pwdaccountlockedtime'][0];
if(isset($ppolicy_entry[0]['pwdlockout'][0]))
{
$pwdLockout = strtolower($ppolicy_entry[0]['pwdlockout'][0]) == "true" ? true : false;
}
else
{
$pwdLockout = false;
}
if(isset($ppolicy_entry[0]['pwdlockoutduration'][0]))
{
$pwdLockoutDuration = $ppolicy_entry[0]['pwdlockoutduration'][0];
}
if(isset($entry[0]['pwdaccountlockedtime'][0]))
{
$pwdAccountLockedTime = $entry[0]['pwdaccountlockedtime'][0];
}
else
{
$pwdAccountLockedTime = null;
}

if ( $pwdAccountLockedTime === "000001010000Z" ) {
$isLocked = true;
Expand All @@ -146,8 +163,14 @@
}

# Expiration
$pwdMaxAge = $ppolicy_entry[0]['pwdmaxage'][0];
$pwdChangedTime = $entry[0]['pwdchangedtime'][0];
if(isset($ppolicy_entry[0]['pwdmaxage'][0]))
{
$pwdMaxAge = $ppolicy_entry[0]['pwdmaxage'][0];
}
if(isset($entry[0]['pwdchangedtime'][0]))
{
$pwdChangedTime = $entry[0]['pwdchangedtime'][0];
}

if (isset($pwdChangedTime) and isset($pwdMaxAge) and ($pwdMaxAge > 0)) {
$changedDate = ldapDate2phpDate($pwdChangedTime);
Expand Down Expand Up @@ -186,4 +209,12 @@
$smarty->assign("prehookresult", $prehookresult);
$smarty->assign("posthookresult", $posthookresult);
if ($pwdLockout == false) $smarty->assign("use_lockaccount", $pwdLockout);
if(isset($messages[$resetpasswordresult]))
{
$smarty->assign('msg_resetpasswordresult',$messages[$resetpasswordresult]);
}
else
{
$smarty->assign('msg_resetpasswordresult','');
}
?>
36 changes: 35 additions & 1 deletion htdocs/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,41 @@
isset($ldap_bindpw) ? $ldap_bindpw : null,
isset($ldap_network_timeout) ? $ldap_network_timeout : null,
$ldap_user_base,
null,
isset($ldap_size_limit) ? $ldap_size_limit : 0,
isset($ldap_krb5ccname) ? $ldap_krb5ccname : null
);

#==============================================================================
# Other default values
#==============================================================================
if (!isset($pwd_forbidden_chars)) { $pwd_forbidden_chars = ""; }

# Password policy array
$pwd_policy_config = array(
"pwd_show_policy" => $pwd_show_policy,
"pwd_min_length" => $pwd_min_length,
"pwd_max_length" => $pwd_max_length,
"pwd_min_lower" => $pwd_min_lower,
"pwd_min_upper" => $pwd_min_upper,
"pwd_min_digit" => $pwd_min_digit,
"pwd_min_special" => $pwd_min_special,
"pwd_special_chars" => $pwd_special_chars,
"pwd_no_reuse" => false, # old password not available
"pwd_forbidden_chars" => $pwd_forbidden_chars,
"pwd_diff_last_min_chars" => 0, # old password not available
"pwd_diff_login" => $pwd_diff_login,
"pwd_complexity" => $pwd_complexity,
"use_pwnedpasswords" => $use_pwnedpasswords,
"pwd_no_special_at_ends" => $pwd_no_special_at_ends,
"pwd_forbidden_words" => $pwd_forbidden_words,
"pwd_forbidden_ldap_fields" => $pwd_forbidden_ldap_fields,
"pwd_display_entropy" => $pwd_display_entropy,
"pwd_check_entropy" => $pwd_check_entropy,
"pwd_min_entropy" => $pwd_min_entropy
);

if (!isset($pwd_show_policy_pos)) { $pwd_show_policy_pos = "above"; }

#==============================================================================
# Smarty
#==============================================================================
Expand Down Expand Up @@ -134,6 +165,7 @@
$smarty->assign('use_showauditlog',$use_showauditlog);
$smarty->assign('fake_password_inputs',$fake_password_inputs);


# Assign messages
$smarty->assign('lang',$lang);
foreach ($messages as $key => $message) {
Expand Down Expand Up @@ -193,6 +225,8 @@
if ( file_exists($page.".php") ) { require_once($page.".php"); }
$smarty->assign('page',$page);

\Ltb\Ppolicy::smarty_assign_ppolicy($smarty, $pwd_show_policy_pos, $pwd_show_policy, $result, $pwd_policy_config);

if ($result) {
$smarty->assign('error',$messages[$result]);
} else {
Expand Down
71 changes: 55 additions & 16 deletions htdocs/resetpassword.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,32 +57,71 @@
}
}

# save LDAP modifications to apply in $entry variable
$entry["userPassword"] = $password;
if ( $pwdreset === "true" ) {
$entry["pwdReset"] = "TRUE";
}

if ( isset($prehook) ) {
# Get current entry first
$entries_search = $ldapInstance->search_with_scope("base", $dn, '(objectClass=*)');
$errno = ldap_errno($ldap);
if ( $errno ) {
$result = "ldaperror";
error_log("LDAP - Search error $errno (".ldap_error($ldap).")");
}
$entry_search = ldap_first_entry($ldap, $entries_search);
$entry_array = ldap_get_attributes($ldap, $entry_search);
# Get identifier attribute
$identifiers = ldap_get_values( $ldap,
$entry_search,
$attributes_map['identifier']['attribute']
);
$identifier = $identifiers[0];
if ( !isset($identifier) || $identifier == "" ) {
$result = "ldaperror";
error_log("LDAP - Unable to find identifier for LDAP entry ".
var_export($entry_array, true));
}

if ( !isset($prehook_login_value) ) {
$prehook_return = 255;
$prehook_message = "No login found, cannot execute prehook script";
} else {
$command = hook_command($prehook, $prehook_login_value, $password, null, $prehook_password_encodebase64);
exec($command, $prehook_output, $prehook_return);
$prehook_message = $prehook_output[0];
}
#==============================================================================
# Check password strength
#==============================================================================
if( $result != "ldaperror" )
{
$result = \Ltb\Ppolicy::check_password_strength( $password,
"",
$pwd_policy_config,
$identifier,
$entry_array,
array()
);
}

if ( $prehook_return > 0 and !$ignore_prehook_return) {
$result = "passwordrefused";
} else {
$modification = ldap_mod_replace($ldap, $dn, $entry);
$errno = ldap_errno($ldap);
if ( $errno ) {
if( $result === "")
{
if ( isset($prehook) ) {

if ( !isset($prehook_login_value) ) {
$prehook_return = 255;
$prehook_message = "No login found, cannot execute prehook script";
} else {
$command = hook_command($prehook, $prehook_login_value, $password, null, $prehook_password_encodebase64);
exec($command, $prehook_output, $prehook_return);
$prehook_message = $prehook_output[0];
}
}

if ( $prehook_return > 0 and !$ignore_prehook_return) {
$result = "passwordrefused";
} else {
$result = "passwordchanged";
$modification = ldap_mod_replace($ldap, $dn, $entry);
$errno = ldap_errno($ldap);
if ( $errno ) {
$result = "passwordrefused";
} else {
$result = "passwordchanged";
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion htdocs/search.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
require_once("../conf/config.inc.php");
require __DIR__ . '/../vendor/autoload.php';

$filter_escape_chars = null;
$filter_escape_chars = "";
if (!$search_use_substring_match) { $filter_escape_chars = "*"; }

$search_query = ldap_escape($_POST["search"], $filter_escape_chars, LDAP_ESCAPE_FILTER);
Expand Down
43 changes: 43 additions & 0 deletions lang/en.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,48 @@
$messages['welcome'] = "Welcome to LDAP Tool Box service desk";
$messages['willexpireaccounts'] = "Passwords soon expired";
$messages['willexpireaccountstitle'] = "Passwords that will expire within $willexpiredays days";
$messages['notcomplex'] = "Your password does not have enough different classes of characters";
$messages['tooshort'] = "Your password is too short";
$messages['toobig'] = "Your password is too long";
$messages['minlower'] = "Your password does not have enough lowercase characters";
$messages['policyminlower'] = "Minimum number of lowercase characters:";
$messages['minupper'] = "Your password does not have enough uppercase characters";
$messages['policyminupper'] = "Minimum number of uppercase characters:";
$messages['mindigit'] = "Your password does not have enough digits";
$messages['policymindigit'] = "Minimum number of digits:";
$messages['minspecial'] = "Your password does not have enough special characters";
$messages['policyminspecial'] = "Minimum number of special characters:";
$messages['forbiddenchars'] = "You password contains forbidden characters";
$messages['policyforbiddenchars'] = "Forbidden characters:";
$messages['specialatends'] = "Your new password has its only special character at the beginning or end";
$messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end";
$messages['sameasold'] = "Your new password is identical to your old password";
$messages['sameaslogin'] = "Your new password is identical to your login";
$messages['policydiffminchars'] = "Minimum number of new unique characters:";
$messages['diffminchars'] = "Your new password is too similar to your old password";
$messages['forbiddenwords'] = "Your passwords contains forbidden words or strings";
$messages['policyforbiddenwords'] = "Your password must not contain:";
$messages['forbiddenldapfields'] = "Your password contains values from your LDAP entry";
$messages['policyforbiddenldapfields'] = "Your password may not contain values from the following LDAP fields:";
$messages['sameascustompwd'] = "The new password is not unique across other password fields";
$messages['pwned'] = "Your new password has already been published on leaks, you should consider changing it on any other service that it is in use";
$messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site";
$messages['insufficiententropy'] = "Insufficient entropy for new password";
$messages['policy'] = "Your password must conform to the following constraints:";
$messages['policyminlength'] = "Minimum length:";
$messages['policymaxlength'] = "Maximum length:";
$messages['policyminlower'] = "Minimum number of lowercase characters:";
$messages['policyminupper'] = "Minimum number of uppercase characters:";
$messages['policymindigit'] = "Minimum number of digits:";
$messages['policyminspecial'] = "Minimum number of special characters:";
$messages['policycomplex'] = "Minimum number of different classes of characters:";
$messages['policyforbiddenchars'] = "Forbidden characters:";
$messages['policydiffminchars'] = "Minimum number of new unique characters:";
$messages['policynoreuse'] = "Your new password may not be the same as your old password";
$messages['policynoreusecustompwdfield'] = "Your new password may not be the same as your login password";
$messages['policydifflogin'] = "Your new password may not be the same as your login";
$messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site";
$messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end";
$messages['policyentropy'] = "Password strength";

?>
Loading

0 comments on commit e1d6a0e

Please sign in to comment.