TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
SaltedPasswordService.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Saltedpasswords;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
19 
25 {
31  public $prefixId = 'tx_saltedpasswords_sv1';
32 
38  public $extKey = 'saltedpasswords';
39 
45  protected $extConf;
46 
53  protected $objInstanceSaltedPW = null;
54 
63  protected $authenticationFailed = false;
64 
72  public function init()
73  {
74  $available = false;
75  $mode = TYPO3_MODE;
76  if ($this->info['requestedServiceSubType'] === 'authUserBE') {
77  $mode = 'BE';
78  } elseif ($this->info['requestedServiceSubType'] === 'authUserFE') {
79  $mode = 'FE';
80  }
81  if (\TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility::isUsageEnabled($mode)) {
82  $available = true;
84  }
85  return $available ? parent::init() : false;
86  }
87 
96  public function compareUident(array $user, array $loginData, $passwordCompareStrategy = '')
97  {
98  $validPasswd = false;
99  $password = $loginData['uident_text'];
100  // Determine method used for given salted hashed password
101  $this->objInstanceSaltedPW = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance($user['password']);
102  // Existing record is in format of Salted Hash password
103  if (is_object($this->objInstanceSaltedPW)) {
104  $validPasswd = $this->objInstanceSaltedPW->checkPassword($password, $user['password']);
105  // Record is in format of Salted Hash password but authentication failed
106  // skip further authentication methods
107  if (!$validPasswd) {
108  $this->authenticationFailed = true;
109  }
111  $skip = false;
112  // Test for wrong salted hashing method
113  if ($validPasswd && !(get_class($this->objInstanceSaltedPW) == $defaultHashingClassName) || is_subclass_of($this->objInstanceSaltedPW, $defaultHashingClassName)) {
114  // Instantiate default method class
115  $this->objInstanceSaltedPW = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null);
116  $this->updatePassword((int)$user['uid'], ['password' => $this->objInstanceSaltedPW->getHashedPassword($password)]);
117  }
118  if ($validPasswd && !$skip && $this->objInstanceSaltedPW->isHashUpdateNeeded($user['password'])) {
119  $this->updatePassword((int)$user['uid'], ['password' => $this->objInstanceSaltedPW->getHashedPassword($password)]);
120  }
121  } elseif (!(int)$this->extConf['forceSalted']) {
122  // Stored password is in deprecated salted hashing method
123  $hashingMethod = substr($user['password'], 0, 2);
124  if ($hashingMethod === 'C$' || $hashingMethod === 'M$') {
125  // Instantiate default method class
126  $this->objInstanceSaltedPW = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(substr($user['password'], 1));
127  // md5
128  if ($hashingMethod === 'M$') {
129  $validPasswd = $this->objInstanceSaltedPW->checkPassword(md5($password), substr($user['password'], 1));
130  } else {
131  $validPasswd = $this->objInstanceSaltedPW->checkPassword($password, substr($user['password'], 1));
132  }
133  // Skip further authentication methods
134  if (!$validPasswd) {
135  $this->authenticationFailed = true;
136  }
137  } elseif (preg_match('/[0-9abcdef]{32,32}/', $user['password'])) {
138  $validPasswd = md5($password) === (string)$user['password'];
139  // Skip further authentication methods
140  if (!$validPasswd) {
141  $this->authenticationFailed = true;
142  }
143  } else {
144  $validPasswd = (string)$password !== '' && (string)$password === (string)$user['password'];
145  }
146  // Should we store the new format value in DB?
147  if ($validPasswd && (int)$this->extConf['updatePasswd']) {
148  // Instantiate default method class
149  $this->objInstanceSaltedPW = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null);
150  $this->updatePassword((int)$user['uid'], ['password' => $this->objInstanceSaltedPW->getHashedPassword($password)]);
151  }
152  }
153  return $validPasswd;
154  }
155 
167  public function authUser(array $user)
168  {
169  $OK = 100;
170  // The salted password service can only work correctly, if a non empty username along with a non empty password is provided.
171  // Otherwise a different service is allowed to check for other login credentials
172  if ((string)$this->login['uident_text'] !== '' && (string)$this->login['uname'] !== '') {
173  $validPasswd = $this->compareUident($user, $this->login);
174  if (!$validPasswd) {
175  // Failed login attempt (wrong password)
176  $errorMessage = 'Login-attempt from %s (%s), username \'%s\', password not accepted!';
177  // No delegation to further services
178  if ((int)$this->extConf['onlyAuthService'] || $this->authenticationFailed) {
179  $this->writeLogMessage(TYPO3_MODE . ' Authentication failed - wrong password for username \'%s\'', $this->login['uname']);
180  $OK = 0;
181  } else {
182  $this->writeLogMessage($errorMessage, $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname']);
183  }
184  $this->writelog(255, 3, 3, 1, $errorMessage, [
185  $this->authInfo['REMOTE_ADDR'],
186  $this->authInfo['REMOTE_HOST'],
187  $this->login['uname']
188  ]);
189  GeneralUtility::sysLog(sprintf($errorMessage, $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname']), 'core', GeneralUtility::SYSLOG_SEVERITY_INFO);
190  } elseif ($validPasswd && $user['lockToDomain'] && strcasecmp($user['lockToDomain'], $this->authInfo['HTTP_HOST'])) {
191  // Lock domain didn't match, so error:
192  $errorMessage = 'Login-attempt from %s (%s), username \'%s\', locked domain \'%s\' did not match \'%s\'!';
193  $this->writeLogMessage($errorMessage, $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $this->login['uname'], $user['lockToDomain'], $this->authInfo['HTTP_HOST']);
194  $this->writelog(255, 3, 3, 1, $errorMessage, [
195  $this->authInfo['REMOTE_ADDR'],
196  $this->authInfo['REMOTE_HOST'],
197  $user[$this->db_user['username_column']],
198  $user['lockToDomain'],
199  $this->authInfo['HTTP_HOST']
200  ]);
201  GeneralUtility::sysLog(sprintf($errorMessage, $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'], $user[$this->db_user['username_column']], $user['lockToDomain'], $this->authInfo['HTTP_HOST']), 'core', GeneralUtility::SYSLOG_SEVERITY_INFO);
202  $OK = 0;
203  } elseif ($validPasswd) {
204  $this->writeLogMessage(TYPO3_MODE . ' Authentication successful for username \'%s\'', $this->login['uname']);
205  $OK = 200;
206  }
207  }
208  return $OK;
209  }
210 
218  protected function updatePassword($uid, $updateFields)
219  {
220  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
221  ->getConnectionForTable($this->pObj->user_table);
222 
223  $connection->update(
224  $this->pObj->user_table,
225  $updateFields,
226  ['uid' => (int)$uid]
227  );
228 
230  sprintf('Automatic password update for user record in %s with uid %u', $this->pObj->user_table, $uid),
231  $this->extKey,
232  1
233  );
234  }
235 
250  public function writeLogMessage($message, ...$params)
251  {
252  if (!empty($params)) {
253  $message = vsprintf($message, $params);
254  }
255  if (TYPO3_MODE === 'BE') {
256  GeneralUtility::sysLog($message, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_NOTICE);
257  } else {
259  $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
260  $timeTracker->setTSlogMessage($message);
261  }
262  if (TYPO3_DLOG) {
264  }
265  }
266 }
static getSaltingInstance($saltedHash= '', $mode=TYPO3_MODE)
Definition: SaltFactory.php:83
compareUident(array $user, array $loginData, $passwordCompareStrategy= '')
static makeInstance($className,...$constructorArguments)
writelog($type, $action, $error, $details_nr, $details, $data, $tablename= '', $recuid= '', $recpid= '')
static devLog($msg, $extKey, $severity=0, $dataVar=false)