2006-05-17 08:00:12 +02:00
< ? php
2008-03-15 18:27:36 +01:00
/**
* eGroupWare API - Authentication baseclass
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ link http :// www . egroupware . org
2010-01-28 05:22:37 +01:00
* @ author Ralf Becker < ralfbecker @ outdoor - training . de >
2008-03-15 18:27:36 +01:00
* @ author Miles Lott < milos @ groupwhere . org >
2008-05-31 08:25:04 +02:00
* @ copyright 2004 by Miles Lott < milos @ groupwhere . org >
2008-03-15 18:27:36 +01:00
* @ license http :// opensource . org / licenses / lgpl - license . php LGPL - GNU Lesser General Public License
* @ package api
* @ subpackage authentication
* @ version $Id $
*/
2004-01-18 22:12:53 +01:00
2008-07-16 11:29:13 +02:00
// allow to set an application depending authentication type (eg. for syncml, groupdav, ...)
if ( isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type_' . $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ]]) &&
$GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type_' . $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ]])
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type' ] = $GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type_' . $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ]];
}
2008-03-15 18:27:36 +01:00
if ( empty ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type' ]))
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type' ] = 'sql' ;
}
2008-07-16 11:29:13 +02:00
//error_log('using auth_type='.$GLOBALS['egw_info']['server']['auth_type'].', currentapp='.$GLOBALS['egw_info']['flags']['currentapp']);
2004-01-18 22:12:53 +01:00
2008-03-15 18:27:36 +01:00
/**
* eGroupWare API - Authentication baseclass , password auth and crypt functions
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* Many functions based on code from Frank Thomas < frank @ thomas - alfeld . de >
* which can be seen at http :// www . thomas - alfeld . de / frank /
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* Other functions from class . common . inc . php originally from phpGroupWare
*/
2010-01-28 05:22:37 +01:00
class auth
2008-03-15 18:27:36 +01:00
{
static $error ;
2010-05-13 12:39:48 +02:00
2010-01-28 05:22:37 +01:00
/**
* Holds instance of backend
2010-05-13 12:39:48 +02:00
*
2010-01-28 05:22:37 +01:00
* @ var auth_backend
*/
private $backend ;
2010-05-13 12:39:48 +02:00
2010-01-28 05:22:37 +01:00
function __construct ()
{
$backend_class = 'auth_' . $GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type' ];
2010-05-13 12:39:48 +02:00
2010-01-28 05:22:37 +01:00
$this -> backend = new $backend_class ;
2010-05-13 12:39:48 +02:00
2010-01-28 05:22:37 +01:00
if ( ! is_a ( $this -> backend , 'auth_backend' ))
{
throw new egw_exception_assertion_failed ( " Auth backend class $backend_class is NO auth_backend! " );
}
}
2004-01-18 22:12:53 +01:00
2010-09-22 12:16:32 +02:00
/**
* check_password_age
2011-05-04 09:50:03 +02:00
* check if users are supposed to change their password every x sdays , then check if password is of old age
2010-09-22 12:16:32 +02:00
* or the devil - admin reset the users password and forced the user to change his password on next login .
2011-05-04 09:50:03 +02:00
*
2010-09-22 12:16:32 +02:00
* @ param string $app to know where you are / or where you want to go
* @ param string $class to know where you are / or where you want to go
* @ param string $method to know where you are / or where you want to go
* @ return boolean true if check determined , that you passed the test , otherwise void , as we get redirected
*/
static function check_password_age ( $app = '' , $class = '' , $method = '' )
{
2010-10-21 16:02:36 +02:00
// dont check anything for anonymous sessions/ users that are flagged as anonymous
if ( is_object ( $GLOBALS [ 'egw' ] -> session ) && $GLOBALS [ 'egw' ] -> session -> session_flags == 'A' ) return true ;
static $UserKnowsAboutPwdChange ;
2011-05-19 12:47:27 +02:00
if ( is_null ( $UserKnowsAboutPwdChange )) $UserKnowsAboutPwdChange =& egw_cache :: getSession ( 'phpgwapi' , 'auth_UserKnowsAboutPwdChange' );
2010-10-21 16:02:36 +02:00
// some statics to make information and timecalculation a) more readable in conditions b) persistent per request
static $alpwchange_val ;
static $passwordAgeBorder ;
static $daysLeftUntilChangeReq ;
// current style name for account last password change timestamp
$alpwchange = 'account_lastpwd_change' ;
// some debug output and develop options to move the horizons and warn levels around
2010-10-28 13:03:05 +02:00
//$GLOBALS['egw_info']['server']['change_pwd_every_x_days'] =35;
//$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change']=5;
2010-09-22 12:16:32 +02:00
//echo egw_time::to('now','ts').'<br>';
2010-10-21 16:02:36 +02:00
//echo "User changed password at:".egw_time::to($GLOBALS['egw_info']['user'][$alpwchange]).'<br>';
//echo "User password is ".((egw_time::to('now','ts')-$GLOBALS['egw_info']['user'][$alpwchange])/86400)." days old<br>";
//echo "Users must change passwords every ".$GLOBALS['egw_info']['server']['change_pwd_every_x_days'].' days ('.($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400).') seconds.<br>';
2010-09-22 12:16:32 +02:00
//echo egw_time::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400).'<br>';
2010-10-21 16:02:36 +02:00
// if neither timestamp isset return true, nothing to do (exept this means the password is too old)
2011-05-04 09:50:03 +02:00
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lastpasswd_change' ]) &&
2010-09-22 17:21:04 +02:00
! isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ $alpwchange ]) &&
empty ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'change_pwd_every_x_days' ])
) return true ;
2010-09-22 13:41:58 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lastpasswd_change' ] && ! $GLOBALS [ 'egw_info' ][ 'user' ][ $alpwchange ])
{
2011-03-16 15:22:24 +01:00
// use old style names, as the current one seems not to be set.
2010-10-21 16:02:36 +02:00
$alpwchange = 'account_lastpasswd_change' ;
}
// initalize statics - better readability of conditions
2011-03-16 15:22:24 +01:00
if ( is_null ( $alpwchange_val ))
{
$backend_class = 'auth_' . $GLOBALS [ 'egw_info' ][ 'server' ][ 'auth_type' ];
$backend = new $backend_class ;
// this may change behavior, as it should detect forced PasswordChanges from your Authentication System too.
// on the other side, if your auth system does not require an forcedPasswordChange, you will not be asked.
if ( method_exists ( $backend , 'getLastPwdChange' )) $alpwchange_val = $backend -> getLastPwdChange ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lid' ]);
if ( is_null ( $alpwchange_val ) || $alpwchange_val === false ) $alpwchange_val = $GLOBALS [ 'egw_info' ][ 'user' ][ $alpwchange ];
}
2011-05-04 09:50:03 +02:00
if ( is_null ( $passwordAgeBorder ) && $GLOBALS [ 'egw_info' ][ 'server' ][ 'change_pwd_every_x_days' ])
2010-10-21 16:02:36 +02:00
{
$passwordAgeBorder = ( egw_time :: to ( 'now' , 'ts' ) - ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'change_pwd_every_x_days' ] * 86400 ));
2010-09-22 13:41:58 +02:00
}
2010-10-21 16:02:36 +02:00
if ( is_null ( $daysLeftUntilChangeReq ) && $GLOBALS [ 'egw_info' ][ 'server' ][ 'warn_about_upcoming_pwd_change' ])
{
// maxage - passwordage = days left until change is required
$daysLeftUntilChangeReq = ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'change_pwd_every_x_days' ] - (( egw_time :: to ( 'now' , 'ts' ) - $alpwchange_val ) / 86400 ));
}
//echo "Warn about the upcomming change ".$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'].' days before that time is reached<br>';
//$result = $GLOBALS['egw_info']['server']['change_pwd_every_x_days'] - $daysLeftUntilChangeReq;
//echo $GLOBALS['egw_info']['server']['change_pwd_every_x_days'].' - '.$daysLeftUntilChangeReq.'='. $result.'<br>';
2010-09-22 12:16:32 +02:00
if ( ! ( $app == 'preferences' && $class == 'uipassword' && $method == 'change' ) &&
2010-10-21 16:02:36 +02:00
(
( $GLOBALS [ 'egw_info' ][ 'server' ][ 'change_pwd_every_x_days' ] &&
2010-09-22 12:16:32 +02:00
( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'preferences' ] || $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'password' ]) &&
2010-10-21 16:02:36 +02:00
(
( $passwordAgeBorder > $alpwchange_val ) ||
(
2011-05-04 09:50:03 +02:00
$GLOBALS [ 'egw_info' ][ 'server' ][ 'warn_about_upcoming_pwd_change' ] &&
$GLOBALS [ 'egw_info' ][ 'server' ][ 'warn_about_upcoming_pwd_change' ] > $daysLeftUntilChangeReq
2010-10-21 16:02:36 +02:00
)
)
) || $alpwchange_val == 0
2011-05-04 09:50:03 +02:00
)
2010-09-22 12:16:32 +02:00
)
{
2010-09-24 10:20:52 +02:00
if ( $GLOBALS [ 'egw' ] -> acl -> check ( 'nopasswordchange' , 1 , 'preferences' )) return true ; // user has no rights to change password
2010-10-21 16:02:36 +02:00
if ( $UserKnowsAboutPwdChange === true && ! ( $passwordAgeBorder > $alpwchange_val || $alpwchange_val == 0 )) return true ; // user has already been informed about the upcomming password expiration
2011-05-04 09:50:03 +02:00
if ( $alpwchange_val == 0 )
2010-09-22 12:16:32 +02:00
{
$message = lang ( 'an admin required that you must change your password upon login.' );
}
2010-10-21 16:02:36 +02:00
elseif (( $passwordAgeBorder < $alpwchange_val ) ||
(
$GLOBALS [ 'egw_info' ][ 'server' ][ 'warn_about_upcoming_pwd_change' ] &&
2010-10-28 13:03:05 +02:00
$GLOBALS [ 'egw_info' ][ 'server' ][ 'warn_about_upcoming_pwd_change' ] > $daysLeftUntilChangeReq &&
$daysLeftUntilChangeReq > 0
2010-10-21 16:02:36 +02:00
)
)
{
$UserKnowsAboutPwdChange = true ;
$message = lang ( 'your password is about to expire in %1 days, you may change your password now' , round ( $daysLeftUntilChangeReq ));
}
elseif ( $passwordAgeBorder > $alpwchange_val && $alpwchange_val > 0 )
2010-09-22 12:16:32 +02:00
{
2010-10-21 16:02:36 +02:00
error_log ( __METHOD__ . ' Password of ' . $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lid' ] . ' (' . $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_fullname' ] . ') is of old age.' . array2string ( array (
'ts' => $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lastpwd_change' ],
'date' => egw_time :: to ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lastpwd_change' ]))));
2010-09-22 12:16:32 +02:00
$message = lang ( 'it has been more then %1 days since you changed your password' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'change_pwd_every_x_days' ]);
}
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'password' ]) egw :: redirect_link ( '/preferences/password.php' , array ( 'message' => $message ));
egw :: redirect_link ( '/index.php' , array ( 'menuaction' => 'preferences.uipassword.change' , 'message' => $message ));
}
return true ;
}
2011-03-16 15:22:24 +01:00
/**
* fetch the last pwd change for the user
*
* @ param string $username username of account to authenticate
2011-05-04 09:50:03 +02:00
* @ return mixed false or shadowlastchange * 24 * 3600
2011-03-16 15:22:24 +01:00
*/
function getLastPwdChange ( $username )
{
if ( method_exists ( $this -> backend , 'getLastPwdChange' )) return $this -> backend -> getLastPwdChange ( $username );
return false ;
}
/**
* changes account_lastpwd_change in ldap datababse
*
* @ param int $account_id account id of user whose passwd should be changed
* @ param string $passwd must be cleartext , usually not used , but may be used to authenticate as user to do the change -> ldap
* @ param int $lastpwdchange must be a unixtimestamp
* @ return boolean true if account_lastpwd_change successful changed , false otherwise
*/
function setLastPwdChange ( $account_id = 0 , $passwd = NULL , $lastpwdchange = NULL )
{
if ( method_exists ( $this -> backend , 'setLastPwdChange' )) return $this -> backend -> setLastPwdChange ( $account_id , $passwd , $lastpwdchange );
return false ;
}
2010-01-28 05:22:37 +01:00
/**
* password authentication against password stored in sql datababse
*
* @ param string $username username of account to authenticate
* @ param string $passwd corresponding password
* @ param string $passwd_type = 'text' 'text' for cleartext passwords ( default )
* @ return boolean true if successful authenticated , false otherwise
*/
function authenticate ( $username , $passwd , $passwd_type = 'text' )
{
return $this -> backend -> authenticate ( $username , $passwd , $passwd_type );
}
2010-05-13 12:39:48 +02:00
2010-01-28 05:22:37 +01:00
/**
* changes password in sql datababse
*
* @ param string $old_passwd must be cleartext
* @ param string $new_passwd must be cleartext
* @ param int $account_id account id of user whose passwd should be changed
* @ return boolean true if password successful changed , false otherwise
*/
function change_password ( $old_passwd , $new_passwd , $account_id = 0 )
{
return $this -> backend -> change_password ( $old_passwd , $new_passwd , $account_id );
}
2010-05-13 12:39:48 +02:00
2008-03-15 18:27:36 +01:00
/**
* return a random string of size $size
*
* @ param $size int - size of random string to return
*/
static function randomstring ( $size )
2004-01-18 22:12:53 +01:00
{
2008-03-15 18:27:36 +01:00
$s = '' ;
$random_char = array (
'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' ,
'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' ,
'w' , 'x' , 'y' , 'z' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' ,
'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z'
);
2004-01-18 22:12:53 +01:00
2008-03-15 18:27:36 +01:00
for ( $i = 0 ; $i < $size ; $i ++ )
2004-01-18 22:12:53 +01:00
{
2008-03-15 18:27:36 +01:00
$s .= $random_char [ mt_rand ( 1 , 61 )];
}
return $s ;
}
2004-01-18 22:12:53 +01:00
2008-03-15 18:27:36 +01:00
/**
* encrypt password
*
* uses the encryption type set in setup and calls the appropriate encryption functions
*
* @ param $password password to encrypt
*/
2008-03-25 18:05:38 +01:00
static function encrypt_password ( $password , $sql = False )
2008-03-15 18:27:36 +01:00
{
if ( $sql )
{
return self :: encrypt_sql ( $password );
2004-01-18 22:12:53 +01:00
}
2008-03-15 18:27:36 +01:00
return self :: encrypt_ldap ( $password );
}
2004-01-18 22:12:53 +01:00
2008-03-15 18:27:36 +01:00
/**
* compares an encrypted password
*
* encryption type set in setup and calls the appropriate encryption functions
*
* @ param $cleartext cleartext password
* @ param $encrypted encrypted password , can have a { hash } prefix , which overrides $type
* @ param $type type of encryption
* @ param $username used as optional key of encryption for md5_hmac
*/
static function compare_password ( $cleartext , $encrypted , $type , $username = '' )
{
// allow to specify the hash type to prefix the hash, to easy migrate passwords from ldap
$saved_enc = $encrypted ;
if ( preg_match ( '/^\\{([a-z_5]+)\\}(.+)$/i' , $encrypted , $matches ))
2004-01-18 22:12:53 +01:00
{
2008-03-15 18:27:36 +01:00
$type = strtolower ( $matches [ 1 ]);
$encrypted = $matches [ 2 ];
2008-05-31 08:25:04 +02:00
2008-03-15 18:27:36 +01:00
switch ( $type ) // some hashs are specially "packed" in ldap
2004-01-18 22:12:53 +01:00
{
2008-03-15 18:27:36 +01:00
case 'md5' :
$encrypted = implode ( '' , unpack ( 'H*' , base64_decode ( $encrypted )));
break ;
case 'plain' :
case 'crypt' :
// nothing to do
break ;
default :
$encrypted = $saved_enc ;
// ToDo: the others ...
2004-01-18 22:12:53 +01:00
}
}
2008-03-15 18:27:36 +01:00
switch ( $type )
{
2008-05-31 08:25:04 +02:00
case 'plain' :
2008-03-15 18:27:36 +01:00
if ( strcmp ( $cleartext , $encrypted ) == 0 )
{
return True ;
}
return False ;
case 'smd5' :
return self :: smd5_compare ( $cleartext , $encrypted );
case 'sha' :
return self :: sha_compare ( $cleartext , $encrypted );
case 'ssha' :
return self :: ssha_compare ( $cleartext , $encrypted );
case 'crypt' :
case 'md5_crypt' :
case 'blowfish_crypt' :
case 'ext_crypt' :
return self :: crypt_compare ( $cleartext , $encrypted , $type );
case 'md5_hmac' :
return self :: md5_hmac_compare ( $cleartext , $encrypted , $username );
case 'md5' :
default :
return strcmp ( md5 ( $cleartext ), $encrypted ) == 0 ? true : false ;
}
}
2004-01-18 22:12:53 +01:00
2008-03-15 18:27:36 +01:00
/**
* encrypt password for ldap
*
* uses the encryption type set in setup and calls the appropriate encryption functions
*
* @ param $password password to encrypt
2011-05-04 11:44:39 +02:00
* @ param $type = null default to $GLOBALS [ 'egw_info' ][ 'server' ][ 'ldap_encryption_type' ]
* @ return string
2008-03-15 18:27:36 +01:00
*/
2011-05-04 11:44:39 +02:00
static function encrypt_ldap ( $password , $type = null )
2008-03-15 18:27:36 +01:00
{
2011-05-04 11:44:39 +02:00
if ( is_null ( $type )) $type = $GLOBALS [ 'egw_info' ][ 'server' ][ 'ldap_encryption_type' ];
2008-03-15 18:27:36 +01:00
$salt = '' ;
2011-05-04 11:44:39 +02:00
switch ( strtolower ( $type ))
2005-05-10 21:00:55 +02:00
{
2008-03-15 18:27:36 +01:00
default : // eg. setup >> config never saved
case 'des' :
$salt = self :: randomstring ( 2 );
$_password = crypt ( $password , $salt );
$e_password = '{crypt}' . $_password ;
break ;
2008-05-31 08:25:04 +02:00
case 'blowfish_crypt' :
if ( @ defined ( 'CRYPT_BLOWFISH' ) && CRYPT_BLOWFISH == 1 )
{
$salt = '$2$' . self :: randomstring ( 13 );
$e_password = '{crypt}' . crypt ( $password , $salt );
break ;
}
self :: $error = 'no blowfish crypt' ;
break ;
case 'md5_crypt' :
if ( @ defined ( 'CRYPT_MD5' ) && CRYPT_MD5 == 1 )
{
$salt = '$1$' . self :: randomstring ( 9 );
$e_password = '{crypt}' . crypt ( $password , $salt );
break ;
}
self :: $error = 'no md5 crypt' ;
break ;
case 'ext_crypt' :
if ( @ defined ( 'CRYPT_EXT_DES' ) && CRYPT_EXT_DES == 1 )
{
$salt = self :: randomstring ( 9 );
$e_password = '{crypt}' . crypt ( $password , $salt );
break ;
}
self :: $error = 'no ext crypt' ;
break ;
2008-03-15 18:27:36 +01:00
case 'md5' :
/* New method taken from the openldap - software list as recommended by
* Kervin L . Pierre " <kervin@blueprint-tech.com>
*/
$e_password = '{md5}' . base64_encode ( pack ( " H* " , md5 ( $password )));
break ;
case 'smd5' :
2011-05-04 11:44:39 +02:00
$salt = self :: randomstring ( 16 );
2010-05-13 12:39:48 +02:00
$hash = md5 ( $password . $salt , true );
2008-03-15 18:27:36 +01:00
$e_password = '{SMD5}' . base64_encode ( $hash . $salt );
break ;
case 'sha' :
2010-05-13 12:39:48 +02:00
$e_password = '{SHA}' . base64_encode ( sha1 ( $password , true ));
2008-03-15 18:27:36 +01:00
break ;
case 'ssha' :
2011-05-04 11:44:39 +02:00
$salt = self :: randomstring ( 16 );
2010-05-13 12:39:48 +02:00
$hash = sha1 ( $password . $salt , true );
2008-03-15 18:27:36 +01:00
$e_password = '{SSHA}' . base64_encode ( $hash . $salt );
break ;
case 'plain' :
// if plain no type is prepended
$e_password = $password ;
break ;
2005-05-10 21:00:55 +02:00
}
2008-03-15 18:27:36 +01:00
return $e_password ;
}
2008-05-31 08:25:04 +02:00
2008-03-15 18:27:36 +01:00
/**
* Create a password for storage in the accounts table
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ param string $password
* @ return string hash
*/
static function encrypt_sql ( $password )
{
/* Grab configured type, or default to md5() (old method) */
$type = @ $GLOBALS [ 'egw_info' ][ 'server' ][ 'sql_encryption_type' ]
? strtolower ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'sql_encryption_type' ])
: 'md5' ;
switch ( $type )
2004-01-18 22:12:53 +01:00
{
2008-03-15 18:27:36 +01:00
case 'plain' :
// since md5 is the default, type plain must be prepended, for eGroupware to understand
return '{PLAIN}' . $password ;
case 'crypt' :
if ( @ defined ( 'CRYPT_STD_DES' ) && CRYPT_STD_DES == 1 )
{
$salt = self :: randomstring ( 2 );
return crypt ( $password , $salt );
}
self :: $error = 'no std crypt' ;
break ;
case 'blowfish_crypt' :
if ( @ defined ( 'CRYPT_BLOWFISH' ) && CRYPT_BLOWFISH == 1 )
{
$salt = '$2$' . self :: randomstring ( 13 );
return crypt ( $password , $salt );
}
self :: $error = 'no blowfish crypt' ;
break ;
case 'md5_crypt' :
if ( @ defined ( 'CRYPT_MD5' ) && CRYPT_MD5 == 1 )
{
$salt = '$1$' . self :: randomstring ( 9 );
return crypt ( $password , $salt );
}
self :: $error = 'no md5 crypt' ;
break ;
case 'ext_crypt' :
if ( @ defined ( 'CRYPT_EXT_DES' ) && CRYPT_EXT_DES == 1 )
{
$salt = self :: randomstring ( 9 );
return crypt ( $password , $salt );
}
self :: $error = 'no ext crypt' ;
break ;
case 'smd5' :
2011-05-04 11:44:39 +02:00
$salt = self :: randomstring ( 16 );
2010-05-13 12:39:48 +02:00
$hash = md5 ( $password . $salt , true );
2008-03-15 18:27:36 +01:00
return '{SMD5}' . base64_encode ( $hash . $salt );
case 'sha' :
2010-05-13 12:39:48 +02:00
return '{SHA}' . base64_encode ( sha1 ( $password , true ));
2008-03-15 18:27:36 +01:00
case 'ssha' :
2011-05-04 11:44:39 +02:00
$salt = self :: randomstring ( 16 );
2010-05-13 12:39:48 +02:00
$hash = sha1 ( $password . $salt , true );
2008-03-15 18:27:36 +01:00
return '{SSHA}' . base64_encode ( $hash . $salt );
case 'md5' :
default :
/* This is the old standard for password storage in SQL */
return md5 ( $password );
2004-01-18 22:12:53 +01:00
}
2008-03-15 18:27:36 +01:00
if ( ! self :: $error )
2006-06-07 01:42:36 +02:00
{
2008-03-15 18:27:36 +01:00
self :: $error = 'no valid encryption available' ;
2006-06-07 01:42:36 +02:00
}
2008-03-15 18:27:36 +01:00
return False ;
}
2004-01-18 22:12:53 +01:00
2008-03-15 18:27:36 +01:00
/**
* Checks if a given password is " safe "
*
* @ param string $login
* @ abstract atm a simple check in length , #digits, #uppercase and #lowercase
* could be made more safe using e . g . pecl library cracklib
* but as pecl dosn 't run on any platform and isn' t GPL ' d
* i haven ' t implemented it yet
* Windows compatible check is : 7 char lenth , 1 Up , 1 Low , 1 Num and 1 Special
* @ author cornelius weiss < egw at von - und - zu - weiss . de >
* @ return mixed false if password is considered " safe " or a string $message if " unsafe "
*/
2010-09-22 12:16:32 +02:00
static function crackcheck ( $passwd , $reqstrength = 5 )
2008-03-15 18:27:36 +01:00
{
if ( ! preg_match ( '/.{' . ( $noc = 7 ) . ',}/' , $passwd ))
2004-01-18 22:12:53 +01:00
{
2010-09-22 12:16:32 +02:00
$message [] = lang ( 'Password must have at least %1 characters' , $noc ) . '<br>' ;
}
else
{
$strength ++ ;
2004-01-26 04:01:54 +01:00
}
2008-03-15 18:27:36 +01:00
if ( ! preg_match ( '/(.*\d.*){' . ( $non = 1 ) . ',}/' , $passwd ))
2006-03-13 22:56:28 +01:00
{
2010-09-22 12:16:32 +02:00
$message [] = lang ( 'Password must contain at least %1 numbers' , $non ) . '<br>' ;
}
else
{
$strength ++ ;
2006-03-13 22:56:28 +01:00
}
2008-03-15 18:27:36 +01:00
if ( ! preg_match ( '/(.*[[:upper:]].*){' . ( $nou = 1 ) . ',}/' , $passwd ))
{
2010-09-22 12:16:32 +02:00
$message [] = lang ( 'Password must contain at least %1 uppercase letters' , $nou ) . '<br>' ;
}
else
{
$strength ++ ;
2008-03-15 18:27:36 +01:00
}
if ( ! preg_match ( '/(.*[[:lower:]].*){' . ( $nol = 1 ) . ',}/' , $passwd ))
{
2010-09-22 12:16:32 +02:00
$message [] = lang ( 'Password must contain at least %1 lowercase letters' , $nol ) . '<br>' ;
}
else
{
$strength ++ ;
2008-03-15 18:27:36 +01:00
}
if ( ! preg_match ( '/(.*[\\!"#$%&\'()*+,-.\/:;<=>?@\[\]\^_ {|}~`].*){' . ( $nol = 1 ) . ',}/' , $passwd ))
2004-01-26 04:01:54 +01:00
{
2010-09-22 12:16:32 +02:00
$message [] = lang ( 'Password must contain at least %1 special characters' , $nol ) . '<br>' ;
}
else
{
$strength ++ ;
}
if ( count ( $message ) > 0 && $reqstrength > $strength )
{
$outmessage = lang ( 'Your Password does not meet the required strength.<br> You must meet %1 criteria. You met only %2 criteria. <br>Your Password failed the following criteria:' , $reqstrength , $strength );
$outmessage .= '<br>' . implode ( ' ' , $message );
}
else
{
$outmessage = false ;
2008-03-15 18:27:36 +01:00
}
2010-09-22 12:16:32 +02:00
return $outmessage ? $outmessage : false ;
2008-03-15 18:27:36 +01:00
}
2004-02-05 03:14:31 +01:00
2008-03-15 18:27:36 +01:00
/**
* compare SMD5 - encrypted passwords for authentication
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ param string $form_val user input value for comparison
* @ param string $db_val stored value ( from database )
* @ return boolean True on successful comparison
*/
static function smd5_compare ( $form_val , $db_val )
{
/* Start with the first char after {SMD5} */
$hash = base64_decode ( substr ( $db_val , 6 ));
2004-02-05 03:01:39 +01:00
2008-03-15 18:27:36 +01:00
/* SMD5 hashes are 16 bytes long */
2011-05-04 09:50:03 +02:00
$orig_hash = cut_bytes ( $hash , 0 , 16 ); // binary string need to use cut_bytes, not mb_substr(,,'utf-8')!
$salt = cut_bytes ( $hash , 16 );
2004-01-26 04:01:54 +01:00
2010-05-13 12:39:48 +02:00
$new_hash = md5 ( $form_val . $salt , true );
2008-03-15 18:27:36 +01:00
//echo '<br> DB: ' . base64_encode($orig_hash) . '<br>FORM: ' . base64_encode($new_hash);
2004-01-20 22:31:33 +01:00
2008-05-31 08:25:04 +02:00
return strcmp ( $orig_hash , $new_hash ) == 0 ;
2008-03-15 18:27:36 +01:00
}
/**
* compare SHA - encrypted passwords for authentication
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ param string $form_val user input value for comparison
* @ param string $db_val stored value ( from database )
* @ return boolean True on successful comparison
*/
static function sha_compare ( $form_val , $db_val )
{
/* Start with the first char after {SHA} */
$hash = base64_decode ( substr ( $db_val , 5 ));
2010-05-13 12:39:48 +02:00
$new_hash = sha1 ( $form_val , true );
2008-03-15 18:27:36 +01:00
//echo '<br> DB: ' . base64_encode($orig_hash) . '<br>FORM: ' . base64_encode($new_hash);
2004-01-20 22:31:33 +01:00
2008-05-31 08:25:04 +02:00
return strcmp ( $hash , $new_hash ) == 0 ;
2008-03-15 18:27:36 +01:00
}
2004-01-20 22:31:33 +01:00
2008-03-15 18:27:36 +01:00
/**
* compare SSHA - encrypted passwords for authentication
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ param string $form_val user input value for comparison
* @ param string $db_val stored value ( from database )
* @ return boolean True on successful comparison
*/
static function ssha_compare ( $form_val , $db_val )
{
/* Start with the first char after {SSHA} */
$hash = base64_decode ( substr ( $db_val , 6 ));
2004-01-20 22:31:33 +01:00
2008-03-15 18:27:36 +01:00
// SHA-1 hashes are 160 bits long
2011-05-04 09:50:03 +02:00
$orig_hash = cut_bytes ( $hash , 0 , 20 ); // binary string need to use cut_bytes, not mb_substr(,,'utf-8')!
$salt = cut_bytes ( $hash , 20 );
2010-05-13 12:39:48 +02:00
$new_hash = sha1 ( $form_val . $salt , true );
2004-01-20 22:31:33 +01:00
2011-05-04 09:50:03 +02:00
//error_log(__METHOD__."('$form_val', '$db_val') hash='$hash', orig_hash='$orig_hash', salt='$salt', new_hash='$new_hash' returning ".array2string(strcmp($orig_hash,$new_hash) == 0));
2008-05-31 08:25:04 +02:00
return strcmp ( $orig_hash , $new_hash ) == 0 ;
2008-03-15 18:27:36 +01:00
}
2004-01-20 22:31:33 +01:00
2008-03-15 18:27:36 +01:00
/**
* compare crypted passwords for authentication whether des , ext_des , md5 , or blowfish crypt
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ param string $form_val user input value for comparison
* @ param string $db_val stored value ( from database )
* @ param string $type crypt () type
* @ return boolean True on successful comparison
*/
static function crypt_compare ( $form_val , $db_val , $type )
{
$saltlen = array (
'blowfish_crypt' => 16 ,
'md5_crypt' => 12 ,
'ext_crypt' => 9 ,
'crypt' => 2
);
2004-01-20 22:31:33 +01:00
2008-03-15 18:27:36 +01:00
// PHP's crypt(): salt + hash
// notice: "The encryption type is triggered by the salt argument."
$salt = substr ( $db_val , 0 , ( int ) $saltlen [ $type ]);
$new_hash = crypt ( $form_val , $salt );
2008-05-31 08:25:04 +02:00
return strcmp ( $db_val , $new_hash ) == 0 ;
2008-03-15 18:27:36 +01:00
}
2005-05-10 21:00:55 +02:00
2008-03-15 18:27:36 +01:00
/**
* compare md5_hmac - encrypted passwords for authentication ( see RFC2104 )
2008-05-31 08:25:04 +02:00
*
2008-03-15 18:27:36 +01:00
* @ param string $form_val user input value for comparison
* @ param string $db_val stored value ( from database )
* @ param string $key key for md5_hmac - encryption ( username for imported smf users )
* @ return boolean True on successful comparison
*/
static function md5_hmac_compare ( $form_val , $db_val , $key )
{
$key = str_pad ( strlen ( $key ) <= 64 ? $key : pack ( 'H*' , md5 ( $key )), 64 , chr ( 0x00 ));
$md5_hmac = md5 (( $key ^ str_repeat ( chr ( 0x5c ), 64 )) . pack ( 'H*' , md5 (( $key ^ str_repeat ( chr ( 0x36 ), 64 )) . $form_val )));
2008-05-31 08:25:04 +02:00
return strcmp ( $md5_hmac , $db_val ) == 0 ;
2004-01-18 22:12:53 +01:00
}
2008-03-15 18:27:36 +01:00
}
2010-01-28 05:22:37 +01:00
/**
* Interface for authentication backend
*/
interface auth_backend
{
/**
* password authentication against password stored in sql datababse
*
* @ param string $username username of account to authenticate
* @ param string $passwd corresponding password
* @ param string $passwd_type = 'text' 'text' for cleartext passwords ( default )
* @ return boolean true if successful authenticated , false otherwise
*/
function authenticate ( $username , $passwd , $passwd_type = 'text' );
2010-05-13 12:39:48 +02:00
2010-01-28 05:22:37 +01:00
/**
* changes password in sql datababse
*
* @ param string $old_passwd must be cleartext
* @ param string $new_passwd must be cleartext
* @ param int $account_id account id of user whose passwd should be changed
* @ return boolean true if password successful changed , false otherwise
*/
function change_password ( $old_passwd , $new_passwd , $account_id = 0 );
}