diff --git a/PasswordHash.php b/PasswordHash.php --- a/PasswordHash.php +++ b/PasswordHash.php @@ -1,253 +1,253 @@ in 2004-2006 and placed in # the public domain. Revised in subsequent years, still public domain. # # There's absolutely no warranty. # # The homepage URL for this framework is: # # http://www.openwall.com/phpass/ # # Please be sure to update the Version line if you edit this file in any way. # It is suggested that you leave the main version number intact, but indicate # your project name (after the slash) and add your own revision information. # # Please do not change the "private" password hashing method implemented in # here, thereby making your hashes incompatible. However, if you must, please # change the hash type identifier (the "$P$") to something different. # # Obviously, since this code is in the public domain, the above are not # requirements (there can be none), but merely suggestions. # class PasswordHash { var $itoa64; var $iteration_count_log2; var $portable_hashes; var $random_state; function PasswordHash($iteration_count_log2, $portable_hashes) { $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) $iteration_count_log2 = 8; $this->iteration_count_log2 = $iteration_count_log2; $this->portable_hashes = $portable_hashes; $this->random_state = microtime(); if (function_exists('getmypid')) $this->random_state .= getmypid(); } function get_random_bytes($count) { $output = ''; if (is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) { $output = fread($fh, $count); fclose($fh); } if (strlen($output) < $count) { $output = ''; for ($i = 0; $i < $count; $i += 16) { $this->random_state = md5(microtime() . $this->random_state); $output .= pack('H*', md5($this->random_state)); } $output = substr($output, 0, $count); } return $output; } function encode64($input, $count) { $output = ''; $i = 0; do { $value = ord($input[$i++]); $output .= $this->itoa64[$value & 0x3f]; if ($i < $count) $value |= ord($input[$i]) << 8; $output .= $this->itoa64[($value >> 6) & 0x3f]; if ($i++ >= $count) break; if ($i < $count) $value |= ord($input[$i]) << 16; $output .= $this->itoa64[($value >> 12) & 0x3f]; if ($i++ >= $count) break; $output .= $this->itoa64[($value >> 18) & 0x3f]; } while ($i < $count); return $output; } function gensalt_private($input) { $output = '$P$'; $output .= $this->itoa64[min($this->iteration_count_log2 + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; $output .= $this->encode64($input, 6); return $output; } function crypt_private($password, $setting) { $output = '*0'; - if (substr($setting, 0, 2) == $output) + if (substr($setting, 0, 2) === $output) $output = '*1'; $id = substr($setting, 0, 3); # We use "$P$", phpBB3 uses "$H$" for the same thing - if ($id != '$P$' && $id != '$H$') + if ($id !== '$P$' && $id !== '$H$') return $output; $count_log2 = strpos($this->itoa64, $setting[3]); if ($count_log2 < 7 || $count_log2 > 30) return $output; $count = 1 << $count_log2; $salt = substr($setting, 4, 8); - if (strlen($salt) != 8) + if (strlen($salt) !== 8) return $output; # We're kind of forced to use MD5 here since it's the only # cryptographic primitive available in all versions of PHP # currently in use. To implement our own low-level crypto # 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). if (PHP_VERSION >= '5') { $hash = md5($salt . $password, TRUE); do { $hash = md5($hash . $password, TRUE); } while (--$count); } else { $hash = pack('H*', md5($salt . $password)); do { $hash = pack('H*', md5($hash . $password)); } while (--$count); } $output = substr($setting, 0, 12); $output .= $this->encode64($hash, 16); return $output; } function gensalt_extended($input) { $count_log2 = min($this->iteration_count_log2 + 8, 24); # This should be odd to not reveal weak DES keys, and the # maximum valid value is (2**24 - 1) which is odd anyway. $count = (1 << $count_log2) - 1; $output = '_'; $output .= $this->itoa64[$count & 0x3f]; $output .= $this->itoa64[($count >> 6) & 0x3f]; $output .= $this->itoa64[($count >> 12) & 0x3f]; $output .= $this->itoa64[($count >> 18) & 0x3f]; $output .= $this->encode64($input, 3); return $output; } function gensalt_blowfish($input) { # This one needs to use a different order of characters and a # different encoding scheme from the one in encode64() above. # We care because the last character in our encoded string will # only represent 2 bits. While two known implementations of # bcrypt will happily accept and correct a salt string which # has the 4 unused bits set to non-zero, we do not want to take # chances and we also do not want to waste an additional byte # of entropy. $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = '$2a$'; $output .= chr(ord('0') + $this->iteration_count_log2 / 10); $output .= chr(ord('0') + $this->iteration_count_log2 % 10); $output .= '$'; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (1); return $output; } function HashPassword($password) { $random = ''; - if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) { $random = $this->get_random_bytes(16); $hash = crypt($password, $this->gensalt_blowfish($random)); - if (strlen($hash) == 60) + if (strlen($hash) === 60) return $hash; } - if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (CRYPT_EXT_DES === 1 && !$this->portable_hashes) { if (strlen($random) < 3) $random = $this->get_random_bytes(3); $hash = crypt($password, $this->gensalt_extended($random)); - if (strlen($hash) == 20) + if (strlen($hash) === 20) return $hash; } if (strlen($random) < 6) $random = $this->get_random_bytes(6); $hash = $this->crypt_private($password, $this->gensalt_private($random)); - if (strlen($hash) == 34) + if (strlen($hash) === 34) return $hash; # Returning '*' on error is safe here, but would _not_ be safe # in a crypt(3)-like function used _both_ for generating new # hashes and for validating passwords against existing hashes. return '*'; } function CheckPassword($password, $stored_hash) { $hash = $this->crypt_private($password, $stored_hash); - if ($hash[0] == '*') + if ($hash[0] === '*') $hash = crypt($password, $stored_hash); - return $hash == $stored_hash; + return $hash === $stored_hash; } } ?> diff --git a/ajax/settings.php b/ajax/settings.php --- a/ajax/settings.php +++ b/ajax/settings.php @@ -1,66 +1,66 @@ array("message" => $l -> t("Not submitted for us.")))); return false; } OCP\JSON::success(array('data' => array('message' => $l -> t('Application settings successfully stored.')))); return true; diff --git a/templates/settings.php b/templates/settings.php --- a/templates/settings.php +++ b/templates/settings.php @@ -1,135 +1,135 @@ = 7 ? 'section' : 'personalblock'; ?>
t('SQL'); ?>
'MySQL', 'pgsql' => 'PostgreSQL'); ?>
'MD5', 'md5crypt' => 'MD5 Crypt', 'cleartext' => 'Cleartext', 'mysql_encrypt' => 'mySQL ENCRYPT()', 'system' => 'System (crypt)', 'mysql_password' => 'mySQL PASSWORD()', 'joomla' => 'Joomla MD5 Encryption', 'joomla2' => 'Joomla > 2.5.18 phpass', 'ssha256' => 'Salted SSHA256', 'redmine' => 'Redmine'); ?> 'No Synchronisation', 'initial' => 'Synchronise only once', 'forceoc' => 'ownCloud always wins', 'forcesql' => 'SQL always wins'); ?>
title="Allow changing passwords. Imposes a security risk as password salts are not recreated">
title="Invert the logic of the active column (for blocked users in the SQL DB)" />
>t('No Mapping') ?>
>t('Append Server Hostname') ?>
>t('Append Default') ?>
>t('Map Domains') ?> "; } ?>
" . htmlspecialchars($domains[$i]) . "" . htmlspecialchars($maps[$i]) . "delete
title="Strip Domain Part from Username when logging in and retrieving username lists">
t('Saving...'); ?>
diff --git a/user_sql.php b/user_sql.php --- a/user_sql.php +++ b/user_sql.php @@ -1,833 +1,833 @@ * * credits go to Ed W for several SQL injection fixes and caching support * credits go to Frédéric France for providing Joomla support * credits go to Mark Jansenn for providing Joomla 2.5.18+ / 3.2.1+ support * credits go to Dominik Grothaus for providing SSHA256 support and fixing a few bugs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * */ class OC_USER_SQL extends OC_User_Backend implements OC_User_Interface { protected $cache; // cached settings protected $sql_host; protected $sql_username; protected $sql_database; protected $sql_password; protected $sql_table; protected $sql_column_username; protected $sql_column_password; protected $sql_column_active; protected $sql_column_active_invert; protected $sql_column_displayname; protected $sql_column_email; protected $mail_sync_mode; protected $sql_type; protected $db_conn; protected $db; protected $default_domain; protected $strip_domain; protected $crypt_type; protected $domain_settings; protected $domain_array; protected $map_array; protected $allow_password_change; protected $session_cache_name; public function __construct() { $this -> db_conn = false; $memcache = \OC::$server->getMemCacheFactory(); if ( $memcache -> isAvailable()) { $this -> cache = $memcache -> create(); } $this -> sql_host = OCP\Config::getAppValue('user_sql', 'sql_host', ''); $this -> sql_username = OCP\Config::getAppValue('user_sql', 'sql_user', ''); $this -> sql_database = OCP\Config::getAppValue('user_sql', 'sql_database', ''); $this -> sql_password = OCP\Config::getAppValue('user_sql', 'sql_password', ''); $this -> sql_table = OCP\Config::getAppValue('user_sql', 'sql_table', ''); $this -> sql_column_username = OCP\Config::getAppValue('user_sql', 'sql_column_username', ''); $this -> sql_column_password = OCP\Config::getAppValue('user_sql', 'sql_column_password', ''); $this -> sql_column_displayname = OCP\Config::getAppValue('user_sql', 'sql_column_displayname', $this->sql_column_username); $this -> sql_column_email = OCP\Config::getAppValue('user_sql', 'sql_column_email', ''); $this -> sql_column_active = OCP\Config::getAppValue('user_sql', 'sql_column_active', ''); $this -> sql_column_active_invert = OCP\Config::getAppValue('user_sql', 'sql_column_active_invert', 0); $this -> sql_type = OCP\Config::getAppValue('user_sql', 'sql_type', ''); $this -> default_domain = OCP\Config::getAppValue('user_sql', 'default_domain', ''); $this -> strip_domain = OCP\Config::getAppValue('user_sql', 'strip_domain', 0); $this -> allow_password_change = OCP\Config::getAppValue('user_sql', 'allow_password_change', 0); $this -> crypt_type = OCP\Config::getAppValue('user_sql', 'crypt_type', 'md5crypt'); $this -> domain_settings = OCP\Config::getAppValue('user_sql', 'domain_settings', 'none'); $this -> domain_array = explode(",", OCP\Config::getAppValue('user_sql', 'domain_array', '')); $this -> map_array = explode(",", OCP\Config::getAppValue('user_sql', 'map_array', '')); $this -> mail_sync_mode = OCP\Config::getAppValue('user_sql', 'mail_sync_mode', 'none'); $this -> session_cache_name = 'USER_SQL_CACHE'; $dsn = $this -> sql_type . ":host=" . $this -> sql_host . ";dbname=" . $this -> sql_database; try { $this -> db = new PDO($dsn, $this -> sql_username, $this -> sql_password); $this -> db -> query("SET NAMES 'UTF8'"); $this -> db_conn = true; } catch (PDOException $e) { OC_Log::write('OC_USER_SQL', 'Failed to connect to the database: ' . $e -> getMessage(), OC_Log::ERROR); } return false; } private function doEmailSync($uid) { OC_Log::write('OC_USER_SQL', "Entering doEmailSync for UID: $uid", OC_Log::DEBUG); if($this -> sql_column_email === '') return false; - if($this -> mail_sync_mode == 'none') + if($this -> mail_sync_mode === 'none') return false; $ocUid = $uid; $uid = $this -> doUserDomainMapping($uid); $query = "SELECT $this->sql_column_email FROM $this->sql_table WHERE $this->sql_column_username = :uid"; OC_Log::write('OC_USER_SQL', "Preparing query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { return false; } OC_Log::write('OC_USER_SQL', "Fetching result...", OC_Log::DEBUG); $row = $result -> fetch(); if(!$row) { return false; } $newMail = $row[$this -> sql_column_email]; $currMail = OCP\Config::getUserValue($ocUid, 'settings', 'email', ''); switch($this -> mail_sync_mode) { case 'initial': if($currMail === '') OCP\Config::setUserValue($ocUid, 'settings', 'email', $newMail); break; case 'forcesql': - if($currMail != $newMail) + if($currMail !== $newMail) OCP\Config::setUserValue($ocUid, 'settings', 'email', $newMail); break; case 'forceoc': - if(($currMail !== '') && ($currMail != $newMail)) + if(($currMail !== '') && ($currMail !== $newMail)) { $query = "UPDATE $this->sql_table SET $this->sql_column_email = :currMail WHERE $this->sql_column_username = :uid"; OC_Log::write('OC_USER_SQL', "Preapring query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":currMail", $currMail); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { $err = $result -> errorInfo(); OC_Log::write('OC_USER_SQL', "Query failed: " . $err[2], OC_Log::DEBUG); OC_Log::write('OC_USER_SQL', "Could not update E-Mail address in SQL database!", OC_Log::ERROR); } } break; } return true; } private function doUserDomainMapping($uid) { $uid = trim($uid); switch($this->domain_settings) { case "default" : OC_Log::write('OC_USER_SQL', "Default mapping", OC_Log::DEBUG); if($this -> default_domain && (strpos($uid, '@') === false)) $uid .= "@" . $this -> default_domain; break; case "server" : OC_Log::write('OC_USER_SQL', "Server based mapping", OC_Log::DEBUG); if(strpos($uid, '@') === false) $uid .= "@" . $_SERVER['SERVER_NAME']; break; case "mapping" : OC_Log::write('OC_USER_SQL', 'Domain mapping selected', OC_Log::DEBUG); if(strpos($uid, '@') === false) { for($i = 0; $i < count($this -> domain_array); $i++) { OC_Log::write('OC_USER_SQL', 'Checking domain in mapping: ' . $this -> domain_array[$i], OC_Log::DEBUG); - if($_SERVER['SERVER_NAME'] == trim($this -> domain_array[$i])) + if($_SERVER['SERVER_NAME'] === trim($this -> domain_array[$i])) { OC_Log::write('OC_USER_SQL', 'Found domain in mapping: ' . $this -> domain_array[$i], OC_Log::DEBUG); $uid .= "@" . trim($this -> map_array[$i]); break; } } } break; case "none" : default : OC_Log::write('OC_USER_SQL', "No mapping", OC_Log::DEBUG); break; } $uid = strtolower($uid); OC_Log::write('OC_USER_SQL', 'Returning mapped UID: ' . $uid, OC_Log::DEBUG); return $uid; } public function implementsAction($actions) { return (bool)((OC_USER_BACKEND_CHECK_PASSWORD | OC_USER_BACKEND_GET_DISPLAYNAME) & $actions); } public function hasUserListings() { return true; } public function createUser() { // Can't create user OC_Log::write('OC_USER_SQL', 'Not possible to create local users from web frontend using SQL user backend', OC_Log::ERROR); return false; } public function deleteUser($uid) { // Can't delete user OC_Log::write('OC_USER_SQL', 'Not possible to delete local users from web frontend using SQL user backend', OC_Log::ERROR); return false; } public function setPassword($uid, $password) { // Update the user's password - this might affect other services, that // use the same database, as well OC_Log::write('OC_USER_SQL', "Entering setPassword for UID: $uid", OC_Log::DEBUG); if(!$this -> db_conn || !$this->allow_password_change) { return false; } $uid = $this -> doUserDomainMapping($uid); $query = "SELECT $this->sql_column_password FROM $this->sql_table WHERE $this->sql_column_username = :uid"; OC_Log::write('OC_USER_SQL', "Preparing query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { return false; } OC_Log::write('OC_USER_SQL', "Fetching result...", OC_Log::DEBUG); $row = $result -> fetch(); if(!$row) { return false; } $old_password = $row[$this -> sql_column_password]; - if($this -> crypt_type == 'joomla2') + if($this -> crypt_type === 'joomla2') { if(!class_exists('PasswordHash')) require_once('PasswordHash.php'); $hasher = new PasswordHash(10, true); $enc_password = $hasher->HashPassword($password); } // Redmine stores the salt separatedly, this doesn't play nice with the way // we check passwords - elseif($this -> crypt_type == 'redmine') + elseif($this -> crypt_type === 'redmine') { $query = "SELECT salt FROM $this->sql_table WHERE $this->sql_column_username =:uid;"; $res = $this->db->prepare($query); $res->bindparam(":uid", $uid); if(!$res->execute()) return false; $salt = $res->fetch(); if(!$salt) return false; $enc_password = sha1($salt['salt'].sha1($password)); } else { $enc_password = $this -> pacrypt($password, $old_password); } $query = "UPDATE $this->sql_table SET $this->sql_column_password = :enc_password WHERE $this->sql_column_username = :uid"; OC_Log::write('OC_USER_SQL', "Preapring query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":enc_password", $enc_password); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { $err = $result -> errorInfo(); OC_Log::write('OC_USER_SQL', "Query failed: " . $err[2], OC_Log::DEBUG); OC_Log::write('OC_USER_SQL', "Could not update password!", OC_Log::ERROR); return false; } OC_Log::write('OC_USER_SQL', "Updated password successfully, return true", OC_Log::DEBUG); return true; } /** * @brief Check if the password is correct * @param $uid The username * @param $password The password * @returns true/false * * Check if the password is correct without logging in the user */ public function checkPassword($uid, $password) { OC_Log::write('OC_USER_SQL', "Entering checkPassword() for UID: $uid", OC_Log::DEBUG); if(!$this -> db_conn) { return false; } $uid = $this -> doUserDomainMapping($uid); $query = "SELECT $this->sql_column_username, $this->sql_column_password FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - if($this -> sql_column_active != '') + if($this -> sql_column_active !== '') $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; OC_Log::write('OC_USER_SQL', "Preparing query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { $err = $result -> errorInfo(); OC_Log::write('OC_USER_SQL', "Query failed: " . $err[2], OC_Log::DEBUG); return false; } OC_Log::write('OC_USER_SQL', "Fetching row...", OC_Log::DEBUG); $row = $result -> fetch(); if(!$row) { OC_Log::write('OC_USER_SQL', "Got no row, return false", OC_Log::DEBUG); return false; } OC_Log::write('OC_USER_SQL', "Encrypting and checking password", OC_Log::DEBUG); // Joomla 2.5.18 switched to phPass, which doesn't play nice with the way // we check passwords - if($this -> crypt_type == 'joomla2') + if($this -> crypt_type === 'joomla2') { if(!class_exists('PasswordHash')) require_once('PasswordHash.php'); $hasher = new PasswordHash(10, true); $ret = $hasher -> CheckPassword($password, $row[$this -> sql_column_password]); } // Redmine stores the salt separatedly, this doesn't play nice with the way // we check passwords - elseif($this -> crypt_type == 'redmine') + elseif($this -> crypt_type === 'redmine') { $query = "SELECT salt FROM $this->sql_table WHERE $this->sql_column_username =:uid;"; $res = $this->db->prepare($query); $res->bindparam(":uid", $uid); if(!$res->execute()) return false; $salt = $res->fetch(); if(!$salt) return false; - $ret = sha1($salt['salt'].sha1($password)) == $row[$this->sql_column_password]; + $ret = sha1($salt['salt'].sha1($password)) === $row[$this->sql_column_password]; } else { - $ret = $this -> pacrypt($password, $row[$this -> sql_column_password]) == $row[$this -> sql_column_password]; + $ret = $this -> pacrypt($password, $row[$this -> sql_column_password]) === $row[$this -> sql_column_password]; } if($ret) { OC_Log::write('OC_USER_SQL', "Passwords matching, return true", OC_Log::DEBUG); if($this -> strip_domain) { $uid = explode("@", $uid); $uid = $uid[0]; } return $uid; } else { OC_Log::write('OC_USER_SQL', "Passwords do not match, return false", OC_Log::DEBUG); return false; } } /** * @brief Get a list of all users * @returns array with all uids * * Get a list of all users. */ public function getUsers($search = '', $limit = null, $offset = null) { OC_Log::write('OC_USER_SQL', "Entering getUsers() with Search: $search, Limit: $limit, Offset: $offset", OC_Log::DEBUG); $users = array(); if(!$this -> db_conn) { return false; } $query = "SELECT $this->sql_column_username FROM $this->sql_table"; $query .= " WHERE $this->sql_column_username LIKE :search"; - if($this -> sql_column_active != '') + if($this -> sql_column_active !== '') $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; $query .= " ORDER BY $this->sql_column_username"; - if($limit != null) + if($limit !== null) { $limit = intval($limit); $query .= " LIMIT $limit"; } - if($offset != null) + if($offset !== null) { $offset = intval($offset); $query .= " OFFSET $offset"; } OC_Log::write('OC_USER_SQL', "Preparing query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); - if($search != '') + if($search !== '') { $search = "%".$this -> doUserDomainMapping($search."%")."%"; } else { $search = "%".$this -> doUserDomainMapping("")."%"; } $result -> bindParam(":search", $search); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { $err = $result -> errorInfo(); OC_Log::write('OC_USER_SQL', "Query failed: " . $err[2], OC_Log::DEBUG); return array(); } OC_Log::write('OC_USER_SQL', "Fetching results...", OC_Log::DEBUG); while($row = $result -> fetch()) { $uid = $row[$this -> sql_column_username]; if($this -> strip_domain) { $uid = explode("@", $uid); $uid = $uid[0]; } $users[] = strtolower($uid); } OC_Log::write('OC_USER_SQL', "Return list of results", OC_Log::DEBUG); return $users; } /** * @brief check if a user exists * @param string $uid the username * @return boolean */ public function userExists($uid) { $cacheKey = 'sql_user_exists_' . $uid; $cacheVal = $this -> getCache ($cacheKey); if(!is_null($cacheVal)) return (bool)$cacheVal; OC_Log::write('OC_USER_SQL', "Entering userExists() for UID: $uid", OC_Log::DEBUG); if(!$this -> db_conn) { return false; } $uid = $this -> doUserDomainMapping($uid); $query = "SELECT $this->sql_column_username FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - if($this -> sql_column_active != '') + if($this -> sql_column_active !== '') $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; OC_Log::write('OC_USER_SQL', "Preparing query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { $err = $result -> errorInfo(); OC_Log::write('OC_USER_SQL', "Query failed: " . $err[2], OC_Log::DEBUG); return false; } OC_Log::write('OC_USER_SQL', "Fetching results...", OC_Log::DEBUG); $exists = (bool)$result -> fetch(); $this -> setCache ($cacheKey, $exists, 60); if(!$exists) { OC_Log::write('OC_USER_SQL', "Empty row, user does not exists, return false", OC_Log::DEBUG); return false; } else { OC_Log::write('OC_USER_SQL', "User exists, return true", OC_Log::DEBUG); return true; } } public function getDisplayName($uid) { OC_Log::write('OC_USER_SQL', "Entering getDisplayName() for UID: $uid", OC_Log::DEBUG); if(!$this -> db_conn) { return false; } $this -> doEmailSync($uid); $uid = $this -> doUserDomainMapping($uid); if(!$this -> userExists($uid)) { return false; } $query = "SELECT $this->sql_column_displayname FROM $this->sql_table WHERE $this->sql_column_username = :uid"; - if($this -> sql_column_active != '') + if($this -> sql_column_active !== '') $query .= " AND " .($this->sql_column_active_invert ? "NOT " : "" ).$this->sql_column_active; OC_Log::write('OC_USER_SQL', "Preparing query: $query", OC_Log::DEBUG); $result = $this -> db -> prepare($query); $result -> bindParam(":uid", $uid); OC_Log::write('OC_USER_SQL', "Executing query...", OC_Log::DEBUG); if(!$result -> execute()) { $err = $result -> errorInfo(); OC_Log::write('OC_USER_SQL', "Query failed: " . $err[2], OC_Log::DEBUG); return false; } OC_Log::write('OC_USER_SQL', "Fetching results...", OC_Log::DEBUG); $row = $result -> fetch(); if(!$row) { OC_Log::write('OC_USER_SQL', "Empty row, user has no display name or does not exist, return false", OC_Log::DEBUG); return false; } else { OC_Log::write('OC_USER_SQL', "User exists, return true", OC_Log::DEBUG); $displayName = $row[$this -> sql_column_displayname]; return $displayName; ; } return false; } public function getDisplayNames($search = '', $limit = null, $offset = null) { $uids = $this -> getUsers($search, $limit, $offset); $displayNames = array(); foreach($uids as $uid) { $displayNames[$uid] = $this -> getDisplayName($uid); } return $displayNames; } /** * The following functions were directly taken from PostfixAdmin and just * slightly modified * to suit our needs. * Encrypt a password, using the apparopriate hashing mechanism as defined in * config.inc.php ($this->crypt_type). * When wanting to compare one pw to another, it's necessary to provide the * salt used - hence * the second parameter ($pw_db), which is the existing hash from the DB. * * @param string $pw * @param string $encrypted password * @return string encrypted password. */ private function pacrypt($pw, $pw_db = "") { OC_Log::write('OC_USER_SQL', "Entering private pacrypt()", OC_Log::DEBUG); $pw = stripslashes($pw); $password = ""; $salt = ""; - if($this -> crypt_type == 'md5crypt') + if($this -> crypt_type === 'md5crypt') { $split_salt = preg_split('/\$/', $pw_db); if(isset($split_salt[2])) { $salt = $split_salt[2]; } $password = $this -> md5crypt($pw, $salt); - } elseif($this -> crypt_type == 'md5') + } elseif($this -> crypt_type === 'md5') { $password = md5($pw); - } elseif($this -> crypt_type == 'system') + } elseif($this -> crypt_type === 'system') { // We never generate salts, as user creation is not allowed here $password = crypt($pw, $pw_db); - } elseif($this -> crypt_type == 'cleartext') + } elseif($this -> crypt_type === 'cleartext') { $password = $pw; } // See // https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583 // this is apparently useful for pam_mysql etc. - elseif($this -> crypt_type == 'mysql_encrypt') + elseif($this -> crypt_type === 'mysql_encrypt') { if(!$this -> db_conn) { return false; } - if($pw_db != "") + if($pw_db !== "") { $salt = substr($pw_db, 0, 2); $query = "SELECT ENCRYPT(:pw, :salt);"; } else { $query = "SELECT ENCRYPT(:pw);"; } $result = $this -> db -> prepare($query); $result -> bindParam(":pw", $pw); - if($pw_db != "") + if($pw_db !== "") $result -> bindParam(":salt", $salt); if(!$result -> execute()) { return false; } $row = $result -> fetch(); if(!$row) { return false; } $password = $row[0]; - } elseif($this -> crypt_type == 'mysql_password') + } elseif($this -> crypt_type === 'mysql_password') { if(!$this -> db_conn) { return false; } $query = "SELECT PASSWORD(:pw);"; $result = $this -> db -> prepare($query); $result -> bindParam(":pw", $pw); if(!$result -> execute()) { return false; } $row = $result -> fetch(); if(!$row) { return false; } $password = $row[0]; } // The following is by Frédéric France - elseif($this -> crypt_type == 'joomla') + elseif($this -> crypt_type === 'joomla') { $split_salt = preg_split('/:/', $pw_db); if(isset($split_salt[1])) { $salt = $split_salt[1]; } $password = ($salt) ? md5($pw . $salt) : md5($pw); $password .= ':' . $salt; } - elseif($this-> crypt_type == 'ssha256') + elseif($this-> crypt_type === 'ssha256') { $salted_password = base64_decode(preg_replace('/{SSHA256}/i','',$pw_db)); $salt = substr($salted_password,-(strlen($salted_password)-32)); $password = $this->ssha256($pw,$salt); } else { OC_Log::write('OC_USER_SQL', "unknown/invalid crypt_type settings: $this->crypt_type", OC_Log::ERROR); die('unknown/invalid Encryption type setting: ' . $this -> crypt_type); } OC_Log::write('OC_USER_SQL', "pacrypt() done, return", OC_Log::DEBUG); return $password; } // // md5crypt // Action: Creates MD5 encrypted password // Call: md5crypt (string cleartextpassword) // private function md5crypt($pw, $salt = "", $magic = "") { $MAGIC = "$1$"; - if($magic == "") + if($magic === "") $magic = $MAGIC; - if($salt == "") + if($salt === "") $salt = $this -> create_salt(); $slist = explode("$", $salt); - if($slist[0] == "1") + if($slist[0] === "1") $salt = $slist[1]; $salt = substr($salt, 0, 8); $ctx = $pw . $magic . $salt; $final = $this -> pahex2bin(md5($pw . $salt . $pw)); for($i = strlen($pw); $i > 0; $i -= 16) { if($i > 16) { $ctx .= substr($final, 0, 16); } else { $ctx .= substr($final, 0, $i); } } $i = strlen($pw); while($i > 0) { if($i & 1) $ctx .= chr(0); else $ctx .= $pw[0]; $i = $i>>1; } $final = $this -> pahex2bin(md5($ctx)); for($i = 0; $i < 1000; $i++) { $ctx1 = ""; if($i & 1) { $ctx1 .= $pw; } else { $ctx1 .= substr($final, 0, 16); } if($i % 3) $ctx1 .= $salt; if($i % 7) $ctx1 .= $pw; if($i & 1) { $ctx1 .= substr($final, 0, 16); } else { $ctx1 .= $pw; } $final = $this -> pahex2bin(md5($ctx1)); } $passwd = ""; $passwd .= $this -> to64(((ord($final[0])<<16) | (ord($final[6])<<8) | (ord($final[12]))), 4); $passwd .= $this -> to64(((ord($final[1])<<16) | (ord($final[7])<<8) | (ord($final[13]))), 4); $passwd .= $this -> to64(((ord($final[2])<<16) | (ord($final[8])<<8) | (ord($final[14]))), 4); $passwd .= $this -> to64(((ord($final[3])<<16) | (ord($final[9])<<8) | (ord($final[15]))), 4); $passwd .= $this -> to64(((ord($final[4])<<16) | (ord($final[10])<<8) | (ord($final[5]))), 4); $passwd .= $this -> to64(ord($final[11]), 2); return "$magic$salt\$$passwd"; } private function create_salt() { srand((double) microtime() * 1000000); $salt = substr(md5(rand(0, 9999999)), 0, 8); return $salt; } private function ssha256($pw, $salt) { return '{SSHA256}'.base64_encode(hash('sha256',$pw.$salt,true).$salt); } private function pahex2bin($str) { if(function_exists('hex2bin')) { return hex2bin($str); } else { $len = strlen($str); $nstr = ""; for($i = 0; $i < $len; $i += 2) { $num = sscanf(substr($str, $i, 2), "%x"); $nstr .= chr($num[0]); } return $nstr; } } private function to64($v, $n) { $ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; $ret = ""; while(($n - 1) >= 0) { $n--; $ret .= $ITOA64[$v & 0x3f]; $v = $v>>6; } return $ret; } /** * Store a value in memcache or the session, if no memcache is available * @param string $key * @param mixed $value * @param int $ttl (optional) defaults to 3600 seconds. */ private function setCache($key,$value,$ttl=3600) { if ($this -> cache === NULL) { $_SESSION[$this -> session_cache_name][$key] = array( 'value' => $value, 'time' => time(), 'ttl' => $ttl, ); } else { $this -> cache -> set($key,$value,$ttl); } } /** * Fetch a value from memcache or session, if memcache is not available. * Returns NULL if there's no value stored or the value expired. * @param string $key * @return mixed|NULL */ private function getCache($key) { $retVal = NULL; if ($this -> cache === NULL) { if (isset($_SESSION[$this -> session_cache_name],$_SESSION[$this -> session_cache_name][$key])) { $value = $_SESSION[$this -> session_cache_name][$key]; if (time() < $value['time'] + $value['ttl']) { $retVal = $value['value']; } } } else { $retVal = $this -> cache -> get ($key); } return $retVal; } } ?>