Integrated saltedpasswords completely instead of using svn:externals
authorOliver Hader <oliver.hader@typo3.org>
Tue, 17 Nov 2009 22:44:19 +0000 (22:44 +0000)
committerOliver Hader <oliver.hader@typo3.org>
Tue, 17 Nov 2009 22:44:19 +0000 (22:44 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@6467 709f56b5-9817-0410-a4d7-c38de5d9e867

27 files changed:
ChangeLog
typo3/sysext/saltedpasswords/ChangeLog [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/class.tx_saltedpasswords_div.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_be.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_fe.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_abstract_salts.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_factory.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_md5.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/classes/salts/interfaces/interface.tx_saltedpasswords_salts.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_autoload.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_conf_template.txt [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_emconf.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_icon.gif [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_localconf.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_tables.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_tables.sql [new file with mode: 0644]
typo3/sysext/saltedpasswords/locallang.xml [new file with mode: 0644]
typo3/sysext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_div_testcase.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_blowfish_testcase.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_factory_testcase.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_md5_testcase.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_phpass_testcase.php [new file with mode: 0644]

index db9eb34..d410aa6 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,7 @@
        * Raised DBAL version from 0.9.25 to 1.0.0
        * Raised Extbase and Fluid versions from 0.9.17 to 1.0.0
        * !!! Moved Advanced Frontend Editing to TER
+       * Integrated saltedpasswords completely instead of using svn:externals
 
 2009-11-17  Benjamin Mack  <benni@typo3.org>
 
diff --git a/typo3/sysext/saltedpasswords/ChangeLog b/typo3/sysext/saltedpasswords/ChangeLog
new file mode 100644 (file)
index 0000000..3c0c247
--- /dev/null
@@ -0,0 +1,72 @@
+2009-11-17  Oliver Hader  <oliver@typo3.org>
+
+       * Set version to 1.0.0
+
+2009-11-05  Oliver Hader  <oliver@typo3.org>
+
+       * Set version to 0.9.2
+
+2009-11-03  Oliver Hader  <oliver@typo3.org>
+
+       * Fixed bug: Define tx_saltedpasswords_salts_phpass to be the default due to compatibility reasons
+
+2009-10-21  Oliver Hader  <oliver@typo3.org>
+
+       * Fixed bug: Disable to use the AJAX relogin of the TYPO3 Core
+
+2009-10-16  Oliver Hader  <oliver@typo3.org>
+
+       * Set version to 0.9.1
+
+2009-10-16  Oliver Hader  <oliver@typo3.org>
+
+       * Fixed bug: Evaluation function for t3lib_TCEmain does not need to store already salted password again
+       * Cleanup: Fixed formatting and cleaned up PHPdoc comments
+       * Cleanup: Added type hints in signatures of methods
+
+2009-09-29  Oliver Hader  <oliver@typo3.org>
+
+       * Set version to 0.9.0
+
+2009-09-28  Oliver Hader  <oliver@typo3.org>
+
+       * Cleanup: Removed require_once() calls and TYPO3_MODE checks, fixed descriptions (thanks to Michael Stucki)
+       * Cleanup: Fixed strucural flaws in tx_saltedpasswords_emconfhelper and changed descriptions (thanks to Michael Stucki)
+       * Cleanup: Removed superfluous whitespaces
+
+2009-09-22  Michael Stucki  <michael@typo3.org>
+
+       * Cleanup: Various code cleanup before reviewing the code...
+
+2009-09-22  Oliver Hader  <oliver@typo3.org>
+
+       * Fixed bug: PHP warning occurs when extension is configured the first time in EM
+       * Cleanup: Removed require_once() calls and TYPO3_MODE checks from unit tests
+       * Fixed bug: Use proper setUp() and tearDown() methods in unit tests
+       * Fixed bug: Mark unit tests as skipped if blowfish is not installed
+
+2009-09-09  Marcus Krause  <marcus#exp2009@t3sec.info>
+
+       * Refactoring: Extracted method getLengthBase64FromBytes()
+
+2009-09-08  Marcus Krause  <marcus#exp2009@t3sec.info>
+
+       * Feature: Added lots of unit tests
+       * Feature: Added phpass salted hashing method
+       * Feature/Cleanup: Refactored extension to use an OOP approach
+
+2009-09-04  Steffen Ritter  <info@rs-websystems.de>
+
+       * Feature: Introduced/Registered hook in felogin, for new "forgotPassword" handling (introduced with Core #10017)
+       * Feature: Configuration Check and UserInformation in Ext-EmConf via Flash-Messages
+
+2009-07-20  Steffen Ritter  <info@rs-websystems.de>
+
+       * Cleanup: Removed TYPO3 4.2 compatibility
+
+2009-07-19  Oliver Hader  <oliver@typo3.org>
+
+       * Cleanup: Added ChangeLog file
+       * Cleanup: Added short labels for extension configuration template shown in Extension Manager
+       * Cleanup: Removed superfluous whitespace characters
+       * Cleanup: Fixed SQL extension definition
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/class.tx_saltedpasswords_div.php b/typo3/sysext/saltedpasswords/classes/class.tx_saltedpasswords_div.php
new file mode 100644 (file)
index 0000000..1af7e1e
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) Marcus Krause (marcus#exp2009@t3sec.info)
+*  (c) Steffen Ritter (info@rs-websystems.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_div"
+ * that provides various helper functions.
+ *
+ * $Id$
+ */
+
+/**
+ * General library class.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ * @author             Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since       2009-06-14
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_div {
+               /**
+                * Keeps this extension's key.
+                */
+               const EXTKEY = 'saltedpasswords';
+
+
+               /**
+                * Returns extension configuration data from $TYPO3_CONF_VARS (configurable in Extension Manager)
+                *
+                * @author  Rainer Kuhn <kuhn@punkt.de>
+                * @author  Marcus Krause <marcus#exp2009@t3sec.info>
+                *
+                * @param       string          TYPO3_MODE, wether Configuration for Frontend or Backend should be delivered
+                * @return      array           extension configuration data
+                */
+               public static function returnExtConf($mode = TYPO3_MODE) {
+                       $currentConfiguration = self::returnExtConfDefaults();
+
+                       if (isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['saltedpasswords'])) {
+                               $extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['saltedpasswords']);
+
+                                       // Merge default configuration with modified configuration:
+                               if (isset($extensionConfiguration[$mode . '.'])) {
+                                       $currentConfiguration = array_merge(
+                                               $currentConfiguration,
+                                               $extensionConfiguration[$mode . '.']
+                                       );
+                               }
+                       }
+
+                       return $currentConfiguration;
+               }
+
+               /**
+                * Hook function for felogin "forgotPassword" functionality
+                * encrypts the new password before storing in database
+                *
+                * @param       array                   $params: Parameter the hook delivers
+                * @param       tx_felogin_pi1  $pObj: Parent Object from which the hook is called
+                * @return      void
+                *
+                */
+               public function feloginForgotPasswordHook(array &$params, tx_felogin_pi1 $pObj) {
+                       if (self::isUsageEnabled('FE')) {
+                               $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance();
+                               $params['newPassword'] = $this->objInstanceSaltedPW->getHashedPassword($params['newPassword']);
+                       }
+               }
+
+               /**
+                * Returns default configuration of this extension.
+                *
+                * @return      array           default extension configuration data for localconf.php
+                */
+               public static function returnExtConfDefaults() {
+                       return array(
+                               'onlyAuthService' => '0',
+                               'forceSalted' => '0',
+                               'updatePasswd' => '1',
+                               'saltedPWHashingMethod' => 'tx_saltedpasswords_salts_phpass',
+                               'enabled' => '1',
+                       );
+               }
+
+               /**
+                * Function determines the default(=configured) type of
+                * salted hashing method to be used.
+                *
+                * @param       string          $mode: (optional) The TYPO3 mode (FE or BE) saltedpasswords shall be used for
+                * @return      string          classname of object to be used
+                */
+               public static function getDefaultSaltingHashingMethod($mode = TYPO3_MODE) {
+
+                       $extConf = self::returnExtConf($mode);
+                       $classNameToUse = 'tx_saltedpasswords_salts_md5';
+                       if (in_array($extConf['saltedPWHashingMethod'], array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods']))) {
+                               $classNameToUse = $extConf['saltedPWHashingMethod'];
+                       }
+
+                       return $classNameToUse;
+               }
+
+               /**
+                * Returns information if salted password hashes are
+                * indeed used in the TYPO3_MODE.
+                *
+                * @param       string          $mode: (optional) The TYPO3 mode (FE or BE) saltedpasswords shall be used for
+                * @return      boolean         TRUE, if salted password hashes are used in the TYPO3_MODE, otherwise FALSE
+                */
+               public static function isUsageEnabled($mode = TYPO3_MODE) {
+                               // Login Security Level Recognition
+                       $extConf = self::returnExtConf($mode);
+                       $securityLevel = $GLOBALS['TYPO3_CONF_VARS'][$mode]['loginSecurityLevel'];
+                       if ($mode == 'BE' && $extConf['enabled']) {
+                               return (($securityLevel =='normal' && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] > 0) || $securityLevel == 'rsa');
+                       } else if ($mode =='FE' && $extConf['enabled']) {
+                               return t3lib_div::inList('normal,rsa', $securityLevel);
+                       }
+
+                       return FALSE;
+               }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php b/typo3/sysext/saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php
new file mode 100644 (file)
index 0000000..b70c346
--- /dev/null
@@ -0,0 +1,432 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) Steffen Ritter (info@rs-websystems.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+       // Make sure that we are executed only in TYPO3 context
+if (!defined ('TYPO3_MODE')) die ('Access denied.');
+
+
+/**
+ * class providing configuration checks for saltedpasswords.
+ *
+ * @author     Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since      2009-09-04
+ * @package    TYPO3
+ * @subpackage tx_saltedpasswords
+ */
+class tx_saltedpasswords_emconfhelper {
+       /**
+        * @var integer
+        */
+       protected $errorType = t3lib_FlashMessage::OK;
+
+       /**
+        * @var string
+        */
+       protected $header;
+
+       /**
+        * @var string
+        */
+       protected $preText;
+
+       /*
+        * @var array
+        */
+       protected $problems = array();
+
+       /**
+        * Set the error level if no higher level
+        * is set already
+        *
+        * @param       string          $level: one out of error, ok, warning, info
+        * @return      void
+        */
+       private function setErrorLevel($level) {
+
+               switch ($level) {
+                       case 'error':
+                               $this->errorType = t3lib_FlashMessage::ERROR;
+                               $this->header = 'Errors found in your configuration';
+                               $this->preText = 'SaltedPasswords will not work until these problems have been resolved:<br />';
+                       break;
+                       case 'warning':
+                               if ($this->errorType < t3lib_FlashMessage::ERROR) {
+                                       $this->errorType = t3lib_FlashMessage::WARNING;
+                                       $this->header = 'Warnings about your configuration';
+                                       $this->preText = 'SaltedPasswords might behave different than expectated:<br />';
+                               }
+                       break;
+                       case 'info':
+                               if ($this->errorType < t3lib_FlashMessage::WARNING) {
+                                       $this->errorType = t3lib_FlashMessage::INFO;
+                                       $this->header = 'Additional information';
+                                       $this->preText = '<br />';
+                               }
+                       break;
+                       case 'ok':
+                                       // TODO: Remove INFO condition as it has lower importance
+                               if ($this->errorType < t3lib_FlashMessage::WARNING && $this->errorType != t3lib_FlashMessage::INFO) {
+                                       $this->errorType = t3lib_FlashMessage::OK;
+                                       $this->header = 'No errors were found';
+                                       $this->preText = 'SaltedPasswords has been configured correctly and works as expected.<br />';
+                               }
+                       break;
+               }
+       }
+
+       /**
+        * Renders the flash messages if problems have been found.
+        *
+        * @return      string          The flash message as HTML.
+        */
+       private function renderFlashMessage() {
+               $message = '';
+                       // if there are problems, render them into an unordered list
+               if (count($this->problems) > 0) {
+                       $message = <<< EOT
+<ul>
+       <li>###PROBLEMS###</li>
+</ul>
+EOT;
+                       $message = str_replace('###PROBLEMS###', implode('<br />&nbsp;</li><li>', $this->problems), $message);
+
+                       if ($this->errorType > t3lib_FlashMessage::OK) {
+                               $message .= <<< EOT
+<br />
+Note, that a wrong configuration might have impact on the security of
+your TYPO3 installation and the usability of the backend.
+EOT;
+                       }
+               }
+
+               if (empty($message)) {
+                       $this->setErrorLevel('ok');
+               }
+
+               $message = $this->preText . $message;
+               $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage', $message, $this->header, $this->errorType);
+
+               return $flashMessage->render();
+       }
+
+       /**
+        * Initializes this object.
+        *
+        * @return void
+        */
+       private function init() {
+               $requestSetup = $this->processPostData((array)$_REQUEST['data']);
+               $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['saltedpasswords']);
+               $this->extConf['BE'] = array_merge((array)$extConf['BE.'], (array)$requestSetup['BE.']);
+               $this->extConf['FE'] = array_merge((array)$extConf['FE.'], (array)$requestSetup['FE.']);
+               $GLOBALS['LANG']->includeLLFile('EXT:saltedpasswords/locallang.xml');
+       }
+
+       /**
+        * Checks the backend configuration and shows a message if necessary.
+        *
+        * @param       array                           $params: Field information to be rendered
+        * @param       t3lib_tsStyleConfig     $pObj: The calling parent object.
+        * @return      string                          Messages as HTML if something needs to be reported
+        */
+       public function checkConfigurationBackend(array $params, t3lib_tsStyleConfig $pObj) {
+               $this->init();
+               $extConf = $this->extConf['BE'];
+
+                       // the backend is called over SSL
+               $SSL = (($GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] > 0 ? TRUE : FALSE) && ($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'] != 'superchallenged'));
+                       // rsaAuth is loaded/active
+               $RSAauth = (t3lib_extMgm::isLoaded('rsaauth') && ($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'] == 'rsa'));
+
+               if ($extConf['enabled']) {
+                               // SSL configured?
+                       if ($SSL) {
+                               $this->setErrorLevel('ok');
+                               $problems[] = 'The backend is configured to use SaltedPasswords over SSL.';
+                       } elseif ($RSAauth) {
+                               $this->setErrorLevel('ok');
+                               $problems[] = 'The backend is configured to use SaltedPasswords with RSA authentification.';
+                       } else {
+                               $this->setErrorLevel('error');
+                               $problems[] = <<< EOT
+Backend requirements for SaltedPasswords are not met, therefore the
+authentication will not work even if it was explicitely enabled for backend
+usage:<br />
+<ul>
+       <li>Install the "rsaauth" extension and use the Install Tool to set the
+               Login Security Level for the backend to "rsa"
+               (\$TYPO3_CONF_VARS['BE']['loginSecurityLevel'])</li>
+
+       <li>If you have the option to use SSL, you can also configure your
+               backend for SSL usage:<br />
+               Use the Install Tool to set the Security-Level for the backend
+               to "normal" (\$TYPO3_CONF_VARS['BE']['loginSecurityLevel']) and
+               the SSL-locking option to a value greater than "0"
+               (see description - \$TYPO3_CONF_VARS['BE']['lockSSL'])</li>
+</ul>
+<br />
+It is also possible to use "lockSSL" and "rsa" Login Security Level at the same
+time.
+EOT;
+                       }
+
+                               // only saltedpasswords as authsservice
+                       if ($extConf['onlyAuthService']) {
+                                       // warn user taht the combination with "forceSalted" may lock him out from Backend
+                               if ($extConf['forceSalted']) {
+                                       $this->setErrorLevel('warning');
+                                       $problems[] = <<< EOT
+SaltedPasswords has been configured to be the only authentication service for
+the backend. Additionally, usage of salted passwords is enforced (forceSalted).
+The result is that there is no chance to login with users not having a salted
+password hash.<br />
+<b><i>WARNING:</i></b> This may lock you out of the backend!
+EOT;
+                               } else {
+                                               // inform the user that things like openid won't work anymore
+                                       $this->setErrorLevel('info');
+                                       $problems[] = <<< EOT
+SaltedPasswords has been configured to be the only authentication service for
+the backend. This means that other services like "ipauth", "openid", etc. will
+be ignored (except "rsauth", which is implicitely used).
+EOT;
+                               }
+                       }
+                               // forceSalted is set
+                       if ($extConf['forceSalted'] && !$extConf['onlyAuthService']) {
+                               $this->setErrorLevel('warning');
+                               $problems[] = <<< EOT
+SaltedPasswords has been configured to enforce salted passwords (forceSalted).
+<br />
+This means that only passwords in the format of this extension will succeed for
+login.<br />
+<b><i>IMPORTANT:</i></b> This has the effect that passwords that are set from
+the Install Tool will not work!
+EOT;
+                       }
+                               // updatePasswd wont work with "forceSalted"
+                       if ($extConf['updatePasswd'] && $extConf['forceSalted']) {
+                               $this->setErrorLevel('error');
+                               $problems[] = <<< EOT
+SaltedPasswords is configured wrong and will not work as expected:<br />
+It is not possible to set "updatePasswd" and "forceSalted" at the same time.
+Please disable either one of them.
+EOT;
+                       }
+                               // check if the configured hash-method is available on system
+                       if (!$instance = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL,'BE') || !$instance->isAvailable()) {
+                               $this->setErrorLevel('error');
+                               $problems[] = <<< EOT
+The selected method for hashing your salted passwords is not available on this
+system! Please check your configuration.
+EOT;
+                       }
+
+               } else {
+                       // not enabled warning
+                       $this->setErrorLevel('info');
+                       $problems[] = 'SaltedPasswords has been disabled for backend users.';
+               }
+
+               $this->problems = $problems;
+
+               return $this->renderFlashMessage();
+       }
+
+       /**
+        * Checks the frontend configuration and shows a message if necessary.
+        *
+        * @param       array                           $params: Field information to be rendered
+        * @param       t3lib_tsStyleConfig     $pObj: The calling parent object.
+        * @return      string                          Messages as HTML if something needs to be reported
+        */
+       public function checkConfigurationFrontend(array $params, t3lib_tsStyleConfig $pObj) {
+               $this->init();
+               $extConf = $this->extConf['FE'];
+
+               if ($extConf['enabled']) {
+                               // inform the user if securityLevel in FE is superchallenged or blank --> extension won't work
+                       if (!t3lib_div::inList('normal,rsa', $GLOBALS['TYPO3_CONF_VARS']['FE']['loginSecurityLevel'])) {
+                               $this->setErrorLevel('info');
+                               $problems[] = <<< EOT
+<b>IMPORTANT:</b><br />
+Frontend requirements for SaltedPasswords are not met, therefore the
+authentication will not work even if it was explicitely enabled for frontend
+usage:<br />
+<ul>
+       <li>Install the "rsaauth" extension and use the Install Tool to set the
+               Login Security Level for the frontend to "rsa"
+               (\$TYPO3_CONF_VARS['FE']['loginSecurityLevel'])</li>
+
+       <li>Alternatively, use the Install Tool to set the Login Security Level
+               for the frontend to "normal"
+               (\$TYPO3_CONF_VARS['FE']['loginSecurityLevel'])</li>
+</ul>
+<br />
+Make sure that the Login Security Level is not set to "" or "superchallenged"!
+EOT;
+                       }
+                               // only saltedpasswords as authsservice
+                       if ($extConf['onlyAuthService']) {
+                                       // warn user taht the combination with "forceSalted" may lock him out from frontend
+                               if ($extConf['forceSalted']) {
+                                       $this->setErrorLevel('warning');
+                                       $problems[] = <<< EOT
+SaltedPasswords has been configured to enforce salted passwords (forceSalted).
+<br />
+This means that only passwords in the format of this extension will succeed for
+login.<br />
+<b><i>IMPORTANT:</i></b> Because of this, it is not possible to login with
+users not having a salted password hash (e.g. existing frontend users).
+EOT;
+                               } else {
+                                               // inform the user that things like openid won't work anymore
+                                       $this->setErrorLevel('info');
+                                       $problems[] = <<< EOT
+SaltedPasswords has been configured to be the only authentication service for
+frontend logins. This means that other services like "ipauth", "openid", etc.
+will be ignored.
+EOT;
+                               }
+                       }
+                               // forceSalted is set
+                       if ($extConf['forceSalted'] && !$extConf['onlyAuthService']) {
+                               $this->setErrorLevel('warning');
+                               $problems[] = <<< EOT
+SaltedPasswords has been configured to enforce salted passwords (forceSalted).
+<br />
+This means that only passwords in the format of this extension will succeed for
+login.<br />
+<b><i>IMPORTANT:</i></b> This has the effect that passwords that were set
+before SaltedPasswords was used will not work (in fact, they need to be
+redefined).
+EOT;
+                       }
+                               // updatePasswd wont work with "forceSalted"
+                       if ($extConf['updatePasswd'] && $extConf['forceSalted']) {
+                               $this->setErrorLevel('error');
+                               $problems[] = <<< EOT
+SaltedPasswords is configured wrong and will not work as expected:<br />
+It is not possible to set "updatePasswd" and "forceSalted" at the same time.
+Please disable either one of them.
+EOT;
+                       }
+
+               } else {
+                       // not enabled warning
+                       $this->setErrorLevel('info');
+                       $problems[] = 'SaltedPasswords has been disabled for frontend users.';
+               }
+
+               $this->problems = $problems;
+
+               return $this->renderFlashMessage();
+       }
+
+       /**
+        * Renders a selector element that allows to select the hash method to be used.
+        *
+        * @param       array                           $params: Field information to be rendered
+        * @param       t3lib_tsStyleConfig     $pObj: The calling parent object.
+        * @param       string                          $disposal: The configuration disposal ('FE' or 'BE')
+        * @return      string                          The HTML selector
+        */
+       protected function buildHashMethodSelector(array $params, t3lib_tsStyleConfig $pObj, $disposal) {
+               $this->init();
+               $fieldName = substr($params['fieldName'], 5, -1);
+               $unknownVariablePleaseRenameMe = '\'' . substr(md5($fieldName), 0, 10) . '\'';
+
+               $p_field = '';
+
+               foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods'] as $class => $reference) {
+                       $classInstance = t3lib_div::getUserObj($reference, 'tx_');
+
+                       if ($classInstance instanceof tx_saltedpasswords_salts && $classInstance->isAvailable()) {
+                               $sel = ($this->extConf[$disposal]['saltedPWHashingMethod'] == $class) ? ' selected="selected" ' : '';
+                               $label = 'ext.saltedpasswords.title.' . $class;
+                               $p_field .= '<option value="' . htmlspecialchars($class) . '"' . $sel . '>' . $GLOBALS['LANG']->getLL($label) . '</option>';
+                       }
+               }
+
+               $p_field = '<select id="' . $fieldName . '" name="' . $params['fieldName'] . '" onChange="uFormUrl(' . $unknownVariablePleaseRenameMe . ')">' . $p_field . '</select>';
+
+               return $p_field;
+       }
+
+       /**
+        * Renders a selector element that allows to select the hash method to be used (frontend disposal).
+        *
+        * @param       array                           $params: Field information to be rendered
+        * @param       t3lib_tsStyleConfig     $pObj: The calling parent object.
+        * @return      string                          The HTML selector
+        */
+       public function buildHashMethodSelectorFE(array $params, t3lib_tsStyleConfig $pObj) {
+               return $this->buildHashMethodSelector($params, $pObj, 'FE');
+       }
+
+       /**
+        * Renders a selector element that allows to select the hash method to be used (backend disposal)
+        *
+        * @param       array                           $params: Field information to be rendered
+        * @param       t3lib_tsStyleConfig     $pObj: The calling parent object.
+        * @return      string                          The HTML selector
+        */
+       public function buildHashMethodSelectorBE(array $params, t3lib_tsStyleConfig $pObj) {
+               return $this->buildHashMethodSelector($params, $pObj, 'BE');
+       }
+
+       /**
+        * Processes the information submitted by the user using a POST request and
+        * transforms it to a TypoScript node notation.
+        *
+        * @param       array           $postArray: Incoming POST information
+        * @return      array           Processed and transformed POST information
+        */
+       private function processPostData(array $postArray = array()) {
+               foreach ($postArray as $key => $value) {
+                       // TODO: Explain
+                       $parts = explode('.', $key, 2);
+
+                       if (count($parts)==2) {
+                               // TODO: Explain
+                               $value = $this->processPostData(array($parts[1] => $value));
+                               $postArray[$parts[0].'.'] = array_merge((array)$postArray[$parts[0].'.'], $value);
+                       } else {
+                               // TODO: Explain
+                               $postArray[$parts[0]] = $value;
+                       }
+               }
+
+               return $postArray;
+       }
+
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval.php b/typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval.php
new file mode 100644 (file)
index 0000000..c7f3c3f
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) Marcus Krause (marcus#exp2009@t3sec.info)
+*  (c) Steffen Ritter (info@rs-websystems.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Class implementing salted evaluation methods.
+ *
+ * @author     Marcus Krause <marcus#exp2009@t3sec.info>
+ * @author     Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since      2009-06-14
+ * @package    TYPO3
+ * @subpackage tx_saltedpasswords
+ */
+class tx_saltedpasswords_eval {
+       /**
+        * Keeps TYPO3 mode.
+        *
+        * Either 'FE' or 'BE'.
+        *
+        * @var string
+        */
+       protected $mode = NULL;
+
+       /**
+        * This function just return the field value as it is. No transforming,
+        * hashing will be done on server-side.
+        *
+        * @return      JavaScript code for evaluating the
+        */
+       function returnFieldJS() {
+               return 'return value;';
+       }
+
+       /**
+        * Function uses Portable PHP Hashing Framework to create a proper password string if needed
+        *
+        * @param       mixed           $value: The value that has to be checked.
+        * @param       string          $is_in: Is-In String
+        * @param       integer         $set: Determines if the field can be set (value correct) or not, e.g. if input is required but the value is empty, then $set should be set to FALSE. (PASSED BY REFERENCE!)
+        * @return      The new value of the field
+        */
+       function evaluateFieldValue($value, $is_in, &$set) {
+               $isEnabled = $this->mode ? tx_saltedpasswords_div::isUsageEnabled($this->mode) : tx_saltedpasswords_div::isUsageEnabled();
+
+               if ($isEnabled) {
+                       $set = FALSE;
+                       $isMD5 = preg_match('/[0-9abcdef]{32,32}/', $value);
+                       $isSaltedHash = t3lib_div::inList('$1$,$2$,$2a,$P$', substr($value, 0, 3));
+
+                       $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL, $this->mode);
+
+                       if ($isMD5) {
+                               $set = TRUE;
+                               $value = 'M' . $this->objInstanceSaltedPW->getHashedPassword($value);
+                       } else if (!$isSaltedHash) {
+                               $set = TRUE;
+                               $value = $this->objInstanceSaltedPW->getHashedPassword($value);
+                       }
+               }
+
+               return $value;
+       }
+}
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_be.php b/typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_be.php
new file mode 100644 (file)
index 0000000..124e214
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) Marcus Krause (marcus#exp2009@t3sec.info)
+*  (c) Steffen Ritter (info@rs-websystems.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Class implementing salted evaluation methods for BE users.
+ *
+ * @author     Marcus Krause <marcus#exp2009@t3sec.info>
+ * @author     Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since      2009-06-14
+ * @package    TYPO3
+ * @subpackage tx_saltedpasswords
+ */
+class tx_saltedpasswords_eval_be extends tx_saltedpasswords_eval {
+       /**
+        * Class constructor.
+        *
+        * @return      tx_saltedpasswords_eval_fe      instance of object
+        */
+       public function __construct() {
+               $this->mode = 'BE';
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_be.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_be.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_fe.php b/typo3/sysext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_fe.php
new file mode 100644 (file)
index 0000000..6fd1215
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) Marcus Krause (marcus#exp2009@t3sec.info)
+*  (c) Steffen Ritter (info@rs-websystems.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * Class implementing salted evaluation methods for FE users.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ * @author             Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since       2009-06-14
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_eval_fe extends tx_saltedpasswords_eval {
+       /**
+        * Class constructor.
+        *
+        * @return      tx_saltedpasswords_eval_fe      instance of object
+        */
+       public function __construct() {
+               $this->mode = 'FE';
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_fe.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_fe.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_abstract_salts.php b/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_abstract_salts.php
new file mode 100644 (file)
index 0000000..f62e919
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains abstract class "tx_saltedpasswords_abstract_salts"
+ * to be used in classes that provide salted hashing.
+ *
+ * $Id: class.tx_saltedpasswords_abstract_salts.php 25459 2009-10-16 14:36:41Z ohader $
+ */
+
+
+/**
+ * Abtract class with methods needed to be extended
+ * in a salted hashing class.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ *
+ * @abstract
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+abstract class tx_saltedpasswords_abstract_salts {
+       /**
+        * Method applies settings (prefix, optional hash count, optional suffix)
+        * to a salt.
+        *
+        * @param       string          $salt:  a salt to apply setting to
+        * @return      string          salt with setting
+        */
+       abstract protected function applySettingsToSalt($salt);
+
+       /**
+        * Generates a random base salt settings for the hash.
+        *
+        * @return      string          a string containing settings and a random salt
+        */
+       abstract protected function getGeneratedSalt();
+
+       /**
+        * Returns a string for mapping an int to the corresponding base 64 character.
+        *
+        * @return      string          string for mapping an int to the corresponding base 64 character
+        */
+       abstract protected function getItoa64();
+
+       /**
+        * Returns setting string to indicate type of hashing method.
+        *
+        * @return      string          setting string of hashing method
+        */
+       abstract protected function getSetting();
+
+       /**
+        * Encodes bytes into printable base 64 using the *nix standard from crypt().
+        *
+        * @param       string          $input: the string containing bytes to encode.
+        * @param       integer         $count: the number of characters (bytes) to encode.
+        * @return      string          encoded string
+        */
+       public function base64Encode($input, $count) {
+               $output = '';
+               $i = 0;
+               $itoa64 = $this->getItoa64();
+               do {
+                       $value = ord($input[$i++]);
+                       $output .= $itoa64[$value & 0x3f];
+                       if ($i < $count) {
+                               $value |= ord($input[$i]) << 8;
+                       }
+                       $output .= $itoa64[($value >> 6) & 0x3f];
+                       if ($i++ >= $count) {
+                               break;
+                       }
+                       if ($i < $count) {
+                               $value |= ord($input[$i]) << 16;
+                       }
+                       $output .= $itoa64[($value >> 12) & 0x3f];
+                       if ($i++ >= $count) {
+                               break;
+                       }
+                       $output .= $itoa64[($value >> 18) & 0x3f];
+               } while ($i < $count);
+               return $output;
+       }
+
+       /**
+        * Method determines required length of base64 characters for a given
+        * length of a byte string.
+        *
+        * @param       integer         $byteLength: length of bytes to calculate in base64 chars
+        * @return      integer         required length of base64 characters
+        */
+       protected function getLengthBase64FromBytes($byteLength) {
+                       // calculates bytes in bits in base64
+               return intval(ceil(($byteLength * 8) / 6));
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_abstract_salts.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_abstract_salts.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php b/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php
new file mode 100644 (file)
index 0000000..ea1fb77
--- /dev/null
@@ -0,0 +1,336 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_salts_blowfish"
+ * that provides Blowfish salted hashing.
+ *
+ * $Id$
+ */
+
+
+/**
+ * Class that implements Blowfish salted hashing based on PHP's
+ * crypt() function.
+ *
+ * Warning: Blowfish salted hashing with PHP's crypt() is not available
+ * on every system.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ *
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_blowfish extends tx_saltedpasswords_salts_md5 {
+       /**
+        * The default log2 number of iterations for password stretching.
+        */
+       const HASH_COUNT = 7;
+
+       /**
+        * The default maximum allowed log2 number of iterations for
+        * password stretching.
+        */
+       const MAX_HASH_COUNT = 17;
+
+       /**
+        * The default minimum allowed log2 number of iterations for
+        * password stretching.
+        */
+       const MIN_HASH_COUNT = 4;
+
+
+       /**
+        * Keeps log2 number
+        * of iterations for password stretching.
+        *
+        * @var integer
+        */
+       static protected $hashCount;
+
+       /**
+        * Keeps maximum allowed log2 number
+        * of iterations for password stretching.
+        *
+        * @var integer
+        */
+       static protected $maxHashCount;
+
+       /**
+        * Keeps minimum allowed log2 number
+        * of iterations for password stretching.
+        *
+        * @var integer
+        */
+       static protected $minHashCount;
+
+       /**
+        * Keeps length of a Blowfish salt in bytes.
+        *
+        * @var integer
+        */
+       static protected $saltLengthBlowfish = 16;
+
+       /**
+        * Setting string to indicate type of hashing method (blowfish).
+        *
+        * @var string
+        */
+       static protected $settingBlowfish = '$2a$';
+
+
+       /**
+        * Method applies settings (prefix, hash count) to a salt.
+        *
+        * Overwrites {@link tx_saltedpasswords_salts_md5::applySettingsToSalt()}
+        * with Blowfish specifics.
+        *
+        * @param       string          $salt:  a salt to apply setting to
+        * @return      string          salt with setting
+        */
+       protected function applySettingsToSalt($salt) {
+               $saltWithSettings = $salt;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+                                       // salt without setting
+               if (strlen($salt) == $reqLenBase64) {
+                       $saltWithSettings = $this->getSetting()
+                               . sprintf('%02u', $this->getHashCount()) . '$'
+                               . $salt;
+               }
+
+               return $saltWithSettings;
+       }
+
+       /**
+        * Parses the log2 iteration count from a stored hash or setting string.
+        *
+        * @param       string          $setting: complete hash or a hash's setting string or to get log2 iteration count from
+        * @return      integer         used hashcount for given hash string
+        */
+       protected function getCountLog2($setting) {
+               $countLog2 = NULL;
+               $setting = substr($setting, strlen($this->getSetting()));
+               $firstSplitPos = strpos($setting, '$');
+
+                       // hashcount existing
+               if (($firstSplitPos !== FALSE)
+                       && ($firstSplitPos <= 2) && is_numeric(substr($setting, 0, $firstSplitPos))) {
+                       $countLog2 = intval(substr($setting, 0, $firstSplitPos));
+               }
+
+               return $countLog2;
+       }
+
+       /**
+        * Method returns log2 number of iterations for password stretching.
+        *
+        * @return      integer         log2 number of iterations for password stretching
+        * @see         HASH_COUNT
+        * @see         $hashCount
+        * @see         setHashCount()
+        */
+       public function getHashCount() {
+               return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
+       }
+
+       /**
+        * Method returns maximum allowed log2 number of iterations for password stretching.
+        *
+        * @return      integer         maximum allowed log2 number of iterations for password stretching
+        * @see         MAX_HASH_COUNT
+        * @see         $maxHashCount
+        * @see         setMaxHashCount()
+        */
+       public function getMaxHashCount() {
+               return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
+       }
+
+       /**
+        * Returns wether all prequesites for the hashing methods are matched
+        *
+        * @return      boolean         method available
+        */
+       public function isAvailable() {
+               return CRYPT_BLOWFISH;
+       }
+
+       /**
+        * Method returns minimum allowed log2 number of iterations for password stretching.
+        *
+        * @return      integer         minimum allowed log2 number of iterations for password stretching
+        * @see         MIN_HASH_COUNT
+        * @see         $minHashCount
+        * @see         setMinHashCount()
+        */
+       public function getMinHashCount() {
+               return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
+       }
+
+       /**
+        * Returns length of a Blowfish salt in bytes.
+        *
+        * Overwrites {@link tx_saltedpasswords_salts_md5::getSaltLength()}
+        * with Blowfish specifics.
+        *
+        * @return      integer         length of a Blowfish salt in bytes
+        */
+       public function getSaltLength() {
+               return self::$saltLengthBlowfish;
+       }
+
+       /**
+        * Returns setting string of Blowfish salted hashes.
+        *
+        * Overwrites {@link tx_saltedpasswords_salts_md5::getSetting()}
+        * with Blowfish specifics.
+        *
+        * @return      string          setting string of Blowfish salted hashes
+        */
+       public function getSetting() {
+               return self::$settingBlowfish;
+       }
+
+       /**
+        * Checks whether a user's hashed password needs to be replaced with a new hash.
+        *
+        * This is typically called during the login process when the plain text
+        * password is available.  A new hash is needed when the desired iteration
+        * count has changed through a change in the variable $hashCount or
+        * HASH_COUNT.
+        *
+        * @param       string          $saltedPW: salted hash to check if it needs an update
+        * @return      boolean         TRUE if salted hash needs an update, otherwise FALSE
+        */
+       public function isHashUpdateNeeded($saltedPW) {
+                       // Check whether this was an updated password.
+               if ((strncmp($saltedPW, '$2', 2)) || !$this->isValidSalt($saltedPW)) {
+                       return TRUE;
+               }
+                       // Check whether the iteration count used differs from the standard number.
+               $countLog2 = $this->getCountLog2($saltedPW);
+
+               return (!is_NULL($countLog2) && ($countLog2 < $this->getHashCount()));
+       }
+
+       /**
+        * Method determines if a given string is a valid salt.
+        *
+        * Overwrites {@link tx_saltedpasswords_salts_md5::isValidSalt()} with
+        * Blowfish specifics.
+        *
+        * @param       string          $salt: string to check
+        * @return      boolean         TRUE if it's valid salt, otherwise FALSE
+        */
+       public function isValidSalt($salt) {
+               $isValid = $skip = FALSE;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+               if (strlen($salt) >= $reqLenBase64) {
+                               // salt with prefixed setting
+                       if (!strncmp('$', $salt, 1)) {
+                               if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
+                                       $isValid = TRUE;
+                                       $salt = substr($salt, strrpos($salt, '$') + 1);
+                               } else {
+                                       $skip = TRUE;
+                               }
+                       }
+
+                               // checking base64 characters
+                       if (!$skip && (strlen($salt) >= $reqLenBase64)) {
+                               if (preg_match('/^[' . preg_quote($this->getItoa64(),'/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
+                                       $isValid = TRUE;
+                               }
+                       }
+               }
+
+               return $isValid;
+       }
+
+       /**
+        * Method determines if a given string is a valid salted hashed password.
+        *
+        * @param       string          $saltedPW: string to check
+        * @return      boolean         TRUE if it's valid salted hashed password, otherwise FALSE
+        */
+       public function isValidSaltedPW($saltedPW) {
+               $isValid = FALSE;
+
+               $isValid = (!strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting()))) ? TRUE : FALSE;
+
+               if ($isValid) {
+                       $isValid = $this->isValidSalt($saltedPW);
+               }
+
+               return $isValid;
+       }
+
+       /**
+        * Method sets log2 number of iterations for password stretching.
+        *
+        * @param       integer         $hashCount: log2 number of iterations for password stretching to set
+        * @see         HASH_COUNT
+        * @see         $hashCount
+        * @see         getHashCount()
+        */
+       public function setHashCount($hashCount = NULL) {
+               self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
+       }
+
+       /**
+        * Method sets maximum allowed log2 number of iterations for password stretching.
+        *
+        * @param       integer         $maxHashCount: maximum allowed log2 number of iterations for password stretching to set
+        * @see         MAX_HASH_COUNT
+        * @see         $maxHashCount
+        * @see         getMaxHashCount()
+        */
+       public function setMaxHashCount($maxHashCount = NULL) {
+               self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
+       }
+
+       /**
+        * Method sets minimum allowed log2 number of iterations for password stretching.
+        *
+        * @param       integer         $minHashCount: minimum allowed log2 number of iterations for password stretching to set
+        * @see         MIN_HASH_COUNT
+        * @see         $minHashCount
+        * @see         getMinHashCount()
+        */
+       public function setMinHashCount($minHashCount = NULL) {
+               self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_factory.php b/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_factory.php
new file mode 100644 (file)
index 0000000..847244a
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_salts_factory"
+ * that provides a salted hashing method factory.
+ *
+ * $Id$
+ */
+
+
+/**
+ * Class that implements Blowfish salted hashing based on PHP's
+ * crypt() function.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ *
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_factory {
+       /**
+        * An instance of the salted hashing method.
+        * This member is set in the getSaltingInstance() function.
+        *
+        * @var tx_saltedpasswords_abstract_salts
+        */
+       static protected $instance = NULL;
+
+
+       /**
+        * Obtains a salting hashing method instance.
+        *
+        * This function will return an instance of a class that implements
+        * tx_saltedpasswords_abstract_salts.
+        *
+        * Use parameter NULL to reset the factory!
+        *
+        * @param       string          $saltedHash: (optional) salted hashed password to determine the type of used method from or NULL to reset the factory
+        * @param       string          $mode: (optional) The TYPO3 mode (FE or BE) saltedpasswords shall be used for
+        * @return      tx_saltedpasswords_abstract_salts       an instance of salting hashing method object
+        */
+       static public function getSaltingInstance($saltedHash = '', $mode = TYPO3_MODE) {
+                       // creating new instance when
+                       // * no instance existing
+                       // * a salted hash given to determine salted hashing method from
+                       // * a NULL parameter given to reset instance back to default method
+               if (!is_object(self::$instance) || !empty($saltedHash) || is_NULL($saltedHash)) {
+
+                               // determine method by checking the given hash
+                       if (!empty($saltedHash)) {
+                               $result = self::determineSaltingHashingMethod($saltedHash);
+                               if(!$result) {
+                                       self::$instance = NULL;
+                               }
+                       } else {
+                               $classNameToUse = tx_saltedpasswords_div::getDefaultSaltingHashingMethod($mode);
+                               $availableClasses = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods'];
+                               self::$instance = t3lib_div::getUserObj($availableClasses[$classNameToUse], 'tx_');
+                       }
+               }
+
+               return self::$instance;
+       }
+
+       /**
+        * Method tries to determine the salting hashing method used for given salt.
+        *
+        * Method implicitly sets the instance of the found method object in the class property when found.
+        *
+        * @param       string          $saltedHash
+        * @return      boolean         TRUE, if salting hashing method has been found, otherwise FALSE
+        */
+       static protected function determineSaltingHashingMethod($saltedHash) {
+               $methodFound = FALSE;
+               $defaultMethods = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods'];
+               foreach($defaultMethods as $method) {
+                       $objectInstance = t3lib_div::getUserObj($method, 'tx_');
+                       if ($objectInstance instanceof tx_saltedpasswords_salts) {
+                               $methodFound = $objectInstance->isValidSaltedPW($saltedHash);
+                               if ($methodFound) {
+                                       self::$instance = $objectInstance;
+                                       break;
+                               }
+                       }
+               }
+
+               return $methodFound;
+       }
+
+       /**
+        * Method sets a custom salting hashing method class.
+        *
+        * @param       string          $resource: object resource to use (e.g. 'EXT:saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php:tx_saltedpasswords_salts_blowfish')
+        * @return      tx_saltedpasswords_abstract_salts       an instance of salting hashing method object
+        */
+       static public function setPreferredHashingMethod($resource) {
+               self::$instance = NULL;
+               $objectInstance = t3lib_div::getUserObj($resource);
+               if (is_object($objectInstance)
+                       && is_subclass_of($objectInstance, 'tx_saltedpasswords_abstract_salts')) {
+                               self::$instance = $objectInstance;
+               }
+
+               return self::$instance;
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_md5.php b/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_md5.php
new file mode 100644 (file)
index 0000000..eaed2db
--- /dev/null
@@ -0,0 +1,267 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_salts_md5"
+ * that provides MD5 salted hashing.
+ *
+ * $Id$
+ */
+
+
+/**
+ * Class that implements MD5 salted hashing based on PHP's
+ * crypt() function.
+ *
+ * MD5 salted hashing with PHP's crypt() should be available
+ * on most of the systems.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ *
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_md5 extends tx_saltedpasswords_abstract_salts implements tx_saltedpasswords_salts {
+       /**
+        * Keeps a string for mapping an int to the corresponding
+        * base 64 character.
+        */
+       const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+       /**
+        * Keeps length of a MD5 salt in bytes.
+        *
+        * @var integer
+        */
+       static protected $saltLengthMD5 = 6;
+
+       /**
+        * Keeps suffix to be appended to a salt.
+        *
+        * @var string
+        */
+       static protected $saltSuffixMD5 = '$';
+
+       /**
+        * Setting string to indicate type of hashing method (md5).
+        *
+        * @var string
+        */
+       static protected $settingMD5 = '$1$';
+
+
+       /**
+        * Method applies settings (prefix, suffix) to a salt.
+        *
+        * @param       string          $salt: a salt to apply setting to
+        * @return      string          salt with setting
+        */
+       protected function applySettingsToSalt($salt) {
+               $saltWithSettings = $salt;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+                       // salt without setting
+               if (strlen($salt) == $reqLenBase64) {
+                       $saltWithSettings = $this->getSetting() . $salt . $this->getSaltSuffix();
+
+               }
+
+               return $saltWithSettings;
+       }
+
+       /**
+        * Method checks if a given plaintext password is correct by comparing it with
+        * a given salted hashed password.
+        *
+        * @param       string          $plainPW: plain-text password to compare with salted hash
+        * @param       string          $saltedHashPW: salted hash to compare plain-text password with
+        * @return      boolean         TRUE, if plain-text password matches the salted hash, otherwise FALSE
+        */
+       public function checkPassword($plainPW, $saltedHashPW) {
+               $isCorrect = FALSE;
+
+               if ($this->isValidSalt($saltedHashPW)) {
+                       $isCorrect = (crypt($plainPW,$saltedHashPW) == $saltedHashPW);
+               }
+
+               return $isCorrect;
+       }
+
+       /**
+        * Generates a random base 64-encoded salt prefixed and suffixed with settings for the hash.
+        *
+        * Proper use of salts may defeat a number of attacks, including:
+        *  - The ability to try candidate passwords against multiple hashes at once.
+        *  - The ability to use pre-hashed lists of candidate passwords.
+        *  - The ability to determine whether two users have the same (or different)
+        *    password without actually having to guess one of the passwords.
+        *
+        * @return      string          a character string containing settings and a random salt
+        */
+       protected function getGeneratedSalt() {
+               $randomBytes = t3lib_div::generateRandomBytes($this->getSaltLength());
+
+               return $this->base64Encode($randomBytes, $this->getSaltLength());
+       }
+
+       /**
+        * Method creates a salted hash for a given plaintext password
+        *
+        * @param       string          $password: plaintext password to create a salted hash from
+        * @param       string          $salt: optional custom salt with setting to use
+        * @return      string          salted hashed password
+        */
+       public function getHashedPassword($password, $salt = NULL) {
+               $saltedPW = NULL;
+               if (!empty($password)) {
+                       if (empty($salt) || !$this->isValidSalt($salt)) {
+                               $salt = $this->getGeneratedSalt();
+                       }
+                       $saltedPW = crypt($password, $this->applySettingsToSalt($salt));
+               }
+
+               return $saltedPW;
+       }
+
+       /**
+        * Returns a string for mapping an int to the corresponding base 64 character.
+        *
+        * @return      string          string for mapping an int to the corresponding base 64 character
+        */
+       protected function getItoa64() {
+               return self::ITOA64;
+       }
+
+       /**
+        * Returns wether all prequesites for the hashing methods are matched
+        *
+        * @return      boolean         method available
+        */
+       public function isAvailable() {
+               return CRYPT_MD5;
+       }
+
+       /**
+        * Returns length of a MD5 salt in bytes.
+        *
+        * @return      integer         length of a MD5 salt in bytes
+        */
+       public function getSaltLength() {
+               return self::$saltLengthMD5;
+       }
+
+       /**
+        * Returns suffix to be appended to a salt.
+        *
+        * @return      string          suffix of a salt
+        */
+       protected function getSaltSuffix() {
+               return self::$saltSuffixMD5;
+       }
+
+       /**
+        * Returns setting string of MD5 salted hashes.
+        *
+        * @return      string          setting string of MD5 salted hashes
+        */
+       public function getSetting() {
+               return self::$settingMD5;
+       }
+
+       /**
+        * Checks whether a user's hashed password needs to be replaced with a new hash.
+        *
+        * This is typically called during the login process when the plain text
+        * password is available.  A new hash is needed when the desired iteration
+        * count has changed through a change in the variable $hashCount or
+        * HASH_COUNT or if the user's password hash was generated in an bulk update
+        * with class ext_update.
+        *
+        * @param       string          $passString: salted hash to check if it needs an update
+        * @return      boolean         TRUE if salted hash needs an update, otherwise FALSE
+        */
+       public function isHashUpdateNeeded($passString) {
+               return FALSE;
+       }
+
+       /**
+        * Method determines if a given string is a valid salt
+        *
+        * @param       string          $salt:  string to check
+        * @return      boolean         TRUE if it's valid salt, otherwise FALSE
+        */
+       public function isValidSalt($salt) {
+               $isValid = $skip = FALSE;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+               if (strlen($salt) >= $reqLenBase64) {
+                                               // salt with prefixed setting
+                       if (!strncmp('$', $salt, 1)) {
+                               if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
+                                       $isValid = TRUE;
+                                       $salt = substr($salt, strlen($this->getSetting()));
+                               } else {
+                                       $skip = TRUE;
+                               }
+                       }
+
+                               // checking base64 characters
+                       if (!$skip && (strlen($salt) >= $reqLenBase64)) {
+                               if (preg_match('/^[' . preg_quote($this->getItoa64(),'/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
+                                       $isValid = TRUE;
+                               }
+                       }
+               }
+
+               return $isValid;
+       }
+
+       /**
+        * Method determines if a given string is a valid salted hashed password.
+        *
+        * @param       string          $saltedPW: string to check
+        * @return      boolean         TRUE if it's valid salted hashed password, otherwise FALSE
+        */
+       public function isValidSaltedPW($saltedPW) {
+               $isValid = FALSE;
+
+               $isValid = (!strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting()))) ? TRUE : FALSE;
+               if ($isValid) {
+                       $isValid = $this->isValidSalt($saltedPW);
+               }
+
+               return $isValid;
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_md5.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_md5.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php b/typo3/sysext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php
new file mode 100644 (file)
index 0000000..f12d569
--- /dev/null
@@ -0,0 +1,442 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_salts_phpass"
+ * that provides PHPass salted hashing.
+ *
+ * Derived from Drupal CMS
+ * original license: GNU General Public License (GPL)
+ * @see http://drupal.org/node/29706/
+ *
+ * Based on the Portable PHP password hashing framework
+ * original license: Public Domain
+ * @see http://www.openwall.com/phpass/
+ *
+ * $Id$
+ */
+
+
+/**
+ * Class that implements PHPass salted hashing based on Drupal's
+ * modified Openwall implementation.
+ *
+ * PHPass should work on every system.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ *
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_phpass extends tx_saltedpasswords_abstract_salts implements tx_saltedpasswords_salts {
+       /**
+        * Keeps a string for mapping an int to the corresponding
+        * base 64 character.
+        */
+       const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+       /**
+        * The default log2 number of iterations for password stretching.
+        */
+       const HASH_COUNT = 14;
+
+       /**
+        * The default maximum allowed log2 number of iterations for
+        * password stretching.
+        */
+       const MAX_HASH_COUNT = 24;
+
+       /**
+        * The default minimum allowed log2 number of iterations for
+        * password stretching.
+        */
+       const MIN_HASH_COUNT = 7;
+
+
+       /**
+        * Keeps log2 number
+        * of iterations for password stretching.
+        *
+        * @var integer
+        */
+       static protected $hashCount;
+
+       /**
+        * Keeps maximum allowed log2 number
+        * of iterations for password stretching.
+        *
+        * @var integer
+        */
+       static protected $maxHashCount;
+
+       /**
+        * Keeps minimum allowed log2 number
+        * of iterations for password stretching.
+        *
+        * @var integer
+        */
+       static protected $minHashCount;
+
+       /**
+        * Keeps length of a PHPass salt in bytes.
+        *
+        * @var integer
+        */
+       static protected $saltLengthPhpass = 6;
+
+       /**
+        * Setting string to indicate type of hashing method (PHPass).
+        *
+        * @var string
+        */
+       static protected $settingPhpass = '$P$';
+
+
+       /**
+        * Method applies settings (prefix, hash count) to a salt.
+        *
+        * Overwrites {@link tx_saltedpasswords_salts_md5::applySettingsToSalt()}
+        * with Blowfish specifics.
+        *
+        * @param       string          $salt: a salt to apply setting to
+        * @return      string          salt with setting
+        */
+       protected function applySettingsToSalt($salt) {
+               $saltWithSettings = $salt;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+                       // salt without setting
+               if (strlen($salt) == $reqLenBase64) {
+                               // We encode the final log2 iteration count in base 64.
+                       $itoa64 = $this->getItoa64();
+                       $saltWithSettings = $this->getSetting() . $itoa64[$this->getHashCount()];
+
+                       $saltWithSettings .= $salt;
+               }
+
+               return $saltWithSettings;
+       }
+
+       /**
+        * Method checks if a given plaintext password is correct by comparing it with
+        * a given salted hashed password.
+        *
+        * @param       string          $plainPW: plain-text password to compare with salted hash
+        * @param       string          $saltedHashPW: salted hash to compare plain-text password with
+        * @return      boolean         TRUE, if plain-text password matches the salted hash, otherwise FALSE
+        */
+       public function checkPassword($plainPW, $saltedHashPW) {
+               $hash = $this->cryptPassword($plainPW, $saltedHashPW);
+
+               return ($hash && $saltedHashPW === $hash);
+       }
+
+       /**
+        * Returns wether all prequesites for the hashing methods are matched
+        *
+        * @return      boolean         method available
+        */
+       public function isAvailable() {
+               return TRUE;
+       }
+
+       /**
+        * Hashes a password using a secure stretched hash.
+        *
+        * By using a salt and repeated hashing the password is "stretched". Its
+        * security is increased because it becomes much more computationally costly
+        * for an attacker to try to break the hash by brute-force computation of the
+        * hashes of a large number of plain-text words or strings to find a match.
+        *
+        * @param       string          $password: plain-text password to hash
+        * @param       string          $setting: an existing hash or the output of getGeneratedSalt()
+        * @return      mixed           a string containing the hashed password (and salt)
+        *                                              or boolean FALSE on failure.
+        */
+       protected function cryptPassword($password, $setting) {
+               $saltedPW = NULL;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+                       // Retrieving settings with salt
+               $setting = substr($setting, 0, strlen($this->getSetting()) + 1 + $reqLenBase64);
+
+               $count_log2 = $this->getCountLog2($setting);
+
+                       // Hashes may be imported from elsewhere, so we allow != HASH_COUNT
+               if ($count_log2 >= $this->getMinHashCount() && $count_log2 <= $this->getMaxHashCount()) {
+
+                       $salt = substr($setting, strlen($this->getSetting()) + 1, $reqLenBase64);
+
+                               // We must use md5() or sha1() here since they are the only cryptographic
+                               // primitives always available in PHP 5. To implement our own low-level
+                               // cryptographic function in PHP would result in much worse performance and
+                               // consequently in lower iteration counts and hashes that are quicker to crack
+                               // (by non-PHP code).
+                       $count = 1 << $count_log2;
+
+                       $hash = md5($salt . $password, TRUE);
+                       do {
+                               $hash = md5($hash . $password, TRUE);
+                       } while (--$count);
+
+                       $saltedPW =  $setting . $this->base64Encode($hash, 16);
+
+                               // base64Encode() of a 16 byte MD5 will always be 22 characters.
+                       return (strlen($saltedPW) == 34) ? $saltedPW : FALSE;
+               }
+
+               return $saltedPW;
+       }
+
+       /**
+        * Parses the log2 iteration count from a stored hash or setting string.
+        *
+        * @param       string          $setting: complete hash or a hash's setting string or to get log2 iteration count from
+        * @return      int                     used hashcount for given hash string
+        */
+       protected function getCountLog2($setting) {
+               return strpos(
+                       $this->getItoa64(),
+                       $setting[strlen($this->getSetting())]
+               );
+       }
+
+       /**
+        * Generates a random base 64-encoded salt prefixed and suffixed with settings for the hash.
+        *
+        * Proper use of salts may defeat a number of attacks, including:
+        *  - The ability to try candidate passwords against multiple hashes at once.
+        *  - The ability to use pre-hashed lists of candidate passwords.
+        *  - The ability to determine whether two users have the same (or different)
+        *    password without actually having to guess one of the passwords.
+        *
+        * @return      string          a character string containing settings and a random salt
+        */
+       protected function getGeneratedSalt() {
+               $randomBytes = t3lib_div::generateRandomBytes($this->getSaltLength());
+
+               return $this->base64Encode($randomBytes, $this->getSaltLength());
+       }
+
+       /**
+        * Method returns log2 number of iterations for password stretching.
+        *
+        * @return      integer         log2 number of iterations for password stretching
+        * @see         HASH_COUNT
+        * @see         $hashCount
+        * @see         setHashCount()
+        */
+       public function getHashCount() {
+               return isset(self::$hashCount) ? self::$hashCount : self::HASH_COUNT;
+       }
+
+       /**
+        * Method creates a salted hash for a given plaintext password
+        *
+        * @param       string          $password: plaintext password to create a salted hash from
+        * @param       string          $salt: optional custom salt with setting to use
+        * @return      string          salted hashed password
+        */
+       public function getHashedPassword($password, $salt = NULL) {
+               $saltedPW = NULL;
+
+               if (!empty($password)) {
+                       if (empty($salt) || !$this->isValidSalt($salt)) {
+                               $salt = $this->getGeneratedSalt();
+                       }
+                       $saltedPW = $this->cryptPassword($password, $this->applySettingsToSalt($salt));
+               }
+
+               return $saltedPW;
+       }
+
+       /**
+        * Returns a string for mapping an int to the corresponding base 64 character.
+        *
+        * @return      string          string for mapping an int to the corresponding base 64 character
+        */
+       protected function getItoa64() {
+               return self::ITOA64;
+       }
+
+       /**
+        * Method returns maximum allowed log2 number of iterations for password stretching.
+        *
+        * @return      integer         maximum allowed log2 number of iterations for password stretching
+        * @see         MAX_HASH_COUNT
+        * @see         $maxHashCount
+        * @see         setMaxHashCount()
+        */
+       public function getMaxHashCount() {
+               return isset(self::$maxHashCount) ? self::$maxHashCount : self::MAX_HASH_COUNT;
+       }
+
+       /**
+        * Method returns minimum allowed log2 number of iterations for password stretching.
+        *
+        * @return      integer         minimum allowed log2 number of iterations for password stretching
+        * @see         MIN_HASH_COUNT
+        * @see         $minHashCount
+        * @see         setMinHashCount()
+        */
+       public function getMinHashCount() {
+               return isset(self::$minHashCount) ? self::$minHashCount : self::MIN_HASH_COUNT;
+       }
+
+       /**
+        * Returns length of a Blowfish salt in bytes.
+        *
+        * @return      integer         length of a Blowfish salt in bytes
+        */
+       public function getSaltLength() {
+               return self::$saltLengthPhpass;
+       }
+
+       /**
+        * Returns setting string of PHPass salted hashes.
+        *
+        * @return      string          setting string of PHPass salted hashes
+        */
+       public function getSetting() {
+               return self::$settingPhpass;
+       }
+
+       /**
+        * Checks whether a user's hashed password needs to be replaced with a new hash.
+        *
+        * This is typically called during the login process when the plain text
+        * password is available.  A new hash is needed when the desired iteration
+        * count has changed through a change in the variable $hashCount or
+        * HASH_COUNT or if the user's password hash was generated in an bulk update
+        * with class ext_update.
+        *
+        * @param       string          $passString  salted hash to check if it needs an update
+        * @return      boolean         TRUE if salted hash needs an update, otherwise FALSE
+        */
+       public function isHashUpdateNeeded($passString) {
+                       // Check whether this was an updated password.
+               if ((strncmp($passString, '$P$', 3)) || (strlen($passString) != 34)) {
+                       return TRUE;
+               }
+                       // Check whether the iteration count used differs from the standard number.
+               return ($this->getCountLog2($passString) < $this->getHashCount());
+       }
+
+       /**
+        * Method determines if a given string is a valid salt.
+        *
+        * @param       string          $salt:  string to check
+        * @return      boolean         TRUE if it's valid salt, otherwise FALSE
+        */
+       public function isValidSalt($salt) {
+               $isValid = $skip = FALSE;
+
+               $reqLenBase64 = $this->getLengthBase64FromBytes($this->getSaltLength());
+
+               if (strlen($salt) >= $reqLenBase64) {
+                               // salt with prefixed setting
+                       if (!strncmp('$', $salt, 1)) {
+                               if (!strncmp($this->getSetting(), $salt, strlen($this->getSetting()))) {
+                                       $isValid = TRUE;
+                                       $salt = substr($salt, strrpos($salt, '$') + 2);
+                               } else {
+                                       $skip = TRUE;
+                               }
+                       }
+
+                               // checking base64 characters
+                       if (!$skip && (strlen($salt) >= $reqLenBase64)) {
+                               if (preg_match('/^[' . preg_quote($this->getItoa64(),'/') . ']{' . $reqLenBase64 . ',' . $reqLenBase64 . '}$/', substr($salt, 0, $reqLenBase64))) {
+                                       $isValid = TRUE;
+                               }
+                       }
+               }
+
+               return $isValid;
+       }
+
+       /**
+        * Method determines if a given string is a valid salted hashed password.
+        *
+        * @param       string          $saltedPW: string to check
+        * @return      boolean         TRUE if it's valid salted hashed password, otherwise FALSE
+        */
+       public function isValidSaltedPW($saltedPW) {
+               $isValid = FALSE;
+
+               $isValid = (!strncmp($this->getSetting(), $saltedPW, strlen($this->getSetting()))) ? TRUE : FALSE;
+               if ($isValid) {
+                       $isValid = $this->isValidSalt($saltedPW);
+               }
+
+               return $isValid;
+       }
+
+       /**
+        * Method sets log2 number of iterations for password stretching.
+        *
+        * @param       integer         $hashCount: log2 number of iterations for password stretching to set
+        * @see         HASH_COUNT
+        * @see         $hashCount
+        * @see         getHashCount()
+        */
+       public function setHashCount($hashCount = NULL) {
+               self::$hashCount = !is_NULL($hashCount) && is_int($hashCount) && $hashCount >= $this->getMinHashCount() && $hashCount <= $this->getMaxHashCount() ? $hashCount : self::HASH_COUNT;
+       }
+
+       /**
+        * Method sets maximum allowed log2 number of iterations for password stretching.
+        *
+        * @param       integer         $maxHashCount: maximum allowed log2 number of iterations for password stretching to set
+        * @see         MAX_HASH_COUNT
+        * @see         $maxHashCount
+        * @see         getMaxHashCount()
+        */
+       public function setMaxHashCount($maxHashCount = NULL) {
+               self::$maxHashCount = !is_NULL($maxHashCount) && is_int($maxHashCount) ? $maxHashCount : self::MAX_HASH_COUNT;
+       }
+
+       /**
+        * Method sets minimum allowed log2 number of iterations for password stretching.
+        *
+        * @param       integer         $minHashCount  minimum allowed log2 number of iterations for password stretching to set
+        * @see         MIN_HASH_COUNT
+        * @see         $minHashCount
+        * @see         getMinHashCount()
+        */
+       public function setMinHashCount($minHashCount = NULL) {
+               self::$minHashCount = !is_NULL($minHashCount) && is_int($minHashCount) ? $minHashCount : self::MIN_HASH_COUNT;
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/salts/interfaces/interface.tx_saltedpasswords_salts.php b/typo3/sysext/saltedpasswords/classes/salts/interfaces/interface.tx_saltedpasswords_salts.php
new file mode 100644 (file)
index 0000000..c07ddf5
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  (c) 2009 Steffen Ritter <info@rs-websystems.de>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains interface "tx_saltedpasswords_salts" to be used in
+ * classes that provide salted hashing.
+ *
+ * $Id: interface.tx_saltedpasswords_salts.php 25462 2009-10-16 14:54:15Z ohader $
+ */
+
+
+/**
+ * Interface with public methods needed to be implemented
+ * in a salting hashing class.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ * @author      Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+interface tx_saltedpasswords_salts {
+       /**
+        * Method checks if a given plaintext password is correct by comparing it with
+        * a given salted hashed password.
+        *
+        * @param       string          $plainPW:  plain-text password to compare with salted hash
+        * @param       string          $saltedHashPW:  salted hash to compare plain-text password with
+        * @return      boolean         TRUE, if plaintext password is correct, otherwise FALSE
+        */
+       public function checkPassword($plainPW, $saltedHashPW);
+
+       /**
+        * Returns length of required salt.
+        *
+        * @return      integer         length of required salt
+        */
+       public function getSaltLength();
+
+       /**
+        * Returns wether all prequesites for the hashing methods are matched
+        *
+        * @return      boolean         method available
+        */
+       public function isAvailable();
+
+
+       /**
+        * Method creates a salted hash for a given plaintext password
+        *
+        * @param       string          $password:  plaintext password to create a salted hash from
+        * @param       string          $salt:  optional custom salt to use
+        * @return      string          salted hashed password
+        */
+       public function getHashedPassword($password, $salt = NULL);
+
+       /**
+        * Checks whether a user's hashed password needs to be replaced with a new hash.
+        *
+        * This is typically called during the login process when the plain text
+        * password is available.  A new hash is needed when the desired iteration
+        * count has changed through a change in the variable $hashCount or
+        * HASH_COUNT or if the user's password hash was generated in an bulk update
+        * with class ext_update.
+        *
+        * @param       string          $passString  salted hash to check if it needs an update
+        * @return      boolean         TRUE if salted hash needs an update, otherwise FALSE
+        */
+       public function isHashUpdateNeeded($passString);
+
+       /**
+        * Method determines if a given string is a valid salt
+        *
+        * @param       string          $salt: string to check
+        * @return      boolean         TRUE if it's valid salt, otherwise FALSE
+        */
+       public function isValidSalt($salt);
+
+       /**
+        * Method determines if a given string is a valid salted hashed password.
+        *
+        * @param       string          $saltedPW: string to check
+        * @return      boolean         TRUE if it's valid salted hashed password, otherwise FALSE
+        */
+       public function isValidSaltedPW($saltedPW);
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/interfaces/interface.tx_saltedpasswords_salts.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/salts/interfaces/interface.tx_saltedpasswords_salts.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/ext_autoload.php b/typo3/sysext/saltedpasswords/ext_autoload.php
new file mode 100644 (file)
index 0000000..5f60acd
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+// DO NOT CHANGE THIS FILE! It is automatically generated by extdeveval::buildAutoloadRegistry.
+// This file was generated on 2009-09-16 14:24
+
+$extensionPath = t3lib_extMgm::extPath('saltedpasswords');
+return array(
+       'tx_saltedpasswords_div' => $extensionPath . 'classes/class.tx_saltedpasswords_div.php',
+       'tx_saltedpasswords_emconfhelper' => $extensionPath . 'classes/class.tx_saltedpasswords_emconfhelper.php',
+       'tx_saltedpasswords_abstract_salts' => $extensionPath . 'classes/salts/class.tx_saltedpasswords_abstract_salts.php',
+       'tx_saltedpasswords_salts_blowfish' => $extensionPath . 'classes/salts/class.tx_saltedpasswords_salts_blowfish.php',
+       'tx_saltedpasswords_salts_factory' => $extensionPath . 'classes/salts/class.tx_saltedpasswords_salts_factory.php',
+       'tx_saltedpasswords_salts_md5' => $extensionPath . 'classes/salts/class.tx_saltedpasswords_salts_md5.php',
+       'tx_saltedpasswords_salts_phpass' => $extensionPath . 'classes/salts/class.tx_saltedpasswords_salts_phpass.php',
+       'tx_saltedpasswords_salts' => $extensionPath . 'classes/salts/interfaces/interface.tx_saltedpasswords_salts.php',
+       'tx_saltedpasswords_eval' => $extensionPath . 'classes/eval/class.tx_saltedpasswords_eval.php',
+       'tx_saltedpasswords_eval_be' => $extensionPath . 'classes/eval/class.tx_saltedpasswords_eval_be.php',
+       'tx_saltedpasswords_eval_fe' => $extensionPath . 'classes/eval/class.tx_saltedpasswords_eval_fe.php',
+       'tx_saltedpasswords_sv1' => $extensionPath . 'sv1/class.tx_saltedpasswords_sv1.php',
+       'tx_saltedpasswords_div_testcase' => $extensionPath . 'tests/tx_saltedpasswords_div_testcase.php',
+       'tx_saltedpasswords_salts_blowfish_testcase' => $extensionPath . 'tests/tx_saltedpasswords_salts_blowfish_testcase.php',
+       'tx_saltedpasswords_salts_factory_testcase' => $extensionPath . 'tests/tx_saltedpasswords_salts_factory_testcase.php',
+       'tx_saltedpasswords_salts_md5_testcase' => $extensionPath . 'tests/tx_saltedpasswords_salts_md5_testcase.php',
+       'tx_saltedpasswords_salts_phpass_testcase' => $extensionPath . 'tests/tx_saltedpasswords_salts_phpass_testcase.php',
+);
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/ext_conf_template.txt b/typo3/sysext/saltedpasswords/ext_conf_template.txt
new file mode 100644 (file)
index 0000000..692e12d
--- /dev/null
@@ -0,0 +1,46 @@
+# cat=basic/enable; type=user[EXT:saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php:tx_saltedpasswords_emconfhelper->checkConfigurationFrontend]; label=Frontend configuration check
+checkConfigurationFE=0
+
+# cat=basic/enable; type=user[EXT:saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php:tx_saltedpasswords_emconfhelper->checkConfigurationBackend]; label=Backend configuration check
+checkConfigurationBE=0
+
+# cat=basic/enable; type=boolean; label=Enable FE: Enable SaltedPasswords in the frontend
+FE.enabled = 1
+
+# cat=basic/enable; type=user[EXT:saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php:tx_saltedpasswords_emconfhelper->buildHashMethodSelectorFE]; label=Hashing method for the frontend: Defines salted hashing method to use. Choose "Portable PHP password hashing" to stay compatible with other CMS (e.g. Drupal, Wordpress). Choose "MD5 salted hashing" to reuse TYPO3 passwords for OS level authentication (other servers could use TYPO3 passwords). Choose "Blowfish salted hashing" for advanced security to reuse passwords on OS level (Blowfish might not be supported on your system TODO).
+FE.saltedPWHashingMethod = tx_saltedpasswords_salts_phpass
+
+# cat=basic/enable; type=boolean; label=Enable BE: Enable SaltedPasswords in the backend
+BE.enabled = 1
+
+# cat=basic/enable; type=user[EXT:saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php:tx_saltedpasswords_emconfhelper->buildHashMethodSelectorBE]; label=Hashing method for the backend: Defines salted hashing method to use. Choose "Portable PHP password hashing" to stay compatible with other CMS (e.g. Drupal, Wordpress). Choose "MD5 salted hashing" to reuse TYPO3 passwords for OS level authentication (other servers could use TYPO3 passwords). Choose "Blowfish salted hashing" for advanced security to reuse passwords on OS level (Blowfish might not be supported on your system TODO).
+BE.saltedPWHashingMethod = tx_saltedpasswords_salts_phpass
+
+# cat=advanced Frontend; type=user[EXT:saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php:tx_saltedpasswords_emconfhelper->checkConfigurationFrontend]; label=Frontend configuration check
+checkConfigurationFE2=0
+
+# cat=advanced Frontend; type=boolean; label=Force salted passwords: Enforce usage of SaltedPasswords. Old MD5 hashed passwords will stop working.
+FE.forceSalted = 0
+
+# cat=advanced Frontend; type=boolean; label=Exclusive FE usage: If enabled and authentication fails, no further authentication service will be tried.
+FE.onlyAuthService = 0
+
+# cat=advanced Frontend; type=boolean; label=Update FE user passwords: Keep existing FE user passwords but automatically convert them to the salted hash format during authentication (will not work if forceSalted is used).
+FE.updatePasswd = 1
+
+# cat=advanced Backend; type=user[EXT:saltedpasswords/classes/class.tx_saltedpasswords_emconfhelper.php:tx_saltedpasswords_emconfhelper->checkConfigurationBackend]; label=Backend configuration check
+checkConfigurationBE2=0
+
+# cat=advanced Backend; type=boolean; label=Force salted passwords: Enforce usage of SaltedPasswords. Old MD5 hashed passwords will stop working.
+BE.forceSalted = 0
+
+# cat=advanced Backend; type=boolean; label=Exclusive BE usage: If enabled and authentication fails, no further authentication service will be tried.
+BE.onlyAuthService = 0
+
+# cat=advanced Backend; type=boolean; label=Update BE user passwords: Keep existing BE user passwords but automatically convert them to the salted hash format during authentication (will not work if forceSalted is used).
+BE.updatePasswd = 1
+
+TSConstantEditor.advanced {
+  description = <span style="background:red; padding:1px 2px; color:#fff; font-weight:bold;">1</span> Install tool has hardcoded md5 hashing, enabling this setting will prevent use of a install-tool-created BE user.<br />Currently same is for changin password with user setup module unless you use pending patch!
+  1=forceSalted
+}
diff --git a/typo3/sysext/saltedpasswords/ext_emconf.php b/typo3/sysext/saltedpasswords/ext_emconf.php
new file mode 100644 (file)
index 0000000..62e13b1
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+########################################################################
+# Extension Manager/Repository config file for ext "saltedpasswords".
+#
+# Auto generated 17-11-2009 20:38
+#
+# Manual updates:
+# Only the data in the array - everything else is removed by next
+# writing. "version" and "dependencies" must not be touched!
+########################################################################
+
+$EM_CONF[$_EXTKEY] = array(
+       'title' => 'Salted user password hashes',
+       'description' => 'Uses a password hashing framework for storing passwords. Integrates into the system extension "felogin". Use SSL or rsaauth to secure datatransfer! Please read the manual first!',
+       'category' => 'services',
+       'shy' => 0,
+       'dependencies' => 'cms',
+       'conflicts' => 'kb_md5fepw,newloginbox,pt_feauthcryptpw,t3sec_saltedpw',
+       'priority' => '',
+       'loadOrder' => '',
+       'module' => '',
+       'state' => 'stable',
+       'uploadfolder' => 0,
+       'createDirs' => '',
+       'modify_tables' => 'fe_users,be_users',
+       'clearCacheOnLoad' => 1,
+       'lockType' => '',
+       'author' => 'Marcus Krause, Steffen Ritter',
+       'author_email' => 'marcus#exp2009@t3sec.info',
+       'author_company' => 'TYPO3 Security Team',
+       'CGLcompliance' => '',
+       'CGLcompliance_note' => '',
+       'version' => '1.0.0',
+       'constraints' => array(
+               'depends' => array(
+                       'typo3' => '4.3.0-0.0.0',
+                       'php' => '5.2.0-0.0.0',
+                       'cms' => '',
+               ),
+               'conflicts' => array(
+                       'kb_md5fepw' => '',
+                       'newloginbox' => '',
+                       'pt_feauthcryptpw' => '',
+                       't3sec_saltedpw' => '',
+               ),
+               'suggests' => array(
+                       'rsaauth' => '',
+               ),
+       ),
+       '_md5_values_when_last_written' => 'a:25:{s:9:"ChangeLog";s:4:"6895";s:16:"ext_autoload.php";s:4:"8275";s:21:"ext_conf_template.txt";s:4:"1a62";s:12:"ext_icon.gif";s:4:"4324";s:17:"ext_localconf.php";s:4:"05a6";s:14:"ext_tables.php";s:4:"8646";s:14:"ext_tables.sql";s:4:"a0d9";s:13:"locallang.xml";s:4:"3d55";s:40:"classes/class.tx_saltedpasswords_div.php";s:4:"44d6";s:49:"classes/class.tx_saltedpasswords_emconfhelper.php";s:4:"f612";s:46:"classes/eval/class.tx_saltedpasswords_eval.php";s:4:"f63a";s:49:"classes/eval/class.tx_saltedpasswords_eval_be.php";s:4:"e7b9";s:49:"classes/eval/class.tx_saltedpasswords_eval_fe.php";s:4:"abc8";s:57:"classes/salts/class.tx_saltedpasswords_abstract_salts.php";s:4:"bc87";s:57:"classes/salts/class.tx_saltedpasswords_salts_blowfish.php";s:4:"c2b8";s:56:"classes/salts/class.tx_saltedpasswords_salts_factory.php";s:4:"0d12";s:52:"classes/salts/class.tx_saltedpasswords_salts_md5.php";s:4:"e87c";s:55:"classes/salts/class.tx_saltedpasswords_salts_phpass.php";s:4:"a6de";s:63:"classes/salts/interfaces/interface.tx_saltedpasswords_salts.php";s:4:"79e3";s:36:"sv1/class.tx_saltedpasswords_sv1.php";s:4:"938e";s:41:"tests/tx_saltedpasswords_div_testcase.php";s:4:"e64d";s:52:"tests/tx_saltedpasswords_salts_blowfish_testcase.php";s:4:"2a8a";s:51:"tests/tx_saltedpasswords_salts_factory_testcase.php";s:4:"989e";s:47:"tests/tx_saltedpasswords_salts_md5_testcase.php";s:4:"8802";s:50:"tests/tx_saltedpasswords_salts_phpass_testcase.php";s:4:"d786";}',
+       'suggests' => array(
+       ),
+);
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/ext_icon.gif b/typo3/sysext/saltedpasswords/ext_icon.gif
new file mode 100644 (file)
index 0000000..8ff6f46
Binary files /dev/null and b/typo3/sysext/saltedpasswords/ext_icon.gif differ
diff --git a/typo3/sysext/saltedpasswords/ext_localconf.php b/typo3/sysext/saltedpasswords/ext_localconf.php
new file mode 100644 (file)
index 0000000..b65d273
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+if (!defined ('TYPO3_MODE')) {
+       die ('Access denied.');
+}
+
+       // form evaluation function for fe_users
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals']['tx_saltedpasswords_eval_fe'] = 'EXT:saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_fe.php';
+
+       // form evaluation function for be_users
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals']['tx_saltedpasswords_eval_be'] = 'EXT:saltedpasswords/classes/eval/class.tx_saltedpasswords_eval_be.php';
+
+       // hook for processing "forgotPassword" in felogin
+$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['felogin']['password_changed'][] = 'EXT:saltedpasswords/classes/class.tx_saltedpasswords_div.php:tx_saltedpasswords_div->feloginForgotPasswordHook';
+
+       // registering all available hashes to factory
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods'] = array(
+       'tx_saltedpasswords_salts_md5'          => 'EXT:saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_md5.php:tx_saltedpasswords_salts_md5',
+       'tx_saltedpasswords_salts_blowfish'     => 'EXT:saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_blowfish.php:tx_saltedpasswords_salts_blowfish',
+       'tx_saltedpasswords_salts_phpass'       => 'EXT:saltedpasswords/classes/salts/class.tx_saltedpasswords_salts_phpass.php:tx_saltedpasswords_salts_phpass'
+);
+
+t3lib_extMgm::addService(
+       'saltedpasswords',
+       'auth',
+       'tx_saltedpasswords_sv1',
+       array(
+               'title' => 'FE/BE Authentification salted',
+               'description' => 'Salting of passwords for Frontend and Backend',
+               'subtype' => 'authUserFE,authUserBE',
+               'available' => TRUE,
+               'priority' => 70, // must be higher than tx_sv_auth (50) and rsaauth (60) but lower than OpenID (75)
+               'quality' => 70,
+               'os' => '',
+               'exec' => '',
+               'classFile' => t3lib_extMgm::extPath('saltedpasswords').'sv1/class.tx_saltedpasswords_sv1.php',
+               'className' => 'tx_saltedpasswords_sv1',
+       )
+);
+
+// Use popup window to refresh login instead of the AJAX relogin:
+$TYPO3_CONF_VARS['BE']['showRefreshLoginPopup'] = 1;
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/ext_tables.php b/typo3/sysext/saltedpasswords/ext_tables.php
new file mode 100644 (file)
index 0000000..6754a49
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+if (!defined ('TYPO3_MODE')) {
+       die ('Access denied.');
+}
+
+t3lib_div::loadTCA('fe_users');
+$TCA['fe_users']['columns']['password']['config']['max'] = 60;
+
+if (tx_saltedpasswords_div::isUsageEnabled('FE')) {
+       $TCA['fe_users']['columns']['password']['config']['eval'] = 'trim,required,tx_saltedpasswords_eval_fe,password';
+}
+
+t3lib_div::loadTCA('be_users');
+$TCA['be_users']['columns']['password']['config']['max'] = 60;
+
+if (tx_saltedpasswords_div::isUsageEnabled('BE')) {
+       $TCA['be_users']['columns']['password']['config']['eval'] = 'trim,required,tx_saltedpasswords_eval_be,password';
+}
+
+$GLOBALS['TYPO3_USER_SETTINGS']['columns']['password']['eval'] = 'tx_saltedpasswords_eval_be';
+$GLOBALS['TYPO3_USER_SETTINGS']['columns']['password2']['eval'] = '';
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/ext_tables.sql b/typo3/sysext/saltedpasswords/ext_tables.sql
new file mode 100644 (file)
index 0000000..b0de93a
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TABLE fe_users (
+       password varchar(60) DEFAULT '' NOT NULL
+);
+
+CREATE TABLE be_users (
+       password varchar(60) DEFAULT '' NOT NULL
+);
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/locallang.xml b/typo3/sysext/saltedpasswords/locallang.xml
new file mode 100644 (file)
index 0000000..9fb0bff
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>\r
+<T3locallang>\r
+       <meta type="array">\r
+               <type>module</type>\r
+               <description>Language labels for extension 'saltedpasswords'</description>\r
+       </meta>\r
+       <data type="array">\r
+               <languageKey index="default" type="array">\r
+                       <label index="ext.saltedpasswords.title.tx_saltedpasswords_salts_phpass">Portable PHP password hashing (phpass)</label>\r
+                       <label index="ext.saltedpasswords.title.tx_saltedpasswords_salts_md5">MD5 salted hashing (secure)</label>\r
+                       <label index="ext.saltedpasswords.title.tx_saltedpasswords_salts_blowfish">Blowfish salted hashing (advanced)</label>\r
+               </languageKey>\r
+       </data>\r
+</T3locallang>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php b/typo3/sysext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php
new file mode 100644 (file)
index 0000000..76a3e72
--- /dev/null
@@ -0,0 +1,310 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) Marcus Krause (marcus#exp2009@t3sec.info)
+*  (c) Steffen Ritter (info@rs-websystems.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains authentication service class for salted hashed passwords.
+ *
+ * $Id$
+ */
+
+
+/**
+ * Class implements salted-password hashes authentication service.
+ *
+ * @author     Marcus Krause <marcus#exp2009@t3sec.info>
+ * @author     Steffen Ritter <info@rs-websystems.de>
+ *
+ * @since      2009-06-14
+ * @package    TYPO3
+ * @subpackage tx_saltedpasswords
+ */
+class tx_saltedpasswords_sv1 extends tx_sv_authbase {
+       /**
+        * Keeps class name.
+        *
+        * @var string
+        */
+       public $prefixId = 'tx_saltedpasswords_sv1';
+
+       /**
+        * Keeps path to this script relative to the extension directory.
+        *
+        * @var string
+        */
+       public $scriptRelPath = 'sv1/class.tx_saltedpasswords_sv1.php';
+
+       /**
+        * Keeps extension key.
+        *
+        * @var string
+        */
+       public $extKey = 'saltedpasswords';
+
+       /**
+        * Keeps extension configuration.
+        *
+        * @var mixed
+        */
+       protected $extConf;
+
+       /**
+        * An instance of the salted hashing method.
+        * This member is set in the getSaltingInstance() function.
+        *
+        * @var tx_saltedpasswords_abstract_salts
+        */
+       protected $objInstanceSaltedPW = NULL;
+
+
+       /**
+        * Checks if service is available. In case of this service we check that
+        * following prerequesties are fulfilled:
+        * - loginSecurityLevel of according TYPO3_MODE is set to normal
+        *
+        * @return      boolean         TRUE if service is available
+        */
+       public function init() {
+               $available = FALSE;
+
+               if (tx_saltedpasswords_div::isUsageEnabled()) {
+                       $available = TRUE;
+                       $this->extConf = tx_saltedpasswords_div::returnExtConf();
+               }
+
+               return $available ? parent::init() : FALSE;
+       }
+
+       /**
+        * Checks the login data with the user record data for builtin login method.
+        *
+        * @param       array           user data array
+        * @param       array           login data array
+        * @param       string          login security level (optional)
+        * @return      boolean         TRUE if login data matched
+        */
+       function compareUident(array $user, array $loginData, $security_level = 'normal') {
+               $validPasswd = FALSE;
+
+                       // could be merged; still here to clarify
+               if (!strcmp(TYPO3_MODE, 'BE')) {
+                       $password = $loginData['uident_text'];
+               } else if (!strcmp(TYPO3_MODE, 'FE')) {
+                       $password = $loginData['uident_text'];
+               }
+
+                       // determine method used for given salted hashed password
+               $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance($user['password']);
+
+                       // existing record is in format of Salted Hash password
+               if (is_object($this->objInstanceSaltedPW)) {
+                       $validPasswd = $this->objInstanceSaltedPW->checkPassword($password,$user['password']);
+
+                       $defaultHashingClassName = tx_saltedpasswords_div::getDefaultSaltingHashingMethod();
+                       $skip = FALSE;
+
+                               // test for wrong salted hashing method
+                       if ($validPasswd && !(get_class($this->objInstanceSaltedPW) == $defaultHashingClassName) || (is_subclass_of($this->objInstanceSaltedPW, $defaultHashingClassName))) {
+                                       // instanciate default method class
+                               $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL);
+                               $this->updatePassword(
+                                       intval($user['uid']),
+                                       array('password' => $this->objInstanceSaltedPW->getHashedPassword($password))
+                               );
+                       }
+
+                       if ($validPasswd && !$skip && $this->objInstanceSaltedPW->isHashUpdateNeeded($user['password'])) {
+                               $this->updatePassword(
+                                       intval($user['uid']),
+                                       array('password' => $this->objInstanceSaltedPW->getHashedPassword($password))
+                               );
+                       }
+                       // we process also clear-text, md5 and passwords updated by Portable PHP password hashing framework
+               } else if (!intval($this->extConf['forceSalted'])) {
+
+                               // stored password is in deprecated salted hashing method
+                       if (t3lib_div::inList('C$,M$', substr($user['password'], 0, 2))) {
+
+                                       // instanciate default method class
+                               $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(substr($user['password'], 1));
+
+                                       // md5
+                               if (!strcmp(substr($user['password'], 0, 1), 'M')) {
+                                       $validPasswd = $this->objInstanceSaltedPW->checkPassword(md5($password), substr($user['password'], 1));
+                               } else {
+                                       $validPasswd = $this->objInstanceSaltedPW->checkPassword(md5($password), substr($user['password'], 1));
+                               }
+
+                               // password is stored as md5
+                       } else if (preg_match('/[0-9abcdef]{32,32}/', $user['password'])) {
+                               $validPasswd = (!strcmp(md5($password), $user['password']) ? TRUE : FALSE);
+
+                               // password is stored plain or unrecognized format
+                       } else {
+                               $validPasswd = (!strcmp($password, $user['password']) ? TRUE : FALSE);
+                       }
+                               // should we store the new format value in DB?
+                       if ($validPasswd && intval($this->extConf['updatePasswd'])) {
+                                       // instanciate default method class
+                               $this->objInstanceSaltedPW = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL);
+                               $this->updatePassword(
+                                       intval($user['uid']), 
+                                       array('password' => $this->objInstanceSaltedPW->getHashedPassword($password))
+                               );
+                       }
+               }
+
+               return $validPasswd;
+       }
+
+       /**
+        * Method adds a further authUser method.
+        *
+        * Will return one of following authentication status codes:
+        *  - 0 - authentication failure
+        *  - 100 - just go on. User is not authenticated but there is still no reason to stop
+        *  - 200 - the service was able to authenticate the user
+        *
+        * @param       array           Array containing FE user data of the logged user.
+        * @return      integer         authentication statuscode, one of 0,100 and 200
+        */
+       public function authUser(array $user) {
+               $OK = 100;
+               $validPasswd = FALSE;
+
+               if ($this->pObj->security_level == 'rsa' && t3lib_extMgm::isLoaded('rsaauth')) {
+                       require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/backends/class.tx_rsaauth_backendfactory.php');
+                       require_once(t3lib_extMgm::extPath('rsaauth') . 'sv1/storage/class.tx_rsaauth_storagefactory.php');
+
+                       $backend = tx_rsaauth_backendfactory::getBackend();
+                       $storage = tx_rsaauth_storagefactory::getStorage();
+                               // Preprocess the password
+                       $password = $this->login['uident'];
+                       $key = $storage->get();
+                       if ($key != NULL && substr($password, 0, 4) == 'rsa:') {
+                               // Decode password and pass to parent
+                               $decryptedPassword = $backend->decrypt($key, substr($password, 4));
+                               $this->login['uident_text'] = $decryptedPassword;
+                       }
+               }
+
+               if ($this->login['uident'] && $this->login['uname']) {
+                       if (!empty($this->login['uident_text'])) {
+                               $validPasswd = $this->compareUident(
+                                       $user,
+                                       $this->login
+                               );
+                       }
+
+                       if (!$validPasswd && intval($this->extConf['onlyAuthService'])) {
+                                       // Failed login attempt (wrong password) - no delegation to further services
+                               $this->writeLog(
+                                       TYPO3_MODE . ' Authentication failed - wrong password for username \'%s\'',
+                                       $this->login['uname']
+                               );
+                               $OK = 0;
+                       } else if(!$validPasswd) {
+                                       // Failed login attempt (wrong password)
+                               $this->writeLog(
+                                       "Login-attempt from %s, username '%s', password not accepted!",
+                                       $this->authInfo['REMOTE_ADDR'], $this->login['uname']
+                               );
+                       } else if ($validPasswd && $user['lockToDomain'] && strcasecmp($user['lockToDomain'], $this->authInfo['HTTP_HOST'])) {
+                                       // Lock domain didn't match, so error:
+                               $this->writeLog(
+                                       "Login-attempt from %s, username '%s', locked domain '%s' did not match '%s'!",
+                                       $this->authInfo['REMOTE_ADDR'], $this->login['uname'], $user['lockToDomain'], $this->authInfo['HTTP_HOST']
+                               );
+                               $OK = 0;
+                       } else if ($validPasswd) {
+                               $this->writeLog(
+                                       TYPO3_MODE . ' Authentication successful for username \'%s\'',
+                                       $this->login['uname']
+                               );
+                               $OK = 200;
+                       }
+               }
+
+               return $OK;
+       }
+
+       /**
+        * Method updates a FE/BE user record - in this case a new password string will be set.
+        *
+        * @param       integer         $uid: uid of user record that will be updated
+        * @param       mixed           $updateFields: Field values as key=>value pairs to be updated in database
+        * @return      void
+        */
+       protected function updatePassword($uid, $updateFields) {
+               if (TYPO3_MODE === 'BE') {
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 'be_users', sprintf('uid = %u', $uid), $updateFields);
+               } else {
+                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 'fe_users', sprintf('uid = %u', $uid), $updateFields);
+               }
+
+               t3lib_div::devLog(sprintf('Automatic password update for %s user with uid %u', TYPO3_MODE, $uid), $this->extKey, 1);
+       }
+
+       /**
+        * Writes log message. Destination log depends on the current system mode.
+        * For FE the function writes to the admin panel log. For BE messages are
+        * sent to the system log. If developer log is enabled, messages are also
+        * sent there.
+        *
+        * This function accepts variable number of arguments and can format
+        * parameters. The syntax is the same as for sprintf()
+        *
+        * @param       string          $message: Message to output
+        * @return      void
+        * @see sprintf()
+        * @see t3lib::divLog()
+        * @see t3lib_div::sysLog()
+        * @see t3lib_timeTrack::setTSlogMessage()
+        */
+       function writeLog($message) {
+               if (func_num_args() > 1) {
+                       $params = func_get_args();
+                       array_shift($params);
+                       $message = vsprintf($message, $params);
+               }
+
+               if (TYPO3_MODE === 'BE') {
+                       t3lib_div::sysLog($message, $this->extKey, 1);
+               } else {
+                       $GLOBALS['TT']->setTSlogMessage($message);
+               }
+
+               if (TYPO3_DLOG) {
+                       t3lib_div::devLog($message, $this->extKey, 1);
+               }
+       }
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php']) {
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/saltedpasswords/sv1/class.tx_saltedpasswords_sv1.php']);
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_div_testcase.php b/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_div_testcase.php
new file mode 100644 (file)
index 0000000..2820795
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains testcases for "tx_saltedpasswords_div"
+ * that provides various helper functions.
+ *
+ * $Id$
+ */
+
+/**
+ * Testcases for class tx_saltedpasswords_div.
+ *
+ * @author  Marcus Krause <marcus#exp2009@t3sec.info>
+ * @package  TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_div_testcase extends tx_phpunit_testcase {
+       protected $backupGlobals = TRUE;
+
+       public function setUp() {
+
+       }
+
+       public function tearDown() {
+
+       }
+
+       /**
+        * @test
+        */
+       public function doesReturnExtConfReturnDefaultSettingsIfNoExtensionConfigurationIsFound() {
+               $this->assertEquals(
+                       tx_saltedpasswords_div::returnExtConfDefaults(),
+                       tx_saltedpasswords_div::returnExtConf('TEST_MODE')
+               );
+       }
+
+       /**
+        * @test
+        */
+       public function doesReturnExtConfReturnMergedSettingsIfExtensionConfigurationIsFound() {
+               $setting = array('setting' => 1);
+
+               $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['saltedpasswords'] = serialize(
+                       array('TEST_MODE.' => $setting)
+               );
+
+               $this->assertEquals(
+                       array_merge(tx_saltedpasswords_div::returnExtConfDefaults(), $setting),
+                       tx_saltedpasswords_div::returnExtConf('TEST_MODE')
+               );
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_blowfish_testcase.php b/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_blowfish_testcase.php
new file mode 100644 (file)
index 0000000..7823b7e
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_salts_blowfish"
+ * that provides Blowfish salted hashing.
+ *
+ * $Id$
+ */
+
+/**
+ * Testcases for class tx_saltedpasswords_salts_blowfish.
+ *
+ * @author  Marcus Krause <marcus#exp2009@t3sec.info>
+ * @package  TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_blowfish_testcase extends tx_phpunit_testcase {
+
+
+       /**
+        * Keeps instance of object to test.
+        *
+        * @var tx_saltedpasswords_salts_blowfish
+        */
+       protected $objectInstance = NULL;
+
+
+       /**
+        * Sets up the fixtures for this testcase.
+        *
+        * @return      void
+        */
+       public function setUp() {
+               $this->objectInstance = t3lib_div::makeInstance('tx_saltedpasswords_salts_blowfish');
+       }
+
+       /**
+        * Tears down objects and settings created in this testcase.
+        *
+        * @return      void
+        */
+       public function tearDown() {
+               unset($this->objectInstance);
+       }
+
+       /**
+        * Marks tests as skipped if the blowfish method is not available.
+        *
+        * @return      void
+        */
+       protected function skipTestIfBlowfishIsNotAvailable() {
+               if (!CRYPT_BLOWFISH) {
+                       $this->markTestSkipped('Blowfish is not supported on your platform.');
+               }
+       }
+
+       /**
+        * @test
+        */
+       public function hasCorrectBaseClass() {
+
+               $hasCorrectBaseClass = (0 === strcmp('tx_saltedpasswords_salts_blowfish', get_class($this->objectInstance))) ? TRUE : FALSE;
+
+                       // XCLASS ?
+               if (!$hasCorrectBaseClass && FALSE != get_parent_class($this->objectInstance)) {
+                       $hasCorrectBaseClass = is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_blowfish');
+               }
+
+               $this->assertTrue($hasCorrectBaseClass);
+       }
+
+       /**
+        * @test
+        */
+       public function nonZeroSaltLength() {
+               $this->assertTrue($this->objectInstance->getSaltLength() > 0);
+       }
+
+       /**
+        * @test
+        */
+       public function emptyPasswordResultsInNullSaltedPassword() {
+               $password = '';
+               $this->assertNull($this->objectInstance->getHashedPassword($password));
+       }
+
+       /**
+        * @test
+        */
+       public function nonEmptyPasswordResultsInNonNullSaltedPassword() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'a';
+               $this->assertNotNull($this->objectInstance->getHashedPassword($password));
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructure() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+
+                       // custom salt without setting
+               $randomBytes = t3lib_div::generateRandomBytes($this->objectInstance->getSaltLength());
+               $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
+               $this->assertTrue($this->objectInstance->isValidSalt($salt));
+
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password, $salt);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForMaximumHashCount() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $maxHashCount = $this->objectInstance->getMaxHashCount();
+               $this->objectInstance->setHashCount($maxHashCount);
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForMinimumHashCount() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $minHashCount = $this->objectInstance->getMinHashCount();
+               $this->objectInstance->setHashCount($minHashCount);
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function authenticationWithValidPassword() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function authenticationWithNonValidPassword() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $password1 = $password . 'INVALID';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function passwordVariationsResultInDifferentHashes() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $pad = 'a';
+               $password = '';
+               $criticalPwLength = 0;
+                       // We're using a constant salt.
+               $saltedHashPWPrevious = $saltedHashPWCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
+
+               for ($i = 0; $i <= 128; $i += 8) {
+                       $password = str_repeat($pad, max($i, 1));
+                       $saltedHashPWPrevious = $saltedHashPWCurrent;
+                       $saltedHashPWCurrent = $this->objectInstance->getHashedPassword($password, $salt);
+                       if ($i > 0 && 0 == strcmp($saltedHashPWPrevious, $saltedHashPWCurrent)) {
+                               $criticalPwLength = $i;
+                               break;
+                       }
+               }
+               $this->assertTrue(($criticalPwLength == 0) || ($criticalPwLength > 32), 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
+       }
+
+       /**
+        * @test
+        */
+       public function modifiedMinHashCount() {
+               $minHashCount = $this->objectInstance->getMinHashCount();
+               $this->objectInstance->setMinHashCount($minHashCount - 1);
+               $this->assertTrue($this->objectInstance->getMinHashCount() < $minHashCount);
+               $this->objectInstance->setMinHashCount($minHashCount + 1);
+               $this->assertTrue($this->objectInstance->getMinHashCount() > $minHashCount);
+       }
+
+       /**
+        * @test
+        */
+       public function modifiedMaxHashCount() {
+               $maxHashCount = $this->objectInstance->getMaxHashCount();
+               $this->objectInstance->setMaxHashCount($maxHashCount + 1);
+               $this->assertTrue($this->objectInstance->getMaxHashCount() > $maxHashCount);
+               $this->objectInstance->setMaxHashCount($maxHashCount - 1);
+               $this->assertTrue($this->objectInstance->getMaxHashCount() < $maxHashCount);
+       }
+
+       /**
+        * @test
+        */
+       public function modifiedHashCount() {
+               $hashCount = $this->objectInstance->getHashCount();
+               $this->objectInstance->setMaxHashCount($hashCount + 1);
+               $this->objectInstance->setHashCount($hashCount + 1);
+               $this->assertTrue($this->objectInstance->getHashCount() > $hashCount);
+               $this->objectInstance->setMinHashCount($hashCount - 1);
+               $this->objectInstance->setHashCount($hashCount - 1);
+               $this->assertTrue($this->objectInstance->getHashCount() < $hashCount);
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function updateNecessityForValidSaltedPassword() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function updateNecessityForIncreasedHashcount() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $increasedHashCount = $this->objectInstance->getHashCount() + 1;
+               $this->objectInstance->setMaxHashCount($increasedHashCount);
+               $this->objectInstance->setHashCount($increasedHashCount);
+               $this->assertTrue($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function updateNecessityForDecreasedHashcount() {
+               $this->skipTestIfBlowfishIsNotAvailable();
+
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $decreasedHashCount = $this->objectInstance->getHashCount() - 1;
+               $this->objectInstance->setMinHashCount($decreasedHashCount);
+               $this->objectInstance->setHashCount($decreasedHashCount);
+               $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_factory_testcase.php b/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_factory_testcase.php
new file mode 100644 (file)
index 0000000..2052fa8
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains class "tx_saltedpasswords_salts_factory"
+ * that provides a salted hashing method factory.
+ *
+ * $Id$
+ */
+
+/**
+ * Testcases for class tx_saltedpasswords_salts_factory.
+ *
+ * @author      Marcus Krause <marcus#exp2009@t3sec.info>
+ *
+ * @since      2009-09-06
+ * @package     TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_factory_testcase extends tx_phpunit_testcase {
+
+
+       /**
+        * Keeps instance of object to test.
+        *
+        * @var tx_saltedpasswords_abstract_salts
+        */
+       protected $objectInstance = NULL;
+
+       /**
+        * Sets up the fixtures for this testcase.
+        *
+        * @return      void
+        */
+       protected function setUp() {
+               $this->objectInstance = tx_saltedpasswords_salts_factory::getSaltingInstance();
+       }
+
+       /**
+        * Tears down objects and settings created in this testcase.
+        *
+        * @return      void
+        */
+       public function tearDown() {
+               unset($this->objectInstance);
+       }
+
+       /**
+        * @test
+        */
+       public function objectInstanceNotNull() {
+               $this->assertNotNull($this->objectInstance);
+       }
+
+       /**
+        * @test
+        */
+       public function objectInstanceExtendsAbstractClass() {
+               $this->assertTrue(is_subclass_of($this->objectInstance, 'tx_saltedpasswords_abstract_salts'));
+       }
+
+       /**
+        * @test
+        */
+       public function objectInstanceImplementsInterface() {
+               $this->assertTrue(method_exists($this->objectInstance, 'checkPassword'), 'Missing method checkPassword() from interface tx_saltedpasswords_salts.');
+               $this->assertTrue(method_exists($this->objectInstance, 'isHashUpdateNeeded'), 'Missing method isHashUpdateNeeded() from interface tx_saltedpasswords_salts.');
+               $this->assertTrue(method_exists($this->objectInstance, 'isValidSalt') , 'Missing method isValidSalt() from interface tx_saltedpasswords_salts.');
+               $this->assertTrue(method_exists($this->objectInstance, 'isValidSaltedPW') , 'Missing method isValidSaltedPW() from interface tx_saltedpasswords_salts.');
+               $this->assertTrue(method_exists($this->objectInstance, 'getHashedPassword'), 'Missing method getHashedPassword() from interface tx_saltedpasswords_salts.');
+               $this->assertTrue(method_exists($this->objectInstance, 'getSaltLength'), 'Missing method getSaltLength() from interface tx_saltedpasswords_salts.');
+       }
+
+       /**
+        * @test
+        */
+       public function base64EncodeReturnsProperLength() {
+                       // 3 Bytes should result in a 6 char length base64 encoded string
+                       // used for MD5 and PHPass salted hashing
+               $byteLength = 3;
+               $reqLengthBase64 = intval(ceil(($byteLength * 8) / 6));
+               $randomBytes = t3lib_div::generateRandomBytes($byteLength);
+               $this->assertTrue(strlen($this->objectInstance->base64Encode($randomBytes, $byteLength)) == $reqLengthBase64);
+
+                       // 16 Bytes should result in a 22 char length base64 encoded string
+                       // used for Blowfish salted hashing
+               $byteLength = 16;
+               $reqLengthBase64 = intval(ceil(($byteLength * 8) / 6));
+               $randomBytes = t3lib_div::generateRandomBytes($byteLength);
+               $this->assertTrue(strlen($this->objectInstance->base64Encode($randomBytes, $byteLength)) == $reqLengthBase64);
+       }
+
+       /**
+        * @test
+        */
+       public function objectInstanceForMD5Salts() {
+               $saltMD5 = '$1$rasmusle$rISCgZzpwk3UhDidwXvin0';
+               $this->objectInstance = tx_saltedpasswords_salts_factory::getSaltingInstance($saltMD5);
+
+               $this->assertTrue((get_class($this->objectInstance) == 'tx_saltedpasswords_salts_md5') || (is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_md5')) );
+       }
+
+       /**
+        * @test
+        */
+       public function objectInstanceForBlowfishSalts() {
+               $saltBlowfish = '$2a$07$abcdefghijklmnopqrstuuIdQV69PAxWYTgmnoGpe0Sk47GNS/9ZW';
+               $this->objectInstance = tx_saltedpasswords_salts_factory::getSaltingInstance($saltBlowfish);
+               $this->assertTrue((get_class($this->objectInstance) == 'tx_saltedpasswords_salts_blowfish') || (is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_blowfish')) );
+       }
+
+       /**
+        * @test
+        */
+       public function objectInstanceForPhpassSalts() {
+               $saltPhpass = '$P$CWF13LlG/0UcAQFUjnnS4LOqyRW43c.';
+               $this->objectInstance = tx_saltedpasswords_salts_factory::getSaltingInstance($saltPhpass);
+               $this->assertTrue((get_class($this->objectInstance) == 'tx_saltedpasswords_salts_phpass') || (is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_phpass')) );
+       }
+
+       /**
+        * @test
+        */
+       public function resettingFactoryInstanceSucceeds() {
+               $defaultClassNameToUse = tx_saltedpasswords_div::getDefaultSaltingHashingMethod();
+
+               $saltedPW = '';
+               if ($defaultClassNameToUse == 'tx_saltedpasswords_salts_md5') {
+                       $saltedPW = '$P$CWF13LlG/0UcAQFUjnnS4LOqyRW43c.';
+               } else {
+                       $saltedPW = '$1$rasmusle$rISCgZzpwk3UhDidwXvin0';
+               }
+               $this->objectInstance = tx_saltedpasswords_salts_factory::getSaltingInstance($saltedPW);
+
+                       // resetting
+               $this->objectInstance = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL);
+               $this->assertTrue((get_class($this->objectInstance) == $defaultClassNameToUse) || (is_subclass_of($this->objectInstance, $defaultClassNameToUse)));
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_md5_testcase.php b/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_md5_testcase.php
new file mode 100644 (file)
index 0000000..93a62b0
--- /dev/null
@@ -0,0 +1,194 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains testcases for "tx_saltedpasswords_salts_md5"
+ * that provides MD5 salted hashing.
+ *
+ * $Id$
+ */
+
+/**
+ * Testcases for class tx_saltedpasswords_salts_md5.
+ *
+ * @author  Marcus Krause <marcus#exp2009@t3sec.info>
+ * @package  TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_md5_testcase extends tx_phpunit_testcase {
+
+
+       /**
+        * Keeps instance of object to test.
+        *
+        * @var tx_saltedpasswords_salts_md5
+        */
+       protected $objectInstance = NULL;
+
+
+       /**
+        * Sets up the fixtures for this testcase.
+        *
+        * @return      void
+        */
+       public function setUp() {
+               $this->objectInstance = t3lib_div::makeInstance('tx_saltedpasswords_salts_md5');
+       }
+
+       /**
+        * Tears down objects and settings created in this testcase.
+        *
+        * @return      void
+        */
+       public function tearDown() {
+               unset($this->objectInstance);
+       }
+
+       /**
+        * Prepares a message to be shown when a salted hashing is not supported.
+        *
+        * @return  string     empty string if salted hashing method is available, otherwise an according warning
+        */
+       protected function getWarningWhenMethodUnavailable() {
+               $warningMsg = '';
+               if (!CRYPT_MD5) {
+                       $warningMsg .= 'MD5 is not supported on your platform. '
+                                               .  'Then, some of the md5 tests will fail.';
+               }
+       }
+
+       /**
+        * @test
+        */
+       public function hasCorrectBaseClass() {
+
+               $hasCorrectBaseClass = (0 === strcmp('tx_saltedpasswords_salts_md5', get_class($this->objectInstance))) ? TRUE : FALSE;
+
+                       // XCLASS ?
+               if (!$hasCorrectBaseClass && FALSE != get_parent_class($this->objectInstance)) {
+                       $hasCorrectBaseClass = is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_md5');
+               }
+
+               $this->assertTrue($hasCorrectBaseClass);
+       }
+
+       /**
+        * @test
+        */
+       public function nonZeroSaltLength() {
+               $this->assertTrue($this->objectInstance->getSaltLength() > 0);
+       }
+
+       /**
+        * @test
+        */
+       public function emptyPasswordResultsInNullSaltedPassword() {
+               $password = '';
+               $this->assertNull($this->objectInstance->getHashedPassword($password));
+       }
+
+       /**
+        * @test
+        */
+       public function nonEmptyPasswordResultsInNonNullSaltedPassword() {
+               $password = 'a';
+               $this->assertNotNull($this->objectInstance->getHashedPassword($password), $this->getWarningWhenMethodUnavailable());
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructure() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW), $this->getWarningWhenMethodUnavailable());
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting() {
+               $password = 'password';
+
+                       // custom salt without setting
+               $randomBytes = t3lib_div::generateRandomBytes($this->objectInstance->getSaltLength());
+               $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
+               $this->assertTrue($this->objectInstance->isValidSalt($salt), $this->getWarningWhenMethodUnavailable());
+
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password, $salt);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW), $this->getWarningWhenMethodUnavailable());
+       }
+
+       /**
+        * @test
+        */
+       public function authenticationWithValidPassword() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPW), $this->getWarningWhenMethodUnavailable());
+       }
+
+       /**
+        * @test
+        */
+       public function authenticationWithNonValidPassword() {
+               $password = 'password';
+               $password1 = $password . 'INVALID';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPW), $this->getWarningWhenMethodUnavailable());
+       }
+
+       /**
+        * @test
+        */
+       public function passwordVariationsResultInDifferentHashes() {
+               $pad = 'a';
+               $password = '';
+               $criticalPwLength = 0;
+                       // We're using a constant salt.
+               $saltedHashPWPrevious = $saltedHashPWCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
+               for ($i = 0; $i <= 128; $i += 8) {
+                       $password = str_repeat($pad, max($i, 1));
+                       $saltedHashPWPrevious = $saltedHashPWCurrent;
+                       $saltedHashPWCurrent = $this->objectInstance->getHashedPassword($password, $salt);
+                       if ($i > 0 && 0 == strcmp($saltedHashPWPrevious, $saltedHashPWCurrent)) {
+                               $criticalPwLength = $i;
+                               break;
+                       }
+               }
+               $this->assertTrue(($criticalPwLength == 0) || ($criticalPwLength > 32), $this->getWarningWhenMethodUnavailable() . 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
+       }
+
+       /**
+        * @test
+        */
+       public function noUpdateNecessityForMd5() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_phpass_testcase.php b/typo3/sysext/saltedpasswords/tests/tx_saltedpasswords_salts_phpass_testcase.php
new file mode 100644 (file)
index 0000000..4468e23
--- /dev/null
@@ -0,0 +1,273 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Marcus Krause <marcus#exp2009@t3sec.info>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Contains testcases for "tx_saltedpasswords_salts_phpass"
+ * that provides PHPass salted hashing.
+ *
+ * $Id$
+ */
+
+/**
+ * Testcases for class tx_saltedpasswords_salts_phpass.
+ *
+ * @author  Marcus Krause <marcus#exp2009@t3sec.info>
+ * @package  TYPO3
+ * @subpackage  tx_saltedpasswords
+ */
+class tx_saltedpasswords_salts_phpass_testcase extends tx_phpunit_testcase {
+
+
+       /**
+        * Keeps instance of object to test.
+        *
+        * @var tx_saltedpasswords_salts_phpass
+        */
+       protected $objectInstance = NULL;
+
+
+       /**
+        * Sets up the fixtures for this testcase.
+        *
+        * @return      void
+        */
+       public function setUp() {
+               $this->objectInstance = t3lib_div::makeInstance('tx_saltedpasswords_salts_phpass');
+       }
+
+       /**
+        * Tears down objects and settings created in this testcase.
+        *
+        * @return      void
+        */
+       public function tearDown() {
+               unset($this->objectInstance);
+       }
+
+       /**
+        * @test
+        */
+       public function hasCorrectBaseClass() {
+
+               $hasCorrectBaseClass = (0 === strcmp('tx_saltedpasswords_salts_phpass', get_class($this->objectInstance))) ? TRUE : FALSE;
+
+                       // XCLASS ?
+               if (!$hasCorrectBaseClass && FALSE != get_parent_class($this->objectInstance)) {
+                       $hasCorrectBaseClass = is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_phpass');
+               }
+
+               $this->assertTrue($hasCorrectBaseClass);
+       }
+
+       /**
+        * @test
+        */
+       public function nonZeroSaltLength() {
+               $this->assertTrue($this->objectInstance->getSaltLength() > 0);
+       }
+
+       /**
+        * @test
+        */
+       public function emptyPasswordResultsInNullSaltedPassword() {
+               $password = '';
+               $this->assertNull($this->objectInstance->getHashedPassword($password));
+       }
+
+       /**
+        * @test
+        */
+       public function nonEmptyPasswordResultsInNonNullSaltedPassword() {
+               $password = 'a';
+               $this->assertNotNull($this->objectInstance->getHashedPassword($password));
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructure() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting() {
+               $password = 'password';
+
+                       // custom salt without setting
+               $randomBytes = t3lib_div::generateRandomBytes($this->objectInstance->getSaltLength());
+               $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
+               $this->assertTrue($this->objectInstance->isValidSalt($salt));
+
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password, $salt);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForMaximumHashCount() {
+               $password = 'password';
+               $maxHashCount = $this->objectInstance->getMaxHashCount();
+               $this->objectInstance->setHashCount($maxHashCount);
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function createdSaltedHashOfProperStructureForMinimumHashCount() {
+               $password = 'password';
+               $minHashCount = $this->objectInstance->getMinHashCount();
+               $this->objectInstance->setHashCount($minHashCount);
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function authenticationWithValidPassword() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function authenticationWithNonValidPassword() {
+               $password = 'password';
+               $password1 = $password . 'INVALID';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function passwordVariationsResultInDifferentHashes() {
+               $pad = 'a';
+               $password = '';
+               $criticalPwLength = 0;
+                       // We're using a constant salt.
+               $saltedHashPWPrevious = $saltedHashPWCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
+
+               for ($i = 0; $i <= 128; $i += 8) {
+                       $password = str_repeat($pad, max($i, 1));
+                       $saltedHashPWPrevious = $saltedHashPWCurrent;
+                       $saltedHashPWCurrent = $this->objectInstance->getHashedPassword($password, $salt);
+                       if ($i > 0 && 0 == strcmp($saltedHashPWPrevious, $saltedHashPWCurrent)) {
+                               $criticalPwLength = $i;
+                               break;
+                       }
+               }
+               $this->assertTrue(($criticalPwLength == 0) || ($criticalPwLength > 32), 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
+       }
+
+       /**
+        * @test
+        */
+       public function modifiedMinHashCount() {
+               $minHashCount = $this->objectInstance->getMinHashCount();
+               $this->objectInstance->setMinHashCount($minHashCount - 1);
+               $this->assertTrue($this->objectInstance->getMinHashCount() < $minHashCount);
+               $this->objectInstance->setMinHashCount($minHashCount + 1);
+               $this->assertTrue($this->objectInstance->getMinHashCount() > $minHashCount);
+       }
+
+       /**
+        * @test
+        */
+       public function modifiedMaxHashCount() {
+               $maxHashCount = $this->objectInstance->getMaxHashCount();
+               $this->objectInstance->setMaxHashCount($maxHashCount + 1);
+               $this->assertTrue($this->objectInstance->getMaxHashCount() > $maxHashCount);
+               $this->objectInstance->setMaxHashCount($maxHashCount - 1);
+               $this->assertTrue($this->objectInstance->getMaxHashCount() < $maxHashCount);
+       }
+
+       /**
+        * @test
+        */
+       public function modifiedHashCount() {
+               $hashCount = $this->objectInstance->getHashCount();
+               $this->objectInstance->setMaxHashCount($hashCount + 1);
+               $this->objectInstance->setHashCount($hashCount + 1);
+               $this->assertTrue($this->objectInstance->getHashCount() > $hashCount);
+               $this->objectInstance->setMinHashCount($hashCount - 1);
+               $this->objectInstance->setHashCount($hashCount - 1);
+               $this->assertTrue($this->objectInstance->getHashCount() < $hashCount);
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function updateNecessityForValidSaltedPassword() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+       }
+
+       /**
+        * @test
+        */
+       public function updateNecessityForIncreasedHashcount() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $increasedHashCount = $this->objectInstance->getHashCount() + 1;
+               $this->objectInstance->setMaxHashCount($increasedHashCount);
+               $this->objectInstance->setHashCount($increasedHashCount);
+               $this->assertTrue($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+
+       /**
+        * @test
+        */
+       public function updateNecessityForDecreasedHashcount() {
+               $password = 'password';
+               $saltedHashPW = $this->objectInstance->getHashedPassword($password);
+               $decreasedHashCount = $this->objectInstance->getHashCount() - 1;
+               $this->objectInstance->setMinHashCount($decreasedHashCount);
+               $this->objectInstance->setHashCount($decreasedHashCount);
+               $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPW));
+                       // reset hashcount
+               $this->objectInstance->setHashCount(NULL);
+       }
+}
+?>
\ No newline at end of file