Rework/improve on password encrypt/decrypt method

This commit is contained in:
Benjamin Renard 2019-06-20 19:55:31 +02:00
parent ab72dbb8a3
commit 5af71fdd79
3 changed files with 124 additions and 31 deletions

View file

@ -44,9 +44,9 @@
<term>isLoginPassword</term> <term>isLoginPassword</term>
<listitem> <listitem>
<simpara>Booléen définissant si le mot de passe est celui utilisé par l'utilisateur <simpara>Booléen définissant si le mot de passe est celui utilisé par l'utilisateur
pour se logguer à l'annuaire LDAP. Si c'est le cas, le mot de passe saisi dans le pour se logguer à l'annuaire LDAP. Si c'est le cas, pour vérifier si le mot de passe
formulaire sera utilisé pour une tentative de connexion de l'utilisateur afin de correspond avec un autre, une tentative de connexion de l'utilisateur à l'annuaire
déterminer si le mot de passe a été modifié ou non. (Par défaut : <literal>Vrai</literal>)</simpara> sera faite. (Par défaut : <literal>Faux</literal>)</simpara>
</listitem> </listitem>
</varlistentry> </varlistentry>

View file

@ -111,8 +111,10 @@ class LSattr_ldap_password extends LSattr_ldap {
* *
* @retval strinf The encode password * @retval strinf The encode password
*/ */
public function encodePassword($clearPassword) { public function encodePassword($clearPassword, $encode=null, $encode_function=null, $salt=null) {
if (is_null($encode))
$encode = $this -> getConfig('ldap_options.encode', 'md5crypt', 'string'); $encode = $this -> getConfig('ldap_options.encode', 'md5crypt', 'string');
if (is_null($encode_function))
$encode_function = $this -> getConfig('ldap_options.encode_function'); $encode_function = $this -> getConfig('ldap_options.encode_function');
if ($encode_function || $encode == 'function') { if ($encode_function || $encode == 'function') {
if ( (!$encode_function) || (!is_callable($encode_function)) ) { if ( (!$encode_function) || (!is_callable($encode_function)) ) {
@ -130,7 +132,9 @@ class LSattr_ldap_password extends LSattr_ldap {
return '{CRYPT}' . crypt($clearPassword,substr($clearPassword,0,2)); return '{CRYPT}' . crypt($clearPassword,substr($clearPassword,0,2));
} }
else { else {
return '{CRYPT}' . crypt($clearPassword,$this -> getSalt(2)); if (is_null($salt))
$salt = $this -> getSalt(2);
return '{CRYPT}' . crypt($clearPassword, $salt);
} }
break; break;
case 'ext_des': case 'ext_des':
@ -138,7 +142,9 @@ class LSattr_ldap_password extends LSattr_ldap {
LSerror :: addErrorCode('LSattr_ldap_password_01','ext_des'); LSerror :: addErrorCode('LSattr_ldap_password_01','ext_des');
} }
else { else {
return '{CRYPT}' . crypt( $clearPassword, '_' . $this -> getSalt(8) ); if (is_null($salt))
$salt = $this -> getSalt(8);
return '{CRYPT}' . crypt( $clearPassword, '_' . $salt );
} }
break; break;
case 'blowfish': case 'blowfish':
@ -146,7 +152,9 @@ class LSattr_ldap_password extends LSattr_ldap {
LSerror :: addErrorCode('LSattr_ldap_password_01','blowfish'); LSerror :: addErrorCode('LSattr_ldap_password_01','blowfish');
} }
else { else {
return '{CRYPT}' . crypt( $clearPassword, '$2a$12$' . $this -> getSalt(13) ); if (is_null($salt))
$salt = '$2y$12$' . $this -> getSalt(22);
return '{CRYPT}' . crypt( $clearPassword, $salt );
} }
break; break;
case 'sha': case 'sha':
@ -191,6 +199,7 @@ class LSattr_ldap_password extends LSattr_ldap {
} }
if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) { if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
mt_srand( (double) microtime() * 1000000 ); mt_srand( (double) microtime() * 1000000 );
if (is_null($salt))
$salt = mhash_keygen_s2k( $mhash_type, $clearPassword, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 ); $salt = mhash_keygen_s2k( $mhash_type, $clearPassword, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 );
return "{".strtoupper($encode)."}".base64_encode( mhash( $mhash_type, $clearPassword.$salt ).$salt ); return "{".strtoupper($encode)."}".base64_encode( mhash( $mhash_type, $clearPassword.$salt ).$salt );
} }
@ -201,8 +210,9 @@ class LSattr_ldap_password extends LSattr_ldap {
case 'smd5': case 'smd5':
if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) { if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
mt_srand( (double) microtime() * 1000000 ); mt_srand( (double) microtime() * 1000000 );
if (is_null($salt))
$salt = mhash_keygen_s2k( MHASH_MD5, $password_clear, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 ); $salt = mhash_keygen_s2k( MHASH_MD5, $password_clear, substr( pack( "h*", md5( mt_rand() ) ), 0, 8 ), 4 );
return "{SMD5}".base64_encode( mhash( MHASH_MD5, $password_clear.$salt ).$salt ); return "{SMD5}".base64_encode( mhash( MHASH_MD5, $clearPassword.$salt ).$salt );
} }
else { else {
LSerror :: addErrorCode('LSattr_ldap_password_01','smd5'); LSerror :: addErrorCode('LSattr_ldap_password_01','smd5');
@ -216,7 +226,9 @@ class LSattr_ldap_password extends LSattr_ldap {
LSerror :: addErrorCode('LSattr_ldap_password_01','md5crypt'); LSerror :: addErrorCode('LSattr_ldap_password_01','md5crypt');
} }
else { else {
return '{CRYPT}'.crypt($clearPassword,'$1$'.$this -> getSalt().'$'); if (is_null($salt))
$salt = $this -> getSalt();
return '{CRYPT}'.crypt($clearPassword,'$1$'.$salt.'$');
} }
break; break;
case 'clear': case 'clear':
@ -230,6 +242,102 @@ class LSattr_ldap_password extends LSattr_ldap {
return $clearPassword; return $clearPassword;
} }
function verify($clearPassword, $hashedPassword=null) {
// If $hashedPassword is not provided, use attribute values
if (is_null($hashedPassword))
$hashedPassword = $this -> attribute -> getValue();
// If $hashedPassword is array, iter to find valid password
if (is_array($hashedPassword)) {
foreach($hashedPassword as $pwd)
if ($this -> verify($clearPassword, $pwd))
return true;
return false;
}
// Verify $hashedPassword is a string
elseif (!is_string($hashedPassword))
return false;
// Custom verify function configured ? If yes, use it
$verifyFunction = $this -> getConfig('ldap_options.verify_function', null);
if (!is_null($verifyFunction) && is_callable($verifyFunction))
return call_user_func($verifyFunction, $clearPassword, $hashedPassword);
// Custom encode function configured ? If yes, use it
$encodeFunction = $this -> getConfig('ldap_options.encode_function', null);
if (!is_null($encodeFunction) && is_callable($encodeFunction))
return (strcasecmp(call_user_func_array($encodeFunction, array(&$this -> attribute -> ldapObject, $clearPassword)), $hashedPassword) == 0);
// Extract cipher
if (preg_match('/{([^}]+)}(.*)/',$hashedPassword,$matches)) {
$hashedPasswordData = $matches[2];
$cypher = strtolower($matches[1]);
} else {
$cypher = null;
}
// Verify password according on cypher
switch($cypher) {
# SSHA crypted passwords
case 'ssha':
case 'ssha256':
case 'ssha512':
case 'smd5':
$data = base64_decode($hashedPasswordData);
# Salt = last 4 bytes
$salt = substr($data, -4);
$new_hash = $this -> encodePassword($clearPassword, $cypher, null, $salt);
return (strcmp($hashedPassword,$new_hash) == 0);
break;
# Non-salted cyphers
case 'sha':
case 'sha256':
case 'sha512':
case 'md5':
$new_hash = $this -> encodePassword($clearPassword, $cypher);
return (strcasecmp($new_hash, $hashedPassword) == 0);
break;
# Crypt passwords
case 'crypt':
# Check if it's blowfish crypt
if (preg_match('/^\\$2+/',$hashedPasswordData)) {
list($dummy, $version, $rounds, $salt_hash) = explode('$',$hashedPasswordData);
$salt = '$'.$version.'$'.$rounds.'$'.substr($salt_hash, 0, 22);
$new_hash = $this -> encodePassword($clearPassword, 'blowfish', null, $salt);
return (strcasecmp($new_hash, $hashedPassword) == 0);
}
# Check if it's an md5crypt
elseif (strstr($hashedPasswordData,'$1$')) {
list($dummy,$type,$salt,$hash) = explode('$',$hashedPasswordData);
$new_hash = $this -> encodePassword($clearPassword, 'md5crypt', null, $salt);
return (strcasecmp($new_hash, $hashedPassword) == 0);
}
# Check if it's ext_des crypt
elseif (strstr($hashedPasswordData,'_')) {
return (crypt($clearPassword,$hashedPasswordData) == $hashedPasswordData);
}
# Password is plain crypt
else {
return (crypt($clearPassword,$hashedPasswordData) == $hashedPasswordData);
}
break;
# No crypt is given
default:
# Assume is a plaintext password
return (strcasecmp($clearPassword, $hashedPassword) == 0);
}
// It's supposed to never append, but just in case, return false
return false;
}
/** /**
* Return salt (random string) * Return salt (random string)
* *

View file

@ -200,22 +200,7 @@ class LSformElement_password extends LSformElement {
return LSsession :: checkUserPwd($this -> attr_html -> attribute -> ldapObject, $pwd); return LSsession :: checkUserPwd($this -> attr_html -> attribute -> ldapObject, $pwd);
} }
else { else {
$hash = $this -> attr_html -> attribute -> ldap -> encodePassword($pwd); return $this -> attr_html -> attribute -> ldap -> verify($pwd);
$find=false;
if (is_array($this -> attr_html -> attribute -> data)) {
$data = $this -> attr_html -> attribute -> data;
}
elseif (!is_array($this -> attr_html -> attribute -> data) && !empty($this -> attr_html -> attribute -> data)) {
$data = array($this -> attr_html -> attribute -> data);
}
else {
return $find;
}
foreach($data as $val) {
if ($hash == $val)
$find=true;
}
return $find;
} }
} }
@ -351,7 +336,7 @@ class LSformElement_password extends LSformElement {
} }
public function isLoginPassword() { public function isLoginPassword() {
return $this -> getParam('html_options.isLoginPassword', true); return $this -> getParam('html_options.isLoginPassword', false, 'bool');
} }
} }