diff --git a/Core/Frameworks/Baikal/Core/BcryptAuth.php b/Core/Frameworks/Baikal/Core/BcryptAuth.php new file mode 100755 index 000000000..4a53a639c --- /dev/null +++ b/Core/Frameworks/Baikal/Core/BcryptAuth.php @@ -0,0 +1,84 @@ + + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class BcryptAuth extends \Sabre\DAV\Auth\Backend\AbstractBasic { + + /** + * Reference to PDO connection + * + * @var PDO + */ + protected $pdo; + + /** + * PDO table name we'll be using + * + * @var string + */ + protected $tableName; + + /** + * Authentication realm + * + * @var string + */ + protected $authRealm; + + /** + * Creates the backend object. + * + * If the filename argument is passed in, it will parse out the specified file fist. + * + * @param PDO $pdo + * @param string $tableName The PDO table name to use + */ + function __construct(\PDO $pdo, $authRealm, $tableName = 'users') { + + $this->pdo = $pdo; + $this->tableName = $tableName; + $this->authRealm = $authRealm; + } + + /** + * Validates a username and password + * + * This method should return true or false depending on if login + * succeeded. + * + * @param string $username + * @param string $password + * @return bool + */ + function validateUserPass($username, $password) { + + $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM ' . $this->tableName . ' WHERE username = ?'); + $stmt->execute([$username]); + $result = $stmt->fetchAll(); + if (!count($result)) return false; + + if (substr($result[0]['digesta1'], 0, 4) == '$2y$') { + $check = password_verify($password, $result[0]['digesta1']); + } else { + $hash = md5($username . ':' . $this->authRealm . ':' . $password); + if ($result[0]['digesta1'] === $hash) { + $check = true; + } + } + if ($check == true) { + return true; + } + return false; + + } + +} diff --git a/Core/Frameworks/Baikal/Core/Server.php b/Core/Frameworks/Baikal/Core/Server.php old mode 100644 new mode 100755 index b5792bad8..eccc633d0 --- a/Core/Frameworks/Baikal/Core/Server.php +++ b/Core/Frameworks/Baikal/Core/Server.php @@ -132,10 +132,10 @@ function start() { protected function initServer() { if ($this->authType === 'Basic') { - $authBackend = new \Baikal\Core\PDOBasicAuth($this->pdo, $this->authRealm); + $authBackend = new \Baikal\Core\BcryptAuth($this->pdo, $this->authRealm); } else { - $authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo); - $authBackend->setRealm($this->authRealm); + $authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo); + $authBackend->setRealm($this->authRealm); } $principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($this->pdo); diff --git a/Core/Frameworks/Baikal/Core/Tools.php b/Core/Frameworks/Baikal/Core/Tools.php index 9c1caae03..6b1fa30d6 100644 --- a/Core/Frameworks/Baikal/Core/Tools.php +++ b/Core/Frameworks/Baikal/Core/Tools.php @@ -27,15 +27,18 @@ namespace Baikal\Core; -class Tools { - static function &db() { - return $GLOBALS["pdo"]; +class Tools +{ + static function &db() + { + return $GLOBALS['pdo']; } - static function assertEnvironmentIsOk() { + static function assertEnvironmentIsOk() + { # Asserting Baikal Context - if (!defined("BAIKAL_CONTEXT") || BAIKAL_CONTEXT !== true) { - die("Bootstrap.php may not be included outside the Baikal context"); + if (!defined('BAIKAL_CONTEXT') || BAIKAL_CONTEXT !== true) { + die('Bootstrap.php may not be included outside the Baikal context'); } # Asserting PDO @@ -50,21 +53,24 @@ static function assertEnvironmentIsOk() { } } - static function configureEnvironment() { + static function configureEnvironment() + { set_exception_handler('\Baikal\Core\Tools::handleException'); - ini_set("error_reporting", E_ALL); + ini_set('error_reporting', E_ALL); } - static function handleException($exception) { - echo "
" . $exception . "
";
+    static function handleException($exception)
+    {
+        echo '
' . $exception . '
';
     }
 
-    static function assertBaikalIsOk() {
+    static function assertBaikalIsOk()
+    {
 
         # DB connexion has not been asserted earlier by Flake, to give us a chance to trigger the install tool
         # We assert it right now
-        if (!\Flake\Framework::isDBInitialized() && (!defined("BAIKAL_CONTEXT_INSTALL") || BAIKAL_CONTEXT_INSTALL === false)) {
-            throw new \Exception("Fatal error: no connection to a database is available.");
+        if (!\Flake\Framework::isDBInitialized() && (!defined('BAIKAL_CONTEXT_INSTALL') || BAIKAL_CONTEXT_INSTALL === false)) {
+            throw new \Exception('Fatal error: no connection to a database is available.');
         }
 
         # Asserting that the database is structurally complete
@@ -73,51 +79,52 @@ static function assertBaikalIsOk() {
         #}
 
         # Asserting config file exists
-        if (!file_exists(PROJECT_PATH_SPECIFIC . "config.php")) {
-            throw new \Exception("Specific/config.php does not exist. Please use the Install tool to create it.");
+        if (!file_exists(PROJECT_PATH_SPECIFIC . 'config.php')) {
+            throw new \Exception('Specific/config.php does not exist. Please use the Install tool to create it.');
         }
 
         # Asserting config file is readable
-        if (!is_readable(PROJECT_PATH_SPECIFIC . "config.php")) {
+        if (!is_readable(PROJECT_PATH_SPECIFIC . 'config.php')) {
             throw new \Exception("Specific/config.php is not readable. Please give read permissions to httpd user on file 'Specific/config.php'.");
         }
 
         # Asserting config file is writable
-        if (!is_writable(PROJECT_PATH_SPECIFIC . "config.php")) {
+        if (!is_writable(PROJECT_PATH_SPECIFIC . 'config.php')) {
             throw new \Exception("Specific/config.php is not writable. Please give write permissions to httpd user on file 'Specific/config.php'.");
         }
 
         # Asserting system config file exists
-        if (!file_exists(PROJECT_PATH_SPECIFIC . "config.system.php")) {
-            throw new \Exception("Specific/config.system.php does not exist. Please use the Install tool to create it.");
+        if (!file_exists(PROJECT_PATH_SPECIFIC . 'config.system.php')) {
+            throw new \Exception('Specific/config.system.php does not exist. Please use the Install tool to create it.');
         }
 
         # Asserting system config file is readable
-        if (!is_readable(PROJECT_PATH_SPECIFIC . "config.system.php")) {
+        if (!is_readable(PROJECT_PATH_SPECIFIC . 'config.system.php')) {
             throw new \Exception("Specific/config.system.php is not readable. Please give read permissions to httpd user on file 'Specific/config.system.php'.");
         }
 
         # Asserting system config file is writable
-        if (!is_writable(PROJECT_PATH_SPECIFIC . "config.system.php")) {
+        if (!is_writable(PROJECT_PATH_SPECIFIC . 'config.system.php')) {
             throw new \Exception("Specific/config.system.php is not writable. Please give write permissions to httpd user on file 'Specific/config.system.php'.");
         }
     }
 
-    static function getRequiredTablesList() {
+    static function getRequiredTablesList()
+    {
         return [
-            "addressbooks",
-            "calendarobjects",
-            "calendars",
-            "cards",
-            "groupmembers",
-            "locks",
-            "principals",
-            "users",
+            'addressbooks',
+            'calendarobjects',
+            'calendars',
+            'cards',
+            'groupmembers',
+            'locks',
+            'principals',
+            'users',
         ];
     }
 
-    static function isDBStructurallyComplete(\Flake\Core\Database $oDB) {
-
+    static function isDBStructurallyComplete(\Flake\Core\Database $oDB)
+    {
         $aRequiredTables = self::getRequiredTablesList();
         $aPresentTables = $oDB->tables();
 
@@ -129,19 +136,23 @@ static function isDBStructurallyComplete(\Flake\Core\Database $oDB) {
         return true;
     }
 
-    static function bashPrompt($prompt) {
+    static function bashPrompt($prompt)
+    {
         echo $prompt;
         @flush();
         @ob_flush();
         $confirmation = @trim(fgets(STDIN));
+
         return $confirmation;
     }
 
-    static function bashPromptSilent($prompt = "Enter Password:") {
+    static function bashPromptSilent($prompt = 'Enter Password:')
+    {
         $command = "/usr/bin/env bash -c 'echo OK'";
 
         if (rtrim(shell_exec($command)) !== 'OK') {
             trigger_error("Can't invoke bash");
+
             return;
         }
 
@@ -151,20 +162,21 @@ static function bashPromptSilent($prompt = "Enter Password:") {
 
         $password = rtrim(shell_exec($command));
         echo "\n";
+
         return $password;
     }
 
-    static function getCopyrightNotice($sLinePrefixChar = "#", $sLineSuffixChar = "", $sOpening = false, $sClosing = false) {
-
+    static function getCopyrightNotice($sLinePrefixChar = '#', $sLineSuffixChar = '', $sOpening = false, $sClosing = false)
+    {
         if ($sOpening === false) {
-            $sOpening = str_repeat("#", 78);
+            $sOpening = str_repeat('#', 78);
         }
 
         if ($sClosing === false) {
-            $sClosing = str_repeat("#", 78);
+            $sClosing = str_repeat('#', 78);
         }
 
-        $iYear = date("Y");
+        $iYear = date('Y');
 
         $sCode = << [
             "type"    => "string",
-            "comment" => "HTTP authentication type for WebDAV; default Digest"
+            "comment" => "HTTP authentication type for WebDAV; default Basic"
+        ],
+        "BAIKAL_USER_AUTH_TYPE" => [
+        "type"    => "string",
+        "comment" => "Authentication mechanism for user accounts"
         ],
         "BAIKAL_ADMIN_PASSWORDHASH" => [
             "type"    => "string",
@@ -62,7 +66,8 @@ class Standard extends \Baikal\Model\Config {
         "BAIKAL_CARD_ENABLED"       => true,
         "BAIKAL_CAL_ENABLED"        => true,
         "BAIKAL_INVITE_FROM"        => "",
-        "BAIKAL_DAV_AUTH_TYPE"      => "Digest",
+        "BAIKAL_DAV_AUTH_TYPE"      => "Basic",
+        "BAIKAL_USER_AUTH_TYPE"     => "Bcrypt",
         "BAIKAL_ADMIN_PASSWORDHASH" => ""
     ];
 
@@ -96,7 +101,14 @@ function formMorphologyForThisModelInstance() {
         $oMorpho->add(new \Formal\Element\Listbox([
             "prop"    => "BAIKAL_DAV_AUTH_TYPE",
             "label"   => "WebDAV authentication type",
-            "options" => ["Digest", "Basic"]
+            "options" => ["Basic", "Digest"]
+        ]));
+
+        $oMorpho->add(new \Formal\Element\Listbox([
+            "prop"    => "BAIKAL_USER_AUTH_TYPE",
+            "label"   => "Password Storage Hash Type",
+            "options" => ["Bcrypt", "MD5"],
+            "help"    => "If set to BCrypt, WebDAV must be set to BASIC."
         ]));
 
         $oMorpho->add(new \Formal\Element\Password([
@@ -132,14 +144,13 @@ function set($sProp, $sValue) {
             # Special handling for password and passwordconfirm
 
             if ($sProp === "BAIKAL_ADMIN_PASSWORDHASH" && $sValue !== "") {
-                parent::set(
-                    "BAIKAL_ADMIN_PASSWORDHASH",
-                    \BaikalAdmin\Core\Auth::hashAdminPassword($sValue)
-                );
-            }
-
+                        parent::set(
+                            "BAIKAL_ADMIN_PASSWORDHASH",
+                            password_hash($sValue, PASSWORD_BCRYPT)
+                        );
+                  }
             return $this;
-        }
+          }
 
         parent::set($sProp, $sValue);
     }
@@ -191,8 +202,12 @@ protected static function getDefaultConfig() {
 # CalDAV invite From: mail address (comment or leave blank to disable notifications)
 define("BAIKAL_INVITE_FROM", "noreply@$_SERVER[SERVER_NAME]");
 
-# WebDAV authentication type; default Digest
-define("BAIKAL_DAV_AUTH_TYPE", "Digest");
+# WebDAV authentication type; default Basic
+define("BAIKAL_DAV_AUTH_TYPE", "Basic");
+
+
+# Baikal user password hash method; default bcrypt
+define("BAIKAL_USER_AUTH_TYPE", "Bcrypt");
 
 # Baïkal Web admin password hash; Set via Baïkal Web Admin
 define("BAIKAL_ADMIN_PASSWORDHASH", "");
diff --git a/Core/Frameworks/Baikal/Model/User.php b/Core/Frameworks/Baikal/Model/User.php
old mode 100644
new mode 100755
index f744f9983..ebcb65962
--- a/Core/Frameworks/Baikal/Model/User.php
+++ b/Core/Frameworks/Baikal/Model/User.php
@@ -104,10 +104,17 @@ function set($sPropName, $sPropValue) {
             # Special handling for password and passwordconfirm
 
             if ($sPropName === "password" && $sPropValue !== "") {
-                parent::set(
-                    "digesta1",
-                    $this->getPasswordHashForPassword($sPropValue)
-                );
+                if (BAIKAL_USER_AUTH_TYPE === "Bcrypt") {
+                        parent::set(
+                            "digesta1",
+                            password_hash($sPropValue, PASSWORD_BCRYPT)
+                        );
+                } else {
+                        parent::set(
+                            "digesta1",
+                            $this->getPasswordHashForPassword($sPropValue)
+                        );
+                }
             }
 
             return $this;
diff --git a/Core/Frameworks/BaikalAdmin/Core/Auth.php b/Core/Frameworks/BaikalAdmin/Core/Auth.php
old mode 100644
new mode 100755
index 5f4e81c7a..493671945
--- a/Core/Frameworks/BaikalAdmin/Core/Auth.php
+++ b/Core/Frameworks/BaikalAdmin/Core/Auth.php
@@ -45,9 +45,18 @@ static function authenticate() {
         $sUser = \Flake\Util\Tools::POST("login");
         $sPass = \Flake\Util\Tools::POST("password");
 
+    if (substr(BAIKAL_ADMIN_PASSWORDHASH, 0, 4) == "$2y$") {
+            $sPassHash = password_verify($sPass, BAIKAL_ADMIN_PASSWORDHASH);
+    } else {
         $sPassHash = self::hashAdminPassword($sPass);
+        if ($sPassHash === BAIKAL_ADMIN_PASSWORDHASH) {
+            $sPassHash = true;
+        } else {
+            $sPassHash = false;
+        }
+    }
 
-        if ($sUser === "admin" && $sPassHash === BAIKAL_ADMIN_PASSWORDHASH) {
+        if ($sUser === "admin" && $sPassHash == true) {
             $_SESSION["baikaladminauth"] = md5(BAIKAL_ADMIN_PASSWORDHASH);
             return true;
         }
@@ -66,7 +75,7 @@ static function hashAdminPassword($sPassword) {
         } else {
             $sAuthRealm = "BaikalDAV";    # Fallback to default value; useful when initializing App, as all constants are not set yet
         }
-
+    
         return md5('admin:' . $sAuthRealm . ':' . $sPassword);
     }