diff --git a/api/index.php b/api/index.php index 3fbf6bf57..363f2ae13 100644 --- a/api/index.php +++ b/api/index.php @@ -22,8 +22,6 @@ ini_set('session.use_cookies', 0); ini_set("session.gc_maxlifetime", 86400); - define('AUTH_DISABLE_OTP', true); - if (defined('ENABLE_GZIP_OUTPUT') && ENABLE_GZIP_OUTPUT && function_exists("ob_gzhandler")) { diff --git a/classes/api.php b/classes/api.php index 01ea1970d..6fb87d04f 100755 --- a/classes/api.php +++ b/classes/api.php @@ -74,10 +74,10 @@ class API extends Handler { } if (get_pref("ENABLE_API_ACCESS", $uid)) { - if (authenticate_user($login, $password)) { // try login with normal password + if (authenticate_user($login, $password, false, Auth_Base::AUTH_SERVICE_API)) { // try login with normal password $this->wrap(self::STATUS_OK, array("session_id" => session_id(), "api_level" => self::API_LEVEL)); - } else if (authenticate_user($login, $password_base64)) { // else try with base64_decoded password + } else if (authenticate_user($login, $password_base64, false, Auth_Base::AUTH_SERVICE_API)) { // else try with base64_decoded password $this->wrap(self::STATUS_OK, array("session_id" => session_id(), "api_level" => self::API_LEVEL)); } else { // else we are not logged in diff --git a/classes/auth/base.php b/classes/auth/base.php index dbc77f8cd..4cbc23589 100644 --- a/classes/auth/base.php +++ b/classes/auth/base.php @@ -2,6 +2,8 @@ class Auth_Base { private $pdo; + const AUTH_SERVICE_API = '_api'; + function __construct() { $this->pdo = Db::pdo(); } @@ -9,14 +11,14 @@ class Auth_Base { /** * @SuppressWarnings(unused) */ - function check_password($owner_uid, $password) { + function check_password($owner_uid, $password, $service = '') { return false; } /** * @SuppressWarnings(unused) */ - function authenticate($login, $password) { + function authenticate($login, $password, $service = '') { return false; } diff --git a/classes/iauthmodule.php b/classes/iauthmodule.php index 9ec674078..2d0c98709 100644 --- a/classes/iauthmodule.php +++ b/classes/iauthmodule.php @@ -1,4 +1,4 @@ get_hooks(PluginHost::HOOK_AUTH_USER) as $plugin) { - $user_id = (int) $plugin->authenticate($login, $password); + $user_id = (int) $plugin->authenticate($login, $password, $service); if ($user_id) { $auth_module = strtolower(get_class($plugin)); diff --git a/plugins/auth_internal/init.php b/plugins/auth_internal/init.php index 638baa83a..576f8ef05 100644 --- a/plugins/auth_internal/init.php +++ b/plugins/auth_internal/init.php @@ -1,31 +1,30 @@ -host = $host; - $this->pdo = Db::pdo(); + /* @var PluginHost $host */ + function init($host) { + $this->host = $host; + $this->pdo = Db::pdo(); - $host->add_hook($host::HOOK_AUTH_USER, $this); - } + $host->add_hook($host::HOOK_AUTH_USER, $this); + } - function authenticate($login, $password) { + function authenticate($login, $password, $service = '') { - $pwd_hash1 = encrypt_password($password); - $pwd_hash2 = encrypt_password($password, $login); - $otp = $_REQUEST["otp"]; + $pwd_hash1 = encrypt_password($password); + $pwd_hash2 = encrypt_password($password, $login); + $otp = $_REQUEST["otp"]; - if (get_schema_version() > 96) { - if (!defined('AUTH_DISABLE_OTP') || !AUTH_DISABLE_OTP) { + if (get_schema_version() > 96) { $sth = $this->pdo->prepare("SELECT otp_enabled,salt FROM ttrss_users WHERE login = ?"); @@ -42,6 +41,12 @@ class Auth_Internal extends Plugin implements IAuthModule { $otp_check = $topt->now(); if ($otp_enabled) { + + // only allow app password checking if OTP is enabled + if ($service && get_schema_version() > 138) { + return $this->check_app_password($login, $password, $service); + } + if ($otp) { if ($otp != $otp_check) { return false; @@ -83,61 +88,81 @@ class Auth_Internal extends Plugin implements IAuthModule { } } } - } - if (get_schema_version() > 87) { + // check app passwords first but allow regular password as a fallback for the time being + // if OTP is not enabled - $sth = $this->pdo->prepare("SELECT salt FROM ttrss_users WHERE login = ?"); - $sth->execute([$login]); + if ($service && get_schema_version() > 138) { + $user_id = $this->check_app_password($login, $password, $service); - if ($row = $sth->fetch()) { - $salt = $row['salt']; + if ($user_id) + return $user_id; + } - if ($salt == "") { + if (get_schema_version() > 87) { - $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE - login = ? AND (pwd_hash = ? OR pwd_hash = ?)"); + $sth = $this->pdo->prepare("SELECT salt FROM ttrss_users WHERE login = ?"); + $sth->execute([$login]); - $sth->execute([$login, $pwd_hash1, $pwd_hash2]); + if ($row = $sth->fetch()) { + $salt = $row['salt']; - // verify and upgrade password to new salt base + if ($salt == "") { - if ($row = $sth->fetch()) { - // upgrade password to MODE2 + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE + login = ? AND (pwd_hash = ? OR pwd_hash = ?)"); - $user_id = $row['id']; + $sth->execute([$login, $pwd_hash1, $pwd_hash2]); - $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); - $pwd_hash = encrypt_password($password, $salt, true); + // verify and upgrade password to new salt base - $sth = $this->pdo->prepare("UPDATE ttrss_users SET - pwd_hash = ?, salt = ? WHERE login = ?"); + if ($row = $sth->fetch()) { + // upgrade password to MODE2 - $sth->execute([$pwd_hash, $salt, $login]); + $user_id = $row['id']; - return $user_id; + $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); + $pwd_hash = encrypt_password($password, $salt, true); + + $sth = $this->pdo->prepare("UPDATE ttrss_users SET + pwd_hash = ?, salt = ? WHERE login = ?"); + + $sth->execute([$pwd_hash, $salt, $login]); + + return $user_id; + + } else { + return false; + } } else { - return false; + $pwd_hash = encrypt_password($password, $salt, true); + + $sth = $this->pdo->prepare("SELECT id + FROM ttrss_users WHERE + login = ? AND pwd_hash = ?"); + $sth->execute([$login, $pwd_hash]); + + if ($row = $sth->fetch()) { + return $row['id']; + } } } else { - $pwd_hash = encrypt_password($password, $salt, true); - $sth = $this->pdo->prepare("SELECT id - FROM ttrss_users WHERE - login = ? AND pwd_hash = ?"); - $sth->execute([$login, $pwd_hash]); + FROM ttrss_users WHERE + login = ? AND (pwd_hash = ? OR pwd_hash = ?)"); + + $sth->execute([$login, $pwd_hash1, $pwd_hash2]); if ($row = $sth->fetch()) { return $row['id']; } } - } else { $sth = $this->pdo->prepare("SELECT id - FROM ttrss_users WHERE - login = ? AND (pwd_hash = ? OR pwd_hash = ?)"); + FROM ttrss_users WHERE + login = ? AND (pwd_hash = ? OR pwd_hash = ?)"); $sth->execute([$login, $pwd_hash1, $pwd_hash2]); @@ -145,107 +170,99 @@ class Auth_Internal extends Plugin implements IAuthModule { return $row['id']; } } - } else { - $sth = $this->pdo->prepare("SELECT id - FROM ttrss_users WHERE - login = ? AND (pwd_hash = ? OR pwd_hash = ?)"); - $sth->execute([$login, $pwd_hash1, $pwd_hash2]); - - if ($row = $sth->fetch()) { - return $row['id']; - } - } - - return false; - } - - function check_password($owner_uid, $password) { - - $sth = $this->pdo->prepare("SELECT salt,login FROM ttrss_users WHERE - id = ?"); - $sth->execute([$owner_uid]); - - if ($row = $sth->fetch()) { - - $salt = $row['salt']; - $login = $row['login']; - - if (!$salt) { - $password_hash1 = encrypt_password($password); - $password_hash2 = encrypt_password($password, $login); - - $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE - id = ? AND (pwd_hash = ? OR pwd_hash = ?)"); - - $sth->execute([$owner_uid, $password_hash1, $password_hash2]); - - return $sth->fetch(); - - } else { - $password_hash = encrypt_password($password, $salt, true); - - $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE - id = ? AND pwd_hash = ?"); - - $sth->execute([$owner_uid, $password_hash]); - - return $sth->fetch(); - } + return false; } - return false; - } + function check_password($owner_uid, $password) { - function change_password($owner_uid, $old_password, $new_password) { - - if ($this->check_password($owner_uid, $old_password)) { - - $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); - $new_password_hash = encrypt_password($new_password, $new_salt, true); - - $sth = $this->pdo->prepare("UPDATE ttrss_users SET - pwd_hash = ?, salt = ?, otp_enabled = false - WHERE id = ?"); - $sth->execute([$new_password_hash, $new_salt, $owner_uid]); - - $_SESSION["pwd_hash"] = $new_password_hash; - - $sth = $this->pdo->prepare("SELECT email, login FROM ttrss_users WHERE id = ?"); + $sth = $this->pdo->prepare("SELECT salt,login,otp_enabled FROM ttrss_users WHERE + id = ?"); $sth->execute([$owner_uid]); if ($row = $sth->fetch()) { - $mailer = new Mailer(); - require_once "lib/MiniTemplator.class.php"; + $salt = $row['salt']; + $login = $row['login']; - $tpl = new MiniTemplator; + if (!$salt) { + $password_hash1 = encrypt_password($password); + $password_hash2 = encrypt_password($password, $login); - $tpl->readTemplateFromFile("templates/password_change_template.txt"); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE + id = ? AND (pwd_hash = ? OR pwd_hash = ?)"); - $tpl->setVariable('LOGIN', $row["login"]); - $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); + $sth->execute([$owner_uid, $password_hash1, $password_hash2]); - $tpl->addBlock('message'); + return $sth->fetch(); - $tpl->generateOutputToString($message); + } else { + $password_hash = encrypt_password($password, $salt, true); - $mailer->mail(["to_name" => $row["login"], - "to_address" => $row["email"], - "subject" => "[tt-rss] Password change notification", - "message" => $message]); + $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE + id = ? AND pwd_hash = ?"); + $sth->execute([$owner_uid, $password_hash]); + + return $sth->fetch(); + } } - return __("Password has been changed."); - } else { - return "ERROR: ".__('Old password is incorrect.'); + return false; } - } - function api_version() { - return 2; - } + function change_password($owner_uid, $old_password, $new_password) { -} -?> + if ($this->check_password($owner_uid, $old_password)) { + + $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250); + $new_password_hash = encrypt_password($new_password, $new_salt, true); + + $sth = $this->pdo->prepare("UPDATE ttrss_users SET + pwd_hash = ?, salt = ?, otp_enabled = false + WHERE id = ?"); + $sth->execute([$new_password_hash, $new_salt, $owner_uid]); + + $_SESSION["pwd_hash"] = $new_password_hash; + + $sth = $this->pdo->prepare("SELECT email, login FROM ttrss_users WHERE id = ?"); + $sth->execute([$owner_uid]); + + if ($row = $sth->fetch()) { + $mailer = new Mailer(); + + require_once "lib/MiniTemplator.class.php"; + + $tpl = new MiniTemplator; + + $tpl->readTemplateFromFile("templates/password_change_template.txt"); + + $tpl->setVariable('LOGIN', $row["login"]); + $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); + + $tpl->addBlock('message'); + + $tpl->generateOutputToString($message); + + $mailer->mail(["to_name" => $row["login"], + "to_address" => $row["email"], + "subject" => "[tt-rss] Password change notification", + "message" => $message]); + + } + + return __("Password has been changed."); + } else { + return "ERROR: ".__('Old password is incorrect.'); + } + } + + private function check_app_password($login, $password, $service) { + return false; + } + + function api_version() { + return 2; + } + + }