Introduce Authenticator: Centralizes Authentication/password check logic
Este commit está contenido en:
		
							
								
								
									
										108
									
								
								authenticator.cpp
									
									
									
									
									
										Archivo normal
									
								
							
							
						
						
									
										108
									
								
								authenticator.cpp
									
									
									
									
									
										Archivo normal
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <openssl/evp.h>
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
#include "authenticator.h"
 | 
			
		||||
#include "logger.h"
 | 
			
		||||
struct LoginFail
 | 
			
		||||
{
 | 
			
		||||
	std::mutex mutex;
 | 
			
		||||
	std::atomic<unsigned int> count;
 | 
			
		||||
	time_t lastfail;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static std::map<std::string, LoginFail> loginFails;
 | 
			
		||||
 | 
			
		||||
Authenticator::Authenticator(UserDao &userDao)
 | 
			
		||||
{
 | 
			
		||||
	this->userDao = &userDao;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: make failure counter configurable
 | 
			
		||||
bool Authenticator::isBanned(std::string ip)
 | 
			
		||||
{
 | 
			
		||||
	if(utils::hasKey(loginFails, ip))
 | 
			
		||||
	{
 | 
			
		||||
		LoginFail &fl = loginFails[ip];
 | 
			
		||||
		std::lock_guard<std::mutex> lock(fl.mutex);
 | 
			
		||||
		return fl.count > 5 && (time(nullptr) - fl.lastfail) < 1200;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Authenticator::incFailureCount(std::string ip)
 | 
			
		||||
{
 | 
			
		||||
	LoginFail &fl = loginFails[ip];
 | 
			
		||||
	fl.count += 1;
 | 
			
		||||
	fl.lastfail = time(nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<char> Authenticator::pbkdf5(std::string password, const std::vector<char> &salt)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char hash[32];
 | 
			
		||||
	const EVP_MD *sha256 = EVP_sha256();
 | 
			
		||||
	const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
 | 
			
		||||
	int ret = PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
 | 
			
		||||
	if(ret != 1)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Authenticator: pbkdf5: Failed to create hash";
 | 
			
		||||
		return { };
 | 
			
		||||
	}
 | 
			
		||||
	std::vector<char> result;
 | 
			
		||||
 | 
			
		||||
	for(size_t i = 0; i < sizeof(hash); i++)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		result.push_back(static_cast<char>(hash[i]));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::variant<User, AuthenticationError> Authenticator::authenticate(std::string username, std::string password)
 | 
			
		||||
{
 | 
			
		||||
	std::optional<User> user = userDao->find(username);
 | 
			
		||||
	if(user)
 | 
			
		||||
	{
 | 
			
		||||
		if(user->enabled)
 | 
			
		||||
		{
 | 
			
		||||
			auto hashresult = pbkdf5(password, user.value().salt);
 | 
			
		||||
			if(hashresult.size() == 0)
 | 
			
		||||
			{
 | 
			
		||||
				return AuthenticationError::GeneralError;
 | 
			
		||||
			}
 | 
			
		||||
			// TODO: timing attack (even though practical relevancy questionable)
 | 
			
		||||
			if(hashresult == user.value().password)
 | 
			
		||||
			{
 | 
			
		||||
				return user.value();
 | 
			
		||||
			}
 | 
			
		||||
			return AuthenticationError::PasswordNotMatch;
 | 
			
		||||
		}
 | 
			
		||||
		return AuthenticationError::UserDisabled;
 | 
			
		||||
	}
 | 
			
		||||
	return AuthenticationError::UserNotFound;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::variant<User, AuthenticationError> Authenticator::authenticate(std::string username, std::string password,
 | 
			
		||||
																	std::string ip)
 | 
			
		||||
{
 | 
			
		||||
	if(isBanned(ip))
 | 
			
		||||
	{
 | 
			
		||||
		return AuthenticationError::BannedIP;
 | 
			
		||||
	}
 | 
			
		||||
	std::variant<User, AuthenticationError> authresult = authenticate(username, password);
 | 
			
		||||
	if(std::holds_alternative<AuthenticationError>(authresult))
 | 
			
		||||
	{
 | 
			
		||||
		AuthenticationError error = std::get<AuthenticationError>(authresult);
 | 
			
		||||
		if(error == AuthenticationError::PasswordNotMatch)
 | 
			
		||||
		{
 | 
			
		||||
			incFailureCount(ip);
 | 
			
		||||
		}
 | 
			
		||||
		return error;
 | 
			
		||||
	}
 | 
			
		||||
	return std::get<User>(authresult);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<char> Authenticator::hash(std::string password, const std::vector<char> &salt)
 | 
			
		||||
{
 | 
			
		||||
	return this->pbkdf5(password, salt);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								authenticator.h
									
									
									
									
									
										Archivo normal
									
								
							
							
						
						
									
										30
									
								
								authenticator.h
									
									
									
									
									
										Archivo normal
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
#ifndef AUTHENTICATOR_H
 | 
			
		||||
#define AUTHENTICATOR_H
 | 
			
		||||
#include <variant>
 | 
			
		||||
#include "database/userdao.h"
 | 
			
		||||
 | 
			
		||||
enum AuthenticationError
 | 
			
		||||
{
 | 
			
		||||
	UserNotFound,
 | 
			
		||||
	UserDisabled,
 | 
			
		||||
	PasswordNotMatch,
 | 
			
		||||
	BannedIP,
 | 
			
		||||
	GeneralError
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Authenticator
 | 
			
		||||
{
 | 
			
		||||
  private:
 | 
			
		||||
	UserDao *userDao;
 | 
			
		||||
	bool isBanned(std::string ip);
 | 
			
		||||
	void incFailureCount(std::string ip);
 | 
			
		||||
	std::vector<char> pbkdf5(std::string password, const std::vector<char> &salt);
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
	Authenticator(UserDao &userDao);
 | 
			
		||||
	std::variant<User, AuthenticationError> authenticate(std::string username, std::string password);
 | 
			
		||||
	std::variant<User, AuthenticationError> authenticate(std::string username, std::string password, std::string ip);
 | 
			
		||||
	std::vector<char> hash(std::string password, const std::vector<char> &salt);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // AUTHENTICATOR_H
 | 
			
		||||
		Referencia en una nueva incidencia
	
	Block a user