/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2008-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include "core/sessionmanager.h" #include "login.h" #include "utf8/utf8.h" namespace Winix { namespace Fun { Login::Login() { fun.url = L"login"; need_ssl = true; } void Login::ClearTmpStruct() { system->crypt.ClearString(pass_decrypted); // system->crypt.ClearString(up.pass); // system->crypt.ClearString(up.pass_encrypted); // system->crypt.ClearString(up2.pass); // system->crypt.ClearString(up2.pass_encrypted); } bool Login::CheckPasswords(User & user, const std::wstring & password) { if( !user.pass_encrypted.empty() ) { if( system->crypt.RSA(false, config->pass_rsa_private_key, user.pass_encrypted, pass_decrypted) ) { pt::utf8_to_wide(pass_decrypted, user.password); } else { log << log1 << "Login: I cannot decrypt a stored password, login failure" << logend; return false; } } std::wstring password_from_db = user.password; user.password = password; if( user.pass_hash_salted ) salt = config->pass_hash_salt; else salt.clear(); if( !system->crypt.PassHash(salt, user) ) { log << log1 << "Login: I cannot hash a password, login failure" << logend; return false; } // compare char by char until the end of the strings (time attacks) bool result = (user.password == password_from_db); if( !result ) log << log2 << "Login: incorrect login/password" << logend; return result; } /* this method is checking whether there is a person with that login and password in the database return true if it has found one and sets it user_id */ bool Login::CheckUserPass(const std::wstring & login, const std::wstring & password, long & user_id) { bool result; morm::Finder finder(model_connector); User user = finder. select(). where(). eq(L"login", login). get(); if( user.found() ) { user_id = user.id; if( user.has_pass ) { result = CheckPasswords(user, password); } else { log << log2 << "Login: this account has no a password set yet" << logend; result = false; } } else { log << log2 << "Login: there is no a user name: " << login << logend; result = false; } ClearTmpStruct(); return result; } void Login::CheckBan() { IPBan * ip_ban = cur->session->ip_ban; if( !ip_ban ) { ip_ban = &session_manager->AddIPToBanList(cur->request->ip, cur->request->start_time); cur->session->ip_ban = ip_ban; } if( ip_ban->incorrect_login_events < config->incorrect_login_cannot_login_treshold ) { ip_ban->incorrect_login_events += 1; } else { log << log2 << "Login: too many incorrect login attempts from this IP" << logend; if( config->incorrect_login_cannot_login_mode == 0 ) { time_t expires = cur->request->start_time + (time_t)config->incorrect_login_cannot_login_delay; if( ip_ban->expires < expires ) ip_ban->expires = expires; pt::Date date(ip_ban->expires); log << log2 << "Login: logging from this IP address has been blocked until to: " << date << " UTC" << logend; } else { session_manager->IncrementBanLevel(ip_ban); } } } bool Login::ShouldUseCaptchaForCurrentIP() { if( cur->session->ip_ban ) return ShouldUseCaptchaFor(*cur->session->ip_ban); return false; } bool Login::ShouldUseCaptchaFor(const IPBan & ipban) { if( ipban.expires != 0 && cur->request->start_time >= ipban.expires ) return false; // the 'ip block' has expired (but incorrect_login_events has the old value) return ipban.incorrect_login_events >= config->incorrect_login_captcha_treshold; } bool Login::CannotLoginFromCurrentIP() { if( cur->session->ip_ban ) return CannotLoginFrom(*cur->session->ip_ban); return false; } bool Login::CannotLoginFrom(const IPBan & ipban) { if( ipban.IsIPBanned() ) return true; /* * if incorrect_login_cannot_login_mode is equal to one then we only * block logging (there is no a ban actually -- neither the active flag is enabled * nor any ban_level is set) */ if( ipban.expires != 0 && cur->request->start_time < ipban.expires && ipban.incorrect_login_events >= config->incorrect_login_cannot_login_treshold ) return true; return false; } bool Login::CheckAbuse() { time_t diff = (time_t)config->incorrect_login_min_time_between_get_post; if( cur->session->last_time_get + diff > cur->request->start_time ) { log << log2 << "Login: the minimum time between GET and POST have not passed" << logend; return false; } if( ShouldUseCaptchaForCurrentIP() ) { if( !system->rebus.CheckRebus() ) { return false; } } return true; } // if you are logging not from a webbrowser but from an application // then probably you need check_abuse to be false bool Login::LoginUser(const std::wstring & login, const std::wstring & password, bool remember_me, bool use_ses_log, bool check_abuse) { long user_id; if( cur->session->id == 0 ) { log << log2 << "Login: can't login in a temporary session (skipped)" << logend; return false; } if( CannotLoginFromCurrentIP() ) { log << log2 << "Login: you cannot login from this IP address" << logend; return false; } if( login.empty() ) { log << log3 << "Login: login is empty (skipping)" << logend; return false; } if( check_abuse && !CheckAbuse() ) { CheckBan(); return false; } if( CheckUserPass(login, password, user_id) ) { if( system->users.LoginUser(user_id, remember_me, use_ses_log) ) { if( cur->session->ip_ban ) cur->session->ip_ban->incorrect_login_events = 0; return true; } } else { CheckBan(); } return false; } void Login::MakePost() { const std::wstring & login = cur->request->PostVar(L"login"); const std::wstring & pass = cur->request->PostVar(L"password"); const std::wstring & remem = cur->request->PostVar(L"rememberme"); if( LoginUser(login, pass, !remem.empty(), true, true) ) system->RedirectToLastItem(); } void Login::MakeGet() { } } // namespace } // namespace Winix