TYPO3 API  SVNRelease
class.tx_saltedpasswords_sv1.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) Marcus Krause (marcus#exp2009@t3sec.info)
00006 *  (c) Steffen Ritter (info@rs-websystems.de)
00007 *  All rights reserved
00008 *
00009 *  This script is part of the TYPO3 project. The TYPO3 project is
00010 *  free software; you can redistribute it and/or modify
00011 *  it under the terms of the GNU General Public License as published by
00012 *  the Free Software Foundation; either version 2 of the License, or
00013 *  (at your option) any later version.
00014 *
00015 *  The GNU General Public License can be found at
00016 *  http://www.gnu.org/copyleft/gpl.html.
00017 *  A copy is found in the textfile GPL.txt and important notices to the license
00018 *  from the author is found in LICENSE.txt distributed with these scripts.
00019 *
00020 *
00021 *  This script is distributed in the hope that it will be useful,
00022 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024 *  GNU General Public License for more details.
00025 *
00026 *  This copyright notice MUST APPEAR in all copies of the script!
00027 ***************************************************************/
00028 /**
00029  * Contains authentication service class for salted hashed passwords.
00030  *
00031  * $Id: class.tx_saltedpasswords_sv1.php 9758 2010-12-05 11:25:36Z stephenking $
00032  */
00033 
00034 
00035 /**
00036  * Class implements salted-password hashes authentication service.
00037  *
00038  * @author  Marcus Krause <marcus#exp2009@t3sec.info>
00039  * @author  Steffen Ritter <info@rs-websystems.de>
00040  *
00041  * @since   2009-06-14
00042  * @package TYPO3
00043  * @subpackage  tx_saltedpasswords
00044  */
00045 class tx_saltedpasswords_sv1 extends tx_sv_authbase {
00046     /**
00047      * Keeps class name.
00048      *
00049      * @var string
00050      */
00051     public $prefixId = 'tx_saltedpasswords_sv1';
00052 
00053     /**
00054      * Keeps path to this script relative to the extension directory.
00055      *
00056      * @var string
00057      */
00058     public $scriptRelPath = 'sv1/class.tx_saltedpasswords_sv1.php';
00059 
00060     /**
00061      * Keeps extension key.
00062      *
00063      * @var string
00064      */
00065     public $extKey = 'saltedpasswords';
00066 
00067     /**
00068      * Keeps extension configuration.
00069      *
00070      * @var mixed
00071      */
00072     protected $extConf;
00073 
00074     /**
00075      * An instance of the salted hashing method.
00076      * This member is set in the getSaltingInstance() function.
00077      *
00078      * @var tx_saltedpasswords_abstract_salts
00079      */
00080     protected $objInstanceSaltedPW = NULL;
00081 
00082     /**
00083      * Indicates whether the salted password authentication has failed.
00084      *
00085      * Prevents authentication bypass. See vulnerability report:
00086      * { @link http://bugs.typo3.org/view.php?id=13372 }
00087      *
00088      * @var boolean
00089      */
00090     protected $authenticationFailed = FALSE;
00091 
00092     /**
00093      * Checks if service is available. In case of this service we check that
00094      * following prerequesties are fulfilled:
00095      * - loginSecurityLevel of according TYPO3_MODE is set to normal
00096      *
00097      * @return  boolean     TRUE if service is available
00098      */
00099     public function init() {
00100         $available = FALSE;
00101 
00102         if (tx_saltedpasswords_div::isUsageEnabled()) {
00103             $available = TRUE;
00104             $this->extConf = tx_saltedpasswords_div::returnExtConf();
00105         }
00106 
00107         return $available ? parent::init() : FALSE;
00108     }
00109 
00110     /**
00111      * Checks the login data with the user record data for builtin login method.
00112      *
00113      * @param   array       user data array
00114      * @param   array       login data array
00115      * @param   string      login security level (optional)
00116      * @return  boolean     TRUE if login data matched
00117      */
00118     function compareUident(array $user, array $loginData, $security_level = 'normal') {
00119         $validPasswd = FALSE;
00120 
00121             // could be merged; still here to clarify
00122         if (!strcmp(TYPO3_MODE, 'BE')) {
00123             $password = $loginData['uident_text'];
00124         } else if (!strcmp(TYPO3_MODE, 'FE')) {
00125             $password = $loginData['uident_text'];
00126         }
00127 
00128             // determine method used for given salted hashed password
00129         $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance($user['password']);
00130 
00131             // existing record is in format of Salted Hash password
00132         if (is_object($this->objInstanceSaltedPW)) {
00133             $validPasswd = $this->objInstanceSaltedPW->checkPassword($password,$user['password']);
00134 
00135                 // record is in format of Salted Hash password but authentication failed
00136                 // skip further authentication methods
00137             if (!$validPasswd) {
00138                 $this->authenticationFailed = TRUE;
00139             }
00140 
00141             $defaultHashingClassName = tx_saltedpasswords_div::getDefaultSaltingHashingMethod();
00142             $skip = FALSE;
00143 
00144                 // test for wrong salted hashing method
00145             if ($validPasswd && !(get_class($this->objInstanceSaltedPW) == $defaultHashingClassName) || (is_subclass_of($this->objInstanceSaltedPW, $defaultHashingClassName))) {
00146                     // instanciate default method class
00147                 $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL);
00148                 $this->updatePassword(
00149                     intval($user['uid']),
00150                     array('password' => $this->objInstanceSaltedPW->getHashedPassword($password))
00151                 );
00152             }
00153 
00154             if ($validPasswd && !$skip && $this->objInstanceSaltedPW->isHashUpdateNeeded($user['password'])) {
00155                 $this->updatePassword(
00156                     intval($user['uid']),
00157                     array('password' => $this->objInstanceSaltedPW->getHashedPassword($password))
00158                 );
00159             }
00160             // we process also clear-text, md5 and passwords updated by Portable PHP password hashing framework
00161         } else if (!intval($this->extConf['forceSalted'])) {
00162 
00163                 // stored password is in deprecated salted hashing method
00164             if (t3lib_div::inList('C$,M$', substr($user['password'], 0, 2))) {
00165 
00166                     // instanciate default method class
00167                 $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(substr($user['password'], 1));
00168 
00169                     // md5
00170                 if (!strcmp(substr($user['password'], 0, 1), 'M')) {
00171                     $validPasswd = $this->objInstanceSaltedPW->checkPassword(md5($password), substr($user['password'], 1));
00172                 } else {
00173                     $validPasswd = $this->objInstanceSaltedPW->checkPassword($password, substr($user['password'], 1));
00174                 }
00175 
00176                     // skip further authentication methods
00177                 if (!$validPasswd) {
00178                     $this->authenticationFailed = TRUE;
00179                 }
00180 
00181                 // password is stored as md5
00182             } else if (preg_match('/[0-9abcdef]{32,32}/', $user['password'])) {
00183                 $validPasswd = (!strcmp(md5($password), $user['password']) ? TRUE : FALSE);
00184 
00185                     // skip further authentication methods
00186                 if (!$validPasswd) {
00187                     $this->authenticationFailed = TRUE;
00188                 }
00189 
00190                 // password is stored plain or unrecognized format
00191             } else {
00192                 $validPasswd = (!strcmp($password, $user['password']) ? TRUE : FALSE);
00193             }
00194                 // should we store the new format value in DB?
00195             if ($validPasswd && intval($this->extConf['updatePasswd'])) {
00196                     // instanciate default method class
00197                 $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL);
00198                 $this->updatePassword(
00199                     intval($user['uid']),
00200                     array('password' => $this->objInstanceSaltedPW->getHashedPassword($password))
00201                 );
00202             }
00203         }
00204 
00205         return $validPasswd;
00206     }
00207 
00208     /**
00209      * Method adds a further authUser method.
00210      *
00211      * Will return one of following authentication status codes:
00212      *  - 0 - authentication failure
00213      *  - 100 - just go on. User is not authenticated but there is still no reason to stop
00214      *  - 200 - the service was able to authenticate the user
00215      *
00216      * @param   array       Array containing FE user data of the logged user.
00217      * @return  integer     authentication statuscode, one of 0,100 and 200
00218      */
00219     public function authUser(array $user) {
00220         $OK = 100;
00221         $validPasswd = FALSE;
00222 
00223         if ($this->pObj->security_level == 'rsa' && t3lib_extMgm::isLoaded('rsaauth')) {
00224             require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/backends/class.tx_rsaauth_backendfactory.php');
00225             require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/storage/class.tx_rsaauth_storagefactory.php');
00226 
00227             $backend = tx_rsaauth_backendfactory::getBackend();
00228             $storage = tx_rsaauth_storagefactory::getStorage();
00229                 // Preprocess the password
00230             $password = $this->login['uident'];
00231             $key = $storage->get();
00232             if ($key != NULL && substr($password, 0, 4) == 'rsa:') {
00233                 // Decode password and pass to parent
00234                 $decryptedPassword = $backend->decrypt($key, substr($password, 4));
00235                 $this->login['uident_text'] = $decryptedPassword;
00236             }
00237         }
00238 
00239         if ($this->login['uident'] && $this->login['uname']) {
00240             if (!empty($this->login['uident_text'])) {
00241                 $validPasswd = $this->compareUident(
00242                     $user,
00243                     $this->login
00244                 );
00245             }
00246 
00247             if (!$validPasswd && (intval($this->extConf['onlyAuthService']) || $this->authenticationFailed)) {
00248                     // Failed login attempt (wrong password) - no delegation to further services
00249                 $this->writeLog(
00250                     TYPO3_MODE . ' Authentication failed - wrong password for username \'%s\'',
00251                     $this->login['uname']
00252                 );
00253                 $OK = 0;
00254             } else if(!$validPasswd) {
00255                     // Failed login attempt (wrong password)
00256                 $this->writeLog(
00257                     "Login-attempt from %s, username '%s', password not accepted!",
00258                     $this->authInfo['REMOTE_ADDR'], $this->login['uname']
00259                 );
00260             } else if ($validPasswd && $user['lockToDomain'] && strcasecmp($user['lockToDomain'], $this->authInfo['HTTP_HOST'])) {
00261                     // Lock domain didn't match, so error:
00262                 $this->writeLog(
00263                     "Login-attempt from %s, username '%s', locked domain '%s' did not match '%s'!",
00264                     $this->authInfo['REMOTE_ADDR'], $this->login['uname'], $user['lockToDomain'], $this->authInfo['HTTP_HOST']
00265                 );
00266                 $OK = 0;
00267             } else if ($validPasswd) {
00268                 $this->writeLog(
00269                     TYPO3_MODE . ' Authentication successful for username \'%s\'',
00270                     $this->login['uname']
00271                 );
00272                 $OK = 200;
00273             }
00274         }
00275 
00276         return $OK;
00277     }
00278 
00279     /**
00280      * Method updates a FE/BE user record - in this case a new password string will be set.
00281      *
00282      * @param   integer     $uid: uid of user record that will be updated
00283      * @param   mixed       $updateFields: Field values as key=>value pairs to be updated in database
00284      * @return  void
00285      */
00286     protected function updatePassword($uid, $updateFields) {
00287         if (TYPO3_MODE === 'BE') {
00288             $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 'be_users', sprintf('uid = %u', $uid), $updateFields);
00289         } else {
00290             $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 'fe_users', sprintf('uid = %u', $uid), $updateFields);
00291         }
00292 
00293         t3lib_div::devLog(sprintf('Automatic password update for %s user with uid %u', TYPO3_MODE, $uid), $this->extKey, 1);
00294     }
00295 
00296     /**
00297      * Writes log message. Destination log depends on the current system mode.
00298      * For FE the function writes to the admin panel log. For BE messages are
00299      * sent to the system log. If developer log is enabled, messages are also
00300      * sent there.
00301      *
00302      * This function accepts variable number of arguments and can format
00303      * parameters. The syntax is the same as for sprintf()
00304      *
00305      * @param   string      $message: Message to output
00306      * @return  void
00307      * @see sprintf()
00308      * @see t3lib::divLog()
00309      * @see t3lib_div::sysLog()
00310      * @see t3lib_timeTrack::setTSlogMessage()
00311      */
00312     function writeLog($message) {
00313         if (func_num_args() > 1) {
00314             $params = func_get_args();
00315             array_shift($params);
00316             $message = vsprintf($message, $params);
00317         }
00318 
00319         if (TYPO3_MODE === 'BE') {
00320             t3lib_div::sysLog($message, $this->extKey, 1);
00321         } else {
00322             $GLOBALS['TT']->setTSlogMessage($message);
00323         }
00324 
00325         if (TYPO3_DLOG) {
00326             t3lib_div::devLog($message, $this->extKey, 1);
00327         }
00328     }
00329 }
00330 
00331 
00332 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php'])) {
00333     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php']);
00334 }
00335 ?>