From 161ba94f457223cf29d0f02cc44c462d4f27b4ae Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 11:19:30 +0200 Subject: [PATCH] Fix memory management issue, remove strict allocators and make connector more polite to memory overflow errors --- .clangd | 2 + PAM/ssh/bin/rublon_application.cpp | 19 +++---- PAM/ssh/include/rublon/check_application.hpp | 6 +-- PAM/ssh/include/rublon/configuration.hpp | 28 +++------- PAM/ssh/include/rublon/core_handler.hpp | 44 ++++++++-------- PAM/ssh/include/rublon/curl.hpp | 52 +++++++++++-------- PAM/ssh/include/rublon/error.hpp | 8 +-- PAM/ssh/include/rublon/error_handler.hpp | 9 ++-- PAM/ssh/include/rublon/init.hpp | 8 +-- PAM/ssh/include/rublon/json.hpp | 2 +- PAM/ssh/include/rublon/memory.hpp | 42 +++++---------- .../include/rublon/method/method_select.hpp | 2 +- .../rublon/method/passcode_based_auth.hpp | 40 +++++++------- PAM/ssh/include/rublon/rublon.hpp | 20 +++---- PAM/ssh/include/rublon/session.hpp | 18 ++++--- PAM/ssh/include/rublon/utils.hpp | 7 +-- PAM/ssh/include/rublon/websockets.hpp | 4 +- PAM/ssh/lib/pam.cpp | 21 ++++---- 18 files changed, 166 insertions(+), 166 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..2e4beaf --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-std:c++17] diff --git a/PAM/ssh/bin/rublon_application.cpp b/PAM/ssh/bin/rublon_application.cpp index 709b23a..579f879 100644 --- a/PAM/ssh/bin/rublon_application.cpp +++ b/PAM/ssh/bin/rublon_application.cpp @@ -36,16 +36,17 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { return PAM_MAXTRIES; }; - auto session = rublon::RublonFactory{}.startSession(pam); - if(not session.has_value()) { + Session session{pam}; + auto ok = rublon::RublonFactory{}.initializeSession(session); + if(not ok.has_value()) { return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } - if(!session->config().logging) { + if(!session.config().logging) { g_level = LogLevel::Warning; } - auto & CH = session.value().coreHandler(); + auto & CH = session.coreHandler(); auto selectMethod = [&](const MethodSelect & selector) { // return selector.create(pam); @@ -56,13 +57,13 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { }; auto confirmCode = [&](const MethodProxy & method) mutable { // - return method.fire(session.value(), CH, pam); + return method.fire(session, CH, pam); }; auto finalizeTransaction = [&](const AuthenticationStatus & status) mutable -> tl::expected< AuthenticationStatus, Error > { if(status.userAuthorized()) { auto tok = std::string{status.accessToken().data()}; - Finish finish{session.value(), std::move(tok)}; + Finish finish{session, std::move(tok)}; finish.handle(CH); } return status; @@ -73,20 +74,20 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { }; auto mapError = [&](const Error & error) -> tl::expected< int, Error > { - return printAuthMessageAndExit(rublon::ErrorHandler{pam, session->config()}.printErrorDetails(error)); + return printAuthMessageAndExit(rublon::ErrorHandler{pam, session.config()}.printErrorDetails(error)); }; { CheckApplication ca; auto ret = - ca.call(CH, {session.value().config().systemToken.data(), session.value().config().systemToken.size()}).or_else(mapError); + ca.call(CH, {session.config().systemToken.data(), session.config().systemToken.size()}).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); return PAM_MAXTRIES; } } - auto ret = Init{session.value()} + auto ret = Init{session} .handle(CH, pam) // .and_then(selectMethod) .and_then(confirmMethod) diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index b84a595..87590b4 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -136,7 +136,7 @@ class Status { void save() { if(updated()) { - memory::Monotonic_8k_HeapResource tmpResource; + memory::Monotonic_8k_Resource tmpResource; RapidJSONPMRAlloc alloc{&tmpResource}; FileWriter s{_statusFilePath}; rapidjson::PrettyWriter< FileWriter, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc}; @@ -147,7 +147,7 @@ class Status { std::string print() { std::string result; - memory::Monotonic_8k_HeapResource tmpResource; + memory::Monotonic_8k_Resource tmpResource; RapidJSONPMRAlloc alloc{&tmpResource}; StringWriter s{result}; rapidjson::PrettyWriter< StringWriter< std::string >, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc}; @@ -171,7 +171,7 @@ class CheckApplication { public: tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const { - memory::Monotonic_1k_HeapResource mr; + memory::Monotonic_1k_Resource mr; RapidJSONPMRStackAlloc< 2048 > alloc{}; constexpr std::string_view api = "/api/app/init"; Status status; diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 92fd275..32260b6 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -49,18 +49,20 @@ class Configuration { class ConfigurationReader { public: - ConfigurationReader(std::pmr::memory_resource * memResource = memory::default_resource()) : memoryResource(memResource) {} + ConfigurationReader(std::pmr::memory_resource * memResource, std::string_view filepath) : memoryResource(memResource) { + loadFromFile(filepath); + } // Load config from file path - bool loadFromFile(std::string_view filepath) { + void loadFromFile(std::string_view filepath) { using namespace memory::literals; - memory::MonotonicStackResource< 8_kB > stackResource; + memory::MonotonicStackResource< 4_kB > stackResource; std::ifstream file(filepath.data()); if(not file.good()) - return false; + return ; std::pmr::string line{&stackResource}; - line.reserve(8000); // allocate full stack to line + line.reserve(4000); // allocate full stack to line while(std::getline(file, line)) { std::pmr::string key{memoryResource}; @@ -79,8 +81,6 @@ class ConfigurationReader { keyValues[std::move(key)] = std::move(value); } - - return true; } // Load values into Configuration object, with defaults provided @@ -220,18 +220,4 @@ class ConfigurationReader { std::pmr::unordered_map< std::pmr::string, std::pmr::string > keyValues{memoryResource}; }; -class ConfigurationFactory { - public: - ConfigurationFactory() = default; - - std::optional< Configuration > systemConfig() { - std::optional< Configuration > conf{Configuration{}}; - ConfigurationReader reader{}; - reader.loadFromFile("/etc/rublon.config"); - if(auto ok = reader.applyTo(conf.value()); not ok.has_value()) { - return std::nullopt; - } - return conf.value(); - } -}; } // namespace rublon diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index 06ac82d..4ebba2f 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -2,6 +2,7 @@ #include "rublon/error.hpp" #include "rublon/static_string.hpp" +#include #include #include #include @@ -20,12 +21,17 @@ namespace rublon { template < typename HttpHandler = CURL > class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { - Configuration _config; + std::reference_wrapper< const Configuration > _config; mutable std::unique_ptr< WebSocket > _ws; /// TODO try to remove mutable modyfier + HttpHandler http{}; + + const Configuration & config() const noexcept { + return _config.get(); + } void signRequest(Request & request) const { request.headers["X-Rublon-Signature"] = - std::pmr::string{signData(request.body, _config.secretKey).c_str(), request.headers.get_allocator()}; + std::pmr::string{signData(request.body, config().secretKey).c_str(), request.headers.get_allocator()}; } bool hasSignature(const Response & response) const { @@ -50,7 +56,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { bool signatureIsNatValid(const Response & response) const { const auto & xRubResp = response.headers.at("x-rublon-signature"); - const auto & sign = signData(response.body, _config.secretKey); + const auto & sign = signData(response.body, config().secretKey); const bool signatureMatch = xRubResp == sign.data(); if(not signatureMatch) log(LogLevel::Error, "Signature mismatch %s != %s ", xRubResp.c_str(), sign.data()); @@ -68,19 +74,16 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { return coreResponse.HasParseError(); } - protected: - HttpHandler http{}; - public: CoreHandler() = delete; - CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique(_config)}, http{_config} { - log(LogLevel::Debug, "Core Handler apiServer: %s", _config.apiServer.c_str()); + CoreHandler(const Configuration & baseconfig) : _config{baseconfig}, _ws{std::make_unique< WebSocket >(_config)}, http{config()} { + log(LogLevel::Debug, "Core Handler apiServer: %s", config().apiServer.c_str()); } - CoreHandler(CoreHandler &&) noexcept = default; - CoreHandler & operator=(CoreHandler &&) = default; + CoreHandler(const CoreHandler &) = delete; + CoreHandler(CoreHandler &&) noexcept = delete; - CoreHandler(const CoreHandler &) = delete; CoreHandler & operator=(const CoreHandler &) = delete; + CoreHandler & operator=(CoreHandler &&) = delete; ~CoreHandler() noexcept = default; @@ -125,13 +128,12 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { // additional check for mallformed responses (A invalid response, without any x-rublon-signature will stop at this check) return tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}}; } - + log(LogLevel::Debug, "Core Response validated OK"); return resp; } tl::unexpected< Error > handleCoreException(std::string_view exceptionString) const { - log(LogLevel::Debug, "TMP got core exception: %s", exceptionString.data() ); // can happen only during check application step if(auto error = RublonCheckApplicationException::fromString(exceptionString); error.has_value()) return tl::unexpected{Error{error.value()}}; @@ -143,9 +145,9 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { // verification error wrong passcode etc. if(auto error = WerificationError::fromString(exceptionString); error.has_value()) return tl::unexpected{Error{error.value()}}; - - // CoreHandlerError::TransactionAccessTokenExpiredException - + + // CoreHandlerError::TransactionAccessTokenExpiredException + // other exceptions, just "throw" return tl::unexpected{Error{CoreHandlerError{CoreHandlerError::RublonCoreException}}}; } @@ -155,7 +157,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { } tl::expected< Document, Error > request(RapidJSONPMRAlloc & mr, std::string_view path, const Document & body) const { - memory::StrictMonotonic_8k_HeapResource memoryResource; + memory::Monotonic_8k_Resource memoryResource; const auto validateSignature = [&](const auto & arg) { return this->validateSignature(arg); }; const auto validateResponse = [&](const auto & arg) { return this->validateResponse(mr, arg); }; @@ -172,8 +174,8 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { signRequest(request); std::pmr::string uri{&memoryResource}; - uri.reserve(_config.apiServer.size() + path.size() + 1); - uri += _config.apiServer; + uri.reserve(config().apiServer.size() + path.size() + 1); + uri += config().apiServer; uri += path.data(); return http @@ -184,8 +186,8 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { } bool createWSConnection(std::string_view tid) const { - if(not _ws){ - _ws.reset(new WebSocket (_config)); + if(not _ws) { + _ws.reset(new WebSocket(config())); } /// TODO connect can be separated from subscribtion on event /// TODO status of attach is not checked diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 0771af1..e991f04 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -2,6 +2,7 @@ #include "rublon/memory.hpp" #include +#include #include #include #include @@ -54,16 +55,26 @@ struct Response { class CURL { std::unique_ptr< ::CURL, void (*)(::CURL *) > curl; - const Configuration & _config; + std::reference_wrapper< const Configuration > _config; + + const Configuration & conf() const noexcept { + return _config.get(); + } public: CURL(const Configuration & config) : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)}, _config{config} {} + CURL(const CURL &) = delete; + CURL(CURL &&) = delete; + + CURL & operator=(const CURL &) = delete; + CURL & operator=(CURL &&) = delete; + tl::expected< std::reference_wrapper< Response >, ConnectionError > request(std::string_view uri, const Request & request, Response & response) const { using namespace memory::literals; - memory::Monotonic_8k_HeapResource memoryResource; + memory::Monotonic_8k_Resource memoryResource; std::pmr::string response_data{&memoryResource}; response_data.reserve(4_kB); @@ -75,46 +86,45 @@ class CURL { }); // Optional: Build full proxy URL if proxy is enabled - if(_config.proxyEnabled) { + if(conf().proxyEnabled) { // configuration reader check if proxy has needed fields - assert(_config.proxyType.has_value()); - assert(_config.proxyServer.has_value()); + assert(conf().proxyType.has_value()); + assert(conf().proxyServer.has_value()); std::pmr::string proxyUrl{&memoryResource}; - proxyUrl.reserve(conservative_estimate(_config.proxyType, _config.proxyServer, _config.proxyPort) + 10); + proxyUrl.reserve(conservative_estimate(conf().proxyType, conf().proxyServer, conf().proxyPort) + 10); - if(_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || - _config.proxyType == "socks5") { - proxyUrl = *_config.proxyType; + if(conf().proxyType == "http" || conf().proxyType == "https" || conf().proxyType == "socks4" || conf().proxyType == "socks5") { + proxyUrl = *conf().proxyType; proxyUrl += "://"; - proxyUrl += *_config.proxyServer; - if(_config.proxyPort > 0) { + proxyUrl += *conf().proxyServer; + if(conf().proxyPort > 0) { proxyUrl += ":"; - proxyUrl += std::to_string(*_config.proxyPort); + proxyUrl += std::to_string(*conf().proxyPort); } curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str()); - if(_config.proxyType == "socks4") { + if(conf().proxyType == "socks4") { curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); - } else if(_config.proxyType == "socks5") { + } else if(conf().proxyType == "socks5") { curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); } else { curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } - if(_config.proxyAuthRequired) { - assert(_config.proxyUsername.has_value()); - assert(_config.proxyPass.has_value()); + if(conf().proxyAuthRequired) { + assert(conf().proxyUsername.has_value()); + assert(conf().proxyPass.has_value()); std::pmr::string proxyAuth{&memoryResource}; - proxyAuth.reserve(conservative_estimate(_config.proxyUsername, _config.proxyPass)); + proxyAuth.reserve(conservative_estimate(conf().proxyUsername, conf().proxyPass)); - proxyAuth += *_config.proxyUsername; - if(_config.proxyPass->size()) { + proxyAuth += *conf().proxyUsername; + if(conf().proxyPass->size()) { // can proxy have name but no pass? proxyAuth += ":"; - proxyAuth += *_config.proxyPass; + proxyAuth += *conf().proxyPass; } curl_easy_setopt(curl.get(), CURLOPT_PROXYUSERPWD, proxyAuth.c_str()); diff --git a/PAM/ssh/include/rublon/error.hpp b/PAM/ssh/include/rublon/error.hpp index 4d9b41a..e92bf9d 100755 --- a/PAM/ssh/include/rublon/error.hpp +++ b/PAM/ssh/include/rublon/error.hpp @@ -123,15 +123,17 @@ class MethodError { class WerificationError { public: enum ErrorClass { - BadInput, // User input has incorrect characters or length - SecurityKeyException, // code: 16 - PasscodeException, // code: 18 + BadInput, // User input has incorrect characters or length + SecurityKeyException, // code: 16 + PasscodeException, // code: 18 + SendPushException, // code: 21 | this code really should not be here as rest of the codes are strictly for passcode verification TooManyRequestsException // code: 101 }; constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "BadInput", "SecurityKeyException", "PasscodeException", + "SendPushException", "TooManyRequestsException"); constexpr static inline auto prettyName = "Werification Error"; diff --git a/PAM/ssh/include/rublon/error_handler.hpp b/PAM/ssh/include/rublon/error_handler.hpp index a65df2b..132e9d5 100644 --- a/PAM/ssh/include/rublon/error_handler.hpp +++ b/PAM/ssh/include/rublon/error_handler.hpp @@ -66,12 +66,15 @@ class ErrorHandler { case WerificationError::ErrorClass::BadInput: pam.print(R"(Ensure that the Secret Key is correct.)"); return AuthenticationStatus::Action::Denied; - case rublon::WerificationError::SecurityKeyException: + case WerificationError::SecurityKeyException: pam.print(R"(Ensure that the Secret Key is correct.)"); return AuthenticationStatus::Action::Denied; - case rublon::WerificationError::TooManyRequestsException: + case WerificationError::TooManyRequestsException: pam.print(R"(Too many attempts.)"); return AuthenticationStatus::Action::Denied; + case WerificationError::SendPushException: + pam.print(R"(Rublon failed to reach authenticator app)"); + break; } } @@ -89,7 +92,7 @@ class ErrorHandler { log(LogLevel::Error, R"(The provided version of the app is unsupported.)"); log(LogLevel::Error, R"(Try changing the app version.)"); return AuthenticationStatus::Action::Denied; - case rublon::RublonCheckApplicationException::MissingFieldException: + case RublonCheckApplicationException::MissingFieldException: log(LogLevel::Error, R"(The provided version of the app is unsupported.)"); log(LogLevel::Error, R"(Try changing the app version.)"); return AuthenticationStatus::Action::Denied; diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index 216816f..a0aef2a 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -43,7 +43,8 @@ class Init : public AuthenticationStep { } void addParams(Document & coreRequest, const Pam_t & pam) const { - memory::MonotonicStackResource< 1024 > memoryResource; + using namespace memory::literals; + memory::MonotonicStackResource< 2_kB > memoryResource; auto & alloc = coreRequest.GetAllocator(); const auto os = details::osName(&memoryResource); @@ -69,7 +70,8 @@ class Init : public AuthenticationStep { tl::expected< std::reference_wrapper< const Document >, Error > checkEnrolement(const Document & coreResponse, const Pam_t pam) const { using namespace std::string_view_literals; - memory::MonotonicStackResource< 256 > stackResource; + using namespace memory::literals; + memory::MonotonicStackResource< 1_kB > stackResource; RapidJSONPMRAlloc alloc{&stackResource}; const auto * rublonStatus = JSONPointer{"/result/status", &alloc}.Get(coreResponse); @@ -96,7 +98,7 @@ class Init : public AuthenticationStep { const char * _name = "Initialization"; Init(Session & session) : base_t(session) { - log(LogLevel::Debug, "Init"); + log(LogLevel::Debug, "Starting inicialization"); } template < typename Hander_t > diff --git a/PAM/ssh/include/rublon/json.hpp b/PAM/ssh/include/rublon/json.hpp index d158600..d566294 100644 --- a/PAM/ssh/include/rublon/json.hpp +++ b/PAM/ssh/include/rublon/json.hpp @@ -122,7 +122,7 @@ struct FileWriter { template < typename T > static void stringifyTo(const Document & body, T & to) { - memory::Monotonic_1k_HeapResource tmpResource; + memory::Monotonic_1k_Resource tmpResource; RapidJSONPMRAlloc alloc{&tmpResource}; StringWriter< T > s{to}; rapidjson::Writer< StringWriter< T >, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc}; diff --git a/PAM/ssh/include/rublon/memory.hpp b/PAM/ssh/include/rublon/memory.hpp index d6a9e7b..29960ae 100644 --- a/PAM/ssh/include/rublon/memory.hpp +++ b/PAM/ssh/include/rublon/memory.hpp @@ -4,7 +4,6 @@ namespace rublon { namespace memory { - namespace literals{ constexpr std::uint64_t operator"" _kB(unsigned long long kilobytes) { return kilobytes * 1024ULL; @@ -30,50 +29,33 @@ namespace memory { public: MonotonicStackResource() : std::pmr::monotonic_buffer_resource{_buffer, N, std::pmr::null_memory_resource()} {} }; - - class MonotonicHeapResourceBase { + + class MonotonicResourceBase { public: std::pmr::memory_resource * _upstream{}; std::size_t _size{}; void * _buffer{nullptr}; - MonotonicHeapResourceBase(std::size_t size) : _upstream{default_resource()}, _size{size}, _buffer{_upstream->allocate(size)} {} + MonotonicResourceBase(std::size_t size) : _upstream{default_resource()}, _size{size}, _buffer{_upstream->allocate(size)} {} - ~MonotonicHeapResourceBase() { + ~MonotonicResourceBase() { if(_buffer) _upstream->deallocate(_buffer, _size); } }; template < std::size_t N > - class MonotonicHeapResource : MonotonicHeapResourceBase, public std::pmr::monotonic_buffer_resource { + class MonotonicResource : MonotonicResourceBase, public std::pmr::monotonic_buffer_resource { public: - MonotonicHeapResource() - : MonotonicHeapResourceBase{N}, std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, default_resource()} {} + MonotonicResource() + : MonotonicResourceBase{N}, std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, default_resource()} {} }; - template < std::size_t N > - class StrictMonotonicHeapResource : MonotonicHeapResourceBase, public std::pmr::monotonic_buffer_resource { - public: - StrictMonotonicHeapResource() - : MonotonicHeapResourceBase{N}, - std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, std::pmr::null_memory_resource()} {} - }; - - using StrictMonotonic_512_HeapResource = StrictMonotonicHeapResource< 512 >; - using StrictMonotonic_1k_HeapResource = StrictMonotonicHeapResource< 1 * 1024 >; - using StrictMonotonic_2k_HeapResource = StrictMonotonicHeapResource< 2 * 1024 >; - using StrictMonotonic_4k_HeapResource = StrictMonotonicHeapResource< 4 * 1024 >; - using StrictMonotonic_8k_HeapResource = StrictMonotonicHeapResource< 8 * 1024 >; - using StrictMonotonic_16k_HeapResource = StrictMonotonicHeapResource< 16 * 1024 >; - using StrictMonotonic_32k_HeapResource = StrictMonotonicHeapResource< 32 * 1024 >; - - using Monotonic_1k_HeapResource = MonotonicHeapResource< 1 * 1024 >; - using Monotonic_2k_HeapResource = MonotonicHeapResource< 2 * 1024 >; - using Monotonic_4k_HeapResource = MonotonicHeapResource< 4 * 1024 >; - using Monotonic_8k_HeapResource = MonotonicHeapResource< 8 * 1024 >; - using Monotonic_16k_HeapResource = MonotonicHeapResource< 4 * 1024 >; - using Monotonic_32k_HeapResource = MonotonicHeapResource< 8 * 1024 >; + using Monotonic_1k_Resource = MonotonicResource< 1 * 1024 >; + using Monotonic_2k_Resource = MonotonicResource< 2 * 1024 >; + using Monotonic_4k_Resource = MonotonicResource< 4 * 1024 >; + using Monotonic_8k_Resource = MonotonicResource< 8 * 1024 >; + using Monotonic_16k_Resource = MonotonicResource< 16 * 1024 >; } // namespace memory // class RublonMemory { diff --git a/PAM/ssh/include/rublon/method/method_select.hpp b/PAM/ssh/include/rublon/method/method_select.hpp index 0117f33..0c65913 100644 --- a/PAM/ssh/include/rublon/method/method_select.hpp +++ b/PAM/ssh/include/rublon/method/method_select.hpp @@ -155,7 +155,7 @@ class MethodSelect { tl::expected< PostMethod, Error > create(Pam_t & pam) const { rublon::log(LogLevel::Debug, "prompting user to select method"); - memory::StrictMonotonic_2k_HeapResource memoryResource; + memory::Monotonic_2k_Resource memoryResource; std::pmr::map< int, MethodIds > methods_id{&memoryResource}; std::pmr::map< int, std::pmr::string > methods_names{&memoryResource}; diff --git a/PAM/ssh/include/rublon/method/passcode_based_auth.hpp b/PAM/ssh/include/rublon/method/passcode_based_auth.hpp index 1ba5dc9..36fdfc8 100644 --- a/PAM/ssh/include/rublon/method/passcode_based_auth.hpp +++ b/PAM/ssh/include/rublon/method/passcode_based_auth.hpp @@ -12,8 +12,8 @@ namespace rublon::method { class PasscodeBasedAuth : public AuthenticationStep { protected: - const char * uri; - const char * confirmField; + const char * _uri; + const char * _confirmField; static constexpr const char * confirmCodeEndpoint = "/api/transaction/confirmCode"; static constexpr const char * confirmSecuritySSHEndpoint = "/api/transaction/confirmSecurityKeySSH"; @@ -23,10 +23,10 @@ class PasscodeBasedAuth : public AuthenticationStep { static constexpr auto _bypassCodeLength = 9; - const char * userMessage{nullptr}; + const char * _userMessage{nullptr}; - const uint_fast8_t vericodeLength; - const bool onlyDigits; + const uint_fast8_t _vericodeLength; + const bool _onlyDigits; int _prompts; constexpr static bool isdigit(char ch) { @@ -38,17 +38,17 @@ class PasscodeBasedAuth : public AuthenticationStep { } bool hasValidLength(std::string_view userInput) const { - if(userInput.size() == vericodeLength || userInput.size() == _bypassCodeLength) { + if(userInput.size() == _vericodeLength || userInput.size() == _bypassCodeLength) { log(LogLevel::Debug, "User input size %d is correct", userInput.size()); return true; } else { - log(LogLevel::Warning, "User input size %d is different than %d", userInput.size(), vericodeLength); + log(LogLevel::Warning, "User input size %d is different than %d", userInput.size(), _vericodeLength); return false; } } bool hasValidCharacters(std::string_view userInput) const { - if(onlyDigits ? digitsOnly(userInput) : true) { + if(_onlyDigits ? digitsOnly(userInput) : true) { log(LogLevel::Debug, "User input contains valid characters"); return true; } else { @@ -58,12 +58,12 @@ class PasscodeBasedAuth : public AuthenticationStep { } tl::expected< std::reference_wrapper< Document >, Error > readPasscode(Document & body, const Pam_t & pam) const { - ///TODO assert in interactive mode + /// TODO assert in interactive mode auto & alloc = body.GetAllocator(); - auto vericode = pam.scan([](const char * userInput) { return std::string{userInput}; }, userMessage); + auto vericode = pam.scan([](const char * userInput) { return std::string{userInput}; }, _userMessage); if(hasValidLength(vericode) and hasValidCharacters(vericode)) { - Value confirmFieldValue(confirmField, alloc); + Value confirmFieldValue(_confirmField, alloc); body.AddMember(confirmFieldValue, Value{vericode.c_str(), alloc}, alloc); if(_session.hasAccessToken()) { @@ -80,7 +80,6 @@ class PasscodeBasedAuth : public AuthenticationStep { return AuthenticationStatus{AuthenticationStatus::Action::Confirmed}; } - tl::expected< AuthenticationStatus, Error > errorHandler(Error error, const Pam_t & pam, int promptLeft) const { if(promptLeft && error.is< WerificationError >()) { switch(error.get< WerificationError >().errorClass) { @@ -96,6 +95,9 @@ class PasscodeBasedAuth : public AuthenticationStep { case WerificationError::TooManyRequestsException: pam.print("Too Many Attempts. Try again after a minute"); break; + case WerificationError::SendPushException: + // if there is a communication problem we can't do anything here + break; } } return tl::unexpected{error}; @@ -116,11 +118,11 @@ class PasscodeBasedAuth : public AuthenticationStep { Endpoint endpoint, int prompts) : AuthenticationStep(session), - uri{(endpoint == Endpoint::ConfirmCode) ? confirmCodeEndpoint : confirmSecuritySSHEndpoint}, - confirmField{(endpoint == Endpoint::ConfirmCode) ? fieldVericode : fieldOtp}, - userMessage{userMessage}, - vericodeLength{length}, - onlyDigits{numbersOnly}, + _uri{(endpoint == Endpoint::ConfirmCode) ? confirmCodeEndpoint : confirmSecuritySSHEndpoint}, + _confirmField{(endpoint == Endpoint::ConfirmCode) ? fieldVericode : fieldOtp}, + _userMessage{userMessage}, + _vericodeLength{length}, + _onlyDigits{numbersOnly}, _prompts{prompts}, _name{_name} {} @@ -128,9 +130,9 @@ class PasscodeBasedAuth : public AuthenticationStep { tl::expected< AuthenticationStatus, Error > verify(const CoreHandlerInterface< Hander_t > & coreHandler, const Pam_t & pam) const { RapidJSONPMRStackAlloc< 2048 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; - int prompts = _prompts; + int prompts = _prompts; - const auto requestAuthorization = [&](const auto & body) { return coreHandler.request(alloc, uri, body); }; + const auto requestAuthorization = [&](const auto & body) { return coreHandler.request(alloc, _uri, body); }; const auto checkCodeValidity = [&](const auto & coreResponse) { return this->checkAuthenticationStatus(coreResponse, pam); }; const auto waitForCoreToConfirm = [&](const auto &) { return waitForCoreConfirmation(coreHandler); }; const auto handleError = [&](const auto error) { return errorHandler(error, pam, prompts); }; diff --git a/PAM/ssh/include/rublon/rublon.hpp b/PAM/ssh/include/rublon/rublon.hpp index d30ab1d..4374abb 100755 --- a/PAM/ssh/include/rublon/rublon.hpp +++ b/PAM/ssh/include/rublon/rublon.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/memory.hpp" #include #include @@ -11,17 +12,18 @@ namespace rublon { class RublonFactory { public: - tl::expected< Session, Error > startSession(const Pam_t & pam) { - details::initLog(); - - auto config = ConfigurationFactory{}.systemConfig(); - - if(not config.has_value()) { - pam.print("The configuration file does not exist or contains incorrect values"); + tl::expected< void, Error > initializeSession(Session & session) { + log(LogLevel::Debug, "Configuration read start"); + memory::Monotonic_16k_Resource heap; + ConfigurationReader reader{&heap, "/etc/rublon.config"}; + + if(auto ok = reader.applyTo(session.config()); not ok.has_value()) { + log(LogLevel::Warning, "Configuration contains errors"); + session.pam().print("The configuration file does not exist or contains incorrect values"); return tl::unexpected{ConfigurationError{}}; } - - return Session{pam, config.value()}; + log(LogLevel::Debug, "Configuration read success"); + return {}; } }; diff --git a/PAM/ssh/include/rublon/session.hpp b/PAM/ssh/include/rublon/session.hpp index bf22e19..c5abac3 100644 --- a/PAM/ssh/include/rublon/session.hpp +++ b/PAM/ssh/include/rublon/session.hpp @@ -1,32 +1,33 @@ #pragma once -#include "rublon/utils.hpp" +#include #include #include #include #include +#include #include #include namespace rublon { class Session { + std::pmr::memory_resource * mr; const Pam_t & _pam; - const Configuration _config; + Configuration _config; std::pmr::string _tid; std::pmr::string _accessToken; CoreHandler_t _coreHandler; /// TODO log - /// TODO momory resource public: - Session(const Pam_t & pam, const Configuration & config) - : _pam{pam}, _config{config}, _coreHandler{_config} { - log(LogLevel::Debug, __PRETTY_FUNCTION__); + Session(const Pam_t & pam) + : mr{memory::default_resource()}, _pam{pam}, _config{mr}, _coreHandler{_config} { + details::initLog(); } - Session(Session &&) noexcept = default; + Session(Session &&) noexcept = delete; Session(const Session &) = delete; Session & operator=(Session &&) noexcept = delete; @@ -38,6 +39,9 @@ class Session { const auto & pam() const { return _pam; } + auto & config() { + return _config; + } const auto & config() const { return _config; } diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 973584a..4886b2e 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -205,7 +205,7 @@ namespace details { template < typename Headers > inline void headers(std::string_view data, Headers & headers) { - memory::StrictMonotonic_4k_HeapResource stackResource; + memory::Monotonic_4k_Resource stackResource; std::pmr::string tmp{&stackResource}; std::istringstream resp{}; @@ -223,7 +223,8 @@ namespace details { } std::pmr::string hostname(std::pmr::memory_resource * mr) { - std::pmr::string hostname{2048, '\0', mr}; + // longest hostname on linux is 253 characters + std::pmr::string hostname{255, '\0', mr}; if(gethostname(hostname.data(), hostname.size()) != 0) { log(LogLevel::Warning, "Hostname is not available"); return ""; @@ -233,7 +234,7 @@ namespace details { } std::pmr::string osName(std::pmr::memory_resource * mr) { - memory::Monotonic_8k_HeapResource memoryResource; + memory::Monotonic_8k_Resource memoryResource; std::ifstream file(std::filesystem::path{"/etc/os-release"}); if(not file.good()) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index e54761e..017477f 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -78,7 +78,7 @@ class WebSocket { assert(cfg.proxyType.has_value()); assert(cfg.proxyServer.has_value()); - memory::Monotonic_8k_HeapResource memoryResource; + memory::Monotonic_8k_Resource memoryResource; std::pmr::string proxyUrl{&memoryResource}; proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyServer, cfg.proxyPort) + 10); @@ -251,7 +251,7 @@ class WebSocket { startPos = endPos + 2; auto jsonString = input.substr(startPos, input.length() - startPos - 1); - memory::Monotonic_1k_HeapResource mr; + memory::Monotonic_1k_Resource mr; RapidJSONPMRAlloc alloc{&mr}; Document dataJson{&alloc}; diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 8f3b8dc..3f29e20 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -57,17 +57,18 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu pam.print("RUBLON connector has exited with unknown code, access DENY!\n"); return PAM_MAXTRIES; }; - - auto session = rublon::RublonFactory{}.startSession(pam); - if(not session.has_value()) { + + Session session{pam}; + auto ok = rublon::RublonFactory{}.initializeSession(session); + if(not ok.has_value()) { return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } - if(!session->config().logging) { + if(!session.config().logging) { g_level = LogLevel::Warning; } - auto & CH = session.value().coreHandler(); + auto & CH = session.coreHandler(); auto selectMethod = [&](const MethodSelect & selector) { // return selector.create(pam); @@ -78,12 +79,12 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu }; auto confirmCode = [&](const MethodProxy & method) mutable { // - return method.fire(session.value(), CH, pam); + return method.fire(session, CH, pam); }; auto finalizeTransaction = [&](const AuthenticationStatus & status) mutable -> tl::expected< AuthenticationStatus, Error > { if(status.userAuthorized()) { - Finish{session.value(), status.accessToken()}.handle(CH); + Finish{session, status.accessToken()}.handle(CH); } return status; }; @@ -93,12 +94,12 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu }; auto mapError = [&](const Error & error) -> tl::expected< int, Error > { - return printAuthMessageAndExit(rublon::ErrorHandler{pam, session->config()}.printErrorDetails(error)); + return printAuthMessageAndExit(rublon::ErrorHandler{pam, session.config()}.printErrorDetails(error)); }; { CheckApplication ca; - const auto & config = session.value().config(); + const auto & config = session.config(); const auto ret = ca.call(CH, config.systemToken).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); @@ -106,7 +107,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu } } - auto ret = Init{session.value()} + auto ret = Init{session} .handle(CH, pam) // .and_then(selectMethod) .and_then(confirmMethod)